##// END OF EJS Templates
don't hide cell toolbar on rendered text cells...
MinRK -
Show More
@@ -1,419 +1,419 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/events'
8 8 ], function(IPython, $, events) {
9 9 "use strict";
10 10
11 11 var CellToolbar = function (options) {
12 12 // Constructor
13 13 //
14 14 // Parameters:
15 15 // options: dictionary
16 16 // Dictionary of keyword arguments.
17 17 // events: $(Events) instance
18 18 // cell: Cell instance
19 19 // notebook: Notebook instance
20 20 //
21 21 // TODO: This leaks, when cell are deleted
22 22 // There is still a reference to each celltoolbars.
23 23 CellToolbar._instances.push(this);
24 24 this.notebook = options.notebook;
25 25 this.cell = options.cell;
26 26 this.create_element();
27 27 this.rebuild();
28 28 return this;
29 29 };
30 30
31 31
32 32 CellToolbar.prototype.create_element = function () {
33 33 this.inner_element = $('<div/>').addClass('celltoolbar');
34 34 this.element = $('<div/>').addClass('ctb_hideshow')
35 35 .append(this.inner_element);
36 36 };
37 37
38 38
39 39 // The default css style for the outer celltoolbar div
40 40 // (ctb_hideshow) is display: none.
41 41 // To show the cell toolbar, *both* of the following conditions must be met:
42 42 // - A parent container has class `ctb_global_show`
43 43 // - The celltoolbar has the class `ctb_show`
44 44 // This allows global show/hide, as well as per-cell show/hide.
45 45
46 46 CellToolbar.global_hide = function () {
47 47 $('body').removeClass('ctb_global_show');
48 48 };
49 49
50 50
51 51 CellToolbar.global_show = function () {
52 52 $('body').addClass('ctb_global_show');
53 53 };
54 54
55 55
56 56 CellToolbar.prototype.hide = function () {
57 57 this.element.removeClass('ctb_show');
58 58 };
59 59
60 60
61 61 CellToolbar.prototype.show = function () {
62 62 this.element.addClass('ctb_show');
63 63 };
64 64
65 65
66 66 /**
67 67 * Class variable that should contain a dict of all available callback
68 68 * we need to think of wether or not we allow nested namespace
69 69 * @property _callback_dict
70 70 * @private
71 71 * @static
72 72 * @type Dict
73 73 */
74 74 CellToolbar._callback_dict = {};
75 75
76 76
77 77 /**
78 78 * Class variable that should contain the reverse order list of the button
79 79 * to add to the toolbar of each cell
80 80 * @property _ui_controls_list
81 81 * @private
82 82 * @static
83 83 * @type List
84 84 */
85 85 CellToolbar._ui_controls_list = [];
86 86
87 87
88 88 /**
89 89 * Class variable that should contain the CellToolbar instances for each
90 90 * cell of the notebook
91 91 *
92 92 * @private
93 93 * @property _instances
94 94 * @static
95 95 * @type List
96 96 */
97 97 CellToolbar._instances = [];
98 98
99 99
100 100 /**
101 101 * keep a list of all the available presets for the toolbar
102 102 * @private
103 103 * @property _presets
104 104 * @static
105 105 * @type Dict
106 106 */
107 107 CellToolbar._presets = {};
108 108
109 109
110 110 // this is by design not a prototype.
111 111 /**
112 112 * Register a callback to create an UI element in a cell toolbar.
113 113 * @method register_callback
114 114 * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
115 115 * for easier sorting and avoid collision
116 116 * @param callback {function(div, cell)} callback that will be called to generate the ui element
117 117 * @param [cell_types] {List of String|undefined} optional list of cell types. If present the UI element
118 118 * will be added only to cells of types in the list.
119 119 *
120 120 *
121 121 * The callback will receive the following element :
122 122 *
123 123 * * a div in which to add element.
124 124 * * the cell it is responsible from
125 125 *
126 126 * @example
127 127 *
128 128 * Example that create callback for a button that toggle between `true` and `false` label,
129 129 * with the metadata under the key 'foo' to reflect the status of the button.
130 130 *
131 131 * // first param reference to a DOM div
132 132 * // second param reference to the cell.
133 133 * var toggle = function(div, cell) {
134 134 * var button_container = $(div)
135 135 *
136 136 * // let's create a button that show the current value of the metadata
137 137 * var button = $('<div/>').button({label:String(cell.metadata.foo)});
138 138 *
139 139 * // On click, change the metadata value and update the button label
140 140 * button.click(function(){
141 141 * var v = cell.metadata.foo;
142 142 * cell.metadata.foo = !v;
143 143 * button.button("option", "label", String(!v));
144 144 * })
145 145 *
146 146 * // add the button to the DOM div.
147 147 * button_container.append(button);
148 148 * }
149 149 *
150 150 * // now we register the callback under the name `foo` to give the
151 151 * // user the ability to use it later
152 152 * CellToolbar.register_callback('foo', toggle);
153 153 */
154 154 CellToolbar.register_callback = function(name, callback, cell_types) {
155 155 // Overwrite if it already exists.
156 156 CellToolbar._callback_dict[name] = cell_types ? {callback: callback, cell_types: cell_types} : callback;
157 157 };
158 158
159 159
160 160 /**
161 161 * Register a preset of UI element in a cell toolbar.
162 162 * Not supported Yet.
163 163 * @method register_preset
164 164 * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
165 165 * for easier sorting and avoid collision
166 166 * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list
167 167 * should correspond to a name of a registerd callback.
168 168 *
169 169 * @private
170 170 * @example
171 171 *
172 172 * CellToolbar.register_callback('foo.c1', function(div, cell){...});
173 173 * CellToolbar.register_callback('foo.c2', function(div, cell){...});
174 174 * CellToolbar.register_callback('foo.c3', function(div, cell){...});
175 175 * CellToolbar.register_callback('foo.c4', function(div, cell){...});
176 176 * CellToolbar.register_callback('foo.c5', function(div, cell){...});
177 177 *
178 178 * CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])
179 179 * CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])
180 180 */
181 181 CellToolbar.register_preset = function(name, preset_list, notebook) {
182 182 CellToolbar._presets[name] = preset_list;
183 183 events.trigger('preset_added.CellToolbar', {name: name});
184 184 // When "register_callback" is called by a custom extension, it may be executed after notebook is loaded.
185 185 // In that case, activate the preset if needed.
186 186 if (notebook && notebook.metadata && notebook.metadata.celltoolbar === name){
187 187 CellToolbar.activate_preset(name);
188 188 }
189 189 };
190 190
191 191
192 192 /**
193 193 * List the names of the presets that are currently registered.
194 194 *
195 195 * @method list_presets
196 196 * @static
197 197 */
198 198 CellToolbar.list_presets = function() {
199 199 var keys = [];
200 200 for (var k in CellToolbar._presets) {
201 201 keys.push(k);
202 202 }
203 203 return keys;
204 204 };
205 205
206 206
207 207 /**
208 208 * Activate an UI preset from `register_preset`
209 209 *
210 210 * This does not update the selection UI.
211 211 *
212 212 * @method activate_preset
213 213 * @param preset_name {String} string corresponding to the preset name
214 214 *
215 215 * @static
216 216 * @private
217 217 * @example
218 218 *
219 219 * CellToolbar.activate_preset('foo.foo_preset1');
220 220 */
221 221 CellToolbar.activate_preset = function(preset_name){
222 222 var preset = CellToolbar._presets[preset_name];
223 223
224 224 if(preset !== undefined){
225 225 CellToolbar._ui_controls_list = preset;
226 226 CellToolbar.rebuild_all();
227 227 }
228 228
229 229 events.trigger('preset_activated.CellToolbar', {name: preset_name});
230 230 };
231 231
232 232
233 233 /**
234 234 * This should be called on the class and not on a instance as it will trigger
235 235 * rebuild of all the instances.
236 236 * @method rebuild_all
237 237 * @static
238 238 *
239 239 */
240 240 CellToolbar.rebuild_all = function(){
241 241 for(var i=0; i < CellToolbar._instances.length; i++){
242 242 CellToolbar._instances[i].rebuild();
243 243 }
244 244 };
245 245
246 246 /**
247 247 * Rebuild all the button on the toolbar to update its state.
248 248 * @method rebuild
249 249 */
250 250 CellToolbar.prototype.rebuild = function(){
251 251 // strip evrything from the div
252 252 // which is probably inner_element
253 253 // or this.element.
254 254 this.inner_element.empty();
255 255 this.ui_controls_list = [];
256 256
257 257 var callbacks = CellToolbar._callback_dict;
258 258 var preset = CellToolbar._ui_controls_list;
259 259 // Yes we iterate on the class variable, not the instance one.
260 260 for (var i=0; i < preset.length; i++) {
261 261 var key = preset[i];
262 262 var callback = callbacks[key];
263 263 if (!callback) continue;
264 264
265 265 if (typeof callback === 'object') {
266 266 if (callback.cell_types.indexOf(this.cell.cell_type) === -1) continue;
267 267 callback = callback.callback;
268 268 }
269 269
270 270 var local_div = $('<div/>').addClass('button_container');
271 271 try {
272 272 callback(local_div, this.cell, this);
273 273 this.ui_controls_list.push(key);
274 274 } catch (e) {
275 275 console.log("Error in cell toolbar callback " + key, e);
276 276 continue;
277 277 }
278 278 // only append if callback succeeded.
279 279 this.inner_element.append(local_div);
280 280 }
281 281
282 282 // If there are no controls or the cell is a rendered TextCell hide the toolbar.
283 if (!this.ui_controls_list.length || (this.cell.cell_type != 'code' && this.cell.rendered)) {
283 if (!this.ui_controls_list.length) {
284 284 this.hide();
285 285 } else {
286 286 this.show();
287 287 }
288 288 };
289 289
290 290
291 291 /**
292 292 */
293 293 CellToolbar.utils = {};
294 294
295 295
296 296 /**
297 297 * A utility function to generate bindings between a checkbox and cell/metadata
298 298 * @method utils.checkbox_ui_generator
299 299 * @static
300 300 *
301 301 * @param name {string} Label in front of the checkbox
302 302 * @param setter {function( cell, newValue )}
303 303 * A setter method to set the newValue
304 304 * @param getter {function( cell )}
305 305 * A getter methods which return the current value.
306 306 *
307 307 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
308 308 *
309 309 * @example
310 310 *
311 311 * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
312 312 *
313 313 * var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',
314 314 * // setter
315 315 * function(cell, value){
316 316 * // we check that the slideshow namespace exist and create it if needed
317 317 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
318 318 * // set the value
319 319 * cell.metadata.slideshow.isSectionStart = value
320 320 * },
321 321 * //geter
322 322 * function(cell){ var ns = cell.metadata.slideshow;
323 323 * // if the slideshow namespace does not exist return `undefined`
324 324 * // (will be interpreted as `false` by checkbox) otherwise
325 325 * // return the value
326 326 * return (ns == undefined)? undefined: ns.isSectionStart
327 327 * }
328 328 * );
329 329 *
330 330 * CellToolbar.register_callback('newSlide', newSlide);
331 331 *
332 332 */
333 333 CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){
334 334 return function(div, cell, celltoolbar) {
335 335 var button_container = $(div);
336 336
337 337 var chkb = $('<input/>').attr('type', 'checkbox');
338 338 var lbl = $('<label/>').append($('<span/>').text(name));
339 339 lbl.append(chkb);
340 340 chkb.attr("checked", getter(cell));
341 341
342 342 chkb.click(function(){
343 343 var v = getter(cell);
344 344 setter(cell, !v);
345 345 chkb.attr("checked", !v);
346 346 });
347 347 button_container.append($('<span/>').append(lbl));
348 348 };
349 349 };
350 350
351 351
352 352 /**
353 353 * A utility function to generate bindings between a dropdown list cell
354 354 * @method utils.select_ui_generator
355 355 * @static
356 356 *
357 357 * @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.
358 358 * subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,
359 359 * and second the corresponding value to be passed to setter/return by getter. the corresponding value
360 360 * should not be "undefined" or behavior can be unexpected.
361 361 * @param setter {function( cell, newValue )}
362 362 * A setter method to set the newValue
363 363 * @param getter {function( cell )}
364 364 * A getter methods which return the current value of the metadata.
365 365 * @param [label=""] {String} optionnal label for the dropdown menu
366 366 *
367 367 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
368 368 *
369 369 * @example
370 370 *
371 371 * var select_type = CellToolbar.utils.select_ui_generator([
372 372 * ["<None>" , "None" ],
373 373 * ["Header Slide" , "header_slide" ],
374 374 * ["Slide" , "slide" ],
375 375 * ["Fragment" , "fragment" ],
376 376 * ["Skip" , "skip" ],
377 377 * ],
378 378 * // setter
379 379 * function(cell, value){
380 380 * // we check that the slideshow namespace exist and create it if needed
381 381 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
382 382 * // set the value
383 383 * cell.metadata.slideshow.slide_type = value
384 384 * },
385 385 * //geter
386 386 * function(cell){ var ns = cell.metadata.slideshow;
387 387 * // if the slideshow namespace does not exist return `undefined`
388 388 * // (will be interpreted as `false` by checkbox) otherwise
389 389 * // return the value
390 390 * return (ns == undefined)? undefined: ns.slide_type
391 391 * }
392 392 * CellToolbar.register_callback('slideshow.select', select_type);
393 393 *
394 394 */
395 395 CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label) {
396 396 label = label || "";
397 397 return function(div, cell, celltoolbar) {
398 398 var button_container = $(div);
399 399 var lbl = $("<label/>").append($('<span/>').text(label));
400 400 var select = $('<select/>');
401 401 for(var i=0; i < list_list.length; i++){
402 402 var opt = $('<option/>')
403 403 .attr('value', list_list[i][1])
404 404 .text(list_list[i][0]);
405 405 select.append(opt);
406 406 }
407 407 select.val(getter(cell));
408 408 select.change(function(){
409 409 setter(cell, select.val());
410 410 });
411 411 button_container.append($('<span/>').append(lbl).append(select));
412 412 };
413 413 };
414 414
415 415 // Backwards compatability.
416 416 IPython.CellToolbar = CellToolbar;
417 417
418 418 return {'CellToolbar': CellToolbar};
419 419 });
@@ -1,453 +1,449 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'notebook/js/cell',
8 8 'base/js/security',
9 9 'notebook/js/mathjaxutils',
10 10 'notebook/js/celltoolbar',
11 11 'components/marked/lib/marked',
12 12 ], function(IPython, $, cell, security, mathjaxutils, celltoolbar, marked) {
13 13 "use strict";
14 14 var Cell = cell.Cell;
15 15
16 16 var TextCell = function (options) {
17 17 // Constructor
18 18 //
19 19 // Construct a new TextCell, codemirror mode is by default 'htmlmixed',
20 20 // and cell type is 'text' cell start as not redered.
21 21 //
22 22 // Parameters:
23 23 // options: dictionary
24 24 // Dictionary of keyword arguments.
25 25 // events: $(Events) instance
26 26 // config: dictionary
27 27 // keyboard_manager: KeyboardManager instance
28 28 // notebook: Notebook instance
29 29 options = options || {};
30 30
31 31 // in all TextCell/Cell subclasses
32 32 // do not assign most of members here, just pass it down
33 33 // in the options dict potentially overwriting what you wish.
34 34 // they will be assigned in the base class.
35 35 this.notebook = options.notebook;
36 36 this.events = options.events;
37 37 this.config = options.config;
38 38
39 39 // we cannot put this as a class key as it has handle to "this".
40 40 var cm_overwrite_options = {
41 41 onKeyEvent: $.proxy(this.handle_keyevent,this)
42 42 };
43 43 var config = this.mergeopt(TextCell, this.config, {cm_config:cm_overwrite_options});
44 44 Cell.apply(this, [{
45 45 config: config,
46 46 keyboard_manager: options.keyboard_manager,
47 47 events: this.events}]);
48 48
49 49 this.cell_type = this.cell_type || 'text';
50 50 mathjaxutils = mathjaxutils;
51 51 this.rendered = false;
52 52 };
53 53
54 54 TextCell.prototype = new Cell();
55 55
56 56 TextCell.options_default = {
57 57 cm_config : {
58 58 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
59 59 mode: 'htmlmixed',
60 60 lineWrapping : true,
61 61 }
62 62 };
63 63
64 64
65 65 /**
66 66 * Create the DOM element of the TextCell
67 67 * @method create_element
68 68 * @private
69 69 */
70 70 TextCell.prototype.create_element = function () {
71 71 Cell.prototype.create_element.apply(this, arguments);
72 72
73 73 var cell = $("<div>").addClass('cell text_cell');
74 74 cell.attr('tabindex','2');
75 75
76 76 var prompt = $('<div/>').addClass('prompt input_prompt');
77 77 cell.append(prompt);
78 78 var inner_cell = $('<div/>').addClass('inner_cell');
79 79 this.celltoolbar = new celltoolbar.CellToolbar({
80 80 cell: this,
81 81 notebook: this.notebook});
82 82 inner_cell.append(this.celltoolbar.element);
83 83 var input_area = $('<div/>').addClass('input_area');
84 84 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
85 85 // The tabindex=-1 makes this div focusable.
86 86 var render_area = $('<div/>').addClass('text_cell_render rendered_html')
87 87 .attr('tabindex','-1');
88 88 inner_cell.append(input_area).append(render_area);
89 89 cell.append(inner_cell);
90 90 this.element = cell;
91 91 };
92 92
93 93
94 94 /**
95 95 * Bind the DOM evet to cell actions
96 96 * Need to be called after TextCell.create_element
97 97 * @private
98 98 * @method bind_event
99 99 */
100 100 TextCell.prototype.bind_events = function () {
101 101 Cell.prototype.bind_events.apply(this);
102 102 var that = this;
103 103
104 104 this.element.dblclick(function () {
105 105 if (that.selected === false) {
106 106 this.events.trigger('select.Cell', {'cell':that});
107 107 }
108 108 var cont = that.unrender();
109 109 if (cont) {
110 110 that.focus_editor();
111 111 }
112 112 });
113 113 };
114 114
115 115 // Cell level actions
116 116
117 117 TextCell.prototype.select = function () {
118 118 var cont = Cell.prototype.select.apply(this);
119 119 if (cont) {
120 120 if (this.mode === 'edit') {
121 121 this.code_mirror.refresh();
122 122 }
123 123 }
124 124 return cont;
125 125 };
126 126
127 127 TextCell.prototype.unrender = function () {
128 128 if (this.read_only) return;
129 129 var cont = Cell.prototype.unrender.apply(this);
130 130 if (cont) {
131 131 var text_cell = this.element;
132 132 var output = text_cell.find("div.text_cell_render");
133 133 output.hide();
134 134 text_cell.find('div.input_area').show();
135 135 if (this.get_text() === this.placeholder) {
136 136 this.set_text('');
137 137 }
138 138 this.refresh();
139 139 }
140 if (this.celltoolbar.ui_controls_list.length) {
141 this.celltoolbar.show();
142 }
143 140 return cont;
144 141 };
145 142
146 143 TextCell.prototype.execute = function () {
147 144 this.render();
148 145 };
149 146
150 147 /**
151 148 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
152 149 * @method get_text
153 150 * @retrun {string} CodeMirror current text value
154 151 */
155 152 TextCell.prototype.get_text = function() {
156 153 return this.code_mirror.getValue();
157 154 };
158 155
159 156 /**
160 157 * @param {string} text - Codemiror text value
161 158 * @see TextCell#get_text
162 159 * @method set_text
163 160 * */
164 161 TextCell.prototype.set_text = function(text) {
165 162 this.code_mirror.setValue(text);
166 163 this.unrender();
167 164 this.code_mirror.refresh();
168 165 };
169 166
170 167 /**
171 168 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
172 169 * @method get_rendered
173 170 * */
174 171 TextCell.prototype.get_rendered = function() {
175 172 return this.element.find('div.text_cell_render').html();
176 173 };
177 174
178 175 /**
179 176 * @method set_rendered
180 177 */
181 178 TextCell.prototype.set_rendered = function(text) {
182 179 this.element.find('div.text_cell_render').html(text);
183 this.celltoolbar.hide();
184 180 };
185 181
186 182
187 183 /**
188 184 * Create Text cell from JSON
189 185 * @param {json} data - JSON serialized text-cell
190 186 * @method fromJSON
191 187 */
192 188 TextCell.prototype.fromJSON = function (data) {
193 189 Cell.prototype.fromJSON.apply(this, arguments);
194 190 if (data.cell_type === this.cell_type) {
195 191 if (data.source !== undefined) {
196 192 this.set_text(data.source);
197 193 // make this value the starting point, so that we can only undo
198 194 // to this state, instead of a blank cell
199 195 this.code_mirror.clearHistory();
200 196 // TODO: This HTML needs to be treated as potentially dangerous
201 197 // user input and should be handled before set_rendered.
202 198 this.set_rendered(data.rendered || '');
203 199 this.rendered = false;
204 200 this.render();
205 201 }
206 202 }
207 203 };
208 204
209 205 /** Generate JSON from cell
210 206 * @return {object} cell data serialised to json
211 207 */
212 208 TextCell.prototype.toJSON = function () {
213 209 var data = Cell.prototype.toJSON.apply(this);
214 210 data.source = this.get_text();
215 211 if (data.source == this.placeholder) {
216 212 data.source = "";
217 213 }
218 214 return data;
219 215 };
220 216
221 217
222 218 var MarkdownCell = function (options) {
223 219 // Constructor
224 220 //
225 221 // Parameters:
226 222 // options: dictionary
227 223 // Dictionary of keyword arguments.
228 224 // events: $(Events) instance
229 225 // config: dictionary
230 226 // keyboard_manager: KeyboardManager instance
231 227 // notebook: Notebook instance
232 228 options = options || {};
233 229 var config = this.mergeopt(MarkdownCell, options.config);
234 230 TextCell.apply(this, [$.extend({}, options, {config: config})]);
235 231
236 232 this.cell_type = 'markdown';
237 233 };
238 234
239 235 MarkdownCell.options_default = {
240 236 cm_config: {
241 237 mode: 'ipythongfm'
242 238 },
243 239 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
244 240 };
245 241
246 242 MarkdownCell.prototype = new TextCell();
247 243
248 244 /**
249 245 * @method render
250 246 */
251 247 MarkdownCell.prototype.render = function () {
252 248 var cont = TextCell.prototype.render.apply(this);
253 249 if (cont) {
254 250 var text = this.get_text();
255 251 var math = null;
256 252 if (text === "") { text = this.placeholder; }
257 253 var text_and_math = mathjaxutils.remove_math(text);
258 254 text = text_and_math[0];
259 255 math = text_and_math[1];
260 256 var html = marked.parser(marked.lexer(text));
261 257 html = mathjaxutils.replace_math(html, math);
262 258 html = security.sanitize_html(html);
263 259 html = $($.parseHTML(html));
264 260 // links in markdown cells should open in new tabs
265 261 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
266 262 this.set_rendered(html);
267 263 this.element.find('div.input_area').hide();
268 264 this.element.find("div.text_cell_render").show();
269 265 this.typeset();
270 266 }
271 267 return cont;
272 268 };
273 269
274 270
275 271 var RawCell = function (options) {
276 272 // Constructor
277 273 //
278 274 // Parameters:
279 275 // options: dictionary
280 276 // Dictionary of keyword arguments.
281 277 // events: $(Events) instance
282 278 // config: dictionary
283 279 // keyboard_manager: KeyboardManager instance
284 280 // notebook: Notebook instance
285 281 options = options || {};
286 282 var config = this.mergeopt(RawCell, options.config);
287 283 TextCell.apply(this, [$.extend({}, options, {config: config})]);
288 284
289 285 // RawCell should always hide its rendered div
290 286 this.element.find('div.text_cell_render').hide();
291 287 this.cell_type = 'raw';
292 288 };
293 289
294 290 RawCell.options_default = {
295 291 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
296 292 "It will not be rendered in the notebook. " +
297 293 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
298 294 };
299 295
300 296 RawCell.prototype = new TextCell();
301 297
302 298 /** @method bind_events **/
303 299 RawCell.prototype.bind_events = function () {
304 300 TextCell.prototype.bind_events.apply(this);
305 301 var that = this;
306 302 this.element.focusout(function() {
307 303 that.auto_highlight();
308 304 that.render();
309 305 });
310 306
311 307 this.code_mirror.on('focus', function() { that.unrender(); });
312 308 };
313 309
314 310 /**
315 311 * Trigger autodetection of highlight scheme for current cell
316 312 * @method auto_highlight
317 313 */
318 314 RawCell.prototype.auto_highlight = function () {
319 315 this._auto_highlight(this.config.raw_cell_highlight);
320 316 };
321 317
322 318 /** @method render **/
323 319 RawCell.prototype.render = function () {
324 320 var cont = TextCell.prototype.render.apply(this);
325 321 if (cont){
326 322 var text = this.get_text();
327 323 if (text === "") { text = this.placeholder; }
328 324 this.set_text(text);
329 325 this.element.removeClass('rendered');
330 326 }
331 327 return cont;
332 328 };
333 329
334 330
335 331 var HeadingCell = function (options) {
336 332 // Constructor
337 333 //
338 334 // Parameters:
339 335 // options: dictionary
340 336 // Dictionary of keyword arguments.
341 337 // events: $(Events) instance
342 338 // config: dictionary
343 339 // keyboard_manager: KeyboardManager instance
344 340 // notebook: Notebook instance
345 341 options = options || {};
346 342 var config = this.mergeopt(HeadingCell, options.config);
347 343 TextCell.apply(this, [$.extend({}, options, {config: config})]);
348 344
349 345 this.level = 1;
350 346 this.cell_type = 'heading';
351 347 };
352 348
353 349 HeadingCell.options_default = {
354 350 cm_config: {
355 351 theme: 'heading-1'
356 352 },
357 353 placeholder: "Type Heading Here"
358 354 };
359 355
360 356 HeadingCell.prototype = new TextCell();
361 357
362 358 /** @method fromJSON */
363 359 HeadingCell.prototype.fromJSON = function (data) {
364 360 if (data.level !== undefined){
365 361 this.level = data.level;
366 362 }
367 363 TextCell.prototype.fromJSON.apply(this, arguments);
368 364 this.code_mirror.setOption("theme", "heading-"+this.level);
369 365 };
370 366
371 367
372 368 /** @method toJSON */
373 369 HeadingCell.prototype.toJSON = function () {
374 370 var data = TextCell.prototype.toJSON.apply(this);
375 371 data.level = this.get_level();
376 372 return data;
377 373 };
378 374
379 375 /**
380 376 * Change heading level of cell, and re-render
381 377 * @method set_level
382 378 */
383 379 HeadingCell.prototype.set_level = function (level) {
384 380 this.level = level;
385 381 this.code_mirror.setOption("theme", "heading-"+level);
386 382
387 383 if (this.rendered) {
388 384 this.rendered = false;
389 385 this.render();
390 386 }
391 387 };
392 388
393 389 /** The depth of header cell, based on html (h1 to h6)
394 390 * @method get_level
395 391 * @return {integer} level - for 1 to 6
396 392 */
397 393 HeadingCell.prototype.get_level = function () {
398 394 return this.level;
399 395 };
400 396
401 397
402 398 HeadingCell.prototype.get_rendered = function () {
403 399 var r = this.element.find("div.text_cell_render");
404 400 return r.children().first().html();
405 401 };
406 402
407 403 HeadingCell.prototype.render = function () {
408 404 var cont = TextCell.prototype.render.apply(this);
409 405 if (cont) {
410 406 var text = this.get_text();
411 407 var math = null;
412 408 // Markdown headings must be a single line
413 409 text = text.replace(/\n/g, ' ');
414 410 if (text === "") { text = this.placeholder; }
415 411 text = new Array(this.level + 1).join("#") + " " + text;
416 412 var text_and_math = mathjaxutils.remove_math(text);
417 413 text = text_and_math[0];
418 414 math = text_and_math[1];
419 415 var html = marked.parser(marked.lexer(text));
420 416 html = mathjaxutils.replace_math(html, math);
421 417 html = security.sanitize_html(html);
422 418 var h = $($.parseHTML(html));
423 419 // add id and linkback anchor
424 420 var hash = h.text().replace(/ /g, '-');
425 421 h.attr('id', hash);
426 422 h.append(
427 423 $('<a/>')
428 424 .addClass('anchor-link')
429 425 .attr('href', '#' + hash)
430 426 .text('¶')
431 427 );
432 428 this.set_rendered(h);
433 429 this.element.find('div.input_area').hide();
434 430 this.element.find("div.text_cell_render").show();
435 431 this.typeset();
436 432 }
437 433 return cont;
438 434 };
439 435
440 436 // Backwards compatability.
441 437 IPython.TextCell = TextCell;
442 438 IPython.MarkdownCell = MarkdownCell;
443 439 IPython.RawCell = RawCell;
444 440 IPython.HeadingCell = HeadingCell;
445 441
446 442 var textcell = {
447 443 'TextCell': TextCell,
448 444 'MarkdownCell': MarkdownCell,
449 445 'RawCell': RawCell,
450 446 'HeadingCell': HeadingCell,
451 447 };
452 448 return textcell;
453 449 });
General Comments 0
You need to be logged in to leave comments. Login now