smartxml: Document.transaction success callback
[fnpeditor.git] / src / editor / modules / data / data.js
1 define([
2     'libs/jquery',
3     'views/dialog/dialog',
4     'wlxml/wlxml',
5     'wlxml/extensions/list/list',
6     'fnpjs/logging/logging',
7     'fnpjs/datetime'
8 ], function($, Dialog, wlxml, listExtension, logging, datetime) {
9
10 'use strict';
11 /* global gettext, alert, window */
12
13 var logger = logging.getLogger('editor.modules.data'),
14     stubDocument = '<section><div>' + gettext('This is an empty document.') + '</div></section>';
15
16
17 return function(sandbox) {
18
19     var document_id = sandbox.getBootstrappedData().document_id;
20     var history = sandbox.getBootstrappedData().history;
21     var documentDirty = false;
22     var draftDirty = false;
23
24     var documentProperties = {};
25     var data = sandbox.getBootstrappedData();
26     Object.keys(data)
27         .filter(function(key) {
28             return key !== 'history' && key !== 'document';
29         })
30         .forEach(function(key) {
31             documentProperties[key] = data[key];
32         });
33
34     var wlxmlDocument, text;
35
36     var loadDocument = function(text, isDraft, draftTimestamp) {
37         logger.debug('loading document');
38         try {
39             wlxmlDocument = wlxml.WLXMLDocumentFromXML(text);
40         } catch(e) {
41             logger.exception(e);
42             alert(gettext('This document contains errors and can\'t be loaded. :(')); // TODO
43             wlxmlDocument = wlxml.WLXMLDocumentFromXML(stubDocument);
44         }
45
46         wlxmlDocument.registerExtension(listExtension);
47         sandbox.getPlugins().forEach(function(plugin) {
48             if(plugin.documentExtension) {
49                 wlxmlDocument.registerExtension(plugin.documentExtension);
50             }
51         });
52         
53         var modificationFlag = true;
54         var handleChange = function() {
55             documentDirty = true;
56             draftDirty = true;
57             modificationFlag = true;
58         };
59         wlxmlDocument.on('change', handleChange);
60         wlxmlDocument.on('contentSet', handleChange);
61
62         if(window.localStorage) {
63             window.setInterval(function() {
64                 if(modificationFlag) {
65                     modificationFlag = false;
66                     return;
67                 }
68                 if(wlxmlDocument && documentDirty && draftDirty) {
69                     var timestamp = datetime.currentStrfmt();
70                     logger.debug('Saving draft to local storage.');
71                     sandbox.publish('savingStarted', 'local');
72                     window.localStorage.setItem(getLocalStorageKey().content, wlxmlDocument.toXML());
73                     window.localStorage.setItem(getLocalStorageKey().contentTimestamp, timestamp);
74                     sandbox.publish('savingEnded', 'success', 'local', {timestamp: timestamp});
75                     draftDirty = false;
76                 }
77             }, sandbox.getConfig().autoSaveInterval || 2500);
78         }
79         sandbox.publish('ready', isDraft, draftTimestamp);
80     };
81     
82     function readCookie(name) {
83         /* global escape, unescape, document */
84         var nameEQ = escape(name) + '=';
85         var ca = document.cookie.split(';');
86         for (var i = 0; i < ca.length; i++) {
87             var c = ca[i];
88             while (c.charAt(0) === ' ') {
89                 c = c.substring(1, c.length);
90             }
91             if (c.indexOf(nameEQ) === 0) {
92                 return unescape(c.substring(nameEQ.length, c.length));
93             }
94         }
95         return null;
96     }
97     
98     $.ajaxSetup({
99         crossDomain: false,
100         beforeSend: function(xhr, settings) {
101             if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type))) {
102                 xhr.setRequestHeader('X-CSRFToken', readCookie('csrftoken'));
103             }
104         }
105     });
106     
107     var reloadHistory = function() {
108         $.ajax({
109             method: 'get',
110             url: sandbox.getConfig().documentHistoryUrl(document_id),
111             success: function(data) {
112                 history = data;
113                 sandbox.publish('historyItemAdded', data.slice(-1)[0]);
114             },
115         });
116     };
117
118     var getLocalStorageKey = function() {
119         var base = 'draft-id:' + document_id + '-ver:' + documentProperties.version;
120         return {
121             content: base,
122             contentTimestamp: base + '-content-timestamp'
123         };
124     };
125
126    
127     return {
128         start: function() {
129             if(window.localStorage) {
130                 text = window.localStorage.getItem(getLocalStorageKey().content);
131
132                 var timestamp = window.localStorage.getItem(getLocalStorageKey().contentTimestamp),
133                     usingDraft;
134                 if(text) {
135                     logger.debug('Local draft exists');
136                     var dialog = Dialog.create({
137                         title: gettext('Local draft of a document exists'),
138                         text: gettext('Unsaved local draft of this version of the document exists in your browser. Do you want to load it instead?'),
139                         executeButtonText: gettext('Yes, restore local draft'),
140                         cancelButtonText: gettext('No, use version loaded from the server')
141                     });
142                     dialog.on('cancel', function() {
143                         logger.debug('Bootstrapped version chosen');
144                         usingDraft = false;
145                         text = sandbox.getBootstrappedData().document;
146                         
147                     });
148                     dialog.on('execute', function(event) {
149                         logger.debug('Local draft chosen');
150                         usingDraft = true;
151                         event.success();
152                     });
153                     dialog.show();
154                     dialog.on('close', function() {
155                         loadDocument(text, usingDraft, timestamp);
156                     });
157                 } else {
158                     loadDocument(sandbox.getBootstrappedData().document, false);
159                 }
160             } else {
161                 loadDocument(sandbox.getBootstrappedData().document, false);
162             }
163         },
164         getDocument: function() {
165             return wlxmlDocument;
166         },
167         saveDocument: function() {
168             var documentSaveForm = $.extend({
169                         fields: [],
170                         content_field_name: 'text',
171                         version_field_name: 'version'
172                     },
173                     sandbox.getConfig().documentSaveForm
174                 ),
175                 dialog = Dialog.create({
176                     fields: documentSaveForm.fields,
177                     title: gettext('Save Document'),
178                     executeButtonText: gettext('Save'),
179                     cancelButtonText: gettext('Cancel')
180                 });
181             
182             dialog.on('execute', function(event) {
183                 sandbox.publish('savingStarted', 'remote');
184
185                 var formData = event.formData;
186                 formData[documentSaveForm.content_field_name] = wlxmlDocument.toXML();
187                 formData[documentSaveForm.version_field_name] = documentProperties.version;
188                 if(sandbox.getConfig().jsonifySentData) {
189                     formData = JSON.stringify(formData);
190                 }
191
192                 dialog.toggleButtons(false);
193                 $.ajax({
194                     method: 'post',
195                     url: sandbox.getConfig().documentSaveUrl(document_id),
196                     data: formData,
197                     success: function(data) {
198                         event.success();
199                         sandbox.publish('savingEnded', 'success', 'remote', data);
200
201                         Object.keys(data)
202                             .filter(function(key) {
203                                 return key !== 'text';
204                             })
205                             .forEach(function(key) {
206                                 documentProperties[key] = data[key];
207                             });
208
209                         reloadHistory();
210                     },
211                     error: function() {event.error(); sandbox.publish('savingEnded', 'error', 'remote');}
212                 });
213             });
214             dialog.on('cancel', function() {
215             });
216             dialog.show();
217             
218
219         },
220         getHistory: function() {
221             return history;
222         },
223         fetchDiff: function(ver1, ver2) {
224             $.ajax({
225                 method: 'get',
226                 url: sandbox.getConfig().documentDiffUrl(document_id),
227                 data: {from: ver1, to: ver2},
228                 success: function(data) {
229                     sandbox.publish('diffFetched', {table: data, ver1: ver1, ver2: ver2});
230                 },
231             });
232         },
233         restoreVersion: function(version) {
234             var documentRestoreForm = $.extend({
235                         fields: [],
236                         version_field_name: 'version'
237                     },
238                     sandbox.getConfig().documentRestoreForm
239                 ),
240                 dialog = Dialog.create({
241                     fields: documentRestoreForm.fields,
242                     title: gettext('Restore Version'),
243                     executeButtonText: gettext('Restore'),
244                     cancelButtonText: gettext('Cancel')
245                 });
246
247             dialog.on('execute', function(event) {
248                 var formData = event.formData;
249                 formData[documentRestoreForm.version_field_name] = version;
250                 sandbox.publish('restoringStarted', {version: version});
251                 if(sandbox.getConfig().jsonifySentData) {
252                     formData = JSON.stringify(formData);
253                 }
254                 $.ajax({
255                     method: 'post',
256                     dataType: 'json',
257                     url: sandbox.getConfig().documentRestoreUrl(document_id),
258                     data: formData,
259                     success: function(data) {
260                         Object.keys(data)
261                             .filter(function(key) {
262                                 return key !== 'document';
263                             })
264                             .forEach(function(key) {
265                                 documentProperties = data[key];
266                             });
267                         reloadHistory();
268                         wlxmlDocument.loadXML(data.document);
269                         documentDirty = false;
270                         sandbox.publish('documentReverted', data.version);
271                         event.success();
272                     },
273                 });
274             });
275             dialog.show();
276         },
277         dropDraft: function() {
278             logger.debug('Dropping a draft...');
279             wlxmlDocument.loadXML(sandbox.getBootstrappedData().document);
280             draftDirty = false;
281             logger.debug('Draft dropped');
282             sandbox.publish('draftDropped');
283         },
284         getDocumentId: function() {
285             return document_id;
286         },
287         getDocumentProperties: function() {
288             return documentProperties;
289         }
290     };
291 };
292
293 });