##// END OF EJS Templates
checkpoint
Min RK -
Show More
@@ -1,260 +1,260 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 'base/js/dialog',
9 9 'base/js/keyboard',
10 10 'moment',
11 11 ], function(IPython, $, utils, dialog, keyboard, moment) {
12 12 "use strict";
13 13
14 14 var SaveWidget = function (selector, options) {
15 15 /**
16 16 * TODO: Remove circular ref.
17 17 */
18 18 this.notebook = undefined;
19 19 this.selector = selector;
20 20 this.events = options.events;
21 21 this._checkpoint_date = undefined;
22 22 this.keyboard_manager = options.keyboard_manager;
23 23 if (this.selector !== undefined) {
24 24 this.element = $(selector);
25 25 this.bind_events();
26 26 }
27 27 };
28 28
29 29
30 30 SaveWidget.prototype.bind_events = function () {
31 31 var that = this;
32 32 this.element.find('span#notebook_name').click(function () {
33 33 that.rename_notebook({notebook: that.notebook});
34 34 });
35 35 this.events.on('notebook_loaded.Notebook', function () {
36 36 that.update_notebook_name();
37 37 that.update_document_title();
38 38 });
39 39 this.events.on('notebook_saved.Notebook', function () {
40 40 that.update_notebook_name();
41 41 that.update_document_title();
42 42 });
43 43 this.events.on('notebook_renamed.Notebook', function () {
44 44 that.update_notebook_name();
45 45 that.update_document_title();
46 46 that.update_address_bar();
47 47 });
48 48 this.events.on('notebook_save_failed.Notebook', function () {
49 49 that.set_save_status('Autosave Failed!');
50 50 });
51 51 this.events.on('notebook_read_only.Notebook', function () {
52 52 that.set_save_status('(read only)');
53 53 // disable future set_save_status
54 54 that.set_save_status = function () {};
55 55 });
56 56 this.events.on('checkpoints_listed.Notebook', function (event, data) {
57 57 that._set_last_checkpoint(data[0]);
58 58 });
59 59
60 60 this.events.on('checkpoint_created.Notebook', function (event, data) {
61 61 that._set_last_checkpoint(data);
62 62 });
63 63 this.events.on('set_dirty.Notebook', function (event, data) {
64 64 that.set_autosaved(data.value);
65 65 });
66 66 };
67 67
68 68
69 69 SaveWidget.prototype.rename_notebook = function (options) {
70 70 options = options || {};
71 71 var that = this;
72 72 var dialog_body = $('<div/>').append(
73 73 $("<p/>").addClass("rename-message")
74 74 .text('Enter a new notebook name:')
75 75 ).append(
76 76 $("<br/>")
77 77 ).append(
78 78 $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
79 79 .val(options.notebook.get_notebook_name())
80 80 );
81 81 var d = dialog.modal({
82 82 title: "Rename Notebook",
83 83 body: dialog_body,
84 84 notebook: options.notebook,
85 85 keyboard_manager: this.keyboard_manager,
86 86 buttons : {
87 87 "OK": {
88 88 class: "btn-primary",
89 89 click: function () {
90 90 var new_name = d.find('input').val();
91 91 if (!options.notebook.test_notebook_name(new_name)) {
92 92 d.find('.rename-message').text(
93 93 "Invalid notebook name. Notebook names must "+
94 94 "have 1 or more characters and can contain any characters " +
95 95 "except :/\\. Please enter a new notebook name:"
96 96 );
97 97 return false;
98 98 } else {
99 99 d.find('.rename-message').text("Renaming...");
100 100 d.find('input[type="text"]').prop('disabled', true);
101 101 that.notebook.rename(new_name).then(
102 102 function () {
103 103 d.modal('hide');
104 104 }, function (error) {
105 105 d.find('.rename-message').text(error.message || 'Unknown error');
106 106 d.find('input[type="text"]').prop('disabled', false).focus().select();
107 107 }
108 108 );
109 109 return false;
110 110 }
111 111 }
112 112 },
113 113 "Cancel": {}
114 114 },
115 115 open : function () {
116 116 /**
117 117 * Upon ENTER, click the OK button.
118 118 */
119 119 d.find('input[type="text"]').keydown(function (event) {
120 120 if (event.which === keyboard.keycodes.enter) {
121 121 d.find('.btn-primary').first().click();
122 122 return false;
123 123 }
124 124 });
125 125 d.find('input[type="text"]').focus().select();
126 126 }
127 127 });
128 128 };
129 129
130 130
131 131 SaveWidget.prototype.update_notebook_name = function () {
132 132 var nbname = this.notebook.get_notebook_name();
133 133 this.element.find('span#notebook_name').text(nbname);
134 134 };
135 135
136 136
137 137 SaveWidget.prototype.update_document_title = function () {
138 138 var nbname = this.notebook.get_notebook_name();
139 139 document.title = nbname;
140 140 };
141 141
142 142 SaveWidget.prototype.update_address_bar = function(){
143 143 var base_url = this.notebook.base_url;
144 144 var path = this.notebook.notebook_path;
145 145 var state = {path : path};
146 146 window.history.replaceState(state, "", utils.url_join_encode(
147 147 base_url,
148 148 "notebooks",
149 149 path)
150 150 );
151 151 };
152 152
153 153
154 154 SaveWidget.prototype.set_save_status = function (msg) {
155 155 this.element.find('span#autosave_status').text(msg);
156 156 };
157 157
158 158 SaveWidget.prototype._set_checkpoint_status = function (human_date, iso_date) {
159 159 var el = this.element.find('span#checkpoint_status');
160 160 if(human_date){
161 161 el.text("Last Checkpoint: "+human_date).attr('title',iso_date);
162 162 } else {
163 163 el.text('').attr('title', 'no-checkpoint');
164 164 }
165 165 };
166 166
167 167 // compute (roughly) the remaining time in millisecond until the next
168 168 // moment.js relative time update of the string, which by default
169 169 // happend at
170 170 // (a few seconds ago)
171 171 // - 45sec,
172 172 // (a minute ago)
173 173 // - 90sec,
174 174 // ( x minutes ago)
175 175 // - then every minutes until
176 176 // - 45 min,
177 177 // (an hour ago)
178 178 // - 1h45,
179 179 // (x hours ago )
180 180 // - then every hours
181 181 // - 22 hours ago
182 182 var _next_timeago_update = function(deltatime_ms){
183 183 var s = 1000;
184 184 var m = 60*s;
185 185 var h = 60*m;
186 186
187 187 var mtt = moment.relativeTimeThreshold;
188 188
189 189 if(deltatime_ms < mtt.s*s){
190 190 return mtt.s*s-deltatime_ms;
191 191 } else if (deltatime_ms < (mtt.s*s+m)) {
192 192 return (mtt.s*s+m)-deltatime_ms;
193 193 } else if (deltatime_ms < mtt.m*m){
194 194 return m;
195 195 } else if (deltatime_ms < (mtt.m*m+h)){
196 196 return (mtt.m*m+h)-deltatime_ms;
197 197 } else {
198 198 return h;
199 199 }
200 200 };
201 201
202 202 SaveWidget.prototype._regularly_update_checkpoint_date = function(){
203 203 if (!this._checkpoint_date) {
204 204 this._set_checkpoint_status(null);
205 205 console.log('no checkpoint done');
206 206 return;
207 207 }
208 208 var chkd = moment(this._checkpoint_date);
209 209 var longdate = chkd.format('llll');
210 210
211 211 var that = this;
212 212 var recall = function(t){
213 213 /**
214 214 * recall slightly later (1s) as long timeout in js might be imprecise,
215 215 * and you want to be call **after** the change of formatting should happend.
216 216 */
217 217 return setTimeout(
218 218 $.proxy(that._regularly_update_checkpoint_date, that),
219 219 t + 1000
220 220 );
221 221 };
222 222 var tdelta = Math.ceil(new Date()-this._checkpoint_date);
223 223
224 224 // update regularly for the first 6hours and show
225 225 // <x time> ago
226 if(tdelta < tdelta < 6*3600*1000){
226 if(tdelta < 6*3600*1000){
227 227 recall(_next_timeago_update(tdelta));
228 228 this._set_checkpoint_status(chkd.fromNow(), longdate);
229 229 // otherwise update every hour and show
230 230 // <Today | yesterday|...> at hh,mm,ss
231 231 } else {
232 232 recall(1*3600*1000);
233 233 this._set_checkpoint_status(chkd.calendar(), longdate);
234 234 }
235 235 };
236 236
237 237 SaveWidget.prototype._set_last_checkpoint = function (checkpoint) {
238 238 if (checkpoint) {
239 239 this._checkpoint_date = new Date(checkpoint.last_modified);
240 240 } else {
241 241 this._checkpoint_date = null;
242 242 }
243 243 this._regularly_update_checkpoint_date();
244 244
245 245 };
246 246
247 247 SaveWidget.prototype.set_autosaved = function (dirty) {
248 248 if (dirty) {
249 249 this.set_save_status("(unsaved changes)");
250 250 } else {
251 251 this.set_save_status("(autosaved)");
252 252 }
253 253 };
254 254
255 255 // Backwards compatibility.
256 256 IPython.SaveWidget = SaveWidget;
257 257
258 258 return {'SaveWidget': SaveWidget};
259 259
260 260 });
General Comments 0
You need to be logged in to leave comments. Login now