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