##// END OF EJS Templates
Better tabindex support.
Brian E. Granger -
Show More
@@ -1,439 +1,440
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
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var CodeCell = function (notebook) {
16 var CodeCell = function (notebook) {
17 this.code_mirror = null;
17 this.code_mirror = null;
18 this.input_prompt_number = ' ';
18 this.input_prompt_number = ' ';
19 this.is_completing = false;
19 this.is_completing = false;
20 this.completion_cursor = null;
20 this.completion_cursor = null;
21 this.outputs = [];
21 this.outputs = [];
22 this.collapsed = false;
22 this.collapsed = false;
23 IPython.Cell.apply(this, arguments);
23 IPython.Cell.apply(this, arguments);
24 };
24 };
25
25
26
26
27 CodeCell.prototype = new IPython.Cell();
27 CodeCell.prototype = new IPython.Cell();
28
28
29
29
30 CodeCell.prototype.create_element = function () {
30 CodeCell.prototype.create_element = function () {
31 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
31 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
32 cell.attr('tabindex','2');
32 var input = $('<div></div>').addClass('input hbox');
33 var input = $('<div></div>').addClass('input hbox');
33 input.append($('<div/>').addClass('prompt input_prompt'));
34 input.append($('<div/>').addClass('prompt input_prompt'));
34 var input_area = $('<div/>').addClass('input_area box-flex1');
35 var input_area = $('<div/>').addClass('input_area box-flex1');
35 this.code_mirror = CodeMirror(input_area.get(0), {
36 this.code_mirror = CodeMirror(input_area.get(0), {
36 indentUnit : 4,
37 indentUnit : 4,
37 mode: 'python',
38 mode: 'python',
38 theme: 'ipython',
39 theme: 'ipython',
39 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
40 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
40 });
41 });
41 input.append(input_area);
42 input.append(input_area);
42 var output = $('<div></div>').addClass('output vbox');
43 var output = $('<div></div>').addClass('output vbox');
43 cell.append(input).append(output);
44 cell.append(input).append(output);
44 this.element = cell;
45 this.element = cell;
45 this.collapse()
46 this.collapse()
46 };
47 };
47
48
48
49
49 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
50 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
50 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
51 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
51 // is used to provide custom key handling. Its return value is used to determine
52 // is used to provide custom key handling. Its return value is used to determine
52 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
53 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
53 if (event.keyCode === 13 && event.shiftKey) {
54 if (event.keyCode === 13 && event.shiftKey) {
54 // Always ignore shift-enter in CodeMirror as we handle it.
55 // Always ignore shift-enter in CodeMirror as we handle it.
55 return true;
56 return true;
56 } else if (event.keyCode === 9 && event.type == 'keydown') {
57 } else if (event.keyCode === 9 && event.type == 'keydown') {
57 // Tab completion.
58 // Tab completion.
58 var cur = editor.getCursor();
59 var cur = editor.getCursor();
59 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
60 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
60 if (pre_cursor === "") {
61 if (pre_cursor === "") {
61 // Don't autocomplete if the part of the line before the cursor is empty.
62 // Don't autocomplete if the part of the line before the cursor is empty.
62 // In this case, let CodeMirror handle indentation.
63 // In this case, let CodeMirror handle indentation.
63 return false;
64 return false;
64 } else {
65 } else {
65 // Autocomplete the current line.
66 // Autocomplete the current line.
66 event.stop();
67 event.stop();
67 var line = editor.getLine(cur.line);
68 var line = editor.getLine(cur.line);
68 this.is_completing = true;
69 this.is_completing = true;
69 this.completion_cursor = cur;
70 this.completion_cursor = cur;
70 IPython.notebook.complete_cell(this, line, cur.ch);
71 IPython.notebook.complete_cell(this, line, cur.ch);
71 return true;
72 return true;
72 }
73 }
73 } else if (event.keyCode === 8 && event.type == 'keydown') {
74 } else if (event.keyCode === 8 && event.type == 'keydown') {
74 // If backspace and the line ends with 4 spaces, remove them.
75 // If backspace and the line ends with 4 spaces, remove them.
75 var cur = editor.getCursor();
76 var cur = editor.getCursor();
76 var line = editor.getLine(cur.line);
77 var line = editor.getLine(cur.line);
77 var ending = line.slice(-4);
78 var ending = line.slice(-4);
78 if (ending === ' ') {
79 if (ending === ' ') {
79 editor.replaceRange('',
80 editor.replaceRange('',
80 {line: cur.line, ch: cur.ch-4},
81 {line: cur.line, ch: cur.ch-4},
81 {line: cur.line, ch: cur.ch}
82 {line: cur.line, ch: cur.ch}
82 );
83 );
83 event.stop();
84 event.stop();
84 return true;
85 return true;
85 } else {
86 } else {
86 return false;
87 return false;
87 };
88 };
88 } else {
89 } else {
89 // keypress/keyup also trigger on TAB press, and we don't want to use those
90 // keypress/keyup also trigger on TAB press, and we don't want to use those
90 // to disable tab completion.
91 // to disable tab completion.
91 if (this.is_completing && event.keyCode !== 9) {
92 if (this.is_completing && event.keyCode !== 9) {
92 var ed_cur = editor.getCursor();
93 var ed_cur = editor.getCursor();
93 var cc_cur = this.completion_cursor;
94 var cc_cur = this.completion_cursor;
94 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
95 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
95 this.is_completing = false;
96 this.is_completing = false;
96 this.completion_cursor = null;
97 this.completion_cursor = null;
97 };
98 };
98 };
99 };
99 return false;
100 return false;
100 };
101 };
101 };
102 };
102
103
103
104
104 CodeCell.prototype.finish_completing = function (matched_text, matches) {
105 CodeCell.prototype.finish_completing = function (matched_text, matches) {
105 // console.log("Got matches", matched_text, matches);
106 // console.log("Got matches", matched_text, matches);
106 if (!this.is_completing || matches.length === 0) {return;}
107 if (!this.is_completing || matches.length === 0) {return;}
107
108
108 var that = this;
109 var that = this;
109 var cur = this.completion_cursor;
110 var cur = this.completion_cursor;
110
111
111 var insert = function (selected_text) {
112 var insert = function (selected_text) {
112 that.code_mirror.replaceRange(
113 that.code_mirror.replaceRange(
113 selected_text,
114 selected_text,
114 {line: cur.line, ch: (cur.ch-matched_text.length)},
115 {line: cur.line, ch: (cur.ch-matched_text.length)},
115 {line: cur.line, ch: cur.ch}
116 {line: cur.line, ch: cur.ch}
116 );
117 );
117 };
118 };
118
119
119 if (matches.length === 1) {
120 if (matches.length === 1) {
120 insert(matches[0]);
121 insert(matches[0]);
121 setTimeout(function(){that.code_mirror.focus();}, 50);
122 setTimeout(function(){that.code_mirror.focus();}, 50);
122 return;
123 return;
123 };
124 };
124
125
125 var complete = $('<div/>').addClass('completions');
126 var complete = $('<div/>').addClass('completions');
126 var select = $('<select/>').attr('multiple','true');
127 var select = $('<select/>').attr('multiple','true');
127 for (var i=0; i<matches.length; ++i) {
128 for (var i=0; i<matches.length; ++i) {
128 select.append($('<option/>').text(matches[i]));
129 select.append($('<option/>').text(matches[i]));
129 }
130 }
130 select.children().first().attr('selected','true');
131 select.children().first().attr('selected','true');
131 select.attr('size',Math.min(10,matches.length));
132 select.attr('size',Math.min(10,matches.length));
132 var pos = this.code_mirror.cursorCoords();
133 var pos = this.code_mirror.cursorCoords();
133 complete.css('left',pos.x+'px');
134 complete.css('left',pos.x+'px');
134 complete.css('top',pos.yBot+'px');
135 complete.css('top',pos.yBot+'px');
135 complete.append(select);
136 complete.append(select);
136
137
137 $('body').append(complete);
138 $('body').append(complete);
138 var done = false;
139 var done = false;
139
140
140 var close = function () {
141 var close = function () {
141 if (done) return;
142 if (done) return;
142 done = true;
143 done = true;
143 complete.remove();
144 complete.remove();
144 that.is_completing = false;
145 that.is_completing = false;
145 that.completion_cursor = null;
146 that.completion_cursor = null;
146 };
147 };
147
148
148 var pick = function () {
149 var pick = function () {
149 insert(select.val()[0]);
150 insert(select.val()[0]);
150 close();
151 close();
151 setTimeout(function(){that.code_mirror.focus();}, 50);
152 setTimeout(function(){that.code_mirror.focus();}, 50);
152 };
153 };
153
154
154 select.blur(close);
155 select.blur(close);
155 select.keydown(function (event) {
156 select.keydown(function (event) {
156 var code = event.which;
157 var code = event.which;
157 if (code === 13 || code === 32) {
158 if (code === 13 || code === 32) {
158 // Pressing SPACE or ENTER will cause a pick
159 // Pressing SPACE or ENTER will cause a pick
159 event.stopPropagation();
160 event.stopPropagation();
160 event.preventDefault();
161 event.preventDefault();
161 pick();
162 pick();
162 } else if (code === 38 || code === 40) {
163 } else if (code === 38 || code === 40) {
163 // We don't want the document keydown handler to handle UP/DOWN,
164 // We don't want the document keydown handler to handle UP/DOWN,
164 // but we want the default action.
165 // but we want the default action.
165 event.stopPropagation();
166 event.stopPropagation();
166 } else {
167 } else {
167 // All other key presses exit completion.
168 // All other key presses exit completion.
168 event.stopPropagation();
169 event.stopPropagation();
169 event.preventDefault();
170 event.preventDefault();
170 close();
171 close();
171 that.code_mirror.focus();
172 that.code_mirror.focus();
172 }
173 }
173 });
174 });
174 // Double click also causes a pick.
175 // Double click also causes a pick.
175 select.dblclick(pick);
176 select.dblclick(pick);
176 select.focus();
177 select.focus();
177 };
178 };
178
179
179
180
180 CodeCell.prototype.select = function () {
181 CodeCell.prototype.select = function () {
181 IPython.Cell.prototype.select.apply(this);
182 IPython.Cell.prototype.select.apply(this);
182 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
183 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
183 // not causing the cursor to blink if the editor is empty initially.
184 // not causing the cursor to blink if the editor is empty initially.
184 // While this seems to fix the issue, this should be fixed
185 // While this seems to fix the issue, this should be fixed
185 // in CodeMirror proper.
186 // in CodeMirror proper.
186 var s = this.code_mirror.getValue();
187 var s = this.code_mirror.getValue();
187 this.code_mirror.focus();
188 this.code_mirror.focus();
188 if (s === '') this.code_mirror.setValue('');
189 if (s === '') this.code_mirror.setValue('');
189 };
190 };
190
191
191
192
192 CodeCell.prototype.append_output = function (json) {
193 CodeCell.prototype.append_output = function (json) {
193 this.expand();
194 this.expand();
194 if (json.output_type === 'pyout') {
195 if (json.output_type === 'pyout') {
195 this.append_pyout(json);
196 this.append_pyout(json);
196 } else if (json.output_type === 'pyerr') {
197 } else if (json.output_type === 'pyerr') {
197 this.append_pyerr(json);
198 this.append_pyerr(json);
198 } else if (json.output_type === 'display_data') {
199 } else if (json.output_type === 'display_data') {
199 this.append_display_data(json);
200 this.append_display_data(json);
200 } else if (json.output_type === 'stream') {
201 } else if (json.output_type === 'stream') {
201 this.append_stream(json);
202 this.append_stream(json);
202 };
203 };
203 this.outputs.push(json);
204 this.outputs.push(json);
204 };
205 };
205
206
206
207
207 CodeCell.prototype.append_pyout = function (json) {
208 CodeCell.prototype.append_pyout = function (json) {
208 n = json.prompt_number || ' ';
209 n = json.prompt_number || ' ';
209 var toinsert = $("<div/>").addClass("output_pyout hbox output_area");
210 var toinsert = $("<div/>").addClass("output_pyout hbox output_area");
210 toinsert.append($('<div/>').
211 toinsert.append($('<div/>').
211 addClass('prompt output_prompt').
212 addClass('prompt output_prompt').
212 html('Out[' + n + ']:')
213 html('Out[' + n + ']:')
213 );
214 );
214 this.append_mime_type(json, toinsert);
215 this.append_mime_type(json, toinsert);
215 toinsert.children().last().addClass("box_flex1 pyout_area");
216 toinsert.children().last().addClass("box_flex1 pyout_area");
216 this.element.find("div.output").append(toinsert);
217 this.element.find("div.output").append(toinsert);
217 // If we just output latex, typeset it.
218 // If we just output latex, typeset it.
218 if (json.latex !== undefined) {
219 if (json.latex !== undefined) {
219 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
220 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
220 };
221 };
221 };
222 };
222
223
223
224
224 CodeCell.prototype.append_pyerr = function (json) {
225 CodeCell.prototype.append_pyerr = function (json) {
225 var tb = json.traceback;
226 var tb = json.traceback;
226 if (tb !== undefined && tb.length > 0) {
227 if (tb !== undefined && tb.length > 0) {
227 var s = '';
228 var s = '';
228 var len = tb.length;
229 var len = tb.length;
229 for (var i=0; i<len; i++) {
230 for (var i=0; i<len; i++) {
230 s = s + tb[i] + '\n';
231 s = s + tb[i] + '\n';
231 }
232 }
232 s = s + '\n';
233 s = s + '\n';
233 this.append_text(s).addClass('output_area');
234 this.append_text(s).addClass('output_area');
234 };
235 };
235 };
236 };
236
237
237
238
238 CodeCell.prototype.append_stream = function (json) {
239 CodeCell.prototype.append_stream = function (json) {
239 this.append_text(json.text).addClass('output_area');
240 this.append_text(json.text).addClass('output_area');
240 };
241 };
241
242
242
243
243 CodeCell.prototype.append_display_data = function (json) {
244 CodeCell.prototype.append_display_data = function (json) {
244 this.append_mime_type(json).addClass('output_area');
245 this.append_mime_type(json).addClass('output_area');
245 // If we just output latex, typeset it.
246 // If we just output latex, typeset it.
246 if (json.latex !== undefined) {
247 if (json.latex !== undefined) {
247 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
248 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
248 };
249 };
249 };
250 };
250
251
251
252
252 CodeCell.prototype.append_mime_type = function (json, element) {
253 CodeCell.prototype.append_mime_type = function (json, element) {
253 element = element || this.element.find("div.output");
254 element = element || this.element.find("div.output");
254 if (json.html !== undefined) {
255 if (json.html !== undefined) {
255 inserted = this.append_html(json.html, element);
256 inserted = this.append_html(json.html, element);
256 } else if (json.latex !== undefined) {
257 } else if (json.latex !== undefined) {
257 inserted = this.append_latex(json.latex, element);
258 inserted = this.append_latex(json.latex, element);
258 } else if (json.svg !== undefined) {
259 } else if (json.svg !== undefined) {
259 inserted = this.append_svg(json.svg, element);
260 inserted = this.append_svg(json.svg, element);
260 } else if (json.png !== undefined) {
261 } else if (json.png !== undefined) {
261 inserted = this.append_png(json.png, element);
262 inserted = this.append_png(json.png, element);
262 } else if (json.jpeg !== undefined) {
263 } else if (json.jpeg !== undefined) {
263 inserted = this.append_jpeg(json.jpeg, element);
264 inserted = this.append_jpeg(json.jpeg, element);
264 } else if (json.text !== undefined) {
265 } else if (json.text !== undefined) {
265 inserted = this.append_text(json.text, element);
266 inserted = this.append_text(json.text, element);
266 };
267 };
267 return inserted;
268 return inserted;
268 };
269 };
269
270
270
271
271 CodeCell.prototype.append_html = function (html, element) {
272 CodeCell.prototype.append_html = function (html, element) {
272 element = element || this.element.find("div.output");
273 element = element || this.element.find("div.output");
273 var toinsert = $("<div/>").addClass("output_html rendered_html");
274 var toinsert = $("<div/>").addClass("output_html rendered_html");
274 toinsert.append(html);
275 toinsert.append(html);
275 element.append(toinsert);
276 element.append(toinsert);
276 return toinsert;
277 return toinsert;
277 }
278 }
278
279
279
280
280 CodeCell.prototype.append_text = function (data, element) {
281 CodeCell.prototype.append_text = function (data, element) {
281 element = element || this.element.find("div.output");
282 element = element || this.element.find("div.output");
282 var toinsert = $("<div/>").addClass("output_stream");
283 var toinsert = $("<div/>").addClass("output_stream");
283 toinsert.append($("<pre/>").html(data));
284 toinsert.append($("<pre/>").html(data));
284 element.append(toinsert);
285 element.append(toinsert);
285 return toinsert;
286 return toinsert;
286 };
287 };
287
288
288
289
289 CodeCell.prototype.append_svg = function (svg, element) {
290 CodeCell.prototype.append_svg = function (svg, element) {
290 element = element || this.element.find("div.output");
291 element = element || this.element.find("div.output");
291 var toinsert = $("<div/>").addClass("output_svg");
292 var toinsert = $("<div/>").addClass("output_svg");
292 toinsert.append(svg);
293 toinsert.append(svg);
293 element.append(toinsert);
294 element.append(toinsert);
294 return toinsert;
295 return toinsert;
295 };
296 };
296
297
297
298
298 CodeCell.prototype.append_png = function (png, element) {
299 CodeCell.prototype.append_png = function (png, element) {
299 element = element || this.element.find("div.output");
300 element = element || this.element.find("div.output");
300 var toinsert = $("<div/>").addClass("output_png");
301 var toinsert = $("<div/>").addClass("output_png");
301 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
302 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
302 element.append(toinsert);
303 element.append(toinsert);
303 return toinsert;
304 return toinsert;
304 };
305 };
305
306
306
307
307 CodeCell.prototype.append_jpeg = function (jpeg, element) {
308 CodeCell.prototype.append_jpeg = function (jpeg, element) {
308 element = element || this.element.find("div.output");
309 element = element || this.element.find("div.output");
309 var toinsert = $("<div/>").addClass("output_jpeg");
310 var toinsert = $("<div/>").addClass("output_jpeg");
310 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
311 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
311 element.append(toinsert);
312 element.append(toinsert);
312 return toinsert;
313 return toinsert;
313 };
314 };
314
315
315
316
316 CodeCell.prototype.append_latex = function (latex, element) {
317 CodeCell.prototype.append_latex = function (latex, element) {
317 // This method cannot do the typesetting because the latex first has to
318 // This method cannot do the typesetting because the latex first has to
318 // be on the page.
319 // be on the page.
319 element = element || this.element.find("div.output");
320 element = element || this.element.find("div.output");
320 var toinsert = $("<div/>").addClass("output_latex");
321 var toinsert = $("<div/>").addClass("output_latex");
321 toinsert.append(latex);
322 toinsert.append(latex);
322 element.append(toinsert);
323 element.append(toinsert);
323 return toinsert;
324 return toinsert;
324 }
325 }
325
326
326
327
327 CodeCell.prototype.clear_output = function () {
328 CodeCell.prototype.clear_output = function () {
328 this.element.find("div.output").html("");
329 this.element.find("div.output").html("");
329 this.outputs = [];
330 this.outputs = [];
330 };
331 };
331
332
332
333
333 CodeCell.prototype.clear_input = function () {
334 CodeCell.prototype.clear_input = function () {
334 this.code_mirror.setValue('');
335 this.code_mirror.setValue('');
335 };
336 };
336
337
337
338
338 CodeCell.prototype.collapse = function () {
339 CodeCell.prototype.collapse = function () {
339 if (!this.collapsed) {
340 if (!this.collapsed) {
340 this.element.find('div.output').hide();
341 this.element.find('div.output').hide();
341 this.collapsed = true;
342 this.collapsed = true;
342 };
343 };
343 };
344 };
344
345
345
346
346 CodeCell.prototype.expand = function () {
347 CodeCell.prototype.expand = function () {
347 if (this.collapsed) {
348 if (this.collapsed) {
348 this.element.find('div.output').show();
349 this.element.find('div.output').show();
349 this.collapsed = false;
350 this.collapsed = false;
350 };
351 };
351 };
352 };
352
353
353
354
354 CodeCell.prototype.set_input_prompt = function (number) {
355 CodeCell.prototype.set_input_prompt = function (number) {
355 var n = number || ' ';
356 var n = number || ' ';
356 this.input_prompt_number = n
357 this.input_prompt_number = n
357 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
358 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
358 };
359 };
359
360
360
361
361 CodeCell.prototype.get_code = function () {
362 CodeCell.prototype.get_code = function () {
362 return this.code_mirror.getValue();
363 return this.code_mirror.getValue();
363 };
364 };
364
365
365
366
366 CodeCell.prototype.set_code = function (code) {
367 CodeCell.prototype.set_code = function (code) {
367 return this.code_mirror.setValue(code);
368 return this.code_mirror.setValue(code);
368 };
369 };
369
370
370
371
371 CodeCell.prototype.at_top = function () {
372 CodeCell.prototype.at_top = function () {
372 var cursor = this.code_mirror.getCursor();
373 var cursor = this.code_mirror.getCursor();
373 if (cursor.line === 0) {
374 if (cursor.line === 0) {
374 return true;
375 return true;
375 } else {
376 } else {
376 return false;
377 return false;
377 }
378 }
378 };
379 };
379
380
380
381
381 CodeCell.prototype.at_bottom = function () {
382 CodeCell.prototype.at_bottom = function () {
382 var cursor = this.code_mirror.getCursor();
383 var cursor = this.code_mirror.getCursor();
383 if (cursor.line === (this.code_mirror.lineCount()-1)) {
384 if (cursor.line === (this.code_mirror.lineCount()-1)) {
384 return true;
385 return true;
385 } else {
386 } else {
386 return false;
387 return false;
387 }
388 }
388 };
389 };
389
390
390
391
391 CodeCell.prototype.fromJSON = function (data) {
392 CodeCell.prototype.fromJSON = function (data) {
392 // console.log('Import from JSON:', data);
393 // console.log('Import from JSON:', data);
393 if (data.cell_type === 'code') {
394 if (data.cell_type === 'code') {
394 if (data.input !== undefined) {
395 if (data.input !== undefined) {
395 this.set_code(data.input);
396 this.set_code(data.input);
396 }
397 }
397 if (data.prompt_number !== undefined) {
398 if (data.prompt_number !== undefined) {
398 this.set_input_prompt(data.prompt_number);
399 this.set_input_prompt(data.prompt_number);
399 } else {
400 } else {
400 this.set_input_prompt();
401 this.set_input_prompt();
401 };
402 };
402 var len = data.outputs.length;
403 var len = data.outputs.length;
403 for (var i=0; i<len; i++) {
404 for (var i=0; i<len; i++) {
404 this.append_output(data.outputs[i]);
405 this.append_output(data.outputs[i]);
405 };
406 };
406 if (data.collapsed !== undefined) {
407 if (data.collapsed !== undefined) {
407 if (data.collapsed) {
408 if (data.collapsed) {
408 this.collapse();
409 this.collapse();
409 };
410 };
410 };
411 };
411 };
412 };
412 };
413 };
413
414
414
415
415 CodeCell.prototype.toJSON = function () {
416 CodeCell.prototype.toJSON = function () {
416 var data = {};
417 var data = {};
417 data.input = this.get_code();
418 data.input = this.get_code();
418 data.cell_type = 'code';
419 data.cell_type = 'code';
419 if (this.input_prompt_number !== ' ') {
420 if (this.input_prompt_number !== ' ') {
420 data.prompt_number = this.input_prompt_number
421 data.prompt_number = this.input_prompt_number
421 };
422 };
422 var outputs = [];
423 var outputs = [];
423 var len = this.outputs.length;
424 var len = this.outputs.length;
424 for (var i=0; i<len; i++) {
425 for (var i=0; i<len; i++) {
425 outputs[i] = this.outputs[i];
426 outputs[i] = this.outputs[i];
426 };
427 };
427 data.outputs = outputs;
428 data.outputs = outputs;
428 data.language = 'python';
429 data.language = 'python';
429 data.collapsed = this.collapsed;
430 data.collapsed = this.collapsed;
430 // console.log('Export to JSON:',data);
431 // console.log('Export to JSON:',data);
431 return data;
432 return data;
432 };
433 };
433
434
434
435
435 IPython.CodeCell = CodeCell;
436 IPython.CodeCell = CodeCell;
436
437
437 return IPython;
438 return IPython;
438 }(IPython));
439 }(IPython));
439
440
@@ -1,118 +1,119
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 // SaveWidget
9 // SaveWidget
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var SaveWidget = function (selector) {
16 var SaveWidget = function (selector) {
17 this.selector = selector;
17 this.selector = selector;
18 this.notebook_name_re = /[^/\\]+/
18 this.notebook_name_re = /[^/\\]+/
19 if (this.selector !== undefined) {
19 if (this.selector !== undefined) {
20 this.element = $(selector);
20 this.element = $(selector);
21 this.style();
21 this.style();
22 this.bind_events();
22 this.bind_events();
23 }
23 }
24 };
24 };
25
25
26
26
27 SaveWidget.prototype.style = function () {
27 SaveWidget.prototype.style = function () {
28 this.element.find('input#notebook_name').addClass('ui-widget ui-widget-content');
28 this.element.find('input#notebook_name').addClass('ui-widget ui-widget-content');
29 this.element.find('button#save_notebook').button();
29 this.element.find('button#save_notebook').button();
30 var left_panel_width = $('div#left_panel').outerWidth();
30 var left_panel_width = $('div#left_panel').outerWidth();
31 var left_panel_splitter_width = $('div#left_panel_splitter').outerWidth();
31 var left_panel_splitter_width = $('div#left_panel_splitter').outerWidth();
32 $('span#save_widget').css({marginLeft:left_panel_width+left_panel_splitter_width});
32 $('span#save_widget').css({marginLeft:left_panel_width+left_panel_splitter_width});
33 $('input#notebook_name').attr('tabindex','1');
33 };
34 };
34
35
35
36
36 SaveWidget.prototype.bind_events = function () {
37 SaveWidget.prototype.bind_events = function () {
37 var that = this;
38 var that = this;
38 this.element.find('button#save_notebook').click(function () {
39 this.element.find('button#save_notebook').click(function () {
39 IPython.notebook.save_notebook();
40 IPython.notebook.save_notebook();
40 that.set_document_title();
41 that.set_document_title();
41 });
42 });
42 };
43 };
43
44
44
45
45 SaveWidget.prototype.get_notebook_name = function () {
46 SaveWidget.prototype.get_notebook_name = function () {
46 return this.element.find('input#notebook_name').attr('value');
47 return this.element.find('input#notebook_name').attr('value');
47 }
48 }
48
49
49
50
50 SaveWidget.prototype.set_notebook_name = function (nbname) {
51 SaveWidget.prototype.set_notebook_name = function (nbname) {
51 this.element.find('input#notebook_name').attr('value',nbname);
52 this.element.find('input#notebook_name').attr('value',nbname);
52 this.set_document_title();
53 this.set_document_title();
53 }
54 }
54
55
55
56
56 SaveWidget.prototype.set_document_title = function () {
57 SaveWidget.prototype.set_document_title = function () {
57 nbname = this.get_notebook_name();
58 nbname = this.get_notebook_name();
58 document.title = 'IPy: ' + nbname;
59 document.title = 'IPy: ' + nbname;
59 };
60 };
60
61
61
62
62 SaveWidget.prototype.get_notebook_id = function () {
63 SaveWidget.prototype.get_notebook_id = function () {
63 return this.element.find('span#notebook_id').text()
64 return this.element.find('span#notebook_id').text()
64 };
65 };
65
66
66
67
67 SaveWidget.prototype.update_url = function () {
68 SaveWidget.prototype.update_url = function () {
68 var notebook_id = this.get_notebook_id();
69 var notebook_id = this.get_notebook_id();
69 if (notebook_id !== '') {
70 if (notebook_id !== '') {
70 window.history.replaceState({}, '', notebook_id);
71 window.history.replaceState({}, '', notebook_id);
71 };
72 };
72 };
73 };
73
74
74
75
75 SaveWidget.prototype.test_notebook_name = function () {
76 SaveWidget.prototype.test_notebook_name = function () {
76 var nbname = this.get_notebook_name();
77 var nbname = this.get_notebook_name();
77 if (this.notebook_name_re.test(nbname)) {
78 if (this.notebook_name_re.test(nbname)) {
78 return true;
79 return true;
79 } else {
80 } else {
80 var bad_name = $('<div/>');
81 var bad_name = $('<div/>');
81 bad_name.html(
82 bad_name.html(
82 "The notebook name you entered (" +
83 "The notebook name you entered (" +
83 nbname +
84 nbname +
84 ") is not valid. Notebook names can contain any characters except / and \\"
85 ") is not valid. Notebook names can contain any characters except / and \\"
85 );
86 );
86 bad_name.dialog({title: 'Invalid name', modal: true});
87 bad_name.dialog({title: 'Invalid name', modal: true});
87 return false;
88 return false;
88 };
89 };
89 };
90 };
90
91
91
92
92 SaveWidget.prototype.status_save = function () {
93 SaveWidget.prototype.status_save = function () {
93 this.element.find('button#save_notebook').button('option', 'label', 'Save');
94 this.element.find('button#save_notebook').button('option', 'label', 'Save');
94 this.element.find('button#save_notebook').button('enable');
95 this.element.find('button#save_notebook').button('enable');
95 IPython.print_widget.enable();
96 IPython.print_widget.enable();
96 };
97 };
97
98
98
99
99 SaveWidget.prototype.status_saving = function () {
100 SaveWidget.prototype.status_saving = function () {
100 this.element.find('button#save_notebook').button('option', 'label', 'Saving');
101 this.element.find('button#save_notebook').button('option', 'label', 'Saving');
101 this.element.find('button#save_notebook').button('disable');
102 this.element.find('button#save_notebook').button('disable');
102 IPython.print_widget.disable();
103 IPython.print_widget.disable();
103 };
104 };
104
105
105
106
106 SaveWidget.prototype.status_loading = function () {
107 SaveWidget.prototype.status_loading = function () {
107 this.element.find('button#save_notebook').button('option', 'label', 'Loading');
108 this.element.find('button#save_notebook').button('option', 'label', 'Loading');
108 this.element.find('button#save_notebook').button('disable');
109 this.element.find('button#save_notebook').button('disable');
109 IPython.print_widget.disable();
110 IPython.print_widget.disable();
110 };
111 };
111
112
112
113
113 IPython.SaveWidget = SaveWidget;
114 IPython.SaveWidget = SaveWidget;
114
115
115 return IPython;
116 return IPython;
116
117
117 }(IPython));
118 }(IPython));
118
119
@@ -1,257 +1,258
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 // TextCell
9 // TextCell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 // TextCell base class
14 // TextCell base class
15
15
16 var TextCell = function (notebook) {
16 var TextCell = function (notebook) {
17 this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
17 this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
18 this.placeholder = this.placeholder || '';
18 this.placeholder = this.placeholder || '';
19 IPython.Cell.apply(this, arguments);
19 IPython.Cell.apply(this, arguments);
20 this.rendered = false;
20 this.rendered = false;
21 this.cell_type = this.cell_type || 'text';
21 this.cell_type = this.cell_type || 'text';
22 };
22 };
23
23
24
24
25 TextCell.prototype = new IPython.Cell();
25 TextCell.prototype = new IPython.Cell();
26
26
27
27
28 TextCell.prototype.create_element = function () {
28 TextCell.prototype.create_element = function () {
29 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
29 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
30 cell.attr('tabindex','2');
30 var input_area = $('<div/>').addClass('text_cell_input');
31 var input_area = $('<div/>').addClass('text_cell_input');
31 this.code_mirror = CodeMirror(input_area.get(0), {
32 this.code_mirror = CodeMirror(input_area.get(0), {
32 indentUnit : 4,
33 indentUnit : 4,
33 mode: this.code_mirror_mode,
34 mode: this.code_mirror_mode,
34 theme: 'default',
35 theme: 'default',
35 value: this.placeholder
36 value: this.placeholder
36 });
37 });
37 // The tabindex=-1 makes this div focusable.
38 // The tabindex=-1 makes this div focusable.
38 var render_area = $('<div/>').addClass('text_cell_render').
39 var render_area = $('<div/>').addClass('text_cell_render').
39 addClass('rendered_html').attr('tabindex','-1');
40 addClass('rendered_html').attr('tabindex','-1');
40 cell.append(input_area).append(render_area);
41 cell.append(input_area).append(render_area);
41 this.element = cell;
42 this.element = cell;
42 };
43 };
43
44
44
45
45 TextCell.prototype.bind_events = function () {
46 TextCell.prototype.bind_events = function () {
46 IPython.Cell.prototype.bind_events.apply(this);
47 IPython.Cell.prototype.bind_events.apply(this);
47 var that = this;
48 var that = this;
48 this.element.keydown(function (event) {
49 this.element.keydown(function (event) {
49 if (event.which === 13) {
50 if (event.which === 13) {
50 if (that.rendered) {
51 if (that.rendered) {
51 that.edit();
52 that.edit();
52 event.preventDefault();
53 event.preventDefault();
53 };
54 };
54 };
55 };
55 });
56 });
56 };
57 };
57
58
58
59
59 TextCell.prototype.select = function () {
60 TextCell.prototype.select = function () {
60 IPython.Cell.prototype.select.apply(this);
61 IPython.Cell.prototype.select.apply(this);
61 var output = this.element.find("div.text_cell_render");
62 var output = this.element.find("div.text_cell_render");
62 output.trigger('focus');
63 output.trigger('focus');
63 };
64 };
64
65
65
66
66 TextCell.prototype.edit = function () {
67 TextCell.prototype.edit = function () {
67 if (this.rendered === true) {
68 if (this.rendered === true) {
68 var text_cell = this.element;
69 var text_cell = this.element;
69 var output = text_cell.find("div.text_cell_render");
70 var output = text_cell.find("div.text_cell_render");
70 output.hide();
71 output.hide();
71 text_cell.find('div.text_cell_input').show();
72 text_cell.find('div.text_cell_input').show();
72 this.code_mirror.focus();
73 this.code_mirror.focus();
73 this.code_mirror.refresh();
74 this.code_mirror.refresh();
74 this.rendered = false;
75 this.rendered = false;
75 if (this.get_source() === this.placeholder) {
76 if (this.get_source() === this.placeholder) {
76 this.set_source('');
77 this.set_source('');
77 };
78 };
78 };
79 };
79 };
80 };
80
81
81
82
82 // Subclasses must define render.
83 // Subclasses must define render.
83 TextCell.prototype.render = function () {};
84 TextCell.prototype.render = function () {};
84
85
85
86
86 TextCell.prototype.config_mathjax = function () {
87 TextCell.prototype.config_mathjax = function () {
87 var text_cell = this.element;
88 var text_cell = this.element;
88 var that = this;
89 var that = this;
89 text_cell.click(function () {
90 text_cell.click(function () {
90 that.edit();
91 that.edit();
91 }).focusout(function () {
92 }).focusout(function () {
92 that.render();
93 that.render();
93 });
94 });
94
95
95 text_cell.trigger("focusout");
96 text_cell.trigger("focusout");
96 };
97 };
97
98
98
99
99 TextCell.prototype.get_source = function() {
100 TextCell.prototype.get_source = function() {
100 return this.code_mirror.getValue();
101 return this.code_mirror.getValue();
101 };
102 };
102
103
103
104
104 TextCell.prototype.set_source = function(text) {
105 TextCell.prototype.set_source = function(text) {
105 this.code_mirror.setValue(text);
106 this.code_mirror.setValue(text);
106 this.code_mirror.refresh();
107 this.code_mirror.refresh();
107 };
108 };
108
109
109
110
110 TextCell.prototype.get_rendered = function() {
111 TextCell.prototype.get_rendered = function() {
111 return this.element.find('div.text_cell_render').html();
112 return this.element.find('div.text_cell_render').html();
112 };
113 };
113
114
114
115
115 TextCell.prototype.set_rendered = function(text) {
116 TextCell.prototype.set_rendered = function(text) {
116 this.element.find('div.text_cell_render').html(text);
117 this.element.find('div.text_cell_render').html(text);
117 };
118 };
118
119
119
120
120 TextCell.prototype.at_top = function () {
121 TextCell.prototype.at_top = function () {
121 if (this.rendered) {
122 if (this.rendered) {
122 return true;
123 return true;
123 } else {
124 } else {
124 return false;
125 return false;
125 }
126 }
126 };
127 };
127
128
128
129
129 TextCell.prototype.at_bottom = function () {
130 TextCell.prototype.at_bottom = function () {
130 if (this.rendered) {
131 if (this.rendered) {
131 return true;
132 return true;
132 } else {
133 } else {
133 return false;
134 return false;
134 }
135 }
135 };
136 };
136
137
137
138
138 TextCell.prototype.fromJSON = function (data) {
139 TextCell.prototype.fromJSON = function (data) {
139 if (data.cell_type === this.cell_type) {
140 if (data.cell_type === this.cell_type) {
140 if (data.source !== undefined) {
141 if (data.source !== undefined) {
141 this.set_source(data.source);
142 this.set_source(data.source);
142 this.set_rendered(data.rendered || '');
143 this.set_rendered(data.rendered || '');
143 this.rendered = false;
144 this.rendered = false;
144 this.render();
145 this.render();
145 };
146 };
146 };
147 };
147 };
148 };
148
149
149
150
150 TextCell.prototype.toJSON = function () {
151 TextCell.prototype.toJSON = function () {
151 var data = {}
152 var data = {}
152 data.cell_type = this.cell_type;
153 data.cell_type = this.cell_type;
153 data.source = this.get_source();
154 data.source = this.get_source();
154 return data;
155 return data;
155 };
156 };
156
157
157
158
158 // HTMLCell
159 // HTMLCell
159
160
160 var HTMLCell = function (notebook) {
161 var HTMLCell = function (notebook) {
161 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
162 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
162 IPython.TextCell.apply(this, arguments);
163 IPython.TextCell.apply(this, arguments);
163 this.cell_type = 'html';
164 this.cell_type = 'html';
164 };
165 };
165
166
166
167
167 HTMLCell.prototype = new TextCell();
168 HTMLCell.prototype = new TextCell();
168
169
169
170
170 HTMLCell.prototype.render = function () {
171 HTMLCell.prototype.render = function () {
171 if (this.rendered === false) {
172 if (this.rendered === false) {
172 var text = this.get_source();
173 var text = this.get_source();
173 if (text === "") {text = this.placeholder;};
174 if (text === "") {text = this.placeholder;};
174 this.set_rendered(text);
175 this.set_rendered(text);
175 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
176 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
176 this.element.find('div.text_cell_input').hide();
177 this.element.find('div.text_cell_input').hide();
177 this.element.find("div.text_cell_render").show();
178 this.element.find("div.text_cell_render").show();
178 this.rendered = true;
179 this.rendered = true;
179 };
180 };
180 };
181 };
181
182
182
183
183 // MarkdownCell
184 // MarkdownCell
184
185
185 var MarkdownCell = function (notebook) {
186 var MarkdownCell = function (notebook) {
186 this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
187 this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
187 IPython.TextCell.apply(this, arguments);
188 IPython.TextCell.apply(this, arguments);
188 this.cell_type = 'markdown';
189 this.cell_type = 'markdown';
189 };
190 };
190
191
191
192
192 MarkdownCell.prototype = new TextCell();
193 MarkdownCell.prototype = new TextCell();
193
194
194
195
195 MarkdownCell.prototype.render = function () {
196 MarkdownCell.prototype.render = function () {
196 if (this.rendered === false) {
197 if (this.rendered === false) {
197 var text = this.get_source();
198 var text = this.get_source();
198 if (text === "") {text = this.placeholder;};
199 if (text === "") {text = this.placeholder;};
199 var html = IPython.markdown_converter.makeHtml(text);
200 var html = IPython.markdown_converter.makeHtml(text);
200 this.set_rendered(html);
201 this.set_rendered(html);
201 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
202 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
202 this.element.find('div.text_cell_input').hide();
203 this.element.find('div.text_cell_input').hide();
203 this.element.find("div.text_cell_render").show();
204 this.element.find("div.text_cell_render").show();
204 this.rendered = true;
205 this.rendered = true;
205 };
206 };
206 };
207 };
207
208
208
209
209 // RSTCell
210 // RSTCell
210
211
211 var RSTCell = function (notebook) {
212 var RSTCell = function (notebook) {
212 this.placeholder = "Type *ReStructured Text* and LaTeX: $\\alpha^2$";
213 this.placeholder = "Type *ReStructured Text* and LaTeX: $\\alpha^2$";
213 IPython.TextCell.apply(this, arguments);
214 IPython.TextCell.apply(this, arguments);
214 this.cell_type = 'rst';
215 this.cell_type = 'rst';
215 };
216 };
216
217
217
218
218 RSTCell.prototype = new TextCell();
219 RSTCell.prototype = new TextCell();
219
220
220
221
221 RSTCell.prototype.render = function () {
222 RSTCell.prototype.render = function () {
222 if (this.rendered === false) {
223 if (this.rendered === false) {
223 var text = this.get_source();
224 var text = this.get_source();
224 if (text === "") {text = this.placeholder;};
225 if (text === "") {text = this.placeholder;};
225 var settings = {
226 var settings = {
226 processData : false,
227 processData : false,
227 cache : false,
228 cache : false,
228 type : "POST",
229 type : "POST",
229 data : text,
230 data : text,
230 headers : {'Content-Type': 'application/x-rst'},
231 headers : {'Content-Type': 'application/x-rst'},
231 success : $.proxy(this.handle_render,this)
232 success : $.proxy(this.handle_render,this)
232 };
233 };
233 $.ajax("/rstservice/render", settings);
234 $.ajax("/rstservice/render", settings);
234 this.element.find('div.text_cell_input').hide();
235 this.element.find('div.text_cell_input').hide();
235 this.element.find("div.text_cell_render").show();
236 this.element.find("div.text_cell_render").show();
236 this.set_rendered("Rendering...");
237 this.set_rendered("Rendering...");
237 };
238 };
238 };
239 };
239
240
240
241
241 RSTCell.prototype.handle_render = function (data, status, xhr) {
242 RSTCell.prototype.handle_render = function (data, status, xhr) {
242 this.set_rendered(data);
243 this.set_rendered(data);
243 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
244 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
244 this.rendered = true;
245 this.rendered = true;
245 };
246 };
246
247
247
248
248 IPython.TextCell = TextCell;
249 IPython.TextCell = TextCell;
249 IPython.HTMLCell = HTMLCell;
250 IPython.HTMLCell = HTMLCell;
250 IPython.MarkdownCell = MarkdownCell;
251 IPython.MarkdownCell = MarkdownCell;
251 IPython.RSTCell = RSTCell;
252 IPython.RSTCell = RSTCell;
252
253
253
254
254 return IPython;
255 return IPython;
255
256
256 }(IPython));
257 }(IPython));
257
258
General Comments 0
You need to be logged in to leave comments. Login now