Skip to content

Instantly share code, notes, and snippets.

@StuartLittlefair
Created July 28, 2015 11:27
Show Gist options
  • Save StuartLittlefair/76f00637783a9f43861d to your computer and use it in GitHub Desktop.
Save StuartLittlefair/76f00637783a9f43861d to your computer and use it in GitHub Desktop.
How to add bootstrap style panels to markdown cells in the Jupyter notebook
/*global define*/
/**
* To load this extension, add the following to your custom.js:
*
* require(['base/js/events'], function (events) {
* events.on('app_initialized.NotebookApp', function() {
* IPython.load_extensions('nbgrader');
* });
* });
*
**/
define([
'require',
'jquery',
'base/js/namespace',
'base/js/dialog',
'notebook/js/celltoolbar',
'base/js/events'
], function (require, $, IPython, dialog, celltoolbar, events) {
"use strict";
var preset_name = "Style Cells";
var task_cls = "panel-success";
var lo_cls = "panel-warning";
var hint_cls = "panel-hint";
var classes = [task_cls, lo_cls, hint_cls];
var warning;
/**
* Is the cell a task cell?
*/
var is_task = function (cell) {
if (cell.metadata.panel === undefined) {
return false;
} else if (cell.metadata.panel.task === undefined) {
return false;
} else {
return cell.metadata.panel.task;
}
};
/**
* Set whether this cell is or is not a task cell.
*/
var set_task = function (cell, val) {
if (cell.metadata.panel === undefined) {
cell.metadata.panel = {};
}
cell.metadata.panel.task = val;
};
/**
* Is the cell a hint cell?
*/
var is_hint = function (cell) {
if (cell.metadata.panel === undefined) {
return false;
} else if (cell.metadata.panel.hint === undefined) {
return false;
} else {
return cell.metadata.panel.hint;
}
};
/**
* Set whether this cell is or is not a hint cell.
*/
var set_hint = function (cell, val) {
if (cell.metadata.panel === undefined) {
cell.metadata.panel = {};
}
cell.metadata.panel.hint = val;
};
/**
* Is the cell a learning objectives cell?
*/
var is_lo = function (cell) {
if (cell.metadata.panel === undefined) {
return false;
} else if (cell.metadata.panel.lo === undefined) {
return false;
} else {
return cell.metadata.panel.lo;
}
};
/**
* Set whether this cell is or is not a learning objectives cell.
*/
var set_lo = function (cell, val) {
if (cell.metadata.panel === undefined) {
cell.metadata.panel = {};
}
cell.metadata.panel.lo = val;
};
/**
* Get title of cell from metadata
*/
var get_title = function(cell) {
if (cell.metadata.panel === undefined){
return '';
} else if (cell.metadata.panel.title === undefined) {
return '';
} else {
return cell.metadata.panel.title;
}
};
/**
* Set title of cell in metadata
*/
var set_title = function(cell, val) {
if (cell.metadata.panel === undefined){
cell.metadta.panel = {};
}
if (val === undefined) {
cell.metadta.panel.title = '';
} else {
cell.metadata.panel.title = val;
}
};
/**
* Is this a markdown cell or not?
**/
var is_markdown = function(cell){
return cell.cell_type === "markdown";
}
var CellToolbar = celltoolbar.CellToolbar;
var find_inner_cell = function(cell) {
return $(cell.element.children()[1]);
}
var find_markdown_div = function (cell) {
return $(cell.element.children()[1].children[1]);
}
var find_rendered_div = function (cell) {
return $(cell.element.children()[1].children[2]);
}
var wrap_in_panel = function (cell) {
var md_el = find_markdown_div(cell);
var rd_el = find_rendered_div(cell);
if (is_task(cell)){
var section = $('<section class="mypanel panel panel-success">');
} else if (is_hint(cell)){
var section = $('<section class="mypanel panel panel-info">');
} else if (is_lo(cell)){
var section = $('<section class="mypanel panel panel-warning">');
}else{
throw new Error("invalid cell type");
}
if (is_task(cell)){
var icon = $('<span class="fa fa-pencil"></span>');
} else if (is_hint(cell)){
var icon = $('<span class="fa fa-thumb-tack"></span>');
} else if (is_lo(cell)){
var icon = $('<span class="fa fa-certificate"></span>');
}else{
throw new Error("invalid cell type");
}
var panelHead = $('<div class="panel-heading">');
var panelBody = $('<div class="panel-body">');
var dummyText1 = $('<p>',{text:"placeholder1"});
var dummyText2 = $('<p>',{text:"placeholder2"});
var title = get_title(cell);
var header = $('<h3>',
{text:title,
class:'cell-header'});
header.prepend(icon);
panelHead.prepend(header);
panelBody.prepend(dummyText1);
panelBody.prepend(dummyText2);
section.append(panelHead).append(panelBody);
section.insertBefore(md_el);
dummyText1.replaceWith(md_el);
dummyText2.replaceWith(rd_el);
}
/** remove all panels from cell **/
var remove_panel = function (cell) {
console.log('removing panel');
var section = cell.element.find('.mypanel');
var panelBody = section.find('.panel-body');
var panelHeader = section.find('.panel-heading');
panelBody.contents().unwrap();
panelHeader.remove();
section.contents().unwrap();
//var md_el = find_markdown_div(cell);
//var rd_el = find_rendered_div(cell);
//md_el.unwrap();
//rd_el.unwrap();
//rd_el.closest('.panel-heading').remove();
//rd_el.closest('.panel').remove();
}
/**
* Add a display panel to the cell element, depending on the
* cell type.
*/
var add_panels_cell = function (cell) {
// reset class according to style
if ( (cell.element && is_markdown(cell)) ) {
// wipe the slate clean
remove_panel(cell);
/** now wrap this cell with a panel **/
if (is_task(cell) || is_hint(cell) || is_lo(cell) ) {
wrap_in_panel(cell);
}
}
}
/** add panels to all cells **/
var add_panels_all = function() {
var cells = IPython.notebook.get_cells();
for (var i in cells){
add_panels_cell(cells[i]);
}
}
// add classes to all cells on notebook load
events.on('notebook_loaded.Notebook',add_panels_all());
/**
* Create the input text box for the cell title.
*/
var create_title_input = function (div, cell, celltoolbar) {
if (!is_task(cell) && !is_hint(cell) && !is_lo(cell)) {
return;
}
var local_div = $('<div/>');
var text = $('<input/>').attr('type', 'text');
var lbl = $('<label/>').append($('<span/>').text('Title: '));
lbl.append(text);
text.addClass('nbhighlight-title');
text.attr("value", get_title(cell));
text.change(function () {
set_title(cell, text.val());
add_panels_cell(cell);
});
local_div.addClass('nbhighlight-title');
$(div).append(local_div.append($('<span/>').append(lbl)));
IPython.keyboard_manager.register_events(text);
};
/** create toolbar to select cell type **/
var create_cellhighlight_select = function (div, cell, celltoolbar) {
// hack -- the DOM element for the celltoolbar is created before the
// cell type is actually set, so we need to wait until the cell type
// has been set before we can actually create the select menu
if (cell.cell_type === null) {
setTimeout(function () {
create_cellhighlight_select(div, cell, celltoolbar);
}, 100);
} else {
var options_list = [];
options_list.push(["-", ""]);
options_list.push(["Task", "task"]);
options_list.push(["Hint", "hint"]);
options_list.push(["LO", "lo"]);
var setter = function (cell, val) {
if (val === "") {
set_hint(cell, false);
set_task(cell, false);
set_lo(cell, false);
} else if (val === "task") {
set_hint(cell, false);
set_task(cell, true);
set_lo(cell, false);
} else if (val === "hint") {
set_hint(cell, true);
set_task(cell, false);
set_lo(cell, false);
} else if (val === "lo") {
set_hint(cell, false);
set_task(cell, false);
set_lo(cell, true);
} else {
throw new Error("invalid cell type: " + val);
}
};
var getter = function (cell) {
if (is_task(cell)) {
return "task";
}else if (is_hint(cell)) {
return "hint";
} else if (is_lo(cell)) {
return "lo";
} else {
return "";
}
};
var select = $('<select/>');
for(var i=0; i < options_list.length; i++){
var opt = $('<option/>')
.attr('value', options_list[i][1])
.text(options_list[i][0]);
select.append(opt);
}
select.val(getter(cell));
select.change(function () {
setter(cell, select.val());
celltoolbar.rebuild();
add_panels_cell(cell);
});
add_panels_cell(cell);
$(div).append($('<span/>').append(select));
}
};
/**
* Load the nbhighlght toolbar extension.
*/
var load_extension = function () {
CellToolbar.register_callback('set_celltype.celltype_options', create_cellhighlight_select);
CellToolbar.register_callback('set_celltype.title_input', create_title_input);
var preset = [
'set_celltype.celltype_options',
'set_celltype.title_input'
];
CellToolbar.register_preset(preset_name, preset, IPython.notebook);
console.log('nbhighlight extension for metadata editing loaded.');
};
return {
'load_ipython_extension': load_extension
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment