##// END OF EJS Templates
Removing KBN null mode and replacing with enable/disable.
Brian E. Granger -
Show More
@@ -1,146 +1,147 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Utility for modal dialogs with bootstrap
9 // Utility for modal dialogs with bootstrap
10 //============================================================================
10 //============================================================================
11
11
12 IPython.namespace('IPython.dialog');
12 IPython.namespace('IPython.dialog');
13
13
14 IPython.dialog = (function (IPython) {
14 IPython.dialog = (function (IPython) {
15 "use strict";
15 "use strict";
16
16
17 var modal = function (options) {
17 var modal = function (options) {
18 var dialog = $("<div/>").addClass("modal").attr("role", "dialog");
18 var dialog = $("<div/>").addClass("modal").attr("role", "dialog");
19 dialog.append(
19 dialog.append(
20 $("<div/>")
20 $("<div/>")
21 .addClass("modal-header")
21 .addClass("modal-header")
22 .append($("<button>")
22 .append($("<button>")
23 .addClass("close")
23 .addClass("close")
24 .attr("data-dismiss", "modal")
24 .attr("data-dismiss", "modal")
25 .html("&times;")
25 .html("&times;")
26 ).append(
26 ).append(
27 $("<h3/>").text(options.title || "")
27 $("<h3/>").text(options.title || "")
28 )
28 )
29 ).append(
29 ).append(
30 $("<div/>").addClass("modal-body").append(
30 $("<div/>").addClass("modal-body").append(
31 options.body || $("<p/>")
31 options.body || $("<p/>")
32 )
32 )
33 );
33 );
34
34
35 var footer = $("<div/>").addClass("modal-footer");
35 var footer = $("<div/>").addClass("modal-footer");
36
36
37 for (var label in options.buttons) {
37 for (var label in options.buttons) {
38 var btn_opts = options.buttons[label];
38 var btn_opts = options.buttons[label];
39 var button = $("<button/>")
39 var button = $("<button/>")
40 .addClass("btn")
40 .addClass("btn")
41 .attr("data-dismiss", "modal")
41 .attr("data-dismiss", "modal")
42 .text(label);
42 .text(label);
43 if (btn_opts.click) {
43 if (btn_opts.click) {
44 button.click($.proxy(btn_opts.click, dialog));
44 button.click($.proxy(btn_opts.click, dialog));
45 }
45 }
46 if (btn_opts.class) {
46 if (btn_opts.class) {
47 button.addClass(btn_opts.class);
47 button.addClass(btn_opts.class);
48 }
48 }
49 footer.append(button);
49 footer.append(button);
50 }
50 }
51 dialog.append(footer);
51 dialog.append(footer);
52 // hook up on-open event
52 // hook up on-open event
53 dialog.on("shown", function() {
53 dialog.on("shown", function() {
54 setTimeout(function() {
54 setTimeout(function() {
55 footer.find("button").last().focus();
55 footer.find("button").last().focus();
56 if (options.open) {
56 if (options.open) {
57 $.proxy(options.open, dialog)();
57 $.proxy(options.open, dialog)();
58 }
58 }
59 }, 0);
59 }, 0);
60 });
60 });
61
61
62 // destroy dialog on hide, unless explicitly asked not to
62 // destroy dialog on hide, unless explicitly asked not to
63 if (options.destroy === undefined || options.destroy) {
63 if (options.destroy === undefined || options.destroy) {
64 dialog.on("hidden", function () {
64 dialog.on("hidden", function () {
65 dialog.remove();
65 dialog.remove();
66 });
66 });
67 }
67 }
68 dialog.on("hidden", function () {
68 dialog.on("hidden", function () {
69 if (IPython.notebook) {
69 if (IPython.notebook) {
70 var cell = IPython.notebook.get_selected_cell();
70 var cell = IPython.notebook.get_selected_cell();
71 if (cell) cell.select();
71 if (cell) cell.select();
72 IPython.keyboard_manager.enable();
72 IPython.keyboard_manager.command_mode();
73 IPython.keyboard_manager.command_mode();
73 }
74 }
74 });
75 });
75
76
76 if (IPython.keyboard_manager) {
77 if (IPython.keyboard_manager) {
77 IPython.keyboard_manager.null_mode();
78 IPython.keyboard_manager.disable();
78 }
79 }
79
80
80 return dialog.modal(options);
81 return dialog.modal(options);
81 };
82 };
82
83
83 var edit_metadata = function (md, callback, name) {
84 var edit_metadata = function (md, callback, name) {
84 name = name || "Cell";
85 name = name || "Cell";
85 var error_div = $('<div/>').css('color', 'red');
86 var error_div = $('<div/>').css('color', 'red');
86 var message =
87 var message =
87 "Manually edit the JSON below to manipulate the metadata for this " + name + "." +
88 "Manually edit the JSON below to manipulate the metadata for this " + name + "." +
88 " We recommend putting custom metadata attributes in an appropriately named sub-structure," +
89 " We recommend putting custom metadata attributes in an appropriately named sub-structure," +
89 " so they don't conflict with those of others.";
90 " so they don't conflict with those of others.";
90
91
91 var textarea = $('<textarea/>')
92 var textarea = $('<textarea/>')
92 .attr('rows', '13')
93 .attr('rows', '13')
93 .attr('cols', '80')
94 .attr('cols', '80')
94 .attr('name', 'metadata')
95 .attr('name', 'metadata')
95 .text(JSON.stringify(md || {}, null, 2));
96 .text(JSON.stringify(md || {}, null, 2));
96
97
97 var dialogform = $('<div/>').attr('title', 'Edit the metadata')
98 var dialogform = $('<div/>').attr('title', 'Edit the metadata')
98 .append(
99 .append(
99 $('<form/>').append(
100 $('<form/>').append(
100 $('<fieldset/>').append(
101 $('<fieldset/>').append(
101 $('<label/>')
102 $('<label/>')
102 .attr('for','metadata')
103 .attr('for','metadata')
103 .text(message)
104 .text(message)
104 )
105 )
105 .append(error_div)
106 .append(error_div)
106 .append($('<br/>'))
107 .append($('<br/>'))
107 .append(textarea)
108 .append(textarea)
108 )
109 )
109 );
110 );
110 var editor = CodeMirror.fromTextArea(textarea[0], {
111 var editor = CodeMirror.fromTextArea(textarea[0], {
111 lineNumbers: true,
112 lineNumbers: true,
112 matchBrackets: true,
113 matchBrackets: true,
113 indentUnit: 2,
114 indentUnit: 2,
114 autoIndent: true,
115 autoIndent: true,
115 mode: 'application/json',
116 mode: 'application/json',
116 });
117 });
117 IPython.dialog.modal({
118 IPython.dialog.modal({
118 title: "Edit " + name + " Metadata",
119 title: "Edit " + name + " Metadata",
119 body: dialogform,
120 body: dialogform,
120 buttons: {
121 buttons: {
121 OK: { class : "btn-primary",
122 OK: { class : "btn-primary",
122 click: function() {
123 click: function() {
123 // validate json and set it
124 // validate json and set it
124 var new_md;
125 var new_md;
125 try {
126 try {
126 new_md = JSON.parse(editor.getValue());
127 new_md = JSON.parse(editor.getValue());
127 } catch(e) {
128 } catch(e) {
128 console.log(e);
129 console.log(e);
129 error_div.text('WARNING: Could not save invalid JSON.');
130 error_div.text('WARNING: Could not save invalid JSON.');
130 return false;
131 return false;
131 }
132 }
132 callback(new_md);
133 callback(new_md);
133 }
134 }
134 },
135 },
135 Cancel: {}
136 Cancel: {}
136 }
137 }
137 });
138 });
138 editor.refresh();
139 editor.refresh();
139 };
140 };
140
141
141 return {
142 return {
142 modal : modal,
143 modal : modal,
143 edit_metadata : edit_metadata,
144 edit_metadata : edit_metadata,
144 };
145 };
145
146
146 }(IPython));
147 }(IPython));
@@ -1,357 +1,357 b''
1 // function completer.
1 // function completer.
2 //
2 //
3 // completer should be a class that takes an cell instance
3 // completer should be a class that takes an cell instance
4 var IPython = (function (IPython) {
4 var IPython = (function (IPython) {
5 // that will prevent us from misspelling
5 // that will prevent us from misspelling
6 "use strict";
6 "use strict";
7
7
8 // easier key mapping
8 // easier key mapping
9 var key = IPython.utils.keycodes;
9 var key = IPython.utils.keycodes;
10
10
11 function prepend_n_prc(str, n) {
11 function prepend_n_prc(str, n) {
12 for( var i =0 ; i< n ; i++){
12 for( var i =0 ; i< n ; i++){
13 str = '%'+str ;
13 str = '%'+str ;
14 }
14 }
15 return str;
15 return str;
16 }
16 }
17
17
18 function _existing_completion(item, completion_array){
18 function _existing_completion(item, completion_array){
19 for( var c in completion_array ) {
19 for( var c in completion_array ) {
20 if(completion_array[c].trim().substr(-item.length) == item)
20 if(completion_array[c].trim().substr(-item.length) == item)
21 { return true; }
21 { return true; }
22 }
22 }
23 return false;
23 return false;
24 }
24 }
25
25
26 // what is the common start of all completions
26 // what is the common start of all completions
27 function shared_start(B, drop_prct) {
27 function shared_start(B, drop_prct) {
28 if (B.length == 1) {
28 if (B.length == 1) {
29 return B[0];
29 return B[0];
30 }
30 }
31 var A = new Array();
31 var A = new Array();
32 var common;
32 var common;
33 var min_lead_prct = 10;
33 var min_lead_prct = 10;
34 for (var i = 0; i < B.length; i++) {
34 for (var i = 0; i < B.length; i++) {
35 var str = B[i].str;
35 var str = B[i].str;
36 var localmin = 0;
36 var localmin = 0;
37 if(drop_prct === true){
37 if(drop_prct === true){
38 while ( str.substr(0, 1) == '%') {
38 while ( str.substr(0, 1) == '%') {
39 localmin = localmin+1;
39 localmin = localmin+1;
40 str = str.substring(1);
40 str = str.substring(1);
41 }
41 }
42 }
42 }
43 min_lead_prct = Math.min(min_lead_prct, localmin);
43 min_lead_prct = Math.min(min_lead_prct, localmin);
44 A.push(str);
44 A.push(str);
45 }
45 }
46
46
47 if (A.length > 1) {
47 if (A.length > 1) {
48 var tem1, tem2, s;
48 var tem1, tem2, s;
49 A = A.slice(0).sort();
49 A = A.slice(0).sort();
50 tem1 = A[0];
50 tem1 = A[0];
51 s = tem1.length;
51 s = tem1.length;
52 tem2 = A.pop();
52 tem2 = A.pop();
53 while (s && tem2.indexOf(tem1) == -1) {
53 while (s && tem2.indexOf(tem1) == -1) {
54 tem1 = tem1.substring(0, --s);
54 tem1 = tem1.substring(0, --s);
55 }
55 }
56 if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
56 if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
57 return {
57 return {
58 str:prepend_n_prc('', min_lead_prct),
58 str:prepend_n_prc('', min_lead_prct),
59 type: "computed",
59 type: "computed",
60 from: B[0].from,
60 from: B[0].from,
61 to: B[0].to
61 to: B[0].to
62 };
62 };
63 }
63 }
64 return {
64 return {
65 str: prepend_n_prc(tem1, min_lead_prct),
65 str: prepend_n_prc(tem1, min_lead_prct),
66 type: "computed",
66 type: "computed",
67 from: B[0].from,
67 from: B[0].from,
68 to: B[0].to
68 to: B[0].to
69 };
69 };
70 }
70 }
71 return null;
71 return null;
72 }
72 }
73
73
74
74
75 var Completer = function (cell) {
75 var Completer = function (cell) {
76 this.cell = cell;
76 this.cell = cell;
77 this.editor = cell.code_mirror;
77 this.editor = cell.code_mirror;
78 var that = this;
78 var that = this;
79 $([IPython.events]).on('status_busy.Kernel', function () {
79 $([IPython.events]).on('status_busy.Kernel', function () {
80 that.skip_kernel_completion = true;
80 that.skip_kernel_completion = true;
81 });
81 });
82 $([IPython.events]).on('status_idle.Kernel', function () {
82 $([IPython.events]).on('status_idle.Kernel', function () {
83 that.skip_kernel_completion = false;
83 that.skip_kernel_completion = false;
84 });
84 });
85 };
85 };
86
86
87
87
88 Completer.prototype.startCompletion = function () {
88 Completer.prototype.startCompletion = function () {
89 // call for a 'first' completion, that will set the editor and do some
89 // call for a 'first' completion, that will set the editor and do some
90 // special behaviour like autopicking if only one completion availlable
90 // special behaviour like autopicking if only one completion availlable
91 //
91 //
92 if (this.editor.somethingSelected()) return;
92 if (this.editor.somethingSelected()) return;
93 this.done = false;
93 this.done = false;
94 // use to get focus back on opera
94 // use to get focus back on opera
95 this.carry_on_completion(true);
95 this.carry_on_completion(true);
96 };
96 };
97
97
98
98
99 // easy access for julia to monkeypatch
99 // easy access for julia to monkeypatch
100 //
100 //
101 Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;
101 Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;
102
102
103 Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
103 Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
104 return Completer.reinvoke_re.test(pre_cursor);
104 return Completer.reinvoke_re.test(pre_cursor);
105 }
105 }
106
106
107 /**
107 /**
108 *
108 *
109 * pass true as parameter if this is the first invocation of the completer
109 * pass true as parameter if this is the first invocation of the completer
110 * this will prevent the completer to dissmiss itself if it is not on a
110 * this will prevent the completer to dissmiss itself if it is not on a
111 * word boundary like pressing tab after a space, and make it autopick the
111 * word boundary like pressing tab after a space, and make it autopick the
112 * only choice if there is only one which prevent from popping the UI. as
112 * only choice if there is only one which prevent from popping the UI. as
113 * well as fast-forwarding the typing if all completion have a common
113 * well as fast-forwarding the typing if all completion have a common
114 * shared start
114 * shared start
115 **/
115 **/
116 Completer.prototype.carry_on_completion = function (first_invocation) {
116 Completer.prototype.carry_on_completion = function (first_invocation) {
117 // Pass true as parameter if you want the completer to autopick when
117 // Pass true as parameter if you want the completer to autopick when
118 // only one completion. This function is automatically reinvoked at
118 // only one completion. This function is automatically reinvoked at
119 // each keystroke with first_invocation = false
119 // each keystroke with first_invocation = false
120 var cur = this.editor.getCursor();
120 var cur = this.editor.getCursor();
121 var line = this.editor.getLine(cur.line);
121 var line = this.editor.getLine(cur.line);
122 var pre_cursor = this.editor.getRange({
122 var pre_cursor = this.editor.getRange({
123 line: cur.line,
123 line: cur.line,
124 ch: cur.ch - 1
124 ch: cur.ch - 1
125 }, cur);
125 }, cur);
126
126
127 // we need to check that we are still on a word boundary
127 // we need to check that we are still on a word boundary
128 // because while typing the completer is still reinvoking itself
128 // because while typing the completer is still reinvoking itself
129 // so dismiss if we are on a "bad" caracter
129 // so dismiss if we are on a "bad" caracter
130 if (!this.reinvoke(pre_cursor) && !first_invocation) {
130 if (!this.reinvoke(pre_cursor) && !first_invocation) {
131 this.close();
131 this.close();
132 return;
132 return;
133 }
133 }
134
134
135 this.autopick = false;
135 this.autopick = false;
136 if (first_invocation) {
136 if (first_invocation) {
137 this.autopick = true;
137 this.autopick = true;
138 }
138 }
139
139
140 // We want a single cursor position.
140 // We want a single cursor position.
141 if (this.editor.somethingSelected()) {
141 if (this.editor.somethingSelected()) {
142 return;
142 return;
143 };
143 };
144
144
145 // one kernel completion came back, finish_completing will be called with the results
145 // one kernel completion came back, finish_completing will be called with the results
146 // we fork here and directly call finish completing if kernel is busy
146 // we fork here and directly call finish completing if kernel is busy
147 if (this.skip_kernel_completion == true) {
147 if (this.skip_kernel_completion == true) {
148 this.finish_completing({
148 this.finish_completing({
149 'matches': [],
149 'matches': [],
150 matched_text: ""
150 matched_text: ""
151 })
151 })
152 } else {
152 } else {
153 this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
153 this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
154 }
154 }
155 };
155 };
156
156
157 Completer.prototype.finish_completing = function (msg) {
157 Completer.prototype.finish_completing = function (msg) {
158 // let's build a function that wrap all that stuff into what is needed
158 // let's build a function that wrap all that stuff into what is needed
159 // for the new completer:
159 // for the new completer:
160 var content = msg.content;
160 var content = msg.content;
161 var matched_text = content.matched_text;
161 var matched_text = content.matched_text;
162 var matches = content.matches;
162 var matches = content.matches;
163
163
164 var cur = this.editor.getCursor();
164 var cur = this.editor.getCursor();
165 var results = CodeMirror.contextHint(this.editor);
165 var results = CodeMirror.contextHint(this.editor);
166 var filterd_results = Array();
166 var filterd_results = Array();
167 //remove results from context completion
167 //remove results from context completion
168 //that are already in kernel completion
168 //that are already in kernel completion
169 for(var elm in results) {
169 for(var elm in results) {
170 if(_existing_completion(results[elm]['str'], matches) == false)
170 if(_existing_completion(results[elm]['str'], matches) == false)
171 { filterd_results.push(results[elm]); }
171 { filterd_results.push(results[elm]); }
172 }
172 }
173
173
174 // append the introspection result, in order, at at the beginning of
174 // append the introspection result, in order, at at the beginning of
175 // the table and compute the replacement range from current cursor
175 // the table and compute the replacement range from current cursor
176 // positon and matched_text length.
176 // positon and matched_text length.
177 for (var i = matches.length - 1; i >= 0; --i) {
177 for (var i = matches.length - 1; i >= 0; --i) {
178 filterd_results.unshift({
178 filterd_results.unshift({
179 str: matches[i],
179 str: matches[i],
180 type: "introspection",
180 type: "introspection",
181 from: {
181 from: {
182 line: cur.line,
182 line: cur.line,
183 ch: cur.ch - matched_text.length
183 ch: cur.ch - matched_text.length
184 },
184 },
185 to: {
185 to: {
186 line: cur.line,
186 line: cur.line,
187 ch: cur.ch
187 ch: cur.ch
188 }
188 }
189 });
189 });
190 }
190 }
191
191
192 // one the 2 sources results have been merge, deal with it
192 // one the 2 sources results have been merge, deal with it
193 this.raw_result = filterd_results;
193 this.raw_result = filterd_results;
194
194
195 // if empty result return
195 // if empty result return
196 if (!this.raw_result || !this.raw_result.length) return;
196 if (!this.raw_result || !this.raw_result.length) return;
197
197
198 // When there is only one completion, use it directly.
198 // When there is only one completion, use it directly.
199 if (this.autopick == true && this.raw_result.length == 1) {
199 if (this.autopick == true && this.raw_result.length == 1) {
200 this.insert(this.raw_result[0]);
200 this.insert(this.raw_result[0]);
201 return;
201 return;
202 }
202 }
203
203
204 if (this.raw_result.length == 1) {
204 if (this.raw_result.length == 1) {
205 // test if first and only completion totally matches
205 // test if first and only completion totally matches
206 // what is typed, in this case dismiss
206 // what is typed, in this case dismiss
207 var str = this.raw_result[0].str;
207 var str = this.raw_result[0].str;
208 var pre_cursor = this.editor.getRange({
208 var pre_cursor = this.editor.getRange({
209 line: cur.line,
209 line: cur.line,
210 ch: cur.ch - str.length
210 ch: cur.ch - str.length
211 }, cur);
211 }, cur);
212 if (pre_cursor == str) {
212 if (pre_cursor == str) {
213 this.close();
213 this.close();
214 return;
214 return;
215 }
215 }
216 }
216 }
217
217
218 this.complete = $('<div/>').addClass('completions');
218 this.complete = $('<div/>').addClass('completions');
219 this.complete.attr('id', 'complete');
219 this.complete.attr('id', 'complete');
220
220
221 // Currently webkit doesn't use the size attr correctly. See:
221 // Currently webkit doesn't use the size attr correctly. See:
222 // https://code.google.com/p/chromium/issues/detail?id=4579
222 // https://code.google.com/p/chromium/issues/detail?id=4579
223 this.sel = $('<select style="width: auto"/>')
223 this.sel = $('<select style="width: auto"/>')
224 .attr('multiple', 'true')
224 .attr('multiple', 'true')
225 .attr('size', Math.min(10, this.raw_result.length));
225 .attr('size', Math.min(10, this.raw_result.length));
226 this.complete.append(this.sel);
226 this.complete.append(this.sel);
227 $('body').append(this.complete);
227 $('body').append(this.complete);
228
228
229 // After everything is on the page, compute the postion.
229 // After everything is on the page, compute the postion.
230 // We put it above the code if it is too close to the bottom of the page.
230 // We put it above the code if it is too close to the bottom of the page.
231 var cur = this.editor.getCursor();
231 var cur = this.editor.getCursor();
232 cur.ch = cur.ch-matched_text.length;
232 cur.ch = cur.ch-matched_text.length;
233 var pos = this.editor.cursorCoords(cur);
233 var pos = this.editor.cursorCoords(cur);
234 var left = pos.left-3;
234 var left = pos.left-3;
235 var top;
235 var top;
236 var cheight = this.complete.height();
236 var cheight = this.complete.height();
237 var wheight = $(window).height();
237 var wheight = $(window).height();
238 if (pos.bottom+cheight+5 > wheight) {
238 if (pos.bottom+cheight+5 > wheight) {
239 top = pos.top-cheight-4;
239 top = pos.top-cheight-4;
240 } else {
240 } else {
241 top = pos.bottom+1;
241 top = pos.bottom+1;
242 }
242 }
243 this.complete.css('left', left + 'px');
243 this.complete.css('left', left + 'px');
244 this.complete.css('top', top + 'px');
244 this.complete.css('top', top + 'px');
245
245
246
246
247 //build the container
247 //build the container
248 var that = this;
248 var that = this;
249 this.sel.dblclick(function () {
249 this.sel.dblclick(function () {
250 that.pick();
250 that.pick();
251 });
251 });
252 this.sel.blur(this.close);
252 this.sel.blur(this.close);
253 this.sel.keydown(function (event) {
253 this.sel.keydown(function (event) {
254 that.keydown(event);
254 that.keydown(event);
255 });
255 });
256
256
257 this.build_gui_list(this.raw_result);
257 this.build_gui_list(this.raw_result);
258
258
259 this.sel.focus();
259 this.sel.focus();
260 IPython.keyboard_manager.null_mode();
260 IPython.keyboard_manager.disable();
261 // Opera sometimes ignores focusing a freshly created node
261 // Opera sometimes ignores focusing a freshly created node
262 if (window.opera) setTimeout(function () {
262 if (window.opera) setTimeout(function () {
263 if (!this.done) this.sel.focus();
263 if (!this.done) this.sel.focus();
264 }, 100);
264 }, 100);
265 return true;
265 return true;
266 }
266 }
267
267
268 Completer.prototype.insert = function (completion) {
268 Completer.prototype.insert = function (completion) {
269 this.editor.replaceRange(completion.str, completion.from, completion.to);
269 this.editor.replaceRange(completion.str, completion.from, completion.to);
270 }
270 }
271
271
272 Completer.prototype.build_gui_list = function (completions) {
272 Completer.prototype.build_gui_list = function (completions) {
273 for (var i = 0; i < completions.length; ++i) {
273 for (var i = 0; i < completions.length; ++i) {
274 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
274 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
275 this.sel.append(opt);
275 this.sel.append(opt);
276 }
276 }
277 this.sel.children().first().attr('selected', 'true');
277 this.sel.children().first().attr('selected', 'true');
278 this.sel.scrollTop(0);
278 this.sel.scrollTop(0);
279 }
279 }
280
280
281 Completer.prototype.close = function () {
281 Completer.prototype.close = function () {
282 if (this.done) return;
282 if (this.done) return;
283 this.done = true;
283 this.done = true;
284 $('.completions').remove();
284 $('.completions').remove();
285 IPython.keyboard_manager.edit_mode();
285 IPython.keyboard_manager.enable();
286 }
286 }
287
287
288 Completer.prototype.pick = function () {
288 Completer.prototype.pick = function () {
289 this.insert(this.raw_result[this.sel[0].selectedIndex]);
289 this.insert(this.raw_result[this.sel[0].selectedIndex]);
290 this.close();
290 this.close();
291 var that = this;
291 var that = this;
292 setTimeout(function () {
292 setTimeout(function () {
293 that.editor.focus();
293 that.editor.focus();
294 }, 50);
294 }, 50);
295 }
295 }
296
296
297
297
298 Completer.prototype.keydown = function (event) {
298 Completer.prototype.keydown = function (event) {
299 var code = event.keyCode;
299 var code = event.keyCode;
300 var that = this;
300 var that = this;
301 var special_key = false;
301 var special_key = false;
302
302
303 // detect special keys like SHIFT,PGUP,...
303 // detect special keys like SHIFT,PGUP,...
304 for( var _key in key ) {
304 for( var _key in key ) {
305 if (code == key[_key] ) {
305 if (code == key[_key] ) {
306 special_key = true;
306 special_key = true;
307 }
307 }
308 };
308 };
309
309
310 // Enter
310 // Enter
311 if (code == key.ENTER) {
311 if (code == key.ENTER) {
312 CodeMirror.e_stop(event);
312 CodeMirror.e_stop(event);
313 this.pick();
313 this.pick();
314 }
314 }
315 // Escape or backspace
315 // Escape or backspace
316 else if (code == key.ESC) {
316 else if (code == key.ESC) {
317 CodeMirror.e_stop(event);
317 CodeMirror.e_stop(event);
318 this.close();
318 this.close();
319 this.editor.focus();
319 this.editor.focus();
320 } else if (code == key.SPACE || code == key.BACKSPACE) {
320 } else if (code == key.SPACE || code == key.BACKSPACE) {
321 this.close();
321 this.close();
322 this.editor.focus();
322 this.editor.focus();
323 } else if (code == key.TAB) {
323 } else if (code == key.TAB) {
324 //all the fastforwarding operation,
324 //all the fastforwarding operation,
325 //Check that shared start is not null which can append with prefixed completion
325 //Check that shared start is not null which can append with prefixed completion
326 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
326 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
327 // to erase py
327 // to erase py
328 var sh = shared_start(this.raw_result, true);
328 var sh = shared_start(this.raw_result, true);
329 if (sh) {
329 if (sh) {
330 this.insert(sh);
330 this.insert(sh);
331 }
331 }
332 this.close();
332 this.close();
333 CodeMirror.e_stop(event);
333 CodeMirror.e_stop(event);
334 this.editor.focus();
334 this.editor.focus();
335 //reinvoke self
335 //reinvoke self
336 setTimeout(function () {
336 setTimeout(function () {
337 that.carry_on_completion();
337 that.carry_on_completion();
338 }, 50);
338 }, 50);
339 } else if (code == key.UPARROW || code == key.DOWNARROW) {
339 } else if (code == key.UPARROW || code == key.DOWNARROW) {
340 // need to do that to be able to move the arrow
340 // need to do that to be able to move the arrow
341 // when on the first or last line ofo a code cell
341 // when on the first or last line ofo a code cell
342 event.stopPropagation();
342 event.stopPropagation();
343 } else if (special_key != true) {
343 } else if (special_key != true) {
344 this.close();
344 this.close();
345 this.editor.focus();
345 this.editor.focus();
346 //we give focus to the editor immediately and call sell in 50 ms
346 //we give focus to the editor immediately and call sell in 50 ms
347 setTimeout(function () {
347 setTimeout(function () {
348 that.carry_on_completion();
348 that.carry_on_completion();
349 }, 50);
349 }, 50);
350 }
350 }
351 }
351 }
352
352
353
353
354 IPython.Completer = Completer;
354 IPython.Completer = Completer;
355
355
356 return IPython;
356 return IPython;
357 }(IPython));
357 }(IPython));
@@ -1,283 +1,265 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Keyboard management
9 // Keyboard management
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14
14
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 var KeyboardManager = function () {
17 var KeyboardManager = function () {
18 this.mode = 'command';
18 this.mode = 'command';
19 this.last_mode = 'command';
19 this.enabled = true;
20 this.bind_events();
20 this.bind_events();
21 };
21 };
22
22
23 KeyboardManager.prototype.bind_events = function () {
23 KeyboardManager.prototype.bind_events = function () {
24 var that = this;
24 var that = this;
25 $(document).keydown(function (event) {
25 $(document).keydown(function (event) {
26 return that.handle_keydown(event);
26 return that.handle_keydown(event);
27 });
27 });
28 };
28 };
29
29
30 KeyboardManager.prototype.handle_keydown = function (event) {
30 KeyboardManager.prototype.handle_keydown = function (event) {
31 var notebook = IPython.notebook;
31 var notebook = IPython.notebook;
32
32
33 console.log('keyboard_manager', this.mode, event.keyCode);
33 console.log('keyboard_manager', this.mode, event.keyCode);
34
34
35 if (event.which === key.ESC) {
35 if (event.which === key.ESC) {
36 // Intercept escape at highest level to avoid closing
36 // Intercept escape at highest level to avoid closing
37 // websocket connection with firefox
37 // websocket connection with firefox
38 event.preventDefault();
38 event.preventDefault();
39 }
39 }
40
40
41 if (this.mode === 'null') {
41 if (!this.enabled) {
42 return this.handle_null_mode(event);
42 return true;
43 }
43 }
44
44
45 // Event handlers for both command and edit mode
45 // Event handlers for both command and edit mode
46 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
46 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
47 // Save (CTRL+S) or (Command+S on Mac)
47 // Save (CTRL+S) or (Command+S on Mac)
48 notebook.save_checkpoint();
48 notebook.save_checkpoint();
49 event.preventDefault();
49 event.preventDefault();
50 return false;
50 return false;
51 } else if (event.which === key.ESC) {
51 } else if (event.which === key.ESC) {
52 // Intercept escape at highest level to avoid closing
52 // Intercept escape at highest level to avoid closing
53 // websocket connection with firefox
53 // websocket connection with firefox
54 event.preventDefault();
54 event.preventDefault();
55 // Don't return yet to allow edit/command modes to handle
55 // Don't return yet to allow edit/command modes to handle
56 } else if (event.which === key.SHIFT) {
56 } else if (event.which === key.SHIFT) {
57 // ignore shift keydown
57 // ignore shift keydown
58 return true;
58 return true;
59 } else if (event.which === key.ENTER && event.shiftKey) {
59 } else if (event.which === key.ENTER && event.shiftKey) {
60 notebook.execute_selected_cell('shift');
60 notebook.execute_selected_cell('shift');
61 return false;
61 return false;
62 } else if (event.which === key.ENTER && event.altKey) {
62 } else if (event.which === key.ENTER && event.altKey) {
63 // Execute code cell, and insert new in place
63 // Execute code cell, and insert new in place
64 notebook.execute_selected_cell('alt');
64 notebook.execute_selected_cell('alt');
65 return false;
65 return false;
66 } else if (event.which === key.ENTER && event.ctrlKey) {
66 } else if (event.which === key.ENTER && event.ctrlKey) {
67 notebook.execute_selected_cell('ctrl');
67 notebook.execute_selected_cell('ctrl');
68 return false;
68 return false;
69 }
69 }
70
70
71 if (this.mode === 'edit') {
71 if (this.mode === 'edit') {
72 return this.handle_edit_mode(event);
72 return this.handle_edit_mode(event);
73 } else if (this.mode === 'command' && !(event.ctrlKey || event.altKey || event.metaKey)) {
73 } else if (this.mode === 'command' && !(event.ctrlKey || event.altKey || event.metaKey)) {
74 return this.handle_command_mode(event);
74 return this.handle_command_mode(event);
75 }
75 }
76 }
76 }
77
77
78 KeyboardManager.prototype.handle_null_mode = function (event) {
79 return true;
80 }
81
82
83 KeyboardManager.prototype.handle_edit_mode = function (event) {
78 KeyboardManager.prototype.handle_edit_mode = function (event) {
84 var notebook = IPython.notebook;
79 var notebook = IPython.notebook;
85
80
86 if (event.which === key.ESC) {
81 if (event.which === key.ESC) {
87 // ESC
82 // ESC
88 notebook.command_mode();
83 notebook.command_mode();
89 return false;
84 return false;
90 } else if (event.which === 77 && event.ctrlKey) {
85 } else if (event.which === 77 && event.ctrlKey) {
91 // Ctrl-m
86 // Ctrl-m
92 notebook.command_mode();
87 notebook.command_mode();
93 return false;
88 return false;
94 } else if (event.which === key.UPARROW && !event.shiftKey) {
89 } else if (event.which === key.UPARROW && !event.shiftKey) {
95 var cell = notebook.get_selected_cell();
90 var cell = notebook.get_selected_cell();
96 if (cell && cell.at_top()) {
91 if (cell && cell.at_top()) {
97 event.preventDefault();
92 event.preventDefault();
98 notebook.command_mode()
93 notebook.command_mode()
99 notebook.select_prev();
94 notebook.select_prev();
100 notebook.edit_mode();
95 notebook.edit_mode();
101 return false;
96 return false;
102 };
97 };
103 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
98 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
104 var cell = notebook.get_selected_cell();
99 var cell = notebook.get_selected_cell();
105 if (cell && cell.at_bottom()) {
100 if (cell && cell.at_bottom()) {
106 event.preventDefault();
101 event.preventDefault();
107 notebook.command_mode()
102 notebook.command_mode()
108 notebook.select_next();
103 notebook.select_next();
109 notebook.edit_mode();
104 notebook.edit_mode();
110 return false;
105 return false;
111 };
106 };
112 };
107 };
113 return true;
108 return true;
114 }
109 }
115
110
116 KeyboardManager.prototype.handle_command_mode = function (event) {
111 KeyboardManager.prototype.handle_command_mode = function (event) {
117 var notebook = IPython.notebook;
112 var notebook = IPython.notebook;
118
113
119 if (event.which === key.ENTER && !(event.ctrlKey || event.altKey || event.shiftKey)) {
114 if (event.which === key.ENTER && !(event.ctrlKey || event.altKey || event.shiftKey)) {
120 // Enter edit mode = ENTER alone
115 // Enter edit mode = ENTER alone
121 notebook.edit_mode();
116 notebook.edit_mode();
122 return false;
117 return false;
123 } else if (event.which === key.UPARROW && !event.shiftKey) {
118 } else if (event.which === key.UPARROW && !event.shiftKey) {
124 var index = notebook.get_selected_index();
119 var index = notebook.get_selected_index();
125 if (index !== 0 && index !== null) {
120 if (index !== 0 && index !== null) {
126 notebook.select_prev();
121 notebook.select_prev();
127 var cell = notebook.get_selected_cell();
122 var cell = notebook.get_selected_cell();
128 cell.focus_cell();
123 cell.focus_cell();
129 };
124 };
130 return false;
125 return false;
131 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
126 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
132 var index = notebook.get_selected_index();
127 var index = notebook.get_selected_index();
133 if (index !== (notebook.ncells()-1) && index !== null) {
128 if (index !== (notebook.ncells()-1) && index !== null) {
134 notebook.select_next();
129 notebook.select_next();
135 var cell = notebook.get_selected_cell();
130 var cell = notebook.get_selected_cell();
136 cell.focus_cell();
131 cell.focus_cell();
137 };
132 };
138 return false;
133 return false;
139 } else if (event.which === 88) {
134 } else if (event.which === 88) {
140 // Cut selected cell = x
135 // Cut selected cell = x
141 notebook.cut_cell();
136 notebook.cut_cell();
142 return false;
137 return false;
143 } else if (event.which === 67) {
138 } else if (event.which === 67) {
144 // Copy selected cell = c
139 // Copy selected cell = c
145 notebook.copy_cell();
140 notebook.copy_cell();
146 return false;
141 return false;
147 } else if (event.which === 86) {
142 } else if (event.which === 86) {
148 // Paste below selected cell = v
143 // Paste below selected cell = v
149 notebook.paste_cell_below();
144 notebook.paste_cell_below();
150 return false;
145 return false;
151 } else if (event.which === 68) {
146 } else if (event.which === 68) {
152 // Delete selected cell = d
147 // Delete selected cell = d
153 notebook.delete_cell();
148 notebook.delete_cell();
154 return false;
149 return false;
155 } else if (event.which === 65) {
150 } else if (event.which === 65) {
156 // Insert code cell above selected = a
151 // Insert code cell above selected = a
157 notebook.insert_cell_above('code');
152 notebook.insert_cell_above('code');
158 notebook.select_prev();
153 notebook.select_prev();
159 return false;
154 return false;
160 } else if (event.which === 66) {
155 } else if (event.which === 66) {
161 // Insert code cell below selected = b
156 // Insert code cell below selected = b
162 notebook.insert_cell_below('code');
157 notebook.insert_cell_below('code');
163 notebook.select_next();
158 notebook.select_next();
164 return false;
159 return false;
165 } else if (event.which === 89) {
160 } else if (event.which === 89) {
166 // To code = y
161 // To code = y
167 notebook.to_code();
162 notebook.to_code();
168 return false;
163 return false;
169 } else if (event.which === 77) {
164 } else if (event.which === 77) {
170 // To markdown = m
165 // To markdown = m
171 notebook.to_markdown();
166 notebook.to_markdown();
172 return false;
167 return false;
173 } else if (event.which === 84) {
168 } else if (event.which === 84) {
174 // To Raw = t
169 // To Raw = t
175 notebook.to_raw();
170 notebook.to_raw();
176 return false;
171 return false;
177 } else if (event.which === 49) {
172 } else if (event.which === 49) {
178 // To Heading 1 = 1
173 // To Heading 1 = 1
179 notebook.to_heading(undefined, 1);
174 notebook.to_heading(undefined, 1);
180 return false;
175 return false;
181 } else if (event.which === 50) {
176 } else if (event.which === 50) {
182 // To Heading 2 = 2
177 // To Heading 2 = 2
183 notebook.to_heading(undefined, 2);
178 notebook.to_heading(undefined, 2);
184 return false;
179 return false;
185 } else if (event.which === 51) {
180 } else if (event.which === 51) {
186 // To Heading 3 = 3
181 // To Heading 3 = 3
187 notebook.to_heading(undefined, 3);
182 notebook.to_heading(undefined, 3);
188 return false;
183 return false;
189 } else if (event.which === 52) {
184 } else if (event.which === 52) {
190 // To Heading 4 = 4
185 // To Heading 4 = 4
191 notebook.to_heading(undefined, 4);
186 notebook.to_heading(undefined, 4);
192 return false;
187 return false;
193 } else if (event.which === 53) {
188 } else if (event.which === 53) {
194 // To Heading 5 = 5
189 // To Heading 5 = 5
195 notebook.to_heading(undefined, 5);
190 notebook.to_heading(undefined, 5);
196 return false;
191 return false;
197 } else if (event.which === 54) {
192 } else if (event.which === 54) {
198 // To Heading 6 = 6
193 // To Heading 6 = 6
199 notebook.to_heading(undefined, 6);
194 notebook.to_heading(undefined, 6);
200 return false;
195 return false;
201 } else if (event.which === 79) {
196 } else if (event.which === 79) {
202 // Toggle output = o
197 // Toggle output = o
203 if (event.shiftKey) {
198 if (event.shiftKey) {
204 notebook.toggle_output_scroll();
199 notebook.toggle_output_scroll();
205 } else {
200 } else {
206 notebook.toggle_output();
201 notebook.toggle_output();
207 };
202 };
208 return false;
203 return false;
209 } else if (event.which === 83) {
204 } else if (event.which === 83) {
210 // Save notebook = s
205 // Save notebook = s
211 notebook.save_checkpoint();
206 notebook.save_checkpoint();
212 return false;
207 return false;
213 } else if (event.which === 74) {
208 } else if (event.which === 74) {
214 // Move cell down = j
209 // Move cell down = j
215 notebook.move_cell_down();
210 notebook.move_cell_down();
216 return false;
211 return false;
217 } else if (event.which === 75) {
212 } else if (event.which === 75) {
218 // Move cell up = k
213 // Move cell up = k
219 notebook.move_cell_up();
214 notebook.move_cell_up();
220 return false;
215 return false;
221 } else if (event.which === 80) {
216 } else if (event.which === 80) {
222 // Select previous = p
217 // Select previous = p
223 notebook.select_prev();
218 notebook.select_prev();
224 return false;
219 return false;
225 } else if (event.which === 78) {
220 } else if (event.which === 78) {
226 // Select next = n
221 // Select next = n
227 notebook.select_next();
222 notebook.select_next();
228 return false;
223 return false;
229 } else if (event.which === 76) {
224 } else if (event.which === 76) {
230 // Toggle line numbers = l
225 // Toggle line numbers = l
231 notebook.cell_toggle_line_numbers();
226 notebook.cell_toggle_line_numbers();
232 return false;
227 return false;
233 } else if (event.which === 73) {
228 } else if (event.which === 73) {
234 // Interrupt kernel = i
229 // Interrupt kernel = i
235 notebook.kernel.interrupt();
230 notebook.kernel.interrupt();
236 return false;
231 return false;
237 } else if (event.which === 190) {
232 } else if (event.which === 190) {
238 // Restart kernel = . # matches qt console
233 // Restart kernel = . # matches qt console
239 notebook.restart_kernel();
234 notebook.restart_kernel();
240 return false;
235 return false;
241 } else if (event.which === 72) {
236 } else if (event.which === 72) {
242 // Show keyboard shortcuts = h
237 // Show keyboard shortcuts = h
243 IPython.quick_help.show_keyboard_shortcuts();
238 IPython.quick_help.show_keyboard_shortcuts();
244 return false;
239 return false;
245 } else if (event.which === 90) {
240 } else if (event.which === 90) {
246 // Undo last cell delete = z
241 // Undo last cell delete = z
247 notebook.undelete();
242 notebook.undelete();
248 return false;
243 return false;
249 };
244 };
250 // If we havn't handled it, let someone else.
245 // If we havn't handled it, let someone else.
251 return true;
246 return true;
252 };
247 };
253
248
254 KeyboardManager.prototype.edit_mode = function () {
249 KeyboardManager.prototype.edit_mode = function () {
255 console.log('KeyboardManager', 'changing to edit mode');
250 console.log('KeyboardManager', 'changing to edit mode');
256 this.last_mode = this.mode;
251 this.last_mode = this.mode;
257 this.mode = 'edit';
252 this.mode = 'edit';
258 }
253 }
259
254
260 KeyboardManager.prototype.command_mode = function () {
255 KeyboardManager.prototype.command_mode = function () {
261 console.log('KeyboardManager', 'changing to command mode');
256 console.log('KeyboardManager', 'changing to command mode');
262 this.last_mode = this.mode;
257 this.last_mode = this.mode;
263 this.mode = 'command';
258 this.mode = 'command';
264 }
259 }
265
260
266 KeyboardManager.prototype.null_mode = function () {
267 console.log('KeyboardManager', 'changing to null mode');
268 this.last_mode = this.mode;
269 this.mode = 'null';
270 }
271
272 KeyboardManager.prototype.last_mode = function () {
273 var lm = this.last_mode;
274 this.last_mode = this.mode;
275 this.mode = lm;
276 }
277
278
279 IPython.KeyboardManager = KeyboardManager;
261 IPython.KeyboardManager = KeyboardManager;
280
262
281 return IPython;
263 return IPython;
282
264
283 }(IPython));
265 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now