##// END OF EJS Templates
use momentjs for nice dates
Matthias BUSSONNIER -
Show More
@@ -1,1 +1,1 b''
1 Subproject commit a80ac7a2f6d045e3903d3c9e189a10cc96255b05
1 Subproject commit b3909af1b61ca7a412481759fdb441ecdfb3ab66
@@ -1,351 +1,352 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 'notebook/js/tour',
8 'notebook/js/tour',
9 'bootstrap',
9 'bootstrap',
10 ], function(IPython, $, utils, tour) {
10 'moment',
11 ], function(IPython, $, utils, tour, bootstrap, moment) {
11 "use strict";
12 "use strict";
12
13
13 var MenuBar = function (selector, options) {
14 var MenuBar = function (selector, options) {
14 // Constructor
15 // Constructor
15 //
16 //
16 // A MenuBar Class to generate the menubar of IPython notebook
17 // A MenuBar Class to generate the menubar of IPython notebook
17 //
18 //
18 // Parameters:
19 // Parameters:
19 // selector: string
20 // selector: string
20 // options: dictionary
21 // options: dictionary
21 // Dictionary of keyword arguments.
22 // Dictionary of keyword arguments.
22 // notebook: Notebook instance
23 // notebook: Notebook instance
23 // layout_manager: LayoutManager instance
24 // layout_manager: LayoutManager instance
24 // events: $(Events) instance
25 // events: $(Events) instance
25 // save_widget: SaveWidget instance
26 // save_widget: SaveWidget instance
26 // quick_help: QuickHelp instance
27 // quick_help: QuickHelp instance
27 // base_url : string
28 // base_url : string
28 // notebook_path : string
29 // notebook_path : string
29 // notebook_name : string
30 // notebook_name : string
30 options = options || {};
31 options = options || {};
31 this.base_url = options.base_url || utils.get_body_data("baseUrl");
32 this.base_url = options.base_url || utils.get_body_data("baseUrl");
32 this.selector = selector;
33 this.selector = selector;
33 this.notebook = options.notebook;
34 this.notebook = options.notebook;
34 this.layout_manager = options.layout_manager;
35 this.layout_manager = options.layout_manager;
35 this.events = options.events;
36 this.events = options.events;
36 this.save_widget = options.save_widget;
37 this.save_widget = options.save_widget;
37 this.quick_help = options.quick_help;
38 this.quick_help = options.quick_help;
38
39
39 try {
40 try {
40 this.tour = new tour.Tour(this.notebook, this.events);
41 this.tour = new tour.Tour(this.notebook, this.events);
41 } catch (e) {
42 } catch (e) {
42 this.tour = undefined;
43 this.tour = undefined;
43 console.log("Failed to instantiate Notebook Tour", e);
44 console.log("Failed to instantiate Notebook Tour", e);
44 }
45 }
45
46
46 if (this.selector !== undefined) {
47 if (this.selector !== undefined) {
47 this.element = $(selector);
48 this.element = $(selector);
48 this.style();
49 this.style();
49 this.bind_events();
50 this.bind_events();
50 }
51 }
51 };
52 };
52
53
53 // TODO: This has definitively nothing to do with style ...
54 // TODO: This has definitively nothing to do with style ...
54 MenuBar.prototype.style = function () {
55 MenuBar.prototype.style = function () {
55 var that = this;
56 var that = this;
56 this.element.find("li").click(function (event, ui) {
57 this.element.find("li").click(function (event, ui) {
57 // The selected cell loses focus when the menu is entered, so we
58 // The selected cell loses focus when the menu is entered, so we
58 // re-select it upon selection.
59 // re-select it upon selection.
59 var i = that.notebook.get_selected_index();
60 var i = that.notebook.get_selected_index();
60 that.notebook.select(i);
61 that.notebook.select(i);
61 }
62 }
62 );
63 );
63 };
64 };
64
65
65 MenuBar.prototype._nbconvert = function (format, download) {
66 MenuBar.prototype._nbconvert = function (format, download) {
66 download = download || false;
67 download = download || false;
67 var notebook_path = this.notebook.notebook_path;
68 var notebook_path = this.notebook.notebook_path;
68 var notebook_name = this.notebook.notebook_name;
69 var notebook_name = this.notebook.notebook_name;
69 if (this.notebook.dirty) {
70 if (this.notebook.dirty) {
70 this.notebook.save_notebook({async : false});
71 this.notebook.save_notebook({async : false});
71 }
72 }
72 var url = utils.url_join_encode(
73 var url = utils.url_join_encode(
73 this.base_url,
74 this.base_url,
74 'nbconvert',
75 'nbconvert',
75 format,
76 format,
76 notebook_path,
77 notebook_path,
77 notebook_name
78 notebook_name
78 ) + "?download=" + download.toString();
79 ) + "?download=" + download.toString();
79
80
80 window.open(url);
81 window.open(url);
81 };
82 };
82
83
83 MenuBar.prototype.bind_events = function () {
84 MenuBar.prototype.bind_events = function () {
84 // File
85 // File
85 var that = this;
86 var that = this;
86 this.element.find('#new_notebook').click(function () {
87 this.element.find('#new_notebook').click(function () {
87 that.notebook.new_notebook();
88 that.notebook.new_notebook();
88 });
89 });
89 this.element.find('#open_notebook').click(function () {
90 this.element.find('#open_notebook').click(function () {
90 window.open(utils.url_join_encode(
91 window.open(utils.url_join_encode(
91 that.notebook.base_url,
92 that.notebook.base_url,
92 'tree',
93 'tree',
93 that.notebook.notebook_path
94 that.notebook.notebook_path
94 ));
95 ));
95 });
96 });
96 this.element.find('#copy_notebook').click(function () {
97 this.element.find('#copy_notebook').click(function () {
97 that.notebook.copy_notebook();
98 that.notebook.copy_notebook();
98 return false;
99 return false;
99 });
100 });
100 this.element.find('#download_ipynb').click(function () {
101 this.element.find('#download_ipynb').click(function () {
101 var base_url = that.notebook.base_url;
102 var base_url = that.notebook.base_url;
102 var notebook_path = that.notebook.notebook_path;
103 var notebook_path = that.notebook.notebook_path;
103 var notebook_name = that.notebook.notebook_name;
104 var notebook_name = that.notebook.notebook_name;
104 if (that.notebook.dirty) {
105 if (that.notebook.dirty) {
105 that.notebook.save_notebook({async : false});
106 that.notebook.save_notebook({async : false});
106 }
107 }
107
108
108 var url = utils.url_join_encode(
109 var url = utils.url_join_encode(
109 base_url,
110 base_url,
110 'files',
111 'files',
111 notebook_path,
112 notebook_path,
112 notebook_name
113 notebook_name
113 );
114 );
114 window.location.assign(url);
115 window.location.assign(url);
115 });
116 });
116
117
117 this.element.find('#print_preview').click(function () {
118 this.element.find('#print_preview').click(function () {
118 that._nbconvert('html', false);
119 that._nbconvert('html', false);
119 });
120 });
120
121
121 this.element.find('#download_py').click(function () {
122 this.element.find('#download_py').click(function () {
122 that._nbconvert('python', true);
123 that._nbconvert('python', true);
123 });
124 });
124
125
125 this.element.find('#download_html').click(function () {
126 this.element.find('#download_html').click(function () {
126 that._nbconvert('html', true);
127 that._nbconvert('html', true);
127 });
128 });
128
129
129 this.element.find('#download_rst').click(function () {
130 this.element.find('#download_rst').click(function () {
130 that._nbconvert('rst', true);
131 that._nbconvert('rst', true);
131 });
132 });
132
133
133 this.element.find('#download_pdf').click(function () {
134 this.element.find('#download_pdf').click(function () {
134 that._nbconvert('pdf', true);
135 that._nbconvert('pdf', true);
135 });
136 });
136
137
137 this.element.find('#rename_notebook').click(function () {
138 this.element.find('#rename_notebook').click(function () {
138 that.save_widget.rename_notebook({notebook: that.notebook});
139 that.save_widget.rename_notebook({notebook: that.notebook});
139 });
140 });
140 this.element.find('#save_checkpoint').click(function () {
141 this.element.find('#save_checkpoint').click(function () {
141 that.notebook.save_checkpoint();
142 that.notebook.save_checkpoint();
142 });
143 });
143 this.element.find('#restore_checkpoint').click(function () {
144 this.element.find('#restore_checkpoint').click(function () {
144 });
145 });
145 this.element.find('#trust_notebook').click(function () {
146 this.element.find('#trust_notebook').click(function () {
146 that.notebook.trust_notebook();
147 that.notebook.trust_notebook();
147 });
148 });
148 this.events.on('trust_changed.Notebook', function (event, trusted) {
149 this.events.on('trust_changed.Notebook', function (event, trusted) {
149 if (trusted) {
150 if (trusted) {
150 that.element.find('#trust_notebook')
151 that.element.find('#trust_notebook')
151 .addClass("disabled")
152 .addClass("disabled")
152 .find("a").text("Trusted Notebook");
153 .find("a").text("Trusted Notebook");
153 } else {
154 } else {
154 that.element.find('#trust_notebook')
155 that.element.find('#trust_notebook')
155 .removeClass("disabled")
156 .removeClass("disabled")
156 .find("a").text("Trust Notebook");
157 .find("a").text("Trust Notebook");
157 }
158 }
158 });
159 });
159 this.element.find('#kill_and_exit').click(function () {
160 this.element.find('#kill_and_exit').click(function () {
160 that.notebook.session.delete();
161 that.notebook.session.delete();
161 setTimeout(function(){
162 setTimeout(function(){
162 // allow closing of new tabs in Chromium, impossible in FF
163 // allow closing of new tabs in Chromium, impossible in FF
163 window.open('', '_self', '');
164 window.open('', '_self', '');
164 window.close();
165 window.close();
165 }, 500);
166 }, 500);
166 });
167 });
167 // Edit
168 // Edit
168 this.element.find('#cut_cell').click(function () {
169 this.element.find('#cut_cell').click(function () {
169 that.notebook.cut_cell();
170 that.notebook.cut_cell();
170 });
171 });
171 this.element.find('#copy_cell').click(function () {
172 this.element.find('#copy_cell').click(function () {
172 that.notebook.copy_cell();
173 that.notebook.copy_cell();
173 });
174 });
174 this.element.find('#delete_cell').click(function () {
175 this.element.find('#delete_cell').click(function () {
175 that.notebook.delete_cell();
176 that.notebook.delete_cell();
176 });
177 });
177 this.element.find('#undelete_cell').click(function () {
178 this.element.find('#undelete_cell').click(function () {
178 that.notebook.undelete_cell();
179 that.notebook.undelete_cell();
179 });
180 });
180 this.element.find('#split_cell').click(function () {
181 this.element.find('#split_cell').click(function () {
181 that.notebook.split_cell();
182 that.notebook.split_cell();
182 });
183 });
183 this.element.find('#merge_cell_above').click(function () {
184 this.element.find('#merge_cell_above').click(function () {
184 that.notebook.merge_cell_above();
185 that.notebook.merge_cell_above();
185 });
186 });
186 this.element.find('#merge_cell_below').click(function () {
187 this.element.find('#merge_cell_below').click(function () {
187 that.notebook.merge_cell_below();
188 that.notebook.merge_cell_below();
188 });
189 });
189 this.element.find('#move_cell_up').click(function () {
190 this.element.find('#move_cell_up').click(function () {
190 that.notebook.move_cell_up();
191 that.notebook.move_cell_up();
191 });
192 });
192 this.element.find('#move_cell_down').click(function () {
193 this.element.find('#move_cell_down').click(function () {
193 that.notebook.move_cell_down();
194 that.notebook.move_cell_down();
194 });
195 });
195 this.element.find('#edit_nb_metadata').click(function () {
196 this.element.find('#edit_nb_metadata').click(function () {
196 that.notebook.edit_metadata({
197 that.notebook.edit_metadata({
197 notebook: that.notebook,
198 notebook: that.notebook,
198 keyboard_manager: that.notebook.keyboard_manager});
199 keyboard_manager: that.notebook.keyboard_manager});
199 });
200 });
200
201
201 // View
202 // View
202 this.element.find('#toggle_header').click(function () {
203 this.element.find('#toggle_header').click(function () {
203 $('div#header').toggle();
204 $('div#header').toggle();
204 that.layout_manager.do_resize();
205 that.layout_manager.do_resize();
205 });
206 });
206 this.element.find('#toggle_toolbar').click(function () {
207 this.element.find('#toggle_toolbar').click(function () {
207 $('div#maintoolbar').toggle();
208 $('div#maintoolbar').toggle();
208 that.layout_manager.do_resize();
209 that.layout_manager.do_resize();
209 });
210 });
210 // Insert
211 // Insert
211 this.element.find('#insert_cell_above').click(function () {
212 this.element.find('#insert_cell_above').click(function () {
212 that.notebook.insert_cell_above('code');
213 that.notebook.insert_cell_above('code');
213 that.notebook.select_prev();
214 that.notebook.select_prev();
214 });
215 });
215 this.element.find('#insert_cell_below').click(function () {
216 this.element.find('#insert_cell_below').click(function () {
216 that.notebook.insert_cell_below('code');
217 that.notebook.insert_cell_below('code');
217 that.notebook.select_next();
218 that.notebook.select_next();
218 });
219 });
219 // Cell
220 // Cell
220 this.element.find('#run_cell').click(function () {
221 this.element.find('#run_cell').click(function () {
221 that.notebook.execute_cell();
222 that.notebook.execute_cell();
222 });
223 });
223 this.element.find('#run_cell_select_below').click(function () {
224 this.element.find('#run_cell_select_below').click(function () {
224 that.notebook.execute_cell_and_select_below();
225 that.notebook.execute_cell_and_select_below();
225 });
226 });
226 this.element.find('#run_cell_insert_below').click(function () {
227 this.element.find('#run_cell_insert_below').click(function () {
227 that.notebook.execute_cell_and_insert_below();
228 that.notebook.execute_cell_and_insert_below();
228 });
229 });
229 this.element.find('#run_all_cells').click(function () {
230 this.element.find('#run_all_cells').click(function () {
230 that.notebook.execute_all_cells();
231 that.notebook.execute_all_cells();
231 });
232 });
232 this.element.find('#run_all_cells_above').click(function () {
233 this.element.find('#run_all_cells_above').click(function () {
233 that.notebook.execute_cells_above();
234 that.notebook.execute_cells_above();
234 });
235 });
235 this.element.find('#run_all_cells_below').click(function () {
236 this.element.find('#run_all_cells_below').click(function () {
236 that.notebook.execute_cells_below();
237 that.notebook.execute_cells_below();
237 });
238 });
238 this.element.find('#to_code').click(function () {
239 this.element.find('#to_code').click(function () {
239 that.notebook.to_code();
240 that.notebook.to_code();
240 });
241 });
241 this.element.find('#to_markdown').click(function () {
242 this.element.find('#to_markdown').click(function () {
242 that.notebook.to_markdown();
243 that.notebook.to_markdown();
243 });
244 });
244 this.element.find('#to_raw').click(function () {
245 this.element.find('#to_raw').click(function () {
245 that.notebook.to_raw();
246 that.notebook.to_raw();
246 });
247 });
247 this.element.find('#to_heading1').click(function () {
248 this.element.find('#to_heading1').click(function () {
248 that.notebook.to_heading(undefined, 1);
249 that.notebook.to_heading(undefined, 1);
249 });
250 });
250 this.element.find('#to_heading2').click(function () {
251 this.element.find('#to_heading2').click(function () {
251 that.notebook.to_heading(undefined, 2);
252 that.notebook.to_heading(undefined, 2);
252 });
253 });
253 this.element.find('#to_heading3').click(function () {
254 this.element.find('#to_heading3').click(function () {
254 that.notebook.to_heading(undefined, 3);
255 that.notebook.to_heading(undefined, 3);
255 });
256 });
256 this.element.find('#to_heading4').click(function () {
257 this.element.find('#to_heading4').click(function () {
257 that.notebook.to_heading(undefined, 4);
258 that.notebook.to_heading(undefined, 4);
258 });
259 });
259 this.element.find('#to_heading5').click(function () {
260 this.element.find('#to_heading5').click(function () {
260 that.notebook.to_heading(undefined, 5);
261 that.notebook.to_heading(undefined, 5);
261 });
262 });
262 this.element.find('#to_heading6').click(function () {
263 this.element.find('#to_heading6').click(function () {
263 that.notebook.to_heading(undefined, 6);
264 that.notebook.to_heading(undefined, 6);
264 });
265 });
265
266
266 this.element.find('#toggle_current_output').click(function () {
267 this.element.find('#toggle_current_output').click(function () {
267 that.notebook.toggle_output();
268 that.notebook.toggle_output();
268 });
269 });
269 this.element.find('#toggle_current_output_scroll').click(function () {
270 this.element.find('#toggle_current_output_scroll').click(function () {
270 that.notebook.toggle_output_scroll();
271 that.notebook.toggle_output_scroll();
271 });
272 });
272 this.element.find('#clear_current_output').click(function () {
273 this.element.find('#clear_current_output').click(function () {
273 that.notebook.clear_output();
274 that.notebook.clear_output();
274 });
275 });
275
276
276 this.element.find('#toggle_all_output').click(function () {
277 this.element.find('#toggle_all_output').click(function () {
277 that.notebook.toggle_all_output();
278 that.notebook.toggle_all_output();
278 });
279 });
279 this.element.find('#toggle_all_output_scroll').click(function () {
280 this.element.find('#toggle_all_output_scroll').click(function () {
280 that.notebook.toggle_all_output_scroll();
281 that.notebook.toggle_all_output_scroll();
281 });
282 });
282 this.element.find('#clear_all_output').click(function () {
283 this.element.find('#clear_all_output').click(function () {
283 that.notebook.clear_all_output();
284 that.notebook.clear_all_output();
284 });
285 });
285
286
286 // Kernel
287 // Kernel
287 this.element.find('#int_kernel').click(function () {
288 this.element.find('#int_kernel').click(function () {
288 that.notebook.session.interrupt_kernel();
289 that.notebook.session.interrupt_kernel();
289 });
290 });
290 this.element.find('#restart_kernel').click(function () {
291 this.element.find('#restart_kernel').click(function () {
291 that.notebook.restart_kernel();
292 that.notebook.restart_kernel();
292 });
293 });
293 // Help
294 // Help
294 if (this.tour) {
295 if (this.tour) {
295 this.element.find('#notebook_tour').click(function () {
296 this.element.find('#notebook_tour').click(function () {
296 that.tour.start();
297 that.tour.start();
297 });
298 });
298 } else {
299 } else {
299 this.element.find('#notebook_tour').addClass("disabled");
300 this.element.find('#notebook_tour').addClass("disabled");
300 }
301 }
301 this.element.find('#keyboard_shortcuts').click(function () {
302 this.element.find('#keyboard_shortcuts').click(function () {
302 that.quick_help.show_keyboard_shortcuts();
303 that.quick_help.show_keyboard_shortcuts();
303 });
304 });
304
305
305 this.update_restore_checkpoint(null);
306 this.update_restore_checkpoint(null);
306
307
307 this.events.on('checkpoints_listed.Notebook', function (event, data) {
308 this.events.on('checkpoints_listed.Notebook', function (event, data) {
308 that.update_restore_checkpoint(that.notebook.checkpoints);
309 that.update_restore_checkpoint(that.notebook.checkpoints);
309 });
310 });
310
311
311 this.events.on('checkpoint_created.Notebook', function (event, data) {
312 this.events.on('checkpoint_created.Notebook', function (event, data) {
312 that.update_restore_checkpoint(that.notebook.checkpoints);
313 that.update_restore_checkpoint(that.notebook.checkpoints);
313 });
314 });
314 };
315 };
315
316
316 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
317 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
317 var ul = this.element.find("#restore_checkpoint").find("ul");
318 var ul = this.element.find("#restore_checkpoint").find("ul");
318 ul.empty();
319 ul.empty();
319 if (!checkpoints || checkpoints.length === 0) {
320 if (!checkpoints || checkpoints.length === 0) {
320 ul.append(
321 ul.append(
321 $("<li/>")
322 $("<li/>")
322 .addClass("disabled")
323 .addClass("disabled")
323 .append(
324 .append(
324 $("<a/>")
325 $("<a/>")
325 .text("No checkpoints")
326 .text("No checkpoints")
326 )
327 )
327 );
328 );
328 return;
329 return;
329 }
330 }
330
331
331 var that = this;
332 var that = this;
332 checkpoints.map(function (checkpoint) {
333 checkpoints.map(function (checkpoint) {
333 var d = new Date(checkpoint.last_modified);
334 var d = new Date(checkpoint.last_modified);
334 ul.append(
335 ul.append(
335 $("<li/>").append(
336 $("<li/>").append(
336 $("<a/>")
337 $("<a/>")
337 .attr("href", "#")
338 .attr("href", "#")
338 .text(d.format("mmm dd HH:MM:ss"))
339 .text(moment(d).format("LLLL"))
339 .click(function () {
340 .click(function () {
340 that.notebook.restore_checkpoint_dialog(checkpoint);
341 that.notebook.restore_checkpoint_dialog(checkpoint);
341 })
342 })
342 )
343 )
343 );
344 );
344 });
345 });
345 };
346 };
346
347
347 // Backwards compatability.
348 // Backwards compatability.
348 IPython.MenuBar = MenuBar;
349 IPython.MenuBar = MenuBar;
349
350
350 return {'MenuBar': MenuBar};
351 return {'MenuBar': MenuBar};
351 });
352 });
@@ -1,244 +1,245 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 'base/js/dialog',
8 'base/js/dialog',
9 'notebook/js/notificationwidget',
9 'notebook/js/notificationwidget',
10 ], function(IPython, $, utils, dialog, notificationwidget) {
10 'moment'
11 ], function(IPython, $, utils, dialog, notificationwidget, moment) {
11 "use strict";
12 "use strict";
12
13
13 var NotificationArea = function (selector, options) {
14 var NotificationArea = function (selector, options) {
14 // Constructor
15 // Constructor
15 //
16 //
16 // Parameters:
17 // Parameters:
17 // selector: string
18 // selector: string
18 // options: dictionary
19 // options: dictionary
19 // Dictionary of keyword arguments.
20 // Dictionary of keyword arguments.
20 // notebook: Notebook instance
21 // notebook: Notebook instance
21 // events: $(Events) instance
22 // events: $(Events) instance
22 // save_widget: SaveWidget instance
23 // save_widget: SaveWidget instance
23 this.selector = selector;
24 this.selector = selector;
24 this.events = options.events;
25 this.events = options.events;
25 this.save_widget = options.save_widget;
26 this.save_widget = options.save_widget;
26 this.notebook = options.notebook;
27 this.notebook = options.notebook;
27 this.keyboard_manager = options.keyboard_manager;
28 this.keyboard_manager = options.keyboard_manager;
28 if (this.selector !== undefined) {
29 if (this.selector !== undefined) {
29 this.element = $(selector);
30 this.element = $(selector);
30 }
31 }
31 this.widget_dict = {};
32 this.widget_dict = {};
32 };
33 };
33
34
34 NotificationArea.prototype.temp_message = function (msg, timeout, css_class) {
35 NotificationArea.prototype.temp_message = function (msg, timeout, css_class) {
35 if( css_class == 'danger') {css_class = 'ui-state-error';}
36 if( css_class == 'danger') {css_class = 'ui-state-error';}
36 if( css_class == 'warning') {css_class = 'ui-state-highlight';}
37 if( css_class == 'warning') {css_class = 'ui-state-highlight';}
37 var tdiv = $('<div>')
38 var tdiv = $('<div>')
38 .addClass('notification_widget')
39 .addClass('notification_widget')
39 .addClass(css_class)
40 .addClass(css_class)
40 .hide()
41 .hide()
41 .text(msg);
42 .text(msg);
42
43
43 $(this.selector).append(tdiv);
44 $(this.selector).append(tdiv);
44 var tmout = Math.max(1500,(timeout||1500));
45 var tmout = Math.max(1500,(timeout||1500));
45 tdiv.fadeIn(100);
46 tdiv.fadeIn(100);
46
47
47 setTimeout(function () {
48 setTimeout(function () {
48 tdiv.fadeOut(100, function () {tdiv.remove();});
49 tdiv.fadeOut(100, function () {tdiv.remove();});
49 }, tmout);
50 }, tmout);
50 };
51 };
51
52
52 NotificationArea.prototype.widget = function(name) {
53 NotificationArea.prototype.widget = function(name) {
53 if(this.widget_dict[name] === undefined) {
54 if(this.widget_dict[name] === undefined) {
54 return this.new_notification_widget(name);
55 return this.new_notification_widget(name);
55 }
56 }
56 return this.get_widget(name);
57 return this.get_widget(name);
57 };
58 };
58
59
59 NotificationArea.prototype.get_widget = function(name) {
60 NotificationArea.prototype.get_widget = function(name) {
60 if(this.widget_dict[name] === undefined) {
61 if(this.widget_dict[name] === undefined) {
61 throw('no widgets with this name');
62 throw('no widgets with this name');
62 }
63 }
63 return this.widget_dict[name];
64 return this.widget_dict[name];
64 };
65 };
65
66
66 NotificationArea.prototype.new_notification_widget = function(name) {
67 NotificationArea.prototype.new_notification_widget = function(name) {
67 if(this.widget_dict[name] !== undefined) {
68 if(this.widget_dict[name] !== undefined) {
68 throw('widget with that name already exists ! ');
69 throw('widget with that name already exists ! ');
69 }
70 }
70 var div = $('<div/>').attr('id','notification_'+name);
71 var div = $('<div/>').attr('id','notification_'+name);
71 $(this.selector).append(div);
72 $(this.selector).append(div);
72 this.widget_dict[name] = new notificationwidget.NotificationWidget('#notification_'+name);
73 this.widget_dict[name] = new notificationwidget.NotificationWidget('#notification_'+name);
73 return this.widget_dict[name];
74 return this.widget_dict[name];
74 };
75 };
75
76
76 NotificationArea.prototype.init_notification_widgets = function() {
77 NotificationArea.prototype.init_notification_widgets = function() {
77 var that = this;
78 var that = this;
78 var knw = this.new_notification_widget('kernel');
79 var knw = this.new_notification_widget('kernel');
79 var $kernel_ind_icon = $("#kernel_indicator_icon");
80 var $kernel_ind_icon = $("#kernel_indicator_icon");
80 var $modal_ind_icon = $("#modal_indicator_icon");
81 var $modal_ind_icon = $("#modal_indicator_icon");
81
82
82 // Command/Edit mode
83 // Command/Edit mode
83 this.events.on('edit_mode.Notebook',function () {
84 this.events.on('edit_mode.Notebook',function () {
84 that.save_widget.update_document_title();
85 that.save_widget.update_document_title();
85 $modal_ind_icon.attr('class','edit_mode_icon').attr('title','Edit Mode');
86 $modal_ind_icon.attr('class','edit_mode_icon').attr('title','Edit Mode');
86 });
87 });
87
88
88 this.events.on('command_mode.Notebook',function () {
89 this.events.on('command_mode.Notebook',function () {
89 that.save_widget.update_document_title();
90 that.save_widget.update_document_title();
90 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
91 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
91 });
92 });
92
93
93 // Implicitly start off in Command mode, switching to Edit mode will trigger event
94 // Implicitly start off in Command mode, switching to Edit mode will trigger event
94 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
95 $modal_ind_icon.attr('class','command_mode_icon').attr('title','Command Mode');
95
96
96 // Kernel events
97 // Kernel events
97 this.events.on('status_idle.Kernel',function () {
98 this.events.on('status_idle.Kernel',function () {
98 that.save_widget.update_document_title();
99 that.save_widget.update_document_title();
99 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
100 $kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
100 });
101 });
101
102
102 this.events.on('status_busy.Kernel',function () {
103 this.events.on('status_busy.Kernel',function () {
103 window.document.title='(Busy) '+window.document.title;
104 window.document.title='(Busy) '+window.document.title;
104 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
105 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
105 });
106 });
106
107
107 this.events.on('status_restarting.Kernel',function () {
108 this.events.on('status_restarting.Kernel',function () {
108 that.save_widget.update_document_title();
109 that.save_widget.update_document_title();
109 knw.set_message("Restarting kernel", 2000);
110 knw.set_message("Restarting kernel", 2000);
110 });
111 });
111
112
112 this.events.on('status_interrupting.Kernel',function () {
113 this.events.on('status_interrupting.Kernel',function () {
113 knw.set_message("Interrupting kernel", 2000);
114 knw.set_message("Interrupting kernel", 2000);
114 });
115 });
115
116
116 // Start the kernel indicator in the busy state, and send a kernel_info request.
117 // Start the kernel indicator in the busy state, and send a kernel_info request.
117 // When the kernel_info reply arrives, the kernel is idle.
118 // When the kernel_info reply arrives, the kernel is idle.
118 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
119 $kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
119
120
120 this.events.on('status_started.Kernel', function (evt, data) {
121 this.events.on('status_started.Kernel', function (evt, data) {
121 data.kernel.kernel_info(function () {
122 data.kernel.kernel_info(function () {
122 that.events.trigger('status_idle.Kernel');
123 that.events.trigger('status_idle.Kernel');
123 });
124 });
124 });
125 });
125
126
126 this.events.on('status_dead.Kernel',function () {
127 this.events.on('status_dead.Kernel',function () {
127 var msg = 'The kernel has died, and the automatic restart has failed.' +
128 var msg = 'The kernel has died, and the automatic restart has failed.' +
128 ' It is possible the kernel cannot be restarted.' +
129 ' It is possible the kernel cannot be restarted.' +
129 ' If you are not able to restart the kernel, you will still be able to save' +
130 ' If you are not able to restart the kernel, you will still be able to save' +
130 ' the notebook, but running code will no longer work until the notebook' +
131 ' the notebook, but running code will no longer work until the notebook' +
131 ' is reopened.';
132 ' is reopened.';
132
133
133 dialog.modal({
134 dialog.modal({
134 title: "Dead kernel",
135 title: "Dead kernel",
135 body : msg,
136 body : msg,
136 keyboard_manager: that.keyboard_manager,
137 keyboard_manager: that.keyboard_manager,
137 notebook: that.notebook,
138 notebook: that.notebook,
138 buttons : {
139 buttons : {
139 "Manual Restart": {
140 "Manual Restart": {
140 class: "btn-danger",
141 class: "btn-danger",
141 click: function () {
142 click: function () {
142 that.events.trigger('status_restarting.Kernel');
143 that.events.trigger('status_restarting.Kernel');
143 that.notebook.start_kernel();
144 that.notebook.start_kernel();
144 }
145 }
145 },
146 },
146 "Don't restart": {}
147 "Don't restart": {}
147 }
148 }
148 });
149 });
149 });
150 });
150
151
151 this.events.on('websocket_closed.Kernel', function (event, data) {
152 this.events.on('websocket_closed.Kernel', function (event, data) {
152 var kernel = data.kernel;
153 var kernel = data.kernel;
153 var ws_url = data.ws_url;
154 var ws_url = data.ws_url;
154 var early = data.early;
155 var early = data.early;
155 var msg;
156 var msg;
156 if (!early) {
157 if (!early) {
157 knw.set_message('Reconnecting WebSockets', 1000);
158 knw.set_message('Reconnecting WebSockets', 1000);
158 setTimeout(function () {
159 setTimeout(function () {
159 kernel.start_channels();
160 kernel.start_channels();
160 }, 5000);
161 }, 5000);
161 return;
162 return;
162 }
163 }
163 console.log('WebSocket connection failed: ', ws_url);
164 console.log('WebSocket connection failed: ', ws_url);
164 msg = "A WebSocket connection could not be established." +
165 msg = "A WebSocket connection could not be established." +
165 " You will NOT be able to run code. Check your" +
166 " You will NOT be able to run code. Check your" +
166 " network connection or notebook server configuration.";
167 " network connection or notebook server configuration.";
167 dialog.modal({
168 dialog.modal({
168 title: "WebSocket connection failed",
169 title: "WebSocket connection failed",
169 body: msg,
170 body: msg,
170 keyboard_manager: that.keyboard_manager,
171 keyboard_manager: that.keyboard_manager,
171 notebook: that.notebook,
172 notebook: that.notebook,
172 buttons : {
173 buttons : {
173 "OK": {},
174 "OK": {},
174 "Reconnect": {
175 "Reconnect": {
175 click: function () {
176 click: function () {
176 knw.set_message('Reconnecting WebSockets', 1000);
177 knw.set_message('Reconnecting WebSockets', 1000);
177 setTimeout(function () {
178 setTimeout(function () {
178 kernel.start_channels();
179 kernel.start_channels();
179 }, 5000);
180 }, 5000);
180 }
181 }
181 }
182 }
182 }
183 }
183 });
184 });
184 });
185 });
185
186
186
187
187 var nnw = this.new_notification_widget('notebook');
188 var nnw = this.new_notification_widget('notebook');
188
189
189 // Notebook events
190 // Notebook events
190 this.events.on('notebook_loading.Notebook', function () {
191 this.events.on('notebook_loading.Notebook', function () {
191 nnw.set_message("Loading notebook",500);
192 nnw.set_message("Loading notebook",500);
192 });
193 });
193 this.events.on('notebook_loaded.Notebook', function () {
194 this.events.on('notebook_loaded.Notebook', function () {
194 nnw.set_message("Notebook loaded",500);
195 nnw.set_message("Notebook loaded",500);
195 });
196 });
196 this.events.on('notebook_saving.Notebook', function () {
197 this.events.on('notebook_saving.Notebook', function () {
197 nnw.set_message("Saving notebook",500);
198 nnw.set_message("Saving notebook",500);
198 });
199 });
199 this.events.on('notebook_saved.Notebook', function () {
200 this.events.on('notebook_saved.Notebook', function () {
200 nnw.set_message("Notebook saved",2000);
201 nnw.set_message("Notebook saved",2000);
201 });
202 });
202 this.events.on('notebook_save_failed.Notebook', function (evt, xhr, status, data) {
203 this.events.on('notebook_save_failed.Notebook', function (evt, xhr, status, data) {
203 nnw.warning(data || "Notebook save failed");
204 nnw.warning(data || "Notebook save failed");
204 });
205 });
205
206
206 // Checkpoint events
207 // Checkpoint events
207 this.events.on('checkpoint_created.Notebook', function (evt, data) {
208 this.events.on('checkpoint_created.Notebook', function (evt, data) {
208 var msg = "Checkpoint created";
209 var msg = "Checkpoint created";
209 if (data.last_modified) {
210 if (data.last_modified) {
210 var d = new Date(data.last_modified);
211 var d = new Date(data.last_modified);
211 msg = msg + ": " + d.format("HH:MM:ss");
212 msg = msg + ": " + moment(d).format("HH:mm:ss");
212 }
213 }
213 nnw.set_message(msg, 2000);
214 nnw.set_message(msg, 2000);
214 });
215 });
215 this.events.on('checkpoint_failed.Notebook', function () {
216 this.events.on('checkpoint_failed.Notebook', function () {
216 nnw.warning("Checkpoint failed");
217 nnw.warning("Checkpoint failed");
217 });
218 });
218 this.events.on('checkpoint_deleted.Notebook', function () {
219 this.events.on('checkpoint_deleted.Notebook', function () {
219 nnw.set_message("Checkpoint deleted", 500);
220 nnw.set_message("Checkpoint deleted", 500);
220 });
221 });
221 this.events.on('checkpoint_delete_failed.Notebook', function () {
222 this.events.on('checkpoint_delete_failed.Notebook', function () {
222 nnw.warning("Checkpoint delete failed");
223 nnw.warning("Checkpoint delete failed");
223 });
224 });
224 this.events.on('checkpoint_restoring.Notebook', function () {
225 this.events.on('checkpoint_restoring.Notebook', function () {
225 nnw.set_message("Restoring to checkpoint...", 500);
226 nnw.set_message("Restoring to checkpoint...", 500);
226 });
227 });
227 this.events.on('checkpoint_restore_failed.Notebook', function () {
228 this.events.on('checkpoint_restore_failed.Notebook', function () {
228 nnw.warning("Checkpoint restore failed");
229 nnw.warning("Checkpoint restore failed");
229 });
230 });
230
231
231 // Autosave events
232 // Autosave events
232 this.events.on('autosave_disabled.Notebook', function () {
233 this.events.on('autosave_disabled.Notebook', function () {
233 nnw.set_message("Autosave disabled", 2000);
234 nnw.set_message("Autosave disabled", 2000);
234 });
235 });
235 this.events.on('autosave_enabled.Notebook', function (evt, interval) {
236 this.events.on('autosave_enabled.Notebook', function (evt, interval) {
236 nnw.set_message("Saving every " + interval / 1000 + "s", 1000);
237 nnw.set_message("Saving every " + interval / 1000 + "s", 1000);
237 });
238 });
238
239
239 };
240 };
240
241
241 IPython.NotificationArea = NotificationArea;
242 IPython.NotificationArea = NotificationArea;
242
243
243 return {'NotificationArea': NotificationArea};
244 return {'NotificationArea': NotificationArea};
244 });
245 });
@@ -1,173 +1,242 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 'base/js/dialog',
8 'base/js/dialog',
9 'base/js/keyboard',
9 'base/js/keyboard',
10 'dateformat',
10 'moment',
11 ], function(IPython, $, utils, dialog, keyboard, dateformat) {
11 ], function(IPython, $, utils, dialog, keyboard, moment) {
12 "use strict";
12 "use strict";
13
13
14 var SaveWidget = function (selector, options) {
14 var SaveWidget = function (selector, options) {
15 // TODO: Remove circular ref.
15 // TODO: Remove circular ref.
16 this.notebook = undefined;
16 this.notebook = undefined;
17 this.selector = selector;
17 this.selector = selector;
18 this.events = options.events;
18 this.events = options.events;
19 this._checkpoint_date = undefined;
19 this.keyboard_manager = options.keyboard_manager;
20 this.keyboard_manager = options.keyboard_manager;
20 if (this.selector !== undefined) {
21 if (this.selector !== undefined) {
21 this.element = $(selector);
22 this.element = $(selector);
22 this.bind_events();
23 this.bind_events();
23 }
24 }
24 };
25 };
25
26
26
27
27 SaveWidget.prototype.bind_events = function () {
28 SaveWidget.prototype.bind_events = function () {
28 var that = this;
29 var that = this;
29 this.element.find('span#notebook_name').click(function () {
30 this.element.find('span#notebook_name').click(function () {
30 that.rename_notebook();
31 that.rename_notebook();
31 });
32 });
32 this.element.find('span#notebook_name').hover(function () {
33 this.element.find('span#notebook_name').hover(function () {
33 $(this).addClass("ui-state-hover");
34 $(this).addClass("ui-state-hover");
34 }, function () {
35 }, function () {
35 $(this).removeClass("ui-state-hover");
36 $(this).removeClass("ui-state-hover");
36 });
37 });
37 this.events.on('notebook_loaded.Notebook', function () {
38 this.events.on('notebook_loaded.Notebook', function () {
38 that.update_notebook_name();
39 that.update_notebook_name();
39 that.update_document_title();
40 that.update_document_title();
40 });
41 });
41 this.events.on('notebook_saved.Notebook', function () {
42 this.events.on('notebook_saved.Notebook', function () {
42 that.update_notebook_name();
43 that.update_notebook_name();
43 that.update_document_title();
44 that.update_document_title();
44 });
45 });
45 this.events.on('notebook_renamed.Notebook', function () {
46 this.events.on('notebook_renamed.Notebook', function () {
46 that.update_notebook_name();
47 that.update_notebook_name();
47 that.update_document_title();
48 that.update_document_title();
48 that.update_address_bar();
49 that.update_address_bar();
49 });
50 });
50 this.events.on('notebook_save_failed.Notebook', function () {
51 this.events.on('notebook_save_failed.Notebook', function () {
51 that.set_save_status('Autosave Failed!');
52 that.set_save_status('Autosave Failed!');
52 });
53 });
53 this.events.on('checkpoints_listed.Notebook', function (event, data) {
54 this.events.on('checkpoints_listed.Notebook', function (event, data) {
54 that.set_last_checkpoint(data[0]);
55 that._set_last_checkpoint(data[0]);
55 });
56 });
56
57
57 this.events.on('checkpoint_created.Notebook', function (event, data) {
58 this.events.on('checkpoint_created.Notebook', function (event, data) {
58 that.set_last_checkpoint(data);
59 that._set_last_checkpoint(data);
59 });
60 });
60 this.events.on('set_dirty.Notebook', function (event, data) {
61 this.events.on('set_dirty.Notebook', function (event, data) {
61 that.set_autosaved(data.value);
62 that.set_autosaved(data.value);
62 });
63 });
63 };
64 };
64
65
65
66
66 SaveWidget.prototype.rename_notebook = function (options) {
67 SaveWidget.prototype.rename_notebook = function (options) {
67 options = options || {};
68 options = options || {};
68 var that = this;
69 var that = this;
69 var dialog_body = $('<div/>').append(
70 var dialog_body = $('<div/>').append(
70 $("<p/>").addClass("rename-message")
71 $("<p/>").addClass("rename-message")
71 .text('Enter a new notebook name:')
72 .text('Enter a new notebook name:')
72 ).append(
73 ).append(
73 $("<br/>")
74 $("<br/>")
74 ).append(
75 ).append(
75 $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
76 $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
76 .val(that.notebook.get_notebook_name())
77 .val(that.notebook.get_notebook_name())
77 );
78 );
78 dialog.modal({
79 dialog.modal({
79 title: "Rename Notebook",
80 title: "Rename Notebook",
80 body: dialog_body,
81 body: dialog_body,
81 notebook: options.notebook,
82 notebook: options.notebook,
82 keyboard_manager: this.keyboard_manager,
83 keyboard_manager: this.keyboard_manager,
83 buttons : {
84 buttons : {
84 "Cancel": {},
85 "Cancel": {},
85 "OK": {
86 "OK": {
86 class: "btn-primary",
87 class: "btn-primary",
87 click: function () {
88 click: function () {
88 var new_name = $(this).find('input').val();
89 var new_name = $(this).find('input').val();
89 if (!that.notebook.test_notebook_name(new_name)) {
90 if (!that.notebook.test_notebook_name(new_name)) {
90 $(this).find('.rename-message').text(
91 $(this).find('.rename-message').text(
91 "Invalid notebook name. Notebook names must "+
92 "Invalid notebook name. Notebook names must "+
92 "have 1 or more characters and can contain any characters " +
93 "have 1 or more characters and can contain any characters " +
93 "except :/\\. Please enter a new notebook name:"
94 "except :/\\. Please enter a new notebook name:"
94 );
95 );
95 return false;
96 return false;
96 } else {
97 } else {
97 that.notebook.rename(new_name);
98 that.notebook.rename(new_name);
98 }
99 }
99 }}
100 }}
100 },
101 },
101 open : function (event, ui) {
102 open : function (event, ui) {
102 var that = $(this);
103 var that = $(this);
103 // Upon ENTER, click the OK button.
104 // Upon ENTER, click the OK button.
104 that.find('input[type="text"]').keydown(function (event, ui) {
105 that.find('input[type="text"]').keydown(function (event, ui) {
105 if (event.which === keyboard.keycodes.enter) {
106 if (event.which === keyboard.keycodes.enter) {
106 that.find('.btn-primary').first().click();
107 that.find('.btn-primary').first().click();
107 return false;
108 return false;
108 }
109 }
109 });
110 });
110 that.find('input[type="text"]').focus().select();
111 that.find('input[type="text"]').focus().select();
111 }
112 }
112 });
113 });
113 };
114 };
114
115
115
116
116 SaveWidget.prototype.update_notebook_name = function () {
117 SaveWidget.prototype.update_notebook_name = function () {
117 var nbname = this.notebook.get_notebook_name();
118 var nbname = this.notebook.get_notebook_name();
118 this.element.find('span#notebook_name').text(nbname);
119 this.element.find('span#notebook_name').text(nbname);
119 };
120 };
120
121
121
122
122 SaveWidget.prototype.update_document_title = function () {
123 SaveWidget.prototype.update_document_title = function () {
123 var nbname = this.notebook.get_notebook_name();
124 var nbname = this.notebook.get_notebook_name();
124 document.title = nbname;
125 document.title = nbname;
125 };
126 };
126
127
127 SaveWidget.prototype.update_address_bar = function(){
128 SaveWidget.prototype.update_address_bar = function(){
128 var base_url = this.notebook.base_url;
129 var base_url = this.notebook.base_url;
129 var nbname = this.notebook.notebook_name;
130 var nbname = this.notebook.notebook_name;
130 var path = this.notebook.notebook_path;
131 var path = this.notebook.notebook_path;
131 var state = {path : path, name: nbname};
132 var state = {path : path, name: nbname};
132 window.history.replaceState(state, "", utils.url_join_encode(
133 window.history.replaceState(state, "", utils.url_join_encode(
133 base_url,
134 base_url,
134 "notebooks",
135 "notebooks",
135 path,
136 path,
136 nbname)
137 nbname)
137 );
138 );
138 };
139 };
139
140
140
141
141 SaveWidget.prototype.set_save_status = function (msg) {
142 SaveWidget.prototype.set_save_status = function (msg) {
142 this.element.find('span#autosave_status').text(msg);
143 this.element.find('span#autosave_status').text(msg);
143 };
144 };
144
145
145 SaveWidget.prototype.set_checkpoint_status = function (msg) {
146 SaveWidget.prototype._set_checkpoint_status = function (human_date, iso_date) {
146 this.element.find('span#checkpoint_status').text(msg);
147 var el = this.element.find('span#checkpoint_status')
148 if(human_date){
149 el.text("Last Checkpoint: "+human_date).attr('title',iso_date);
150 } else {
151 el.text('').attr('title','no-checkpoint')
152 }
147 };
153 };
148
154
149 SaveWidget.prototype.set_last_checkpoint = function (checkpoint) {
155 // compute (roughly) the remaining time in millisecond until the next
150 if (!checkpoint) {
156 // moment.js relative time update of the string, which by default
151 this.set_checkpoint_status("");
157 // happend at
158 // (a few seconds ago)
159 // - 45sec,
160 // (a minute ago)
161 // - 90sec,
162 // ( x minutes ago)
163 // - then every minutes until
164 // - 45 min,
165 // (an hour ago)
166 // - 1h45,
167 // (x hours ago )
168 // - then every hours
169 // - 22 hours ago
170 var _next_timeago_update = function(deltatime_ms){
171 var s = 1000;
172 var m = 60*s;
173 var h = 60*m;
174
175 var mtt = moment.relativeTimeThreshold;
176
177 if(deltatime_ms < mtt.s*s){
178 return mtt.s*s-deltatime_ms;
179 } else if (deltatime_ms < (mtt.s*s+m)) {
180 return (mtt.s*s+m)-deltatime_ms;
181 } else if (deltatime_ms < mtt.m*m){
182 return m;
183 } else if (deltatime_ms < (mtt.m*m+h)){
184 return (mtt.m*m+h)-deltatime_ms;
185 } else {
186 return h;
187 }
188
189
190 }
191
192 SaveWidget.prototype._regularly_update_checkpoint_date = function(){
193 if (!this._checkpoint_date) {
194 this.set_checkpoint_status(null);
195 console.log('no checkpoint done');
152 return;
196 return;
153 }
197 }
154 var d = new Date(checkpoint.last_modified);
198 var chkd = moment(this._checkpoint_date);
155 this.set_checkpoint_status(
199 var longdate = chkd.format('llll');
156 "Last Checkpoint: " + dateformat(d,'mmm dd HH:MM')
200
157 );
201 var that = this;
202 var recall = function(t){
203 // recall slightly later (1s) as long timeout in js might be imprecise,
204 // and you want to be call **after** the change of formatting should happend.
205 return setTimeout($.proxy(that._regularly_update_checkpoint_date, that),(t+1000))
206 }
207 var tdelta = Math.ceil(new Date()-this._checkpoint_date);
208
209 // update regularly for the first 6hours and show
210 // <x time> ago
211 if(tdelta < tdelta < 6*3600*1000){
212 recall(_next_timeago_update(tdelta));
213 this._set_checkpoint_status( chkd.fromNow(), longdate);
214 // otherwise update every hour and show
215 // <Today | yesterday|...> at hh,mm,ss
216 } else {
217 recall(1*3600*1000)
218 this._set_checkpoint_status( chkd.calendar(), longdate);
219 }
220
221 }
222
223 SaveWidget.prototype._set_last_checkpoint = function (checkpoint) {
224 this._checkpoint_date = new Date(checkpoint.last_modified);
225 this._regularly_update_checkpoint_date();
226
158 };
227 };
159
228
160 SaveWidget.prototype.set_autosaved = function (dirty) {
229 SaveWidget.prototype.set_autosaved = function (dirty) {
161 if (dirty) {
230 if (dirty) {
162 this.set_save_status("(unsaved changes)");
231 this.set_save_status("(unsaved changes)");
163 } else {
232 } else {
164 this.set_save_status("(autosaved)");
233 this.set_save_status("(autosaved)");
165 }
234 }
166 };
235 };
167
236
168 // Backwards compatibility.
237 // Backwards compatibility.
169 IPython.SaveWidget = SaveWidget;
238 IPython.SaveWidget = SaveWidget;
170
239
171 return {'SaveWidget': SaveWidget};
240 return {'SaveWidget': SaveWidget};
172
241
173 });
242 });
@@ -1,34 +1,34 b''
1 span#save_widget {
1 span#save_widget {
2 padding: 0px 5px;
2 padding: 0px 5px;
3 margin-top: 12px;
3 margin-top: 12px;
4 }
4 }
5
5
6 span#checkpoint_status, span#autosave_status {
6 span#checkpoint_status, span#autosave_status {
7 font-size: small;
7 font-size: small;
8 }
8 }
9
9
10 @media (max-width: 767px) {
10 @media (max-width: 767px) {
11 span#save_widget {
11 span#save_widget {
12 font-size: small;
12 font-size: small;
13 }
13 }
14 span#checkpoint_status, span#autosave_status {
14 span#checkpoint_status, span#autosave_status {
15 font-size: x-small;
15 font-size: x-small;
16 }
16 }
17 }
17 }
18
18
19 @media (max-width: 767px) {
19 @media (max-width: 767px) {
20 span#checkpoint_status, span#autosave_status {
20 span#checkpoint_status, span#autosave_status {
21 display: none;
21 display: none;
22 }
22 }
23 }
23 }
24
24
25 @media (min-width: 768px) and (max-width: 979px) {
25 @media (min-width: 768px) and (max-width: 979px) {
26 span#checkpoint_status {
26 span#checkpoint_status {
27 display: none;
27 display: none;
28 }
28 }
29 span#autosave_status {
29 span#autosave_status {
30 font-size: x-small;
30 font-size: x-small;
31 }
31 }
32 }
32 }
33
33
34 No newline at end of file
34
@@ -1,107 +1,108 b''
1 <!DOCTYPE HTML>
1 <!DOCTYPE HTML>
2 <html>
2 <html>
3
3
4 <head>
4 <head>
5 <meta charset="utf-8">
5 <meta charset="utf-8">
6
6
7 <title>{% block title %}IPython Notebook{% endblock %}</title>
7 <title>{% block title %}IPython Notebook{% endblock %}</title>
8 <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
8 <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
12
13 {% block stylesheet %}
13 {% block stylesheet %}
14 <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
14 <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
15 {% endblock %}
15 {% endblock %}
16 <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
16 <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
17 <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
17 <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
18 <script>
18 <script>
19 require.config({
19 require.config({
20 baseUrl: '{{static_url("", include_version=False)}}',
20 baseUrl: '{{static_url("", include_version=False)}}',
21 paths: {
21 paths: {
22 nbextensions : '{{ base_url }}nbextensions',
22 nbextensions : '{{ base_url }}nbextensions',
23 underscore : 'components/underscore/underscore-min',
23 underscore : 'components/underscore/underscore-min',
24 backbone : 'components/backbone/backbone-min',
24 backbone : 'components/backbone/backbone-min',
25 jquery: 'components/jquery/jquery.min',
25 jquery: 'components/jquery/jquery.min',
26 bootstrap: 'components/bootstrap/js/bootstrap.min',
26 bootstrap: 'components/bootstrap/js/bootstrap.min',
27 bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
27 bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
28 dateformat: 'dateformat/date.format',
28 dateformat: 'dateformat/date.format',
29 jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
29 jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
30 highlight: 'components/highlight.js/build/highlight.pack',
30 highlight: 'components/highlight.js/build/highlight.pack',
31 moment: "components/moment/moment",
31 },
32 },
32 shim: {
33 shim: {
33 underscore: {
34 underscore: {
34 exports: '_'
35 exports: '_'
35 },
36 },
36 backbone: {
37 backbone: {
37 deps: ["underscore", "jquery"],
38 deps: ["underscore", "jquery"],
38 exports: "Backbone"
39 exports: "Backbone"
39 },
40 },
40 bootstrap: {
41 bootstrap: {
41 deps: ["jquery"],
42 deps: ["jquery"],
42 exports: "bootstrap"
43 exports: "bootstrap"
43 },
44 },
44 bootstraptour: {
45 bootstraptour: {
45 deps: ["bootstrap"],
46 deps: ["bootstrap"],
46 exports: "Tour"
47 exports: "Tour"
47 },
48 },
48 dateformat: {
49 dateformat: {
49 exports: "dateFormat"
50 exports: "dateFormat"
50 },
51 },
51 jqueryui: {
52 jqueryui: {
52 deps: ["jquery"],
53 deps: ["jquery"],
53 exports: "$"
54 exports: "$"
54 },
55 },
55 highlight: {
56 highlight: {
56 exports: "hljs"
57 exports: "hljs"
57 },
58 },
58 }
59 }
59 });
60 });
60 </script>
61 </script>
61
62
62 {% block meta %}
63 {% block meta %}
63 {% endblock %}
64 {% endblock %}
64
65
65 </head>
66 </head>
66
67
67 <body {% block params %}{% endblock %}>
68 <body {% block params %}{% endblock %}>
68
69
69 <noscript>
70 <noscript>
70 <div id='noscript'>
71 <div id='noscript'>
71 IPython Notebook requires JavaScript.<br>
72 IPython Notebook requires JavaScript.<br>
72 Please enable it to proceed.
73 Please enable it to proceed.
73 </div>
74 </div>
74 </noscript>
75 </noscript>
75
76
76 <div id="header" class="navbar navbar-static-top">
77 <div id="header" class="navbar navbar-static-top">
77 <div class="container">
78 <div class="container">
78 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree/{{notebook_path}}" alt='dashboard'><img src='{{static_url("base/images/ipynblogo.png") }}' alt='IPython Notebook'/></a></div>
79 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree/{{notebook_path}}" alt='dashboard'><img src='{{static_url("base/images/ipynblogo.png") }}' alt='IPython Notebook'/></a></div>
79
80
80 {% block login_widget %}
81 {% block login_widget %}
81
82
82 <span id="login_widget">
83 <span id="login_widget">
83 {% if logged_in %}
84 {% if logged_in %}
84 <button id="logout">Logout</button>
85 <button id="logout">Logout</button>
85 {% elif login_available and not logged_in %}
86 {% elif login_available and not logged_in %}
86 <button id="login">Login</button>
87 <button id="login">Login</button>
87 {% endif %}
88 {% endif %}
88 </span>
89 </span>
89
90
90 {% endblock %}
91 {% endblock %}
91
92
92 {% block header %}
93 {% block header %}
93 {% endblock %}
94 {% endblock %}
94 </div>
95 </div>
95 </div>
96 </div>
96
97
97 <div id="site">
98 <div id="site">
98 {% block site %}
99 {% block site %}
99 {% endblock %}
100 {% endblock %}
100 </div>
101 </div>
101
102
102 {% block script %}
103 {% block script %}
103 {% endblock %}
104 {% endblock %}
104
105
105 </body>
106 </body>
106
107
107 </html>
108 </html>
@@ -1,728 +1,730 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 This module defines the things that are used in setup.py for building IPython
3 This module defines the things that are used in setup.py for building IPython
4
4
5 This includes:
5 This includes:
6
6
7 * The basic arguments to setup
7 * The basic arguments to setup
8 * Functions for finding things like packages, package data, etc.
8 * Functions for finding things like packages, package data, etc.
9 * A function for checking dependencies.
9 * A function for checking dependencies.
10 """
10 """
11
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 from __future__ import print_function
15 from __future__ import print_function
16
16
17 import errno
17 import errno
18 import os
18 import os
19 import sys
19 import sys
20
20
21 from distutils import log
21 from distutils import log
22 from distutils.command.build_py import build_py
22 from distutils.command.build_py import build_py
23 from distutils.command.build_scripts import build_scripts
23 from distutils.command.build_scripts import build_scripts
24 from distutils.command.install import install
24 from distutils.command.install import install
25 from distutils.command.install_scripts import install_scripts
25 from distutils.command.install_scripts import install_scripts
26 from distutils.cmd import Command
26 from distutils.cmd import Command
27 from fnmatch import fnmatch
27 from fnmatch import fnmatch
28 from glob import glob
28 from glob import glob
29 from subprocess import check_call
29 from subprocess import check_call
30
30
31 from setupext import install_data_ext
31 from setupext import install_data_ext
32
32
33 #-------------------------------------------------------------------------------
33 #-------------------------------------------------------------------------------
34 # Useful globals and utility functions
34 # Useful globals and utility functions
35 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
36
36
37 # A few handy globals
37 # A few handy globals
38 isfile = os.path.isfile
38 isfile = os.path.isfile
39 pjoin = os.path.join
39 pjoin = os.path.join
40 repo_root = os.path.dirname(os.path.abspath(__file__))
40 repo_root = os.path.dirname(os.path.abspath(__file__))
41
41
42 def oscmd(s):
42 def oscmd(s):
43 print(">", s)
43 print(">", s)
44 os.system(s)
44 os.system(s)
45
45
46 # Py3 compatibility hacks, without assuming IPython itself is installed with
46 # Py3 compatibility hacks, without assuming IPython itself is installed with
47 # the full py3compat machinery.
47 # the full py3compat machinery.
48
48
49 try:
49 try:
50 execfile
50 execfile
51 except NameError:
51 except NameError:
52 def execfile(fname, globs, locs=None):
52 def execfile(fname, globs, locs=None):
53 locs = locs or globs
53 locs = locs or globs
54 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
54 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
55
55
56 # A little utility we'll need below, since glob() does NOT allow you to do
56 # A little utility we'll need below, since glob() does NOT allow you to do
57 # exclusion on multiple endings!
57 # exclusion on multiple endings!
58 def file_doesnt_endwith(test,endings):
58 def file_doesnt_endwith(test,endings):
59 """Return true if test is a file and its name does NOT end with any
59 """Return true if test is a file and its name does NOT end with any
60 of the strings listed in endings."""
60 of the strings listed in endings."""
61 if not isfile(test):
61 if not isfile(test):
62 return False
62 return False
63 for e in endings:
63 for e in endings:
64 if test.endswith(e):
64 if test.endswith(e):
65 return False
65 return False
66 return True
66 return True
67
67
68 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
69 # Basic project information
69 # Basic project information
70 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
71
71
72 # release.py contains version, authors, license, url, keywords, etc.
72 # release.py contains version, authors, license, url, keywords, etc.
73 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
73 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
74
74
75 # Create a dict with the basic information
75 # Create a dict with the basic information
76 # This dict is eventually passed to setup after additional keys are added.
76 # This dict is eventually passed to setup after additional keys are added.
77 setup_args = dict(
77 setup_args = dict(
78 name = name,
78 name = name,
79 version = version,
79 version = version,
80 description = description,
80 description = description,
81 long_description = long_description,
81 long_description = long_description,
82 author = author,
82 author = author,
83 author_email = author_email,
83 author_email = author_email,
84 url = url,
84 url = url,
85 download_url = download_url,
85 download_url = download_url,
86 license = license,
86 license = license,
87 platforms = platforms,
87 platforms = platforms,
88 keywords = keywords,
88 keywords = keywords,
89 classifiers = classifiers,
89 classifiers = classifiers,
90 cmdclass = {'install_data': install_data_ext},
90 cmdclass = {'install_data': install_data_ext},
91 )
91 )
92
92
93
93
94 #---------------------------------------------------------------------------
94 #---------------------------------------------------------------------------
95 # Find packages
95 # Find packages
96 #---------------------------------------------------------------------------
96 #---------------------------------------------------------------------------
97
97
98 def find_packages():
98 def find_packages():
99 """
99 """
100 Find all of IPython's packages.
100 Find all of IPython's packages.
101 """
101 """
102 excludes = ['deathrow', 'quarantine']
102 excludes = ['deathrow', 'quarantine']
103 packages = []
103 packages = []
104 for dir,subdirs,files in os.walk('IPython'):
104 for dir,subdirs,files in os.walk('IPython'):
105 package = dir.replace(os.path.sep, '.')
105 package = dir.replace(os.path.sep, '.')
106 if any(package.startswith('IPython.'+exc) for exc in excludes):
106 if any(package.startswith('IPython.'+exc) for exc in excludes):
107 # package is to be excluded (e.g. deathrow)
107 # package is to be excluded (e.g. deathrow)
108 continue
108 continue
109 if '__init__.py' not in files:
109 if '__init__.py' not in files:
110 # not a package
110 # not a package
111 continue
111 continue
112 packages.append(package)
112 packages.append(package)
113 return packages
113 return packages
114
114
115 #---------------------------------------------------------------------------
115 #---------------------------------------------------------------------------
116 # Find package data
116 # Find package data
117 #---------------------------------------------------------------------------
117 #---------------------------------------------------------------------------
118
118
119 def find_package_data():
119 def find_package_data():
120 """
120 """
121 Find IPython's package_data.
121 Find IPython's package_data.
122 """
122 """
123 # This is not enough for these things to appear in an sdist.
123 # This is not enough for these things to appear in an sdist.
124 # We need to muck with the MANIFEST to get this to work
124 # We need to muck with the MANIFEST to get this to work
125
125
126 # exclude components and less from the walk;
126 # exclude components and less from the walk;
127 # we will build the components separately
127 # we will build the components separately
128 excludes = [
128 excludes = [
129 pjoin('static', 'components'),
129 pjoin('static', 'components'),
130 pjoin('static', '*', 'less'),
130 pjoin('static', '*', 'less'),
131 ]
131 ]
132
132
133 # walk notebook resources:
133 # walk notebook resources:
134 cwd = os.getcwd()
134 cwd = os.getcwd()
135 os.chdir(os.path.join('IPython', 'html'))
135 os.chdir(os.path.join('IPython', 'html'))
136 static_data = []
136 static_data = []
137 for parent, dirs, files in os.walk('static'):
137 for parent, dirs, files in os.walk('static'):
138 if any(fnmatch(parent, pat) for pat in excludes):
138 if any(fnmatch(parent, pat) for pat in excludes):
139 # prevent descending into subdirs
139 # prevent descending into subdirs
140 dirs[:] = []
140 dirs[:] = []
141 continue
141 continue
142 for f in files:
142 for f in files:
143 static_data.append(pjoin(parent, f))
143 static_data.append(pjoin(parent, f))
144
144
145 components = pjoin("static", "components")
145 components = pjoin("static", "components")
146 # select the components we actually need to install
146 # select the components we actually need to install
147 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
147 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
148 static_data.extend([
148 static_data.extend([
149 pjoin(components, "backbone", "backbone-min.js"),
149 pjoin(components, "backbone", "backbone-min.js"),
150 pjoin(components, "bootstrap", "js", "bootstrap.min.js"),
150 pjoin(components, "bootstrap", "js", "bootstrap.min.js"),
151 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
151 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
152 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
152 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
153 pjoin(components, "font-awesome", "fonts", "*.*"),
153 pjoin(components, "font-awesome", "fonts", "*.*"),
154 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
154 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
155 pjoin(components, "highlight.js", "build", "highlight.pack.js"),
155 pjoin(components, "highlight.js", "build", "highlight.pack.js"),
156 pjoin(components, "jquery", "jquery.min.js"),
156 pjoin(components, "jquery", "jquery.min.js"),
157 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
157 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
158 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
158 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
159 pjoin(components, "jquery-ui", "themes", "smoothness", "images", "*"),
159 pjoin(components, "jquery-ui", "themes", "smoothness", "images", "*"),
160 pjoin(components, "marked", "lib", "marked.js"),
160 pjoin(components, "marked", "lib", "marked.js"),
161 pjoin(components, "requirejs", "require.js"),
161 pjoin(components, "requirejs", "require.js"),
162 pjoin(components, "underscore", "underscore-min.js"),
162 pjoin(components, "underscore", "underscore-min.js"),
163 pjoin(components, "moment", "moment.js"),
164 pjoin(components, "moment", "min","moment.min.js"),
163 ])
165 ])
164
166
165 # Ship all of Codemirror's CSS and JS
167 # Ship all of Codemirror's CSS and JS
166 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
168 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
167 for f in files:
169 for f in files:
168 if f.endswith(('.js', '.css')):
170 if f.endswith(('.js', '.css')):
169 static_data.append(pjoin(parent, f))
171 static_data.append(pjoin(parent, f))
170
172
171 os.chdir(os.path.join('tests',))
173 os.chdir(os.path.join('tests',))
172 js_tests = glob('*.js') + glob('*/*.js')
174 js_tests = glob('*.js') + glob('*/*.js')
173
175
174 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
176 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
175 nbconvert_templates = [os.path.join(dirpath, '*.*')
177 nbconvert_templates = [os.path.join(dirpath, '*.*')
176 for dirpath, _, _ in os.walk('templates')]
178 for dirpath, _, _ in os.walk('templates')]
177
179
178 os.chdir(cwd)
180 os.chdir(cwd)
179
181
180 package_data = {
182 package_data = {
181 'IPython.config.profile' : ['README*', '*/*.py'],
183 'IPython.config.profile' : ['README*', '*/*.py'],
182 'IPython.core.tests' : ['*.png', '*.jpg'],
184 'IPython.core.tests' : ['*.png', '*.jpg'],
183 'IPython.lib.tests' : ['*.wav'],
185 'IPython.lib.tests' : ['*.wav'],
184 'IPython.testing.plugin' : ['*.txt'],
186 'IPython.testing.plugin' : ['*.txt'],
185 'IPython.html' : ['templates/*'] + static_data,
187 'IPython.html' : ['templates/*'] + static_data,
186 'IPython.html.tests' : js_tests,
188 'IPython.html.tests' : js_tests,
187 'IPython.qt.console' : ['resources/icon/*.svg'],
189 'IPython.qt.console' : ['resources/icon/*.svg'],
188 'IPython.nbconvert' : nbconvert_templates +
190 'IPython.nbconvert' : nbconvert_templates +
189 [
191 [
190 'tests/files/*.*',
192 'tests/files/*.*',
191 'exporters/tests/files/*.*',
193 'exporters/tests/files/*.*',
192 'preprocessors/tests/files/*.*',
194 'preprocessors/tests/files/*.*',
193 ],
195 ],
194 'IPython.nbconvert.filters' : ['marked.js'],
196 'IPython.nbconvert.filters' : ['marked.js'],
195 'IPython.nbformat' : ['tests/*.ipynb','v3/v3.withref.json']
197 'IPython.nbformat' : ['tests/*.ipynb','v3/v3.withref.json']
196 }
198 }
197
199
198 return package_data
200 return package_data
199
201
200
202
201 def check_package_data(package_data):
203 def check_package_data(package_data):
202 """verify that package_data globs make sense"""
204 """verify that package_data globs make sense"""
203 print("checking package data")
205 print("checking package data")
204 for pkg, data in package_data.items():
206 for pkg, data in package_data.items():
205 pkg_root = pjoin(*pkg.split('.'))
207 pkg_root = pjoin(*pkg.split('.'))
206 for d in data:
208 for d in data:
207 path = pjoin(pkg_root, d)
209 path = pjoin(pkg_root, d)
208 if '*' in path:
210 if '*' in path:
209 assert len(glob(path)) > 0, "No files match pattern %s" % path
211 assert len(glob(path)) > 0, "No files match pattern %s" % path
210 else:
212 else:
211 assert os.path.exists(path), "Missing package data: %s" % path
213 assert os.path.exists(path), "Missing package data: %s" % path
212
214
213
215
214 def check_package_data_first(command):
216 def check_package_data_first(command):
215 """decorator for checking package_data before running a given command
217 """decorator for checking package_data before running a given command
216
218
217 Probably only needs to wrap build_py
219 Probably only needs to wrap build_py
218 """
220 """
219 class DecoratedCommand(command):
221 class DecoratedCommand(command):
220 def run(self):
222 def run(self):
221 check_package_data(self.package_data)
223 check_package_data(self.package_data)
222 command.run(self)
224 command.run(self)
223 return DecoratedCommand
225 return DecoratedCommand
224
226
225
227
226 #---------------------------------------------------------------------------
228 #---------------------------------------------------------------------------
227 # Find data files
229 # Find data files
228 #---------------------------------------------------------------------------
230 #---------------------------------------------------------------------------
229
231
230 def make_dir_struct(tag,base,out_base):
232 def make_dir_struct(tag,base,out_base):
231 """Make the directory structure of all files below a starting dir.
233 """Make the directory structure of all files below a starting dir.
232
234
233 This is just a convenience routine to help build a nested directory
235 This is just a convenience routine to help build a nested directory
234 hierarchy because distutils is too stupid to do this by itself.
236 hierarchy because distutils is too stupid to do this by itself.
235
237
236 XXX - this needs a proper docstring!
238 XXX - this needs a proper docstring!
237 """
239 """
238
240
239 # we'll use these a lot below
241 # we'll use these a lot below
240 lbase = len(base)
242 lbase = len(base)
241 pathsep = os.path.sep
243 pathsep = os.path.sep
242 lpathsep = len(pathsep)
244 lpathsep = len(pathsep)
243
245
244 out = []
246 out = []
245 for (dirpath,dirnames,filenames) in os.walk(base):
247 for (dirpath,dirnames,filenames) in os.walk(base):
246 # we need to strip out the dirpath from the base to map it to the
248 # we need to strip out the dirpath from the base to map it to the
247 # output (installation) path. This requires possibly stripping the
249 # output (installation) path. This requires possibly stripping the
248 # path separator, because otherwise pjoin will not work correctly
250 # path separator, because otherwise pjoin will not work correctly
249 # (pjoin('foo/','/bar') returns '/bar').
251 # (pjoin('foo/','/bar') returns '/bar').
250
252
251 dp_eff = dirpath[lbase:]
253 dp_eff = dirpath[lbase:]
252 if dp_eff.startswith(pathsep):
254 if dp_eff.startswith(pathsep):
253 dp_eff = dp_eff[lpathsep:]
255 dp_eff = dp_eff[lpathsep:]
254 # The output path must be anchored at the out_base marker
256 # The output path must be anchored at the out_base marker
255 out_path = pjoin(out_base,dp_eff)
257 out_path = pjoin(out_base,dp_eff)
256 # Now we can generate the final filenames. Since os.walk only produces
258 # Now we can generate the final filenames. Since os.walk only produces
257 # filenames, we must join back with the dirpath to get full valid file
259 # filenames, we must join back with the dirpath to get full valid file
258 # paths:
260 # paths:
259 pfiles = [pjoin(dirpath,f) for f in filenames]
261 pfiles = [pjoin(dirpath,f) for f in filenames]
260 # Finally, generate the entry we need, which is a pari of (output
262 # Finally, generate the entry we need, which is a pari of (output
261 # path, files) for use as a data_files parameter in install_data.
263 # path, files) for use as a data_files parameter in install_data.
262 out.append((out_path, pfiles))
264 out.append((out_path, pfiles))
263
265
264 return out
266 return out
265
267
266
268
267 def find_data_files():
269 def find_data_files():
268 """
270 """
269 Find IPython's data_files.
271 Find IPython's data_files.
270
272
271 Just man pages at this point.
273 Just man pages at this point.
272 """
274 """
273
275
274 manpagebase = pjoin('share', 'man', 'man1')
276 manpagebase = pjoin('share', 'man', 'man1')
275
277
276 # Simple file lists can be made by hand
278 # Simple file lists can be made by hand
277 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
279 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
278 if not manpages:
280 if not manpages:
279 # When running from a source tree, the manpages aren't gzipped
281 # When running from a source tree, the manpages aren't gzipped
280 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
282 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
281
283
282 # And assemble the entire output list
284 # And assemble the entire output list
283 data_files = [ (manpagebase, manpages) ]
285 data_files = [ (manpagebase, manpages) ]
284
286
285 return data_files
287 return data_files
286
288
287
289
288 def make_man_update_target(manpage):
290 def make_man_update_target(manpage):
289 """Return a target_update-compliant tuple for the given manpage.
291 """Return a target_update-compliant tuple for the given manpage.
290
292
291 Parameters
293 Parameters
292 ----------
294 ----------
293 manpage : string
295 manpage : string
294 Name of the manpage, must include the section number (trailing number).
296 Name of the manpage, must include the section number (trailing number).
295
297
296 Example
298 Example
297 -------
299 -------
298
300
299 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
301 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
300 ('docs/man/ipython.1.gz',
302 ('docs/man/ipython.1.gz',
301 ['docs/man/ipython.1'],
303 ['docs/man/ipython.1'],
302 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
304 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
303 """
305 """
304 man_dir = pjoin('docs', 'man')
306 man_dir = pjoin('docs', 'man')
305 manpage_gz = manpage + '.gz'
307 manpage_gz = manpage + '.gz'
306 manpath = pjoin(man_dir, manpage)
308 manpath = pjoin(man_dir, manpage)
307 manpath_gz = pjoin(man_dir, manpage_gz)
309 manpath_gz = pjoin(man_dir, manpage_gz)
308 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
310 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
309 locals() )
311 locals() )
310 return (manpath_gz, [manpath], gz_cmd)
312 return (manpath_gz, [manpath], gz_cmd)
311
313
312 # The two functions below are copied from IPython.utils.path, so we don't need
314 # The two functions below are copied from IPython.utils.path, so we don't need
313 # to import IPython during setup, which fails on Python 3.
315 # to import IPython during setup, which fails on Python 3.
314
316
315 def target_outdated(target,deps):
317 def target_outdated(target,deps):
316 """Determine whether a target is out of date.
318 """Determine whether a target is out of date.
317
319
318 target_outdated(target,deps) -> 1/0
320 target_outdated(target,deps) -> 1/0
319
321
320 deps: list of filenames which MUST exist.
322 deps: list of filenames which MUST exist.
321 target: single filename which may or may not exist.
323 target: single filename which may or may not exist.
322
324
323 If target doesn't exist or is older than any file listed in deps, return
325 If target doesn't exist or is older than any file listed in deps, return
324 true, otherwise return false.
326 true, otherwise return false.
325 """
327 """
326 try:
328 try:
327 target_time = os.path.getmtime(target)
329 target_time = os.path.getmtime(target)
328 except os.error:
330 except os.error:
329 return 1
331 return 1
330 for dep in deps:
332 for dep in deps:
331 dep_time = os.path.getmtime(dep)
333 dep_time = os.path.getmtime(dep)
332 if dep_time > target_time:
334 if dep_time > target_time:
333 #print "For target",target,"Dep failed:",dep # dbg
335 #print "For target",target,"Dep failed:",dep # dbg
334 #print "times (dep,tar):",dep_time,target_time # dbg
336 #print "times (dep,tar):",dep_time,target_time # dbg
335 return 1
337 return 1
336 return 0
338 return 0
337
339
338
340
339 def target_update(target,deps,cmd):
341 def target_update(target,deps,cmd):
340 """Update a target with a given command given a list of dependencies.
342 """Update a target with a given command given a list of dependencies.
341
343
342 target_update(target,deps,cmd) -> runs cmd if target is outdated.
344 target_update(target,deps,cmd) -> runs cmd if target is outdated.
343
345
344 This is just a wrapper around target_outdated() which calls the given
346 This is just a wrapper around target_outdated() which calls the given
345 command if target is outdated."""
347 command if target is outdated."""
346
348
347 if target_outdated(target,deps):
349 if target_outdated(target,deps):
348 os.system(cmd)
350 os.system(cmd)
349
351
350 #---------------------------------------------------------------------------
352 #---------------------------------------------------------------------------
351 # Find scripts
353 # Find scripts
352 #---------------------------------------------------------------------------
354 #---------------------------------------------------------------------------
353
355
354 def find_entry_points():
356 def find_entry_points():
355 """Find IPython's scripts.
357 """Find IPython's scripts.
356
358
357 if entry_points is True:
359 if entry_points is True:
358 return setuptools entry_point-style definitions
360 return setuptools entry_point-style definitions
359 else:
361 else:
360 return file paths of plain scripts [default]
362 return file paths of plain scripts [default]
361
363
362 suffix is appended to script names if entry_points is True, so that the
364 suffix is appended to script names if entry_points is True, so that the
363 Python 3 scripts get named "ipython3" etc.
365 Python 3 scripts get named "ipython3" etc.
364 """
366 """
365 ep = [
367 ep = [
366 'ipython%s = IPython:start_ipython',
368 'ipython%s = IPython:start_ipython',
367 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
369 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
368 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
370 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
369 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
371 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
370 'iptest%s = IPython.testing.iptestcontroller:main',
372 'iptest%s = IPython.testing.iptestcontroller:main',
371 ]
373 ]
372 suffix = str(sys.version_info[0])
374 suffix = str(sys.version_info[0])
373 return [e % '' for e in ep] + [e % suffix for e in ep]
375 return [e % '' for e in ep] + [e % suffix for e in ep]
374
376
375 script_src = """#!{executable}
377 script_src = """#!{executable}
376 # This script was automatically generated by setup.py
378 # This script was automatically generated by setup.py
377 if __name__ == '__main__':
379 if __name__ == '__main__':
378 from {mod} import {func}
380 from {mod} import {func}
379 {func}()
381 {func}()
380 """
382 """
381
383
382 class build_scripts_entrypt(build_scripts):
384 class build_scripts_entrypt(build_scripts):
383 def run(self):
385 def run(self):
384 self.mkpath(self.build_dir)
386 self.mkpath(self.build_dir)
385 outfiles = []
387 outfiles = []
386 for script in find_entry_points():
388 for script in find_entry_points():
387 name, entrypt = script.split('=')
389 name, entrypt = script.split('=')
388 name = name.strip()
390 name = name.strip()
389 entrypt = entrypt.strip()
391 entrypt = entrypt.strip()
390 outfile = os.path.join(self.build_dir, name)
392 outfile = os.path.join(self.build_dir, name)
391 outfiles.append(outfile)
393 outfiles.append(outfile)
392 print('Writing script to', outfile)
394 print('Writing script to', outfile)
393
395
394 mod, func = entrypt.split(':')
396 mod, func = entrypt.split(':')
395 with open(outfile, 'w') as f:
397 with open(outfile, 'w') as f:
396 f.write(script_src.format(executable=sys.executable,
398 f.write(script_src.format(executable=sys.executable,
397 mod=mod, func=func))
399 mod=mod, func=func))
398
400
399 return outfiles, outfiles
401 return outfiles, outfiles
400
402
401 class install_lib_symlink(Command):
403 class install_lib_symlink(Command):
402 user_options = [
404 user_options = [
403 ('install-dir=', 'd', "directory to install to"),
405 ('install-dir=', 'd', "directory to install to"),
404 ]
406 ]
405
407
406 def initialize_options(self):
408 def initialize_options(self):
407 self.install_dir = None
409 self.install_dir = None
408
410
409 def finalize_options(self):
411 def finalize_options(self):
410 self.set_undefined_options('symlink',
412 self.set_undefined_options('symlink',
411 ('install_lib', 'install_dir'),
413 ('install_lib', 'install_dir'),
412 )
414 )
413
415
414 def run(self):
416 def run(self):
415 if sys.platform == 'win32':
417 if sys.platform == 'win32':
416 raise Exception("This doesn't work on Windows.")
418 raise Exception("This doesn't work on Windows.")
417 pkg = os.path.join(os.getcwd(), 'IPython')
419 pkg = os.path.join(os.getcwd(), 'IPython')
418 dest = os.path.join(self.install_dir, 'IPython')
420 dest = os.path.join(self.install_dir, 'IPython')
419 if os.path.islink(dest):
421 if os.path.islink(dest):
420 print('removing existing symlink at %s' % dest)
422 print('removing existing symlink at %s' % dest)
421 os.unlink(dest)
423 os.unlink(dest)
422 print('symlinking %s -> %s' % (pkg, dest))
424 print('symlinking %s -> %s' % (pkg, dest))
423 os.symlink(pkg, dest)
425 os.symlink(pkg, dest)
424
426
425 class unsymlink(install):
427 class unsymlink(install):
426 def run(self):
428 def run(self):
427 dest = os.path.join(self.install_lib, 'IPython')
429 dest = os.path.join(self.install_lib, 'IPython')
428 if os.path.islink(dest):
430 if os.path.islink(dest):
429 print('removing symlink at %s' % dest)
431 print('removing symlink at %s' % dest)
430 os.unlink(dest)
432 os.unlink(dest)
431 else:
433 else:
432 print('No symlink exists at %s' % dest)
434 print('No symlink exists at %s' % dest)
433
435
434 class install_symlinked(install):
436 class install_symlinked(install):
435 def run(self):
437 def run(self):
436 if sys.platform == 'win32':
438 if sys.platform == 'win32':
437 raise Exception("This doesn't work on Windows.")
439 raise Exception("This doesn't work on Windows.")
438
440
439 # Run all sub-commands (at least those that need to be run)
441 # Run all sub-commands (at least those that need to be run)
440 for cmd_name in self.get_sub_commands():
442 for cmd_name in self.get_sub_commands():
441 self.run_command(cmd_name)
443 self.run_command(cmd_name)
442
444
443 # 'sub_commands': a list of commands this command might have to run to
445 # 'sub_commands': a list of commands this command might have to run to
444 # get its work done. See cmd.py for more info.
446 # get its work done. See cmd.py for more info.
445 sub_commands = [('install_lib_symlink', lambda self:True),
447 sub_commands = [('install_lib_symlink', lambda self:True),
446 ('install_scripts_sym', lambda self:True),
448 ('install_scripts_sym', lambda self:True),
447 ]
449 ]
448
450
449 class install_scripts_for_symlink(install_scripts):
451 class install_scripts_for_symlink(install_scripts):
450 """Redefined to get options from 'symlink' instead of 'install'.
452 """Redefined to get options from 'symlink' instead of 'install'.
451
453
452 I love distutils almost as much as I love setuptools.
454 I love distutils almost as much as I love setuptools.
453 """
455 """
454 def finalize_options(self):
456 def finalize_options(self):
455 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
457 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
456 self.set_undefined_options('symlink',
458 self.set_undefined_options('symlink',
457 ('install_scripts', 'install_dir'),
459 ('install_scripts', 'install_dir'),
458 ('force', 'force'),
460 ('force', 'force'),
459 ('skip_build', 'skip_build'),
461 ('skip_build', 'skip_build'),
460 )
462 )
461
463
462 #---------------------------------------------------------------------------
464 #---------------------------------------------------------------------------
463 # Verify all dependencies
465 # Verify all dependencies
464 #---------------------------------------------------------------------------
466 #---------------------------------------------------------------------------
465
467
466 def check_for_dependencies():
468 def check_for_dependencies():
467 """Check for IPython's dependencies.
469 """Check for IPython's dependencies.
468
470
469 This function should NOT be called if running under setuptools!
471 This function should NOT be called if running under setuptools!
470 """
472 """
471 from setupext.setupext import (
473 from setupext.setupext import (
472 print_line, print_raw, print_status,
474 print_line, print_raw, print_status,
473 check_for_sphinx, check_for_pygments,
475 check_for_sphinx, check_for_pygments,
474 check_for_nose, check_for_pexpect,
476 check_for_nose, check_for_pexpect,
475 check_for_pyzmq, check_for_readline,
477 check_for_pyzmq, check_for_readline,
476 check_for_jinja2, check_for_tornado
478 check_for_jinja2, check_for_tornado
477 )
479 )
478 print_line()
480 print_line()
479 print_raw("BUILDING IPYTHON")
481 print_raw("BUILDING IPYTHON")
480 print_status('python', sys.version)
482 print_status('python', sys.version)
481 print_status('platform', sys.platform)
483 print_status('platform', sys.platform)
482 if sys.platform == 'win32':
484 if sys.platform == 'win32':
483 print_status('Windows version', sys.getwindowsversion())
485 print_status('Windows version', sys.getwindowsversion())
484
486
485 print_raw("")
487 print_raw("")
486 print_raw("OPTIONAL DEPENDENCIES")
488 print_raw("OPTIONAL DEPENDENCIES")
487
489
488 check_for_sphinx()
490 check_for_sphinx()
489 check_for_pygments()
491 check_for_pygments()
490 check_for_nose()
492 check_for_nose()
491 if os.name == 'posix':
493 if os.name == 'posix':
492 check_for_pexpect()
494 check_for_pexpect()
493 check_for_pyzmq()
495 check_for_pyzmq()
494 check_for_tornado()
496 check_for_tornado()
495 check_for_readline()
497 check_for_readline()
496 check_for_jinja2()
498 check_for_jinja2()
497
499
498 #---------------------------------------------------------------------------
500 #---------------------------------------------------------------------------
499 # VCS related
501 # VCS related
500 #---------------------------------------------------------------------------
502 #---------------------------------------------------------------------------
501
503
502 # utils.submodule has checks for submodule status
504 # utils.submodule has checks for submodule status
503 execfile(pjoin('IPython','utils','submodule.py'), globals())
505 execfile(pjoin('IPython','utils','submodule.py'), globals())
504
506
505 class UpdateSubmodules(Command):
507 class UpdateSubmodules(Command):
506 """Update git submodules
508 """Update git submodules
507
509
508 IPython's external javascript dependencies live in a separate repo.
510 IPython's external javascript dependencies live in a separate repo.
509 """
511 """
510 description = "Update git submodules"
512 description = "Update git submodules"
511 user_options = []
513 user_options = []
512
514
513 def initialize_options(self):
515 def initialize_options(self):
514 pass
516 pass
515
517
516 def finalize_options(self):
518 def finalize_options(self):
517 pass
519 pass
518
520
519 def run(self):
521 def run(self):
520 failure = False
522 failure = False
521 try:
523 try:
522 self.spawn('git submodule init'.split())
524 self.spawn('git submodule init'.split())
523 self.spawn('git submodule update --recursive'.split())
525 self.spawn('git submodule update --recursive'.split())
524 except Exception as e:
526 except Exception as e:
525 failure = e
527 failure = e
526 print(e)
528 print(e)
527
529
528 if not check_submodule_status(repo_root) == 'clean':
530 if not check_submodule_status(repo_root) == 'clean':
529 print("submodules could not be checked out")
531 print("submodules could not be checked out")
530 sys.exit(1)
532 sys.exit(1)
531
533
532
534
533 def git_prebuild(pkg_dir, build_cmd=build_py):
535 def git_prebuild(pkg_dir, build_cmd=build_py):
534 """Return extended build or sdist command class for recording commit
536 """Return extended build or sdist command class for recording commit
535
537
536 records git commit in IPython.utils._sysinfo.commit
538 records git commit in IPython.utils._sysinfo.commit
537
539
538 for use in IPython.utils.sysinfo.sys_info() calls after installation.
540 for use in IPython.utils.sysinfo.sys_info() calls after installation.
539
541
540 Also ensures that submodules exist prior to running
542 Also ensures that submodules exist prior to running
541 """
543 """
542
544
543 class MyBuildPy(build_cmd):
545 class MyBuildPy(build_cmd):
544 ''' Subclass to write commit data into installation tree '''
546 ''' Subclass to write commit data into installation tree '''
545 def run(self):
547 def run(self):
546 build_cmd.run(self)
548 build_cmd.run(self)
547 # this one will only fire for build commands
549 # this one will only fire for build commands
548 if hasattr(self, 'build_lib'):
550 if hasattr(self, 'build_lib'):
549 self._record_commit(self.build_lib)
551 self._record_commit(self.build_lib)
550
552
551 def make_release_tree(self, base_dir, files):
553 def make_release_tree(self, base_dir, files):
552 # this one will fire for sdist
554 # this one will fire for sdist
553 build_cmd.make_release_tree(self, base_dir, files)
555 build_cmd.make_release_tree(self, base_dir, files)
554 self._record_commit(base_dir)
556 self._record_commit(base_dir)
555
557
556 def _record_commit(self, base_dir):
558 def _record_commit(self, base_dir):
557 import subprocess
559 import subprocess
558 proc = subprocess.Popen('git rev-parse --short HEAD',
560 proc = subprocess.Popen('git rev-parse --short HEAD',
559 stdout=subprocess.PIPE,
561 stdout=subprocess.PIPE,
560 stderr=subprocess.PIPE,
562 stderr=subprocess.PIPE,
561 shell=True)
563 shell=True)
562 repo_commit, _ = proc.communicate()
564 repo_commit, _ = proc.communicate()
563 repo_commit = repo_commit.strip().decode("ascii")
565 repo_commit = repo_commit.strip().decode("ascii")
564
566
565 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
567 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
566 if os.path.isfile(out_pth) and not repo_commit:
568 if os.path.isfile(out_pth) and not repo_commit:
567 # nothing to write, don't clobber
569 # nothing to write, don't clobber
568 return
570 return
569
571
570 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
572 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
571
573
572 # remove to avoid overwriting original via hard link
574 # remove to avoid overwriting original via hard link
573 try:
575 try:
574 os.remove(out_pth)
576 os.remove(out_pth)
575 except (IOError, OSError):
577 except (IOError, OSError):
576 pass
578 pass
577 with open(out_pth, 'w') as out_file:
579 with open(out_pth, 'w') as out_file:
578 out_file.writelines([
580 out_file.writelines([
579 '# GENERATED BY setup.py\n',
581 '# GENERATED BY setup.py\n',
580 'commit = "%s"\n' % repo_commit,
582 'commit = "%s"\n' % repo_commit,
581 ])
583 ])
582 return require_submodules(MyBuildPy)
584 return require_submodules(MyBuildPy)
583
585
584
586
585 def require_submodules(command):
587 def require_submodules(command):
586 """decorator for instructing a command to check for submodules before running"""
588 """decorator for instructing a command to check for submodules before running"""
587 class DecoratedCommand(command):
589 class DecoratedCommand(command):
588 def run(self):
590 def run(self):
589 if not check_submodule_status(repo_root) == 'clean':
591 if not check_submodule_status(repo_root) == 'clean':
590 print("submodules missing! Run `setup.py submodule` and try again")
592 print("submodules missing! Run `setup.py submodule` and try again")
591 sys.exit(1)
593 sys.exit(1)
592 command.run(self)
594 command.run(self)
593 return DecoratedCommand
595 return DecoratedCommand
594
596
595 #---------------------------------------------------------------------------
597 #---------------------------------------------------------------------------
596 # bdist related
598 # bdist related
597 #---------------------------------------------------------------------------
599 #---------------------------------------------------------------------------
598
600
599 def get_bdist_wheel():
601 def get_bdist_wheel():
600 """Construct bdist_wheel command for building wheels
602 """Construct bdist_wheel command for building wheels
601
603
602 Constructs py2-none-any tag, instead of py2.7-none-any
604 Constructs py2-none-any tag, instead of py2.7-none-any
603 """
605 """
604 class RequiresWheel(Command):
606 class RequiresWheel(Command):
605 description = "Dummy command for missing bdist_wheel"
607 description = "Dummy command for missing bdist_wheel"
606 user_options = []
608 user_options = []
607
609
608 def initialize_options(self):
610 def initialize_options(self):
609 pass
611 pass
610
612
611 def finalize_options(self):
613 def finalize_options(self):
612 pass
614 pass
613
615
614 def run(self):
616 def run(self):
615 print("bdist_wheel requires the wheel package")
617 print("bdist_wheel requires the wheel package")
616 sys.exit(1)
618 sys.exit(1)
617
619
618 if 'setuptools' not in sys.modules:
620 if 'setuptools' not in sys.modules:
619 return RequiresWheel
621 return RequiresWheel
620 else:
622 else:
621 try:
623 try:
622 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
624 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
623 except ImportError:
625 except ImportError:
624 return RequiresWheel
626 return RequiresWheel
625
627
626 class bdist_wheel_tag(bdist_wheel):
628 class bdist_wheel_tag(bdist_wheel):
627
629
628 def add_requirements(self, metadata_path):
630 def add_requirements(self, metadata_path):
629 """transform platform-dependent requirements"""
631 """transform platform-dependent requirements"""
630 pkg_info = read_pkg_info(metadata_path)
632 pkg_info = read_pkg_info(metadata_path)
631 # pkg_info is an email.Message object (?!)
633 # pkg_info is an email.Message object (?!)
632 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
634 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
633 # and transform them to conditionals
635 # and transform them to conditionals
634 requires = pkg_info.get_all('Requires-Dist')
636 requires = pkg_info.get_all('Requires-Dist')
635 del pkg_info['Requires-Dist']
637 del pkg_info['Requires-Dist']
636 def _remove_startswith(lis, prefix):
638 def _remove_startswith(lis, prefix):
637 """like list.remove, but with startswith instead of =="""
639 """like list.remove, but with startswith instead of =="""
638 found = False
640 found = False
639 for idx, item in enumerate(lis):
641 for idx, item in enumerate(lis):
640 if item.startswith(prefix):
642 if item.startswith(prefix):
641 found = True
643 found = True
642 break
644 break
643 if found:
645 if found:
644 lis.pop(idx)
646 lis.pop(idx)
645
647
646 for pkg in ("gnureadline", "pyreadline", "mock"):
648 for pkg in ("gnureadline", "pyreadline", "mock"):
647 _remove_startswith(requires, pkg)
649 _remove_startswith(requires, pkg)
648 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
650 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
649 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
651 requires.append("pyreadline (>=2.0); extra == 'terminal' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
650 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
652 requires.append("pyreadline (>=2.0); extra == 'all' and sys.platform == 'win32' and platform.python_implementation == 'CPython'")
651 requires.append("mock; extra == 'test' and python_version < '3.3'")
653 requires.append("mock; extra == 'test' and python_version < '3.3'")
652 for r in requires:
654 for r in requires:
653 pkg_info['Requires-Dist'] = r
655 pkg_info['Requires-Dist'] = r
654 write_pkg_info(metadata_path, pkg_info)
656 write_pkg_info(metadata_path, pkg_info)
655
657
656 return bdist_wheel_tag
658 return bdist_wheel_tag
657
659
658 #---------------------------------------------------------------------------
660 #---------------------------------------------------------------------------
659 # Notebook related
661 # Notebook related
660 #---------------------------------------------------------------------------
662 #---------------------------------------------------------------------------
661
663
662 class CompileCSS(Command):
664 class CompileCSS(Command):
663 """Recompile Notebook CSS
665 """Recompile Notebook CSS
664
666
665 Regenerate the compiled CSS from LESS sources.
667 Regenerate the compiled CSS from LESS sources.
666
668
667 Requires various dev dependencies, such as fabric and lessc.
669 Requires various dev dependencies, such as fabric and lessc.
668 """
670 """
669 description = "Recompile Notebook CSS"
671 description = "Recompile Notebook CSS"
670 user_options = [
672 user_options = [
671 ('minify', 'x', "minify CSS"),
673 ('minify', 'x', "minify CSS"),
672 ('force', 'f', "force recompilation of CSS"),
674 ('force', 'f', "force recompilation of CSS"),
673 ]
675 ]
674
676
675 def initialize_options(self):
677 def initialize_options(self):
676 self.minify = False
678 self.minify = False
677 self.force = False
679 self.force = False
678
680
679 def finalize_options(self):
681 def finalize_options(self):
680 self.minify = bool(self.minify)
682 self.minify = bool(self.minify)
681 self.force = bool(self.force)
683 self.force = bool(self.force)
682
684
683 def run(self):
685 def run(self):
684 check_call([
686 check_call([
685 "fab",
687 "fab",
686 "css:minify=%s,force=%s" % (self.minify, self.force),
688 "css:minify=%s,force=%s" % (self.minify, self.force),
687 ], cwd=pjoin(repo_root, "IPython", "html"),
689 ], cwd=pjoin(repo_root, "IPython", "html"),
688 )
690 )
689
691
690
692
691 class JavascriptVersion(Command):
693 class JavascriptVersion(Command):
692 """write the javascript version to notebook javascript"""
694 """write the javascript version to notebook javascript"""
693 description = "Write IPython version to javascript"
695 description = "Write IPython version to javascript"
694 user_options = []
696 user_options = []
695
697
696 def initialize_options(self):
698 def initialize_options(self):
697 pass
699 pass
698
700
699 def finalize_options(self):
701 def finalize_options(self):
700 pass
702 pass
701
703
702 def run(self):
704 def run(self):
703 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
705 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
704 with open(nsfile) as f:
706 with open(nsfile) as f:
705 lines = f.readlines()
707 lines = f.readlines()
706 with open(nsfile, 'w') as f:
708 with open(nsfile, 'w') as f:
707 for line in lines:
709 for line in lines:
708 if line.startswith("IPython.version"):
710 if line.startswith("IPython.version"):
709 line = 'IPython.version = "{0}";\n'.format(version)
711 line = 'IPython.version = "{0}";\n'.format(version)
710 f.write(line)
712 f.write(line)
711
713
712
714
713 def css_js_prerelease(command, strict=True):
715 def css_js_prerelease(command, strict=True):
714 """decorator for building js/minified css prior to a release"""
716 """decorator for building js/minified css prior to a release"""
715 class DecoratedCommand(command):
717 class DecoratedCommand(command):
716 def run(self):
718 def run(self):
717 self.distribution.run_command('jsversion')
719 self.distribution.run_command('jsversion')
718 css = self.distribution.get_command_obj('css')
720 css = self.distribution.get_command_obj('css')
719 css.minify = True
721 css.minify = True
720 try:
722 try:
721 self.distribution.run_command('css')
723 self.distribution.run_command('css')
722 except Exception as e:
724 except Exception as e:
723 if strict:
725 if strict:
724 raise
726 raise
725 else:
727 else:
726 log.warn("Failed to build css sourcemaps: %s" % e)
728 log.warn("Failed to build css sourcemaps: %s" % e)
727 command.run(self)
729 command.run(self)
728 return DecoratedCommand
730 return DecoratedCommand
General Comments 0
You need to be logged in to leave comments. Login now