Created
July 28, 2015 11:27
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*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