##// END OF EJS Templates
Fixing selection and focus logic.
Brian Granger -
Show More
@@ -1,504 +1,535 b''
1 1 var IPYTHON = {};
2 2
3 3
4 4 //============================================================================
5 5 // Notebook
6 6 //============================================================================
7 7
8 8
9 9 var Notebook = function (selector) {
10 10 this.element = $(selector);
11 11 this.element.data("notebook", this);
12 12 this.next_prompt_number = 1;
13 13 this.bind_events();
14 14 }
15 15
16 16
17 17 Notebook.prototype.bind_events = function () {
18 18 var that = this;
19 that.element.keydown(function (event) {
19 $(document).keydown(function (event) {
20 20 console.log(event);
21 21 if (event.which == 38 && event.shiftKey) {
22 22 that.select_prev();
23 23 } else if (event.which == 40 && event.shiftKey) {
24 24 that.select_next();
25 25 } else if (event.which == 13 && event.shiftKey) {
26 26 // The focus is not quite working here.
27 event.preventDefault();
27 28 that.insert_code_cell_after();
28 29 }
29 30 });
30 31 };
31 32
32 33
33 34 // Cell indexing, retrieval, etc.
34 35
35 36
36 37 Notebook.prototype.cell_elements = function () {
37 38 return this.element.children("div.cell");
38 39 }
39 40
40 41
41 42 Notebook.prototype.ncells = function (cell) {
42 43 return this.cell_elements().length;
43 44 }
44 45
45 46
46 47 // TODO: we are often calling cells as cells()[i], which we should optimize
47 48 // to cells(i) or a new method.
48 49 Notebook.prototype.cells = function () {
49 50 return this.cell_elements().toArray().map(function (e) {
50 51 return $(e).data("cell");
51 52 });
52 53 }
53 54
54 55
55 56 Notebook.prototype.find_cell_index = function (cell) {
56 57 var result = null;
57 58 this.cell_elements().filter(function (index) {
58 59 if ($(this).data("cell") === cell) {
59 60 result = index;
60 61 };
61 62 });
62 63 return result;
63 64 };
64 65
65 66
66 67 Notebook.prototype.index_or_selected = function (index) {
67 68 return index || this.selected_index() || 0;
68 69 }
69 70
70 71
71 72 Notebook.prototype.select = function (index) {
72 73 if (index !== undefined && index >= 0 && index < this.ncells()) {
73 74 if (this.selected_index() !== null) {
74 75 this.selected_cell().unselect();
75 76 };
76 77 this.cells()[index].select();
77 78 };
78 79 return this;
79 80 };
80 81
81 82
82 83 Notebook.prototype.select_next = function () {
83 84 var index = this.selected_index();
84 85 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
85 86 this.select(index+1);
86 87 };
87 88 return this;
88 89 };
89 90
90 91
91 92 Notebook.prototype.select_prev = function () {
92 93 var index = this.selected_index();
93 94 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
94 95 this.select(index-1);
95 96 };
96 97 return this;
97 98 };
98 99
99 100
100 101 Notebook.prototype.selected_index = function () {
101 102 var result = null;
102 103 this.cell_elements().filter(function (index) {
103 104 if ($(this).data("cell").selected === true) {
104 105 result = index;
105 106 };
106 107 });
107 108 return result;
108 109 };
109 110
110 111
111 112 Notebook.prototype.selected_cell = function () {
112 113 return this.cell_elements().eq(this.selected_index()).data("cell");
113 114 }
114 115
115 116
116 117 // Cell insertion, deletion and moving.
117 118
118 119
119 120 Notebook.prototype.delete_cell = function (index) {
120 121 var i = index || this.selected_index();
121 122 if (i !== null && i >= 0 && i < this.ncells()) {
122 123 this.cell_elements().eq(i).remove();
123 124 if (i === (this.ncells())) {
124 125 this.select(i-1);
125 126 } else {
126 127 this.select(i);
127 128 };
128 129 };
129 130 return this;
130 131 };
131 132
132 133
133 134 Notebook.prototype.append_cell = function (cell) {
134 135 this.element.append(cell.element);
135 136 return this;
136 137 };
137 138
138 139
139 140 Notebook.prototype.insert_cell_after = function (cell, index) {
140 141 var ncells = this.ncells();
141 142 if (ncells === 0) {
142 143 this.append_cell(cell);
143 144 return this;
144 145 };
145 146 if (index >= 0 && index < ncells) {
146 147 this.cell_elements().eq(index).after(cell.element);
147 148 };
148 149 return this
149 150 };
150 151
151 152
152 153 Notebook.prototype.insert_cell_before = function (cell, index) {
153 154 var ncells = this.ncells();
154 155 if (ncells === 0) {
155 156 this.append_cell(cell);
156 157 return this;
157 158 };
158 if (index > 0 && index < ncells) {
159 if (index >= 0 && index < ncells) {
159 160 this.cell_elements().eq(index).before(cell.element);
160 161 };
161 162 return this;
162 163 };
163 164
164 165
165 166 Notebook.prototype.move_cell_up = function (index) {
166 167 var i = index || this.selected_index();
167 168 if (i !== null && i < this.ncells() && i > 0) {
168 169 var pivot = this.cell_elements().eq(i-1);
169 170 var tomove = this.cell_elements().eq(i);
170 171 if (pivot !== null && tomove !== null) {
171 172 tomove.detach();
172 173 pivot.before(tomove);
174 this.select(i-1);
173 175 };
174 176 };
175 177 return this;
176 178 }
177 179
178 180
179 181 Notebook.prototype.move_cell_down = function (index) {
180 182 var i = index || this.selected_index();
181 183 if (i !== null && i < (this.ncells()-1) && i >= 0) {
182 184 var pivot = this.cell_elements().eq(i+1)
183 185 var tomove = this.cell_elements().eq(i)
184 186 if (pivot !== null && tomove !== null) {
185 187 tomove.detach();
186 188 pivot.after(tomove);
189 this.select(i+1);
187 190 };
188 191 };
189 192 return this;
190 193 }
191 194
192 195
193 196 Notebook.prototype.sort_cells = function () {
194 197 var ncells = this.ncells();
195 198 var swapped;
196 199 do {
197 200 swapped = false
198 201 for (var i=1; i<ncells; i++) {
199 202 current = this.cell_elements().eq(i).data("cell");
200 203 previous = this.cell_elements().eq(i-1).data("cell");
201 204 if (previous.input_prompt_number > current.input_prompt_number) {
202 205 this.move_cell_up(i);
203 206 swapped = true;
204 207 };
205 208 };
206 209 } while (swapped);
207 210 return this;
208 211 };
209 212
210 213
211 214 Notebook.prototype.insert_code_cell_before = function (index) {
212 215 // TODO: Bounds check for i
213 216 var i = this.index_or_selected(index);
214 217 var cell = new CodeCell(this);
215 218 cell.set_input_prompt(this.next_prompt_number);
216 219 this.next_prompt_number = this.next_prompt_number + 1;
217 220 this.insert_cell_before(cell, i);
218 221 this.select(this.find_cell_index(cell));
219 222 return this;
220 223 }
221 224
222 225
223 226 Notebook.prototype.insert_code_cell_after = function (index) {
224 227 // TODO: Bounds check for i
225 228 var i = this.index_or_selected(index);
226 229 var cell = new CodeCell(this);
227 230 cell.set_input_prompt(this.next_prompt_number);
228 231 this.next_prompt_number = this.next_prompt_number + 1;
229 232 this.insert_cell_after(cell, i);
230 233 this.select(this.find_cell_index(cell));
231 234 return this;
232 235 }
233 236
234 237
235 238 Notebook.prototype.insert_text_cell_before = function (index) {
236 239 // TODO: Bounds check for i
237 240 var i = this.index_or_selected(index);
238 241 var cell = new TextCell(this);
239 242 cell.config_mathjax();
240 243 this.insert_cell_before(cell, i);
241 244 this.select(this.find_cell_index(cell));
242 245 return this;
243 246 }
244 247
245 248
246 249 Notebook.prototype.insert_text_cell_after = function (index) {
247 250 // TODO: Bounds check for i
248 251 var i = this.index_or_selected(index);
249 252 var cell = new TextCell(this);
250 253 cell.config_mathjax();
251 254 this.insert_cell_after(cell, i);
252 255 this.select(this.find_cell_index(cell));
253 256 return this;
254 257 }
255 258
256 259
257 260 Notebook.prototype.text_to_code = function (index) {
258 261 // TODO: Bounds check for i
259 262 var i = this.index_or_selected(index);
260 263 var source_element = this.cell_elements().eq(i);
261 264 var source_cell = source_element.data("cell");
262 265 if (source_cell instanceof TextCell) {
263 266 this.insert_code_cell_after(i);
264 267 var target_cell = this.cells()[i+1];
265 268 var text = source_element.find("textarea.text_cell_input").val();
266 269 target_cell.element.find("textarea.input_area").val(text);
267 270 source_element.remove();
268 271 };
269 272 };
270 273
271 274
272 275 Notebook.prototype.code_to_text = function (index) {
273 276 // TODO: Bounds check for i
274 277 var i = this.index_or_selected(index);
275 278 var source_element = this.cell_elements().eq(i);
276 279 var source_cell = source_element.data("cell");
277 280 if (source_cell instanceof CodeCell) {
278 281 this.insert_text_cell_after(i);
279 282 var target_cell = this.cells()[i+1];
280 283 var text = source_element.find("textarea.input_area").val();
281 284 target_cell.element.find("textarea.text_cell_input").val(text);
282 285 target_cell.element.find("textarea.text_cell_input").html(text);
283 286 target_cell.element.find("div.text_cell_render").html(text);
284 287
285 288 source_element.remove();
286 289 };
287 290 };
288 291
289 292
290 293 // Cell collapsing
291 294
292 295 Notebook.prototype.collapse = function (index) {
293 296 var i = this.index_or_selected(index);
294 297 this.cells()[i].collapse();
295 298 }
296 299
297 300
298 301 Notebook.prototype.expand = function (index) {
299 302 var i = this.index_or_selected(index);
300 303 this.cells()[i].expand();
301 304 }
302 305
303 306
304 307 //============================================================================
305 308 // Cell
306 309 //============================================================================
307 310
308 311
309 312 var Cell = function (notebook) {
310 313 this.notebook = notebook;
311 314 this.selected = false;
312 315 this.element;
313 316 this.create_element();
314 317 if (this.element !== undefined) {
315 318 this.element.data("cell", this);
316 319 this.bind_events();
317 320 }
318 321 };
319 322
320 323
321 324 Cell.prototype.select = function () {
322 325 this.element.addClass('ui-widget-content ui-corner-all');
323 326 this.selected = true;
327 // TODO: we need t test across browsers to see if both of these are needed.
328 // In the meantime, there should not be any harm in having them both.
324 329 this.element.find('textarea').trigger('focusin');
330 this.element.find('textarea').trigger('focus');
325 331 };
326 332
327 333
328 334 Cell.prototype.unselect = function () {
329 335 this.element.removeClass('ui-widget-content ui-corner-all');
330 336 this.selected = false;
331 337 };
332 338
333 339
334 340 Cell.prototype.bind_events = function () {
335 341 var that = this;
336 342 var nb = that.notebook
337 343 that.element.click(function (event) {
338 344 if (that.selected === false) {
339 345 nb.select(nb.find_cell_index(that));
340 346 };
341 347 });
342 348 that.element.focusin(function (event) {
343 349 if (that.selected === false) {
344 350 nb.select(nb.find_cell_index(that));
345 351 };
346 352 });
347 353 };
348 354
349 355
350 356 // Subclasses must implement create_element.
351 357 Cell.prototype.create_element = function () {};
352 358
353 359
354 360 //============================================================================
355 361 // CodeCell
356 362 //============================================================================
357 363
358 364
359 365 var CodeCell = function (notebook) {
360 366 Cell.apply(this, arguments);
361 367 this.input_prompt_number = ' ';
362 368 this.output_prompt_number = ' ';
363 369 };
364 370
365 371
366 372 CodeCell.prototype = new Cell();
367 373
368 374
369 375 CodeCell.prototype.create_element = function () {
370 376 var cell = $('<div></div>').addClass('cell code_cell')
371 377 var input = $('<div></div>').addClass('input').append(
372 378 $('<div/>').addClass('prompt input_prompt')
373 379 ).append(
374 380 $('<textarea/>').addClass('input_area').
375 381 attr('rows',1).
376 382 attr('cols',80).
377 383 attr('wrap','hard').
378 384 autoGrow()
379 385 );
380 386 var output = $('<div></div>').addClass('output').append(
381 387 $('<div/>').addClass('prompt output_prompt')
382 388 ).append(
383 389 $('<div/>').addClass('output_area')
384 390 );
385 391 output.hide();
386 392 cell.append(input).append(output);
387 393 this.element = cell;
388 394 };
389 395
390 396
391 397 CodeCell.prototype.collapse = function () {
392 398 this.element.find('div.output').hide();
393 399 };
394 400
395 401
396 402 CodeCell.prototype.expand = function () {
397 403 this.element.find('div.output').show();
398 404 };
399 405
400 406
401 407 CodeCell.prototype.set_prompt = function (number) {
402 408 this.set_input_prompt(number);
403 409 this.set_output_prompt(number);
404 410 };
405 411
406 412 CodeCell.prototype.set_input_prompt = function (number) {
407 413 var n = number || ' ';
408 414 this.input_prompt_number = n
409 415 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
410 416 };
411 417
412 418
413 419 CodeCell.prototype.set_output_prompt = function (number) {
414 420 var n = number || ' ';
415 421 this.output_prompt_number = n
416 422 this.element.find('div.output_prompt').html('Out[' + n + ']:');
417 423 };
418 424
419 425
420 426 //============================================================================
421 427 // TextCell
422 428 //============================================================================
423 429
424 430
425 431 var TextCell = function (notebook) {
426 432 Cell.apply(this, arguments);
427 433 };
428 434
429 435
430 436 TextCell.prototype = new Cell();
431 437
432 438
433 439 TextCell.prototype.create_element = function () {
434 440 var cell = $('<div></div').addClass('cell text_cell').
435 441 append(
436 442 $('<textarea>Type HTML/LaTex content here</textarea>').
437 443 addClass('text_cell_input').
438 444 attr('rows',1).
439 445 attr('cols',80).
440 446 autoGrow()
441 447 ).append(
442 448 $('<div></div>').addClass('text_cell_render')
443 449 )
444 450 this.element = cell;
445 451 };
446 452
447 453
448 TextCell.prototype.config_mathjax = function () {
454 TextCell.prototype.select = function () {
455 this.edit();
456 Cell.prototype.select.apply(this);
457 };
458
459
460 TextCell.prototype.edit = function () {
449 461 var text_cell = this.element;
450 462 var input = text_cell.find("textarea.text_cell_input");
451 var output = text_cell.find("div.text_cell_render");
463 var output = text_cell.find("div.text_cell_render");
464 output.hide();
465 input.show().trigger('focus');
466 };
467
452 468
469 TextCell.prototype.render = function () {
470 var text_cell = this.element;
471 var input = text_cell.find("textarea.text_cell_input");
472 var output = text_cell.find("div.text_cell_render");
473 var text = input.val();
474 output.html(text)
475 input.html(text);
476 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
477 input.hide();
478 output.show();
479 };
480
481
482 TextCell.prototype.config_mathjax = function () {
483 var text_cell = this.element;
484 var that = this;
453 485 text_cell.click(function () {
454 output.hide();
455 input.show().trigger('focus');
486 that.edit();
456 487 }).focusout(function () {
457 var text = input.val();
458 output.html(text)
459 input.html(text);
460 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
461 input.hide();
462 output.show();
488 that.render();
463 489 });
464 490
465 491 text_cell.trigger("focusout");
466 492 };
467 493
468 494
495 //============================================================================
496 // On document ready
497 //============================================================================
498
499
469 500 $(document).ready(function () {
470 501
471 502 MathJax.Hub.Config({
472 503 tex2jax: {
473 504 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
474 505 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
475 506 }
476 507 });
477 508
478 509 $("ul#main_menu").wijmenu({animation:{animated: "slide", duration: 100, easing: null}});
479 510 IPYTHON.notebook = new Notebook('div.notebook');
480 511 IPYTHON.notebook.insert_code_cell_after();
481 512
482 513 $("#move_cell").buttonset();
483 514 $("#move_up").button("option", "icons", {primary:"ui-icon-arrowthick-1-n"});
484 515 $("#move_up").button("option", "text", false);
485 516 $("#move_up").click(function () {IPYTHON.notebook.move_cell_up();});
486 517 $("#move_down").button("option", "icons", {primary:"ui-icon-arrowthick-1-s"});
487 518 $("#move_down").button("option", "text", false);
488 519 $("#move_down").click(function () {IPYTHON.notebook.move_cell_down();});
489 520
490 521 $("#insert_delete").buttonset();
491 522 $("#insert_cell_before").click(function () {IPYTHON.notebook.insert_code_cell_before();});
492 523 $("#insert_cell_after").click(function () {IPYTHON.notebook.insert_code_cell_after();});
493 524 $("#delete_cell").button("option", "icons", {primary:"ui-icon-closethick"});
494 525 $("#delete_cell").button("option", "text", false);
495 526 $("#delete_cell").click(function () {IPYTHON.notebook.delete_cell();});
496 527
497 528 $("#cell_type").buttonset();
498 529 $("#to_code").click(function () {IPYTHON.notebook.text_to_code();});
499 530 $("#to_text").click(function () {IPYTHON.notebook.code_to_text();});
500 531
501 532 $("#sort").buttonset();
502 533 $("#sort_cells").click(function () {IPYTHON.notebook.sort_cells();});
503 534
504 535 }); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now