4
4
define ( [
5
5
'jquery' ,
6
6
'base/js/utils' ,
7
+ 'base/js/i18n' ,
8
+ 'base/js/dialog' ,
7
9
'codemirror/lib/codemirror' ,
8
10
'codemirror/mode/meta' ,
9
11
'codemirror/addon/comment/comment' ,
@@ -19,6 +21,8 @@ define([
19
21
function (
20
22
$ ,
21
23
utils ,
24
+ i18n ,
25
+ dialog ,
22
26
CodeMirror
23
27
) {
24
28
"use strict" ;
@@ -33,6 +37,8 @@ function(
33
37
this . file_path = options . file_path ;
34
38
this . config = options . config ;
35
39
this . file_extension_modes = options . file_extension_modes || { } ;
40
+ this . last_modified = null ;
41
+ this . _changed_on_disk_dialog = null ;
36
42
37
43
this . codemirror = new CodeMirror ( $ ( this . selector ) [ 0 ] ) ;
38
44
this . codemirror . on ( 'changes' , function ( cm , changes ) {
@@ -106,6 +112,7 @@ function(
106
112
that . generation = cm . changeGeneration ( ) ;
107
113
that . events . trigger ( "file_loaded.Editor" , model ) ;
108
114
that . _clean_state ( ) ;
115
+ that . last_modified = new Date ( model . last_modified ) ;
109
116
} ) . catch (
110
117
function ( error ) {
111
118
that . events . trigger ( "file_load_failed.Editor" , error ) ;
@@ -197,6 +204,11 @@ function(
197
204
}
198
205
} ;
199
206
207
+ /**
208
+ * Rename the file.
209
+ * @param {string } new_name
210
+ * @return {Promise } promise that resolves when the file is renamed.
211
+ */
200
212
Editor . prototype . rename = function ( new_name ) {
201
213
/** rename the file */
202
214
var that = this ;
@@ -206,32 +218,111 @@ function(
206
218
function ( model ) {
207
219
that . file_path = model . path ;
208
220
that . events . trigger ( 'file_renamed.Editor' , model ) ;
221
+ that . last_modified = new Date ( model . last_modified ) ;
209
222
that . _set_mode_for_model ( model ) ;
210
223
that . _clean_state ( ) ;
211
224
}
212
225
) ;
213
226
} ;
214
227
215
- Editor . prototype . save = function ( ) {
228
+
229
+ /**
230
+ * Save this file on the server.
231
+ *
232
+ * @param {boolean } check_last_modified - checks if file has been modified on disk
233
+ * @return {Promise } - promise that resolves when the notebook is saved.
234
+ */
235
+ Editor . prototype . save = function ( check_last_modified ) {
216
236
/** save the file */
217
237
if ( ! this . save_enabled ) {
218
238
console . log ( "Not saving, save disabled" ) ;
219
239
return ;
220
240
}
241
+
242
+ // used to check for last modified saves
243
+ if ( check_last_modified === undefined ) {
244
+ check_last_modified = true ;
245
+ }
246
+
221
247
var model = {
222
248
path : this . file_path ,
223
249
type : 'file' ,
224
250
format : 'text' ,
225
251
content : this . codemirror . getValue ( ) ,
226
252
} ;
227
253
var that = this ;
228
- // record change generation for isClean
229
- this . generation = this . codemirror . changeGeneration ( ) ;
230
- that . events . trigger ( "file_saving.Editor" ) ;
231
- return this . contents . save ( this . file_path , model ) . then ( function ( data ) {
232
- that . events . trigger ( "file_saved.Editor" , data ) ;
233
- that . _clean_state ( ) ;
234
- } ) ;
254
+
255
+ var _save = function ( ) {
256
+ that . events . trigger ( "file_saving.Editor" ) ;
257
+ return that . contents . save ( that . file_path , model ) . then ( function ( data ) {
258
+ // record change generation for isClean
259
+ that . generation = that . codemirror . changeGeneration ( ) ;
260
+ that . events . trigger ( "file_saved.Editor" , data ) ;
261
+ that . last_modified = new Date ( data . last_modified ) ;
262
+ that . _clean_state ( ) ;
263
+ } ) ;
264
+ } ;
265
+
266
+ /*
267
+ * Gets the current working file, and checks if the file has been modified on disk. If so, it
268
+ * creates & opens a modal that issues the user a warning and prompts them to overwrite the file.
269
+ *
270
+ * If it can't get the working file, it builds a new file and saves.
271
+ */
272
+ if ( check_last_modified ) {
273
+ return this . contents . get ( that . file_path , { content : false } ) . then (
274
+ function check_if_modified ( data ) {
275
+ var last_modified = new Date ( data . last_modified ) ;
276
+ // We want to check last_modified (disk) > that.last_modified (our last save)
277
+ // In some cases the filesystem reports an inconsistent time,
278
+ // so we allow 0.5 seconds difference before complaining.
279
+ if ( ( last_modified . getTime ( ) - that . last_modified . getTime ( ) ) > 500 ) { // 500 ms
280
+ console . warn ( "Last saving was done on `" + that . last_modified + "`(" + that . _last_modified + "), " +
281
+ "while the current file seem to have been saved on `" + data . last_modified + "`" ) ;
282
+ if ( that . _changed_on_disk_dialog !== null ) {
283
+ // since the modal's event bindings are removed when destroyed, we reinstate
284
+ // save & reload callbacks on the confirmation & reload buttons
285
+ that . _changed_on_disk_dialog . find ( '.save-confirm-btn' ) . click ( _save ) ;
286
+ that . _changed_on_disk_dialog . find ( '.btn-warning' ) . click ( function ( ) { window . location . reload ( ) } ) ;
287
+
288
+ // redisplay existing dialog
289
+ that . _changed_on_disk_dialog . modal ( 'show' ) ;
290
+ } else {
291
+ // create new dialog
292
+ that . _changed_on_disk_dialog = dialog . modal ( {
293
+ keyboard_manager : that . keyboard_manager ,
294
+ title : i18n . msg . _ ( "File changed" ) ,
295
+ body : i18n . msg . _ ( "The file has changed on disk since the last time we opened or saved it. "
296
+ + "Do you want to overwrite the file on disk with the version open here, or load "
297
+ + "the version on disk (reload the page)?" ) ,
298
+ buttons : {
299
+ Reload : {
300
+ class : 'btn-warning' ,
301
+ click : function ( ) {
302
+ window . location . reload ( ) ;
303
+ }
304
+ } ,
305
+ Cancel : { } ,
306
+ Overwrite : {
307
+ class : 'btn-danger save-confirm-btn' ,
308
+ click : function ( ) {
309
+ _save ( ) ;
310
+ }
311
+ } ,
312
+ }
313
+ } ) ;
314
+ }
315
+ } else {
316
+ return _save ( ) ;
317
+ }
318
+ } , function ( error ) {
319
+ console . log ( error ) ;
320
+ // maybe it has been deleted or renamed? Go ahead and save.
321
+ return _save ( ) ;
322
+ } )
323
+ } else {
324
+ return _save ( ) ;
325
+ }
235
326
} ;
236
327
237
328
Editor . prototype . _clean_state = function ( ) {
0 commit comments