##// END OF EJS Templates
Merge pull request #6950 from minrk/new-new-dropdown...
Matthias Bussonnier -
r19280:f1957916 merge
parent child Browse files
Show More
@@ -0,0 +1,135 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 'jquery',
6 'base/js/namespace',
7 'base/js/utils',
8 'base/js/dialog',
9 ], function ($, IPython, utils, dialog) {
10 "use strict";
11
12 var NewNotebookWidget = function (selector, options) {
13 this.selector = selector;
14 this.base_url = options.base_url;
15 this.notebook_path = options.notebook_path;
16 this.contents = options.contents;
17 this.default_kernel = null;
18 this.config = options.config;
19 this.kernelspecs = {};
20 if (this.selector !== undefined) {
21 this.element = $(selector);
22 this.request_kernelspecs();
23 }
24 this.bind_events();
25 };
26
27 NewNotebookWidget.prototype.bind_events = function () {
28 var that = this;
29 this.element.find('#new_notebook').click(function () {
30 that.new_notebook();
31 });
32 };
33
34 NewNotebookWidget.prototype.request_kernelspecs = function () {
35 /** request and then load kernel specs */
36 var url = utils.url_join_encode(this.base_url, 'api/kernelspecs');
37 utils.promising_ajax(url).then($.proxy(this._load_kernelspecs, this));
38 };
39
40 NewNotebookWidget.prototype._load_kernelspecs = function (data) {
41 /** load kernelspec list */
42 var that = this;
43 this.kernelspecs = data.kernelspecs;
44 var menu = this.element.find("#new-notebook-menu");
45 var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
46 var da = data.kernelspecs[a].display_name;
47 var db = data.kernelspecs[b].display_name;
48 if (da === db) {
49 return 0;
50 } else if (da > db) {
51 return 1;
52 } else {
53 return -1;
54 }
55 });
56 for (var i = 0; i < keys.length; i++) {
57 var ks = this.kernelspecs[keys[i]];
58 var li = $("<li>")
59 .attr("id", "kernel-" +ks.name)
60 .data('kernelspec', ks).append(
61 $('<a>')
62 .attr('href', '#')
63 .click($.proxy(this.new_notebook, this, ks.name))
64 .text(ks.display_name)
65 .attr('title', 'Create a new notebook with ' + ks.display_name)
66 );
67 menu.append(li);
68 }
69 this.config.loaded.then(function () {
70 that._load_default_kernelspec(data['default']);
71 });
72 };
73
74 NewNotebookWidget.prototype._load_default_kernelspec = function (default_name) {
75 /** load default kernelspec name from config, if defined */
76 if (this.config.data.NewNotebookWidget &&
77 this.config.data.NewNotebookWidget.default_kernel &&
78 this.kernelspecs[this.config.data.NewNotebookWidget.default_kernel] !== undefined
79 ) {
80 default_name = this.config.data.NewNotebookWidget.default_kernel;
81 }
82 this.set_default_kernel(default_name);
83 };
84
85 NewNotebookWidget.prototype.set_default_kernel = function (kernel_name) {
86 /** select the current default kernel */
87 this.default_kernel = kernel_name;
88 this.config.update({
89 NewNotebookWidget: {
90 default_kernel: kernel_name
91 }
92 });
93 var spec = this.kernelspecs[kernel_name];
94 var display_name;
95 if (spec) {
96 display_name = spec.display_name;
97 this.element.find("#current-kernel")
98 .text(display_name)
99 .attr('title', display_name + " is the default kernel for new notebooks");
100 } else {
101 display_name = 'default kernel';
102 }
103 this.element.find("#new_notebook").attr('title',
104 'Create a new notebook with ' + display_name
105 );
106 };
107
108 NewNotebookWidget.prototype.new_notebook = function (kernel_name) {
109 /** create and open a new notebook */
110 var that = this;
111 kernel_name = kernel_name || this.default_kernel;
112 var w = window.open();
113 this.contents.new_untitled(that.notebook_path, {type: "notebook"}).then(
114 function (data) {
115 var url = utils.url_join_encode(
116 that.base_url, 'notebooks', data.path
117 );
118 if (kernel_name) {
119 url += "?kernel_name=" + kernel_name;
120 }
121 w.location = url;
122 },
123 function (error) {
124 w.close();
125 dialog.modal({
126 title : 'Creating Notebook Failed',
127 body : "The error was: " + error.message,
128 buttons : {'OK' : {'class' : 'btn-primary'}}
129 });
130 }
131 );
132 };
133
134 return {'NewNotebookWidget': NewNotebookWidget};
135 });
@@ -7,8 +7,6 b' from tornado import web'
7
7
8 from ...base.handlers import IPythonHandler, json_errors
8 from ...base.handlers import IPythonHandler, json_errors
9
9
10 from IPython.kernel.kernelspec import _pythonfirst
11
12
10
13 class MainKernelSpecHandler(IPythonHandler):
11 class MainKernelSpecHandler(IPythonHandler):
14 SUPPORTED_METHODS = ('GET',)
12 SUPPORTED_METHODS = ('GET',)
@@ -17,18 +15,21 b' class MainKernelSpecHandler(IPythonHandler):'
17 @json_errors
15 @json_errors
18 def get(self):
16 def get(self):
19 ksm = self.kernel_spec_manager
17 ksm = self.kernel_spec_manager
20 results = []
18 km = self.kernel_manager
21 for kernel_name in sorted(ksm.find_kernel_specs(), key=_pythonfirst):
19 model = {}
20 model['default'] = km.default_kernel_name
21 model['kernelspecs'] = specs = {}
22 for kernel_name in ksm.find_kernel_specs():
22 try:
23 try:
23 d = ksm.get_kernel_spec(kernel_name).to_dict()
24 d = ksm.get_kernel_spec(kernel_name).to_dict()
24 except Exception:
25 except Exception:
25 self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True)
26 self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True)
26 continue
27 continue
27 d['name'] = kernel_name
28 d['name'] = kernel_name
28 results.append(d)
29 specs[kernel_name] = d
29
30
30 self.set_header("Content-Type", 'application/json')
31 self.set_header("Content-Type", 'application/json')
31 self.finish(json.dumps(results))
32 self.finish(json.dumps(model))
32
33
33
34
34 class KernelSpecHandler(IPythonHandler):
35 class KernelSpecHandler(IPythonHandler):
@@ -78,16 +78,22 b' class APITest(NotebookTestBase):'
78 with open(pjoin(bad_kernel_dir, 'kernel.json'), 'w') as f:
78 with open(pjoin(bad_kernel_dir, 'kernel.json'), 'w') as f:
79 f.write("garbage")
79 f.write("garbage")
80
80
81 specs = self.ks_api.list().json()
81 model = self.ks_api.list().json()
82 assert isinstance(specs, list)
82 assert isinstance(model, dict)
83 self.assertEqual(model['default'], NATIVE_KERNEL_NAME)
84 specs = model['kernelspecs']
85 assert isinstance(specs, dict)
83 # 2: the sample kernelspec created in setUp, and the native Python kernel
86 # 2: the sample kernelspec created in setUp, and the native Python kernel
84 self.assertGreaterEqual(len(specs), 2)
87 self.assertGreaterEqual(len(specs), 2)
85
88
86 shutil.rmtree(bad_kernel_dir)
89 shutil.rmtree(bad_kernel_dir)
87
90
88 def test_list_kernelspecs(self):
91 def test_list_kernelspecs(self):
89 specs = self.ks_api.list().json()
92 model = self.ks_api.list().json()
90 assert isinstance(specs, list)
93 assert isinstance(model, dict)
94 self.assertEqual(model['default'], NATIVE_KERNEL_NAME)
95 specs = model['kernelspecs']
96 assert isinstance(specs, dict)
91
97
92 # 2: the sample kernelspec created in setUp, and the native Python kernel
98 # 2: the sample kernelspec created in setUp, and the native Python kernel
93 self.assertGreaterEqual(len(specs), 2)
99 self.assertGreaterEqual(len(specs), 2)
@@ -98,8 +104,8 b' class APITest(NotebookTestBase):'
98 def is_default_kernelspec(s):
104 def is_default_kernelspec(s):
99 return s['name'] == NATIVE_KERNEL_NAME and s['display_name'].startswith("IPython")
105 return s['name'] == NATIVE_KERNEL_NAME and s['display_name'].startswith("IPython")
100
106
101 assert any(is_sample_kernelspec(s) for s in specs), specs
107 assert any(is_sample_kernelspec(s) for s in specs.values()), specs
102 assert any(is_default_kernelspec(s) for s in specs), specs
108 assert any(is_default_kernelspec(s) for s in specs.values()), specs
103
109
104 def test_get_kernelspec(self):
110 def test_get_kernelspec(self):
105 spec = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive
111 spec = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive
@@ -541,7 +541,16 b' define(['
541 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
541 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
542 return OSName;
542 return OSName;
543 })();
543 })();
544
544
545 var get_url_param = function (name) {
546 // get a URL parameter. I cannot believe we actually need this.
547 // Based on http://stackoverflow.com/a/25359264/938949
548 var match = new RegExp('[\?&]' + name + '=([^&]*)').exec(window.location.search);
549 if (match){
550 return decodeURIComponent(match[1] || '');
551 }
552 };
553
545 var is_or_has = function (a, b) {
554 var is_or_has = function (a, b) {
546 /**
555 /**
547 * Is b a child of a or a itself?
556 * Is b a child of a or a itself?
@@ -632,6 +641,7 b' define(['
632 * Like $.ajax, but returning an ES6 promise. success and error settings
641 * Like $.ajax, but returning an ES6 promise. success and error settings
633 * will be ignored.
642 * will be ignored.
634 */
643 */
644 settings = settings || {};
635 return new Promise(function(resolve, reject) {
645 return new Promise(function(resolve, reject) {
636 settings.success = function(data, status, jqXHR) {
646 settings.success = function(data, status, jqXHR) {
637 resolve(data);
647 resolve(data);
@@ -813,6 +823,7 b' define(['
813 from_absolute_cursor_pos : from_absolute_cursor_pos,
823 from_absolute_cursor_pos : from_absolute_cursor_pos,
814 browser : browser,
824 browser : browser,
815 platform: platform,
825 platform: platform,
826 get_url_param: get_url_param,
816 is_or_has : is_or_has,
827 is_or_has : is_or_has,
817 is_focused : is_focused,
828 is_focused : is_focused,
818 mergeopt: mergeopt,
829 mergeopt: mergeopt,
@@ -25,16 +25,27 b' define(['
25
25
26 KernelSelector.prototype.request_kernelspecs = function() {
26 KernelSelector.prototype.request_kernelspecs = function() {
27 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
27 var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
28 $.ajax(url, {success: $.proxy(this._got_kernelspecs, this)});
28 utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this));
29 };
29 };
30
30
31 KernelSelector.prototype._got_kernelspecs = function(data, status, xhr) {
31 KernelSelector.prototype._got_kernelspecs = function(data) {
32 this.kernelspecs = {};
32 this.kernelspecs = data.kernelspecs;
33 var menu = this.element.find("#kernel_selector");
33 var menu = this.element.find("#kernel_selector");
34 var change_kernel_submenu = $("#menu-change-kernel-submenu");
34 var change_kernel_submenu = $("#menu-change-kernel-submenu");
35 for (var i = 0; i < data.length; i++) {
35 var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
36 var ks = data[i];
36 // sort by display_name
37 this.kernelspecs[ks.name] = ks;
37 var da = data.kernelspecs[a].display_name;
38 var db = data.kernelspecs[b].display_name;
39 if (da === db) {
40 return 0;
41 } else if (da > db) {
42 return 1;
43 } else {
44 return -1;
45 }
46 });
47 for (var i = 0; i < keys.length; i++) {
48 var ks = this.kernelspecs[keys[i]];
38 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
49 var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
39 .attr('href', '#')
50 .attr('href', '#')
40 .click($.proxy(this.change_kernel, this, ks.name))
51 .click($.proxy(this.change_kernel, this, ks.name))
@@ -2275,9 +2275,13 b' define(['
2275 // Create the session after the notebook is completely loaded to prevent
2275 // Create the session after the notebook is completely loaded to prevent
2276 // code execution upon loading, which is a security risk.
2276 // code execution upon loading, which is a security risk.
2277 if (this.session === null) {
2277 if (this.session === null) {
2278 var kernelspec = this.metadata.kernelspec || {};
2278 var kernel_name;
2279 var kernel_name = kernelspec.name;
2279 if (this.metadata.kernelspec) {
2280
2280 var kernelspec = this.metadata.kernelspec || {};
2281 kernel_name = kernelspec.name;
2282 } else {
2283 kernel_name = utils.get_url_param('kernel_name');
2284 }
2281 this.start_session(kernel_name);
2285 this.start_session(kernel_name);
2282 }
2286 }
2283 // load our checkpoint list
2287 // load our checkpoint list
@@ -7999,14 +7999,15 b' span#login_widget > .button .badge,'
7999 margin: 0;
7999 margin: 0;
8000 }
8000 }
8001 .alternate_upload input.fileinput {
8001 .alternate_upload input.fileinput {
8002 background-color: red;
8002 display: inline;
8003 position: relative;
8004 opacity: 0;
8003 opacity: 0;
8005 z-index: 2;
8004 z-index: 2;
8006 width: 295px;
8005 width: 12ex;
8007 margin-left: 163px;
8006 margin-right: -12ex;
8008 cursor: pointer;
8007 }
8009 height: 26px;
8008 .alternate_upload .input-overlay {
8009 display: inline-block;
8010 font-weight: bold;
8010 }
8011 }
8011 /**
8012 /**
8012 * Primary styles
8013 * Primary styles
@@ -8164,6 +8165,21 b' input.engine_num_input {'
8164 .file_icon:before.pull-right {
8165 .file_icon:before.pull-right {
8165 margin-left: .3em;
8166 margin-left: .3em;
8166 }
8167 }
8168 ul#new-notebook-menu {
8169 left: auto;
8170 right: 0;
8171 }
8172 .kernel-menu-icon {
8173 padding-right: 12px;
8174 width: 24px;
8175 content: "\f096";
8176 }
8177 .kernel-menu-icon:before {
8178 content: "\f096";
8179 }
8180 .kernel-menu-icon-current:before {
8181 content: "\f00c";
8182 }
8167 /*!
8183 /*!
8168 *
8184 *
8169 * IPython notebook
8185 * IPython notebook
@@ -8,12 +8,14 b' require(['
8 'base/js/events',
8 'base/js/events',
9 'base/js/page',
9 'base/js/page',
10 'base/js/utils',
10 'base/js/utils',
11 'services/config',
11 'contents',
12 'contents',
12 'tree/js/notebooklist',
13 'tree/js/notebooklist',
13 'tree/js/clusterlist',
14 'tree/js/clusterlist',
14 'tree/js/sessionlist',
15 'tree/js/sessionlist',
15 'tree/js/kernellist',
16 'tree/js/kernellist',
16 'tree/js/terminallist',
17 'tree/js/terminallist',
18 'tree/js/newnotebook',
17 'auth/js/loginwidget',
19 'auth/js/loginwidget',
18 // only loaded, not used:
20 // only loaded, not used:
19 'jqueryui',
21 'jqueryui',
@@ -26,12 +28,14 b' require(['
26 events,
28 events,
27 page,
29 page,
28 utils,
30 utils,
31 config,
29 contents_service,
32 contents_service,
30 notebooklist,
33 notebooklist,
31 clusterlist,
34 clusterlist,
32 sesssionlist,
35 sesssionlist,
33 kernellist,
36 kernellist,
34 terminallist,
37 terminallist,
38 newnotebook,
35 loginwidget){
39 loginwidget){
36 "use strict";
40 "use strict";
37
41
@@ -41,6 +45,10 b' require(['
41 base_url: utils.get_body_data("baseUrl"),
45 base_url: utils.get_body_data("baseUrl"),
42 notebook_path: utils.get_body_data("notebookPath"),
46 notebook_path: utils.get_body_data("notebookPath"),
43 };
47 };
48 var cfg = new config.ConfigSection('tree', common_options);
49 cfg.load();
50 common_options.config = cfg;
51
44 var session_list = new sesssionlist.SesssionList($.extend({
52 var session_list = new sesssionlist.SesssionList($.extend({
45 events: events},
53 events: events},
46 common_options));
54 common_options));
@@ -63,24 +71,12 b' require(['
63
71
64 var login_widget = new loginwidget.LoginWidget('#login_widget', common_options);
72 var login_widget = new loginwidget.LoginWidget('#login_widget', common_options);
65
73
66 $('#new_notebook').click(function (e) {
74 var nnw = new newnotebook.NewNotebookWidget("#new-notebook-buttons",
67 var w = window.open();
75 $.extend(
68 contents.new_untitled(common_options.notebook_path, {type: "notebook"}).then(
76 {contents: contents},
69 function (data) {
77 common_options
70 w.location = utils.url_join_encode(
78 )
71 common_options.base_url, 'notebooks', data.path
79 );
72 );
73 },
74 function(error) {
75 w.close();
76 dialog.modal({
77 title : 'Creating Notebook Failed',
78 body : "The error was: " + error.message,
79 buttons : {'OK' : {'class' : 'btn-primary'}}
80 });
81 }
82 );
83 });
84
80
85 var interval_id=0;
81 var interval_id=0;
86 // auto refresh every xx secondes, no need to be fast,
82 // auto refresh every xx secondes, no need to be fast,
@@ -93,18 +89,18 b' require(['
93 */
89 */
94 session_list.load_sessions();
90 session_list.load_sessions();
95 cluster_list.load_list();
91 cluster_list.load_list();
96 if (terminal_list) {
92 if (terminal_list) {
97 terminal_list.load_terminals();
93 terminal_list.load_terminals();
98 }
94 }
99 if (!interval_id){
95 if (!interval_id){
100 interval_id = setInterval(function(){
96 interval_id = setInterval(function(){
101 session_list.load_sessions();
97 session_list.load_sessions();
102 cluster_list.load_list();
98 cluster_list.load_list();
103 if (terminal_list) {
99 if (terminal_list) {
104 terminal_list.load_terminals();
100 terminal_list.load_terminals();
105 }
101 }
106 }, time_refresh*1000);
102 }, time_refresh*1000);
107 }
103 }
108 };
104 };
109
105
110 var disable_autorefresh = function(){
106 var disable_autorefresh = function(){
@@ -134,6 +130,7 b' require(['
134 IPython.session_list = session_list;
130 IPython.session_list = session_list;
135 IPython.kernel_list = kernel_list;
131 IPython.kernel_list = kernel_list;
136 IPython.login_widget = login_widget;
132 IPython.login_widget = login_widget;
133 IPython.new_notebook_widget = nnw;
137
134
138 events.trigger('app_initialized.DashboardApp');
135 events.trigger('app_initialized.DashboardApp');
139
136
@@ -15,12 +15,14 b''
15
15
16 .alternate_upload input.fileinput
16 .alternate_upload input.fileinput
17 {
17 {
18 background-color:red;
18 display: inline;
19 position:relative;
20 opacity: 0;
19 opacity: 0;
21 z-index: 2;
20 z-index: 2;
22 width: 295px;
21 width: 12ex;
23 margin-left:163px;
22 margin-right: -12ex;
24 cursor: pointer;
23 }
25 height: 26px;
24
25 .alternate_upload .input-overlay {
26 display: inline-block;
27 font-weight: bold;
26 }
28 }
@@ -154,3 +154,23 b' input.engine_num_input {'
154 .file_icon:before {
154 .file_icon:before {
155 .icon(@fa-var-file-o)
155 .icon(@fa-var-file-o)
156 }
156 }
157
158 ul#new-notebook-menu {
159 // align right instead of left
160 left: auto;
161 right: 0;
162 }
163
164 .kernel-menu-icon {
165 padding-right: 12px;
166 width: 24px;
167 content: @fa-var-square-o;
168 }
169
170 .kernel-menu-icon:before {
171 content: @fa-var-square-o;
172 }
173
174 .kernel-menu-icon-current:before {
175 content: @fa-var-check;
176 }
@@ -43,7 +43,7 b' class="notebook_app"'
43
43
44 <span id="kernel_selector_widget" class="pull-right dropdown">
44 <span id="kernel_selector_widget" class="pull-right dropdown">
45 <button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
45 <button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
46 <span class='kernel_name'>Python</span>
46 <span class='kernel_name'>Kernel</span>
47 <span class="caret"></span>
47 <span class="caret"></span>
48 </button>
48 </button>
49 <ul id="kernel_selector" class="dropdown-menu">
49 <ul id="kernel_selector" class="dropdown-menu">
@@ -34,19 +34,31 b' data-terminals-available="{{terminals_available}}"'
34 <div class="tab-content">
34 <div class="tab-content">
35 <div id="notebooks" class="tab-pane active">
35 <div id="notebooks" class="tab-pane active">
36 <div id="notebook_toolbar" class="row">
36 <div id="notebook_toolbar" class="row">
37 <div class="col-sm-8 no-padding">
37 <div class="col-sm-12 no-padding">
38 <form id='alternate_upload' class='alternate_upload' >
38 <form id='alternate_upload' class='alternate_upload'>
39 <span id="notebook_list_info" style="position:absolute" >
39 <span id="notebook_list_info">
40 To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
40 To import a notebook, drag the file onto the listing below or
41 <span class="input-overlay">
42 <input type="file" name="datafile" class="fileinput" multiple='multiple'>
43 click here.
44 </span>
41 </span>
45 </span>
42 <input type="file" name="datafile" class="fileinput" multiple='multiple'>
43 </form>
46 </form>
44 </div>
47 <span id="notebook_buttons" class="pull-right">
45 <div class="col-sm-4 no-padding tree-buttons">
48 <div id="new-notebook-buttons" class="btn-group">
46 <span id="notebook_buttons" class="pull-right">
49 <button id="new_notebook" class="btn btn-default btn-xs">
47 <button id="new_notebook" title="Create new notebook" class="btn btn-default btn-xs">New Notebook</button>
50 New Notebook
48 <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="fa fa-refresh"></i></button>
51 </button>
49 </span>
52 <button class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown">
53 <span id="current-kernel">Loading...</span>
54 <span class="caret"></span>
55 </button>
56 <ul id="new-notebook-menu" class="dropdown-menu"></ul>
57 </div>
58
59
60 <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="fa fa-refresh"></i></button>
61 </span>
50 </div>
62 </div>
51 </div>
63 </div>
52
64
General Comments 0
You need to be logged in to leave comments. Login now