##// END OF EJS Templates
add a keycodes structure to utils...
Matthias BUSSONNIER -
Show More
@@ -1,691 +1,692
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-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 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16 var key = IPython.utils.keycodes;
16
17
17 var CodeCell = function (notebook) {
18 var CodeCell = function (notebook) {
18 this.code_mirror = null;
19 this.code_mirror = null;
19 this.input_prompt_number = null;
20 this.input_prompt_number = null;
20 this.is_completing = false;
21 this.is_completing = false;
21 this.completion_cursor = null;
22 this.completion_cursor = null;
22 this.outputs = [];
23 this.outputs = [];
23 this.collapsed = false;
24 this.collapsed = false;
24 this.tooltip_timeout = null;
25 this.tooltip_timeout = null;
25 this.clear_out_timeout = null;
26 this.clear_out_timeout = null;
26 IPython.Cell.apply(this, arguments);
27 IPython.Cell.apply(this, arguments);
27 };
28 };
28
29
29
30
30 CodeCell.prototype = new IPython.Cell();
31 CodeCell.prototype = new IPython.Cell();
31
32
32
33
33 CodeCell.prototype.create_element = function () {
34 CodeCell.prototype.create_element = function () {
34 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
35 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
35 cell.attr('tabindex','2');
36 cell.attr('tabindex','2');
36 var input = $('<div></div>').addClass('input hbox');
37 var input = $('<div></div>').addClass('input hbox');
37 input.append($('<div/>').addClass('prompt input_prompt'));
38 input.append($('<div/>').addClass('prompt input_prompt'));
38 var input_area = $('<div/>').addClass('input_area box-flex1');
39 var input_area = $('<div/>').addClass('input_area box-flex1');
39 this.code_mirror = CodeMirror(input_area.get(0), {
40 this.code_mirror = CodeMirror(input_area.get(0), {
40 indentUnit : 4,
41 indentUnit : 4,
41 mode: 'python',
42 mode: 'python',
42 theme: 'ipython',
43 theme: 'ipython',
43 readOnly: this.read_only,
44 readOnly: this.read_only,
44 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
45 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
45 });
46 });
46 input.append(input_area);
47 input.append(input_area);
47 var output = $('<div></div>').addClass('output vbox');
48 var output = $('<div></div>').addClass('output vbox');
48 cell.append(input).append(output);
49 cell.append(input).append(output);
49 this.element = cell;
50 this.element = cell;
50 this.collapse();
51 this.collapse();
51
52
52 // construct a completer
53 // construct a completer
53 // And give it the function to call to get the completion list
54 // And give it the function to call to get the completion list
54 var that = this;
55 var that = this;
55 this.completer = new IPython.Completer(this.code_mirror,function(callback){that.requestCompletion(callback)});
56 this.completer = new IPython.Completer(this.code_mirror,function(callback){that.requestCompletion(callback)});
56 };
57 };
57
58
58 //TODO, try to diminish the number of parameters.
59 //TODO, try to diminish the number of parameters.
59 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
60 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
60 var that = this;
61 var that = this;
61 if (pre_cursor === "" || pre_cursor === "(" ) {
62 if (pre_cursor === "" || pre_cursor === "(" ) {
62 // don't do anything if line beggin with '(' or is empty
63 // don't do anything if line beggin with '(' or is empty
63 } else {
64 } else {
64 // Will set a timer to request tooltip in `time`
65 // Will set a timer to request tooltip in `time`
65 that.tooltip_timeout = setTimeout(function(){
66 that.tooltip_timeout = setTimeout(function(){
66 IPython.notebook.request_tool_tip(that, pre_cursor)
67 IPython.notebook.request_tool_tip(that, pre_cursor)
67 },time);
68 },time);
68 }
69 }
69 };
70 };
70
71
71 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
72 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
72 // This method gets called in CodeMirror's onKeyDown/onKeyPress
73 // This method gets called in CodeMirror's onKeyDown/onKeyPress
73 // handlers and is used to provide custom key handling. Its return
74 // handlers and is used to provide custom key handling. Its return
74 // value is used to determine if CodeMirror should ignore the event:
75 // value is used to determine if CodeMirror should ignore the event:
75 // true = ignore, false = don't ignore.
76 // true = ignore, false = don't ignore.
76
77
77 if (this.read_only){
78 if (this.read_only){
78 return false;
79 return false;
79 }
80 }
80
81
81 // note that we are comparing and setting the time to wait at each key press.
82 // note that we are comparing and setting the time to wait at each key press.
82 // a better wqy might be to generate a new function on each time change and
83 // a better wqy might be to generate a new function on each time change and
83 // assign it to CodeCell.prototype.request_tooltip_after_time
84 // assign it to CodeCell.prototype.request_tooltip_after_time
84 var tooltip_wait_time = this.notebook.time_before_tooltip;
85 var tooltip_wait_time = this.notebook.time_before_tooltip;
85 var tooltip_on_tab = this.notebook.tooltip_on_tab;
86 var tooltip_on_tab = this.notebook.tooltip_on_tab;
86 var that = this;
87 var that = this;
87 // whatever key is pressed, first, cancel the tooltip request before
88 // whatever key is pressed, first, cancel the tooltip request before
88 // they are sent, and remove tooltip if any
89 // they are sent, and remove tooltip if any
89 if(event.type === 'keydown' ) {
90 if(event.type === 'keydown' ) {
90 that.remove_and_cancel_tooltip();
91 that.remove_and_cancel_tooltip();
91 };
92 };
92
93
93
94
94 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
95 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
95 // Always ignore shift-enter in CodeMirror as we handle it.
96 // Always ignore shift-enter in CodeMirror as we handle it.
96 return true;
97 return true;
97 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
98 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
98 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
99 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
99 // browser and keyboard layout !
100 // browser and keyboard layout !
100 // Pressing '(' , request tooltip, don't forget to reappend it
101 // Pressing '(' , request tooltip, don't forget to reappend it
101 var cursor = editor.getCursor();
102 var cursor = editor.getCursor();
102 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
103 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
103 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
104 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
104 } else if (event.which === 38) {
105 } else if (event.which === key.upArrow) {
105 // If we are not at the top, let CM handle the up arrow and
106 // If we are not at the top, let CM handle the up arrow and
106 // prevent the global keydown handler from handling it.
107 // prevent the global keydown handler from handling it.
107 if (!that.at_top()) {
108 if (!that.at_top()) {
108 event.stop();
109 event.stop();
109 return false;
110 return false;
110 } else {
111 } else {
111 return true;
112 return true;
112 };
113 };
113 } else if (event.which === 40) {
114 } else if (event.which === key.downArrow) {
114 // If we are not at the bottom, let CM handle the down arrow and
115 // If we are not at the bottom, let CM handle the down arrow and
115 // prevent the global keydown handler from handling it.
116 // prevent the global keydown handler from handling it.
116 if (!that.at_bottom()) {
117 if (!that.at_bottom()) {
117 event.stop();
118 event.stop();
118 return false;
119 return false;
119 } else {
120 } else {
120 return true;
121 return true;
121 };
122 };
122 } else if (event.keyCode === 9 && event.type == 'keydown') {
123 } else if (event.keyCode === key.tab && event.type == 'keydown') {
123 // Tab completion.
124 // Tab completion.
124 var cur = editor.getCursor();
125 var cur = editor.getCursor();
125 //Do not trim here because of tooltip
126 //Do not trim here because of tooltip
126 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 if (pre_cursor.trim() === "") {
128 if (pre_cursor.trim() === "") {
128 // Don't autocomplete if the part of the line before the cursor
129 // Don't autocomplete if the part of the line before the cursor
129 // is empty. In this case, let CodeMirror handle indentation.
130 // is empty. In this case, let CodeMirror handle indentation.
130 return false;
131 return false;
131 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
132 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
132 that.request_tooltip_after_time(pre_cursor,0);
133 that.request_tooltip_after_time(pre_cursor,0);
133 // Prevent the event from bubbling up.
134 // Prevent the event from bubbling up.
134 event.stop();
135 event.stop();
135 // Prevent CodeMirror from handling the tab.
136 // Prevent CodeMirror from handling the tab.
136 return true;
137 return true;
137 } else {
138 } else {
138 event.stop();
139 event.stop();
139 this.completer.startCompletion();
140 this.completer.startCompletion();
140 return true;
141 return true;
141 };
142 };
142 } else if (event.keyCode === 8 && event.type == 'keydown') {
143 } else if (event.keyCode === key.backspace && event.type == 'keydown') {
143 // If backspace and the line ends with 4 spaces, remove them.
144 // If backspace and the line ends with 4 spaces, remove them.
144 var cur = editor.getCursor();
145 var cur = editor.getCursor();
145 var line = editor.getLine(cur.line);
146 var line = editor.getLine(cur.line);
146 var ending = line.slice(-4);
147 var ending = line.slice(-4);
147 if (ending === ' ') {
148 if (ending === ' ') {
148 editor.replaceRange('',
149 editor.replaceRange('',
149 {line: cur.line, ch: cur.ch-4},
150 {line: cur.line, ch: cur.ch-4},
150 {line: cur.line, ch: cur.ch}
151 {line: cur.line, ch: cur.ch}
151 );
152 );
152 event.stop();
153 event.stop();
153 return true;
154 return true;
154 } else {
155 } else {
155 return false;
156 return false;
156 };
157 };
157 } else {
158 } else {
158 // keypress/keyup also trigger on TAB press, and we don't want to
159 // keypress/keyup also trigger on TAB press, and we don't want to
159 // use those to disable tab completion.
160 // use those to disable tab completion.
160 if (this.is_completing && event.keyCode !== 9) {
161 if (this.is_completing && event.keyCode !== key.tab) {
161 var ed_cur = editor.getCursor();
162 var ed_cur = editor.getCursor();
162 var cc_cur = this.completion_cursor;
163 var cc_cur = this.completion_cursor;
163 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
164 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
164 this.is_completing = false;
165 this.is_completing = false;
165 this.completion_cursor = null;
166 this.completion_cursor = null;
166 };
167 };
167 };
168 };
168 return false;
169 return false;
169 };
170 };
170 return false;
171 return false;
171 };
172 };
172
173
173 CodeCell.prototype.remove_and_cancel_tooltip = function() {
174 CodeCell.prototype.remove_and_cancel_tooltip = function() {
174 // note that we don't handle closing directly inside the calltip
175 // note that we don't handle closing directly inside the calltip
175 // as in the completer, because it is not focusable, so won't
176 // as in the completer, because it is not focusable, so won't
176 // get the event.
177 // get the event.
177 if (this.tooltip_timeout != null){
178 if (this.tooltip_timeout != null){
178 clearTimeout(this.tooltip_timeout);
179 clearTimeout(this.tooltip_timeout);
179 $('#tooltip').remove();
180 $('#tooltip').remove();
180 this.tooltip_timeout = null;
181 this.tooltip_timeout = null;
181 }
182 }
182 }
183 }
183
184
184 CodeCell.prototype.finish_tooltip = function (reply) {
185 CodeCell.prototype.finish_tooltip = function (reply) {
185 // Extract call tip data; the priority is call, init, main.
186 // Extract call tip data; the priority is call, init, main.
186 defstring = reply.call_def;
187 defstring = reply.call_def;
187 if (defstring == null) { defstring = reply.init_definition; }
188 if (defstring == null) { defstring = reply.init_definition; }
188 if (defstring == null) { defstring = reply.definition; }
189 if (defstring == null) { defstring = reply.definition; }
189
190
190 docstring = reply.call_docstring;
191 docstring = reply.call_docstring;
191 if (docstring == null) { docstring = reply.init_docstring; }
192 if (docstring == null) { docstring = reply.init_docstring; }
192 if (docstring == null) { docstring = reply.docstring; }
193 if (docstring == null) { docstring = reply.docstring; }
193 if (docstring == null) { docstring = "<empty docstring>"; }
194 if (docstring == null) { docstring = "<empty docstring>"; }
194
195
195 name=reply.name;
196 name=reply.name;
196
197
197 var that = this;
198 var that = this;
198 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
199 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
199 // remove to have the tooltip not Limited in X and Y
200 // remove to have the tooltip not Limited in X and Y
200 tooltip.addClass('smalltooltip');
201 tooltip.addClass('smalltooltip');
201 var pre=$('<pre/>').html(utils.fixConsole(docstring));
202 var pre=$('<pre/>').html(utils.fixConsole(docstring));
202 var expandlink=$('<a/>').attr('href',"#");
203 var expandlink=$('<a/>').attr('href',"#");
203 expandlink.addClass("ui-corner-all"); //rounded corner
204 expandlink.addClass("ui-corner-all"); //rounded corner
204 expandlink.attr('role',"button");
205 expandlink.attr('role',"button");
205 //expandlink.addClass('ui-button');
206 //expandlink.addClass('ui-button');
206 //expandlink.addClass('ui-state-default');
207 //expandlink.addClass('ui-state-default');
207 var expandspan=$('<span/>').text('Expand');
208 var expandspan=$('<span/>').text('Expand');
208 expandspan.addClass('ui-icon');
209 expandspan.addClass('ui-icon');
209 expandspan.addClass('ui-icon-plus');
210 expandspan.addClass('ui-icon-plus');
210 expandlink.append(expandspan);
211 expandlink.append(expandspan);
211 expandlink.attr('id','expanbutton');
212 expandlink.attr('id','expanbutton');
212 expandlink.click(function(){
213 expandlink.click(function(){
213 tooltip.removeClass('smalltooltip');
214 tooltip.removeClass('smalltooltip');
214 tooltip.addClass('bigtooltip');
215 tooltip.addClass('bigtooltip');
215 $('#expanbutton').remove();
216 $('#expanbutton').remove();
216 setTimeout(function(){that.code_mirror.focus();}, 50);
217 setTimeout(function(){that.code_mirror.focus();}, 50);
217 });
218 });
218 var morelink=$('<a/>').attr('href',"#");
219 var morelink=$('<a/>').attr('href',"#");
219 morelink.attr('role',"button");
220 morelink.attr('role',"button");
220 morelink.addClass('ui-button');
221 morelink.addClass('ui-button');
221 //morelink.addClass("ui-corner-all"); //rounded corner
222 //morelink.addClass("ui-corner-all"); //rounded corner
222 //morelink.addClass('ui-state-default');
223 //morelink.addClass('ui-state-default');
223 var morespan=$('<span/>').text('Open in Pager');
224 var morespan=$('<span/>').text('Open in Pager');
224 morespan.addClass('ui-icon');
225 morespan.addClass('ui-icon');
225 morespan.addClass('ui-icon-arrowstop-l-n');
226 morespan.addClass('ui-icon-arrowstop-l-n');
226 morelink.append(morespan);
227 morelink.append(morespan);
227 morelink.click(function(){
228 morelink.click(function(){
228 var msg_id = IPython.notebook.kernel.execute(name+"?");
229 var msg_id = IPython.notebook.kernel.execute(name+"?");
229 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
230 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
230 that.remove_and_cancel_tooltip();
231 that.remove_and_cancel_tooltip();
231 setTimeout(function(){that.code_mirror.focus();}, 50);
232 setTimeout(function(){that.code_mirror.focus();}, 50);
232 });
233 });
233
234
234 var closelink=$('<a/>').attr('href',"#");
235 var closelink=$('<a/>').attr('href',"#");
235 closelink.attr('role',"button");
236 closelink.attr('role',"button");
236 closelink.addClass('ui-button');
237 closelink.addClass('ui-button');
237 //closelink.addClass("ui-corner-all"); //rounded corner
238 //closelink.addClass("ui-corner-all"); //rounded corner
238 //closelink.adClass('ui-state-default'); // grey background and blue cross
239 //closelink.adClass('ui-state-default'); // grey background and blue cross
239 var closespan=$('<span/>').text('Close');
240 var closespan=$('<span/>').text('Close');
240 closespan.addClass('ui-icon');
241 closespan.addClass('ui-icon');
241 closespan.addClass('ui-icon-close');
242 closespan.addClass('ui-icon-close');
242 closelink.append(closespan);
243 closelink.append(closespan);
243 closelink.click(function(){
244 closelink.click(function(){
244 that.remove_and_cancel_tooltip();
245 that.remove_and_cancel_tooltip();
245 setTimeout(function(){that.code_mirror.focus();}, 50);
246 setTimeout(function(){that.code_mirror.focus();}, 50);
246 });
247 });
247 //construct the tooltip
248 //construct the tooltip
248 tooltip.append(closelink);
249 tooltip.append(closelink);
249 tooltip.append(expandlink);
250 tooltip.append(expandlink);
250 tooltip.append(morelink);
251 tooltip.append(morelink);
251 if(defstring){
252 if(defstring){
252 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
253 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
253 tooltip.append(defstring_html);
254 tooltip.append(defstring_html);
254 }
255 }
255 tooltip.append(pre);
256 tooltip.append(pre);
256 var pos = this.code_mirror.cursorCoords();
257 var pos = this.code_mirror.cursorCoords();
257 tooltip.css('left',pos.x+'px');
258 tooltip.css('left',pos.x+'px');
258 tooltip.css('top',pos.yBot+'px');
259 tooltip.css('top',pos.yBot+'px');
259 $('body').append(tooltip);
260 $('body').append(tooltip);
260
261
261 // issues with cross-closing if multiple tooltip in less than 5sec
262 // issues with cross-closing if multiple tooltip in less than 5sec
262 // keep it comented for now
263 // keep it comented for now
263 // setTimeout(that.remove_and_cancel_tooltip, 5000);
264 // setTimeout(that.remove_and_cancel_tooltip, 5000);
264 };
265 };
265
266
266 // As you type completer
267 // As you type completer
267 // this should be called by the completer, that in return will
268 // this should be called by the completer, that in return will
268 // be reclled by finish_completing
269 // be reclled by finish_completing
269 CodeCell.prototype.requestCompletion= function(callback)
270 CodeCell.prototype.requestCompletion= function(callback)
270 {
271 {
271 this._compcallback = callback;
272 this._compcallback = callback;
272 var cur = this.code_mirror.getCursor();
273 var cur = this.code_mirror.getCursor();
273 var pre_cursor = this.code_mirror.getRange({line:cur.line,ch:0},cur);
274 var pre_cursor = this.code_mirror.getRange({line:cur.line,ch:0},cur);
274 pre_cursor.trim();
275 pre_cursor.trim();
275 // Autocomplete the current line.
276 // Autocomplete the current line.
276 var line = this.code_mirror.getLine(cur.line);
277 var line = this.code_mirror.getLine(cur.line);
277 this.is_completing = true;
278 this.is_completing = true;
278 this.completion_cursor = cur;
279 this.completion_cursor = cur;
279 // one could fork here and directly call finish completing
280 // one could fork here and directly call finish completing
280 // if kernel is busy
281 // if kernel is busy
281 IPython.notebook.complete_cell(this, line, cur.ch);
282 IPython.notebook.complete_cell(this, line, cur.ch);
282 }
283 }
283
284
284 // called when completion came back from the kernel. this will inspect the
285 // called when completion came back from the kernel. this will inspect the
285 // curent cell for (more) completion merge the resuults with the ones
286 // curent cell for (more) completion merge the resuults with the ones
286 // comming from the kernel and forward it to the completer
287 // comming from the kernel and forward it to the completer
287 CodeCell.prototype.finish_completing = function (matched_text, matches) {
288 CodeCell.prototype.finish_completing = function (matched_text, matches) {
288 // let's build a function that wrap all that stuff into what is needed for the
289 // let's build a function that wrap all that stuff into what is needed for the
289 // new completer:
290 // new completer:
290 //
291 //
291 var cur = this.code_mirror.getCursor();
292 var cur = this.code_mirror.getCursor();
292 var res = CodeMirror.contextHint(this.code_mirror);
293 var res = CodeMirror.contextHint(this.code_mirror);
293
294
294 // append the introspection result, in order, at
295 // append the introspection result, in order, at
295 // at the beginning of the table and compute the replacement rance
296 // at the beginning of the table and compute the replacement rance
296 // from current cursor positon and matched_text length.
297 // from current cursor positon and matched_text length.
297 for(var i= matches.length-1; i>=0 ;--i)
298 for(var i= matches.length-1; i>=0 ;--i)
298 {
299 {
299 res.unshift(
300 res.unshift(
300 {
301 {
301 str : matches[i],
302 str : matches[i],
302 type : "introspection",
303 type : "introspection",
303 from : {line: cur.line, ch: cur.ch-matched_text.length},
304 from : {line: cur.line, ch: cur.ch-matched_text.length},
304 to : {line: cur.line, ch: cur.ch}
305 to : {line: cur.line, ch: cur.ch}
305 }
306 }
306 )
307 )
307 }
308 }
308 this._compcallback(res);
309 this._compcallback(res);
309 };
310 };
310
311
311
312
312 CodeCell.prototype.select = function () {
313 CodeCell.prototype.select = function () {
313 IPython.Cell.prototype.select.apply(this);
314 IPython.Cell.prototype.select.apply(this);
314 this.code_mirror.refresh();
315 this.code_mirror.refresh();
315 this.code_mirror.focus();
316 this.code_mirror.focus();
316 // We used to need an additional refresh() after the focus, but
317 // We used to need an additional refresh() after the focus, but
317 // it appears that this has been fixed in CM. This bug would show
318 // it appears that this has been fixed in CM. This bug would show
318 // up on FF when a newly loaded markdown cell was edited.
319 // up on FF when a newly loaded markdown cell was edited.
319 };
320 };
320
321
321
322
322 CodeCell.prototype.select_all = function () {
323 CodeCell.prototype.select_all = function () {
323 var start = {line: 0, ch: 0};
324 var start = {line: 0, ch: 0};
324 var nlines = this.code_mirror.lineCount();
325 var nlines = this.code_mirror.lineCount();
325 var last_line = this.code_mirror.getLine(nlines-1);
326 var last_line = this.code_mirror.getLine(nlines-1);
326 var end = {line: nlines-1, ch: last_line.length};
327 var end = {line: nlines-1, ch: last_line.length};
327 this.code_mirror.setSelection(start, end);
328 this.code_mirror.setSelection(start, end);
328 };
329 };
329
330
330
331
331 CodeCell.prototype.append_output = function (json, dynamic) {
332 CodeCell.prototype.append_output = function (json, dynamic) {
332 // If dynamic is true, javascript output will be eval'd.
333 // If dynamic is true, javascript output will be eval'd.
333 this.expand();
334 this.expand();
334 this.flush_clear_timeout();
335 this.flush_clear_timeout();
335 if (json.output_type === 'pyout') {
336 if (json.output_type === 'pyout') {
336 this.append_pyout(json, dynamic);
337 this.append_pyout(json, dynamic);
337 } else if (json.output_type === 'pyerr') {
338 } else if (json.output_type === 'pyerr') {
338 this.append_pyerr(json);
339 this.append_pyerr(json);
339 } else if (json.output_type === 'display_data') {
340 } else if (json.output_type === 'display_data') {
340 this.append_display_data(json, dynamic);
341 this.append_display_data(json, dynamic);
341 } else if (json.output_type === 'stream') {
342 } else if (json.output_type === 'stream') {
342 this.append_stream(json);
343 this.append_stream(json);
343 };
344 };
344 this.outputs.push(json);
345 this.outputs.push(json);
345 };
346 };
346
347
347
348
348 CodeCell.prototype.create_output_area = function () {
349 CodeCell.prototype.create_output_area = function () {
349 var oa = $("<div/>").addClass("hbox output_area");
350 var oa = $("<div/>").addClass("hbox output_area");
350 oa.append($('<div/>').addClass('prompt'));
351 oa.append($('<div/>').addClass('prompt'));
351 return oa;
352 return oa;
352 };
353 };
353
354
354
355
355 CodeCell.prototype.append_pyout = function (json, dynamic) {
356 CodeCell.prototype.append_pyout = function (json, dynamic) {
356 var n = json.prompt_number || ' ';
357 var n = json.prompt_number || ' ';
357 var toinsert = this.create_output_area();
358 var toinsert = this.create_output_area();
358 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
359 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
359 this.append_mime_type(json, toinsert, dynamic);
360 this.append_mime_type(json, toinsert, dynamic);
360 this.element.find('div.output').append(toinsert);
361 this.element.find('div.output').append(toinsert);
361 // If we just output latex, typeset it.
362 // If we just output latex, typeset it.
362 if ((json.latex !== undefined) || (json.html !== undefined)) {
363 if ((json.latex !== undefined) || (json.html !== undefined)) {
363 this.typeset();
364 this.typeset();
364 };
365 };
365 };
366 };
366
367
367
368
368 CodeCell.prototype.append_pyerr = function (json) {
369 CodeCell.prototype.append_pyerr = function (json) {
369 var tb = json.traceback;
370 var tb = json.traceback;
370 if (tb !== undefined && tb.length > 0) {
371 if (tb !== undefined && tb.length > 0) {
371 var s = '';
372 var s = '';
372 var len = tb.length;
373 var len = tb.length;
373 for (var i=0; i<len; i++) {
374 for (var i=0; i<len; i++) {
374 s = s + tb[i] + '\n';
375 s = s + tb[i] + '\n';
375 }
376 }
376 s = s + '\n';
377 s = s + '\n';
377 var toinsert = this.create_output_area();
378 var toinsert = this.create_output_area();
378 this.append_text(s, toinsert);
379 this.append_text(s, toinsert);
379 this.element.find('div.output').append(toinsert);
380 this.element.find('div.output').append(toinsert);
380 };
381 };
381 };
382 };
382
383
383
384
384 CodeCell.prototype.append_stream = function (json) {
385 CodeCell.prototype.append_stream = function (json) {
385 // temporary fix: if stream undefined (json file written prior to this patch),
386 // temporary fix: if stream undefined (json file written prior to this patch),
386 // default to most likely stdout:
387 // default to most likely stdout:
387 if (json.stream == undefined){
388 if (json.stream == undefined){
388 json.stream = 'stdout';
389 json.stream = 'stdout';
389 }
390 }
390 if (!utils.fixConsole(json.text)){
391 if (!utils.fixConsole(json.text)){
391 // fixConsole gives nothing (empty string, \r, etc.)
392 // fixConsole gives nothing (empty string, \r, etc.)
392 // so don't append any elements, which might add undesirable space
393 // so don't append any elements, which might add undesirable space
393 return;
394 return;
394 }
395 }
395 var subclass = "output_"+json.stream;
396 var subclass = "output_"+json.stream;
396 if (this.outputs.length > 0){
397 if (this.outputs.length > 0){
397 // have at least one output to consider
398 // have at least one output to consider
398 var last = this.outputs[this.outputs.length-1];
399 var last = this.outputs[this.outputs.length-1];
399 if (last.output_type == 'stream' && json.stream == last.stream){
400 if (last.output_type == 'stream' && json.stream == last.stream){
400 // latest output was in the same stream,
401 // latest output was in the same stream,
401 // so append directly into its pre tag
402 // so append directly into its pre tag
402 // escape ANSI & HTML specials:
403 // escape ANSI & HTML specials:
403 var text = utils.fixConsole(json.text);
404 var text = utils.fixConsole(json.text);
404 this.element.find('div.'+subclass).last().find('pre').append(text);
405 this.element.find('div.'+subclass).last().find('pre').append(text);
405 return;
406 return;
406 }
407 }
407 }
408 }
408
409
409 // If we got here, attach a new div
410 // If we got here, attach a new div
410 var toinsert = this.create_output_area();
411 var toinsert = this.create_output_area();
411 this.append_text(json.text, toinsert, "output_stream "+subclass);
412 this.append_text(json.text, toinsert, "output_stream "+subclass);
412 this.element.find('div.output').append(toinsert);
413 this.element.find('div.output').append(toinsert);
413 };
414 };
414
415
415
416
416 CodeCell.prototype.append_display_data = function (json, dynamic) {
417 CodeCell.prototype.append_display_data = function (json, dynamic) {
417 var toinsert = this.create_output_area();
418 var toinsert = this.create_output_area();
418 this.append_mime_type(json, toinsert, dynamic);
419 this.append_mime_type(json, toinsert, dynamic);
419 this.element.find('div.output').append(toinsert);
420 this.element.find('div.output').append(toinsert);
420 // If we just output latex, typeset it.
421 // If we just output latex, typeset it.
421 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
422 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
422 this.typeset();
423 this.typeset();
423 };
424 };
424 };
425 };
425
426
426
427
427 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
428 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
428 if (json.javascript !== undefined && dynamic) {
429 if (json.javascript !== undefined && dynamic) {
429 this.append_javascript(json.javascript, element, dynamic);
430 this.append_javascript(json.javascript, element, dynamic);
430 } else if (json.html !== undefined) {
431 } else if (json.html !== undefined) {
431 this.append_html(json.html, element);
432 this.append_html(json.html, element);
432 } else if (json.latex !== undefined) {
433 } else if (json.latex !== undefined) {
433 this.append_latex(json.latex, element);
434 this.append_latex(json.latex, element);
434 } else if (json.svg !== undefined) {
435 } else if (json.svg !== undefined) {
435 this.append_svg(json.svg, element);
436 this.append_svg(json.svg, element);
436 } else if (json.png !== undefined) {
437 } else if (json.png !== undefined) {
437 this.append_png(json.png, element);
438 this.append_png(json.png, element);
438 } else if (json.jpeg !== undefined) {
439 } else if (json.jpeg !== undefined) {
439 this.append_jpeg(json.jpeg, element);
440 this.append_jpeg(json.jpeg, element);
440 } else if (json.text !== undefined) {
441 } else if (json.text !== undefined) {
441 this.append_text(json.text, element);
442 this.append_text(json.text, element);
442 };
443 };
443 };
444 };
444
445
445
446
446 CodeCell.prototype.append_html = function (html, element) {
447 CodeCell.prototype.append_html = function (html, element) {
447 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
448 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
448 toinsert.append(html);
449 toinsert.append(html);
449 element.append(toinsert);
450 element.append(toinsert);
450 };
451 };
451
452
452
453
453 CodeCell.prototype.append_javascript = function (js, container) {
454 CodeCell.prototype.append_javascript = function (js, container) {
454 // We just eval the JS code, element appears in the local scope.
455 // We just eval the JS code, element appears in the local scope.
455 var element = $("<div/>").addClass("box_flex1 output_subarea");
456 var element = $("<div/>").addClass("box_flex1 output_subarea");
456 container.append(element);
457 container.append(element);
457 // Div for js shouldn't be drawn, as it will add empty height to the area.
458 // Div for js shouldn't be drawn, as it will add empty height to the area.
458 container.hide();
459 container.hide();
459 // If the Javascript appends content to `element` that should be drawn, then
460 // If the Javascript appends content to `element` that should be drawn, then
460 // it must also call `container.show()`.
461 // it must also call `container.show()`.
461 eval(js);
462 eval(js);
462 }
463 }
463
464
464
465
465 CodeCell.prototype.append_text = function (data, element, extra_class) {
466 CodeCell.prototype.append_text = function (data, element, extra_class) {
466 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
467 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
467 // escape ANSI & HTML specials in plaintext:
468 // escape ANSI & HTML specials in plaintext:
468 data = utils.fixConsole(data);
469 data = utils.fixConsole(data);
469 if (extra_class){
470 if (extra_class){
470 toinsert.addClass(extra_class);
471 toinsert.addClass(extra_class);
471 }
472 }
472 toinsert.append($("<pre/>").html(data));
473 toinsert.append($("<pre/>").html(data));
473 element.append(toinsert);
474 element.append(toinsert);
474 };
475 };
475
476
476
477
477 CodeCell.prototype.append_svg = function (svg, element) {
478 CodeCell.prototype.append_svg = function (svg, element) {
478 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
479 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
479 toinsert.append(svg);
480 toinsert.append(svg);
480 element.append(toinsert);
481 element.append(toinsert);
481 };
482 };
482
483
483
484
484 CodeCell.prototype.append_png = function (png, element) {
485 CodeCell.prototype.append_png = function (png, element) {
485 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
486 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
486 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
487 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
487 element.append(toinsert);
488 element.append(toinsert);
488 };
489 };
489
490
490
491
491 CodeCell.prototype.append_jpeg = function (jpeg, element) {
492 CodeCell.prototype.append_jpeg = function (jpeg, element) {
492 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
493 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
493 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
494 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
494 element.append(toinsert);
495 element.append(toinsert);
495 };
496 };
496
497
497
498
498 CodeCell.prototype.append_latex = function (latex, element) {
499 CodeCell.prototype.append_latex = function (latex, element) {
499 // This method cannot do the typesetting because the latex first has to
500 // This method cannot do the typesetting because the latex first has to
500 // be on the page.
501 // be on the page.
501 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
502 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
502 toinsert.append(latex);
503 toinsert.append(latex);
503 element.append(toinsert);
504 element.append(toinsert);
504 };
505 };
505
506
506
507
507 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
508 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
508 var that = this;
509 var that = this;
509 if (this.clear_out_timeout != null){
510 if (this.clear_out_timeout != null){
510 // fire previous pending clear *immediately*
511 // fire previous pending clear *immediately*
511 clearTimeout(this.clear_out_timeout);
512 clearTimeout(this.clear_out_timeout);
512 this.clear_out_timeout = null;
513 this.clear_out_timeout = null;
513 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
514 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
514 }
515 }
515 // store flags for flushing the timeout
516 // store flags for flushing the timeout
516 this._clear_stdout = stdout;
517 this._clear_stdout = stdout;
517 this._clear_stderr = stderr;
518 this._clear_stderr = stderr;
518 this._clear_other = other;
519 this._clear_other = other;
519 this.clear_out_timeout = setTimeout(function(){
520 this.clear_out_timeout = setTimeout(function(){
520 // really clear timeout only after a short delay
521 // really clear timeout only after a short delay
521 // this reduces flicker in 'clear_output; print' cases
522 // this reduces flicker in 'clear_output; print' cases
522 that.clear_out_timeout = null;
523 that.clear_out_timeout = null;
523 that._clear_stdout = that._clear_stderr = that._clear_other = null;
524 that._clear_stdout = that._clear_stderr = that._clear_other = null;
524 that.clear_output_callback(stdout, stderr, other);
525 that.clear_output_callback(stdout, stderr, other);
525 }, 500
526 }, 500
526 );
527 );
527 };
528 };
528
529
529 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
530 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
530 var output_div = this.element.find("div.output");
531 var output_div = this.element.find("div.output");
531
532
532 if (stdout && stderr && other){
533 if (stdout && stderr && other){
533 // clear all, no need for logic
534 // clear all, no need for logic
534 output_div.html("");
535 output_div.html("");
535 this.outputs = [];
536 this.outputs = [];
536 return;
537 return;
537 }
538 }
538 // remove html output
539 // remove html output
539 // each output_subarea that has an identifying class is in an output_area
540 // each output_subarea that has an identifying class is in an output_area
540 // which is the element to be removed.
541 // which is the element to be removed.
541 if (stdout){
542 if (stdout){
542 output_div.find("div.output_stdout").parent().remove();
543 output_div.find("div.output_stdout").parent().remove();
543 }
544 }
544 if (stderr){
545 if (stderr){
545 output_div.find("div.output_stderr").parent().remove();
546 output_div.find("div.output_stderr").parent().remove();
546 }
547 }
547 if (other){
548 if (other){
548 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
549 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
549 }
550 }
550
551
551 // remove cleared outputs from JSON list:
552 // remove cleared outputs from JSON list:
552 for (var i = this.outputs.length - 1; i >= 0; i--){
553 for (var i = this.outputs.length - 1; i >= 0; i--){
553 var out = this.outputs[i];
554 var out = this.outputs[i];
554 var output_type = out.output_type;
555 var output_type = out.output_type;
555 if (output_type == "display_data" && other){
556 if (output_type == "display_data" && other){
556 this.outputs.splice(i,1);
557 this.outputs.splice(i,1);
557 }else if (output_type == "stream"){
558 }else if (output_type == "stream"){
558 if (stdout && out.stream == "stdout"){
559 if (stdout && out.stream == "stdout"){
559 this.outputs.splice(i,1);
560 this.outputs.splice(i,1);
560 }else if (stderr && out.stream == "stderr"){
561 }else if (stderr && out.stream == "stderr"){
561 this.outputs.splice(i,1);
562 this.outputs.splice(i,1);
562 }
563 }
563 }
564 }
564 }
565 }
565 };
566 };
566
567
567
568
568 CodeCell.prototype.clear_input = function () {
569 CodeCell.prototype.clear_input = function () {
569 this.code_mirror.setValue('');
570 this.code_mirror.setValue('');
570 };
571 };
571
572
572 CodeCell.prototype.flush_clear_timeout = function() {
573 CodeCell.prototype.flush_clear_timeout = function() {
573 var output_div = this.element.find('div.output');
574 var output_div = this.element.find('div.output');
574 if (this.clear_out_timeout){
575 if (this.clear_out_timeout){
575 clearTimeout(this.clear_out_timeout);
576 clearTimeout(this.clear_out_timeout);
576 this.clear_out_timeout = null;
577 this.clear_out_timeout = null;
577 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
578 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
578 };
579 };
579 }
580 }
580
581
581
582
582 CodeCell.prototype.collapse = function () {
583 CodeCell.prototype.collapse = function () {
583 if (!this.collapsed) {
584 if (!this.collapsed) {
584 this.element.find('div.output').hide();
585 this.element.find('div.output').hide();
585 this.collapsed = true;
586 this.collapsed = true;
586 };
587 };
587 };
588 };
588
589
589
590
590 CodeCell.prototype.expand = function () {
591 CodeCell.prototype.expand = function () {
591 if (this.collapsed) {
592 if (this.collapsed) {
592 this.element.find('div.output').show();
593 this.element.find('div.output').show();
593 this.collapsed = false;
594 this.collapsed = false;
594 };
595 };
595 };
596 };
596
597
597
598
598 CodeCell.prototype.toggle_output = function () {
599 CodeCell.prototype.toggle_output = function () {
599 if (this.collapsed) {
600 if (this.collapsed) {
600 this.expand();
601 this.expand();
601 } else {
602 } else {
602 this.collapse();
603 this.collapse();
603 };
604 };
604 };
605 };
605
606
606 CodeCell.prototype.set_input_prompt = function (number) {
607 CodeCell.prototype.set_input_prompt = function (number) {
607 this.input_prompt_number = number;
608 this.input_prompt_number = number;
608 var ns = number || "&nbsp;";
609 var ns = number || "&nbsp;";
609 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
610 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
610 };
611 };
611
612
612
613
613 CodeCell.prototype.get_text = function () {
614 CodeCell.prototype.get_text = function () {
614 return this.code_mirror.getValue();
615 return this.code_mirror.getValue();
615 };
616 };
616
617
617
618
618 CodeCell.prototype.set_text = function (code) {
619 CodeCell.prototype.set_text = function (code) {
619 return this.code_mirror.setValue(code);
620 return this.code_mirror.setValue(code);
620 };
621 };
621
622
622
623
623 CodeCell.prototype.at_top = function () {
624 CodeCell.prototype.at_top = function () {
624 var cursor = this.code_mirror.getCursor();
625 var cursor = this.code_mirror.getCursor();
625 if (cursor.line === 0) {
626 if (cursor.line === 0) {
626 return true;
627 return true;
627 } else {
628 } else {
628 return false;
629 return false;
629 }
630 }
630 };
631 };
631
632
632
633
633 CodeCell.prototype.at_bottom = function () {
634 CodeCell.prototype.at_bottom = function () {
634 var cursor = this.code_mirror.getCursor();
635 var cursor = this.code_mirror.getCursor();
635 if (cursor.line === (this.code_mirror.lineCount()-1)) {
636 if (cursor.line === (this.code_mirror.lineCount()-1)) {
636 return true;
637 return true;
637 } else {
638 } else {
638 return false;
639 return false;
639 }
640 }
640 };
641 };
641
642
642
643
643 CodeCell.prototype.fromJSON = function (data) {
644 CodeCell.prototype.fromJSON = function (data) {
644 if (data.cell_type === 'code') {
645 if (data.cell_type === 'code') {
645 if (data.input !== undefined) {
646 if (data.input !== undefined) {
646 this.set_text(data.input);
647 this.set_text(data.input);
647 }
648 }
648 if (data.prompt_number !== undefined) {
649 if (data.prompt_number !== undefined) {
649 this.set_input_prompt(data.prompt_number);
650 this.set_input_prompt(data.prompt_number);
650 } else {
651 } else {
651 this.set_input_prompt();
652 this.set_input_prompt();
652 };
653 };
653 var len = data.outputs.length;
654 var len = data.outputs.length;
654 for (var i=0; i<len; i++) {
655 for (var i=0; i<len; i++) {
655 // append with dynamic=false.
656 // append with dynamic=false.
656 this.append_output(data.outputs[i], false);
657 this.append_output(data.outputs[i], false);
657 };
658 };
658 if (data.collapsed !== undefined) {
659 if (data.collapsed !== undefined) {
659 if (data.collapsed) {
660 if (data.collapsed) {
660 this.collapse();
661 this.collapse();
661 } else {
662 } else {
662 this.expand();
663 this.expand();
663 };
664 };
664 };
665 };
665 };
666 };
666 };
667 };
667
668
668
669
669 CodeCell.prototype.toJSON = function () {
670 CodeCell.prototype.toJSON = function () {
670 var data = {};
671 var data = {};
671 data.input = this.get_text();
672 data.input = this.get_text();
672 data.cell_type = 'code';
673 data.cell_type = 'code';
673 if (this.input_prompt_number) {
674 if (this.input_prompt_number) {
674 data.prompt_number = this.input_prompt_number;
675 data.prompt_number = this.input_prompt_number;
675 };
676 };
676 var outputs = [];
677 var outputs = [];
677 var len = this.outputs.length;
678 var len = this.outputs.length;
678 for (var i=0; i<len; i++) {
679 for (var i=0; i<len; i++) {
679 outputs[i] = this.outputs[i];
680 outputs[i] = this.outputs[i];
680 };
681 };
681 data.outputs = outputs;
682 data.outputs = outputs;
682 data.language = 'python';
683 data.language = 'python';
683 data.collapsed = this.collapsed;
684 data.collapsed = this.collapsed;
684 return data;
685 return data;
685 };
686 };
686
687
687
688
688 IPython.CodeCell = CodeCell;
689 IPython.CodeCell = CodeCell;
689
690
690 return IPython;
691 return IPython;
691 }(IPython));
692 }(IPython));
@@ -1,102 +1,120
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-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 // Utilities
9 // Utilities
10 //============================================================================
10 //============================================================================
11
11
12 IPython.namespace('IPython.utils');
12 IPython.namespace('IPython.utils');
13
13
14 IPython.utils = (function (IPython) {
14 IPython.utils = (function (IPython) {
15
15
16 var uuid = function () {
16 var uuid = function () {
17 // http://www.ietf.org/rfc/rfc4122.txt
17 // http://www.ietf.org/rfc/rfc4122.txt
18 var s = [];
18 var s = [];
19 var hexDigits = "0123456789ABCDEF";
19 var hexDigits = "0123456789ABCDEF";
20 for (var i = 0; i < 32; i++) {
20 for (var i = 0; i < 32; i++) {
21 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
21 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
22 }
22 }
23 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
23 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
24 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
24 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
25
25
26 var uuid = s.join("");
26 var uuid = s.join("");
27 return uuid;
27 return uuid;
28 };
28 };
29
29
30
30
31 //Fix raw text to parse correctly in crazy XML
31 //Fix raw text to parse correctly in crazy XML
32 function xmlencode(string) {
32 function xmlencode(string) {
33 return string.replace(/\&/g,'&'+'amp;')
33 return string.replace(/\&/g,'&'+'amp;')
34 .replace(/</g,'&'+'lt;')
34 .replace(/</g,'&'+'lt;')
35 .replace(/>/g,'&'+'gt;')
35 .replace(/>/g,'&'+'gt;')
36 .replace(/\'/g,'&'+'apos;')
36 .replace(/\'/g,'&'+'apos;')
37 .replace(/\"/g,'&'+'quot;')
37 .replace(/\"/g,'&'+'quot;')
38 .replace(/`/g,'&'+'#96;');
38 .replace(/`/g,'&'+'#96;');
39 }
39 }
40
40
41
41
42 //Map from terminal commands to CSS classes
42 //Map from terminal commands to CSS classes
43 ansi_colormap = {
43 ansi_colormap = {
44 "30":"ansiblack", "31":"ansired",
44 "30":"ansiblack", "31":"ansired",
45 "32":"ansigreen", "33":"ansiyellow",
45 "32":"ansigreen", "33":"ansiyellow",
46 "34":"ansiblue", "35":"ansipurple","36":"ansicyan",
46 "34":"ansiblue", "35":"ansipurple","36":"ansicyan",
47 "37":"ansigrey", "01":"ansibold"
47 "37":"ansigrey", "01":"ansibold"
48 };
48 };
49
49
50 // Transform ANI color escape codes into HTML <span> tags with css
50 // Transform ANI color escape codes into HTML <span> tags with css
51 // classes listed in the above ansi_colormap object. The actual color used
51 // classes listed in the above ansi_colormap object. The actual color used
52 // are set in the css file.
52 // are set in the css file.
53 function fixConsole(txt) {
53 function fixConsole(txt) {
54 txt = xmlencode(txt);
54 txt = xmlencode(txt);
55 var re = /\033\[([\dA-Fa-f;]*?)m/;
55 var re = /\033\[([\dA-Fa-f;]*?)m/;
56 var opened = false;
56 var opened = false;
57 var cmds = [];
57 var cmds = [];
58 var opener = "";
58 var opener = "";
59 var closer = "";
59 var closer = "";
60 // \r does nothing, so shouldn't be included
60 // \r does nothing, so shouldn't be included
61 txt = txt.replace('\r', '');
61 txt = txt.replace('\r', '');
62 while (re.test(txt)) {
62 while (re.test(txt)) {
63 var cmds = txt.match(re)[1].split(";");
63 var cmds = txt.match(re)[1].split(";");
64 closer = opened?"</span>":"";
64 closer = opened?"</span>":"";
65 opened = cmds.length > 1 || cmds[0] != 0;
65 opened = cmds.length > 1 || cmds[0] != 0;
66 var rep = [];
66 var rep = [];
67 for (var i in cmds)
67 for (var i in cmds)
68 if (typeof(ansi_colormap[cmds[i]]) != "undefined")
68 if (typeof(ansi_colormap[cmds[i]]) != "undefined")
69 rep.push(ansi_colormap[cmds[i]]);
69 rep.push(ansi_colormap[cmds[i]]);
70 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":"";
70 opener = rep.length > 0?"<span class=\""+rep.join(" ")+"\">":"";
71 txt = txt.replace(re, closer + opener);
71 txt = txt.replace(re, closer + opener);
72 }
72 }
73 if (opened) txt += "</span>";
73 if (opened) txt += "</span>";
74 return txt;
74 return txt;
75 }
75 }
76
76
77
77
78 grow = function(element) {
78 grow = function(element) {
79 // Grow the cell by hand. This is used upon reloading from JSON, when the
79 // Grow the cell by hand. This is used upon reloading from JSON, when the
80 // autogrow handler is not called.
80 // autogrow handler is not called.
81 var dom = element.get(0);
81 var dom = element.get(0);
82 var lines_count = 0;
82 var lines_count = 0;
83 // modified split rule from
83 // modified split rule from
84 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
84 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
85 var lines = dom.value.split(/\r|\r\n|\n/);
85 var lines = dom.value.split(/\r|\r\n|\n/);
86 lines_count = lines.length;
86 lines_count = lines.length;
87 if (lines_count >= 1) {
87 if (lines_count >= 1) {
88 dom.rows = lines_count;
88 dom.rows = lines_count;
89 } else {
89 } else {
90 dom.rows = 1;
90 dom.rows = 1;
91 }
91 }
92 };
92 };
93
93
94 // some keycodes that seem to be platform/browser independant
95 var keycodes ={
96 backspace: 8,
97 tab : 9,
98 enter : 13,
99 shift : 16,
100 esc : 27,
101 space : 32,
102 pgUp : 33,
103 pgDown : 34,
104 leftArrow: 37,
105 left : 37,
106 upArrow : 38,
107 rightArrow:39,
108 right : 39,
109 downArrow: 40,
110 };
94
111
95 return {
112 return {
96 uuid : uuid,
113 uuid : uuid,
97 fixConsole : fixConsole,
114 fixConsole : fixConsole,
98 grow : grow
115 keycodes : keycodes,
116 grow : grow,
99 };
117 };
100
118
101 }(IPython));
119 }(IPython));
102
120
General Comments 0
You need to be logged in to leave comments. Login now