diff --git a/extensions/toc2.py b/extensions/toc2.py
new file mode 100644
index 000000000..5c5dd0cd6
--- /dev/null
+++ b/extensions/toc2.py
@@ -0,0 +1,48 @@
+"""Toc2 Exporter class"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2016, the IPython IPython-Contrib Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+from traitlets.config import Config
+from traitlets.config import Config
+from nbconvert.exporters.html import HTMLExporter
+
+#-----------------------------------------------------------------------------
+# Classes
+#-----------------------------------------------------------------------------
+
+class TocExporter(HTMLExporter):
+ """
+ Exports to an html document, embedding toc2 extension (.html)
+ """
+
+ def _file_extension_default(self):
+ return '.html'
+
+ def _template_file_default(self):
+ return 'toc3'
+
+ output_mimetype = 'text/html'
+
+ def _raw_mimetypes_default(self):
+ return ['text/markdown', 'text/html', '']
+
+ @property
+ def default_config(self):
+ import jupyter_core.paths
+ import os
+ c = Config({'ExtractOutputPreprocessor':{'enabled':True}})
+ c.merge(super(TocExporter,self).default_config)
+
+ user_templates = os.path.join(jupyter_core.paths.jupyter_data_dir(), 'templates')
+ c.TemplateExporter.template_path = [
+ '.', user_templates ]
+ return c
\ No newline at end of file
diff --git a/nbextensions/usability/toc2/README.md b/nbextensions/usability/toc2/README.md
index bcc4c5feb..cb5dbc52d 100644
--- a/nbextensions/usability/toc2/README.md
+++ b/nbextensions/usability/toc2/README.md
@@ -2,11 +2,15 @@
## Description and main features
-This extension adds a small button in the main toolbar, which enables to collect all running headers in the notebook and display them in a floating window.
+The toc2 extension enables to collect all running headers and display them in a floating window, as a sidebar or with a navigation menu. The extension is also draggable, resizable, collapsable, dockable and features automatic numerotation with unique links ids, and an optional toc cell. Finally, the toc can preserved when exporting to html.
-![](icon.png)
+#### First demo:
+![](demo.gif)
-The table of contents is automatically updated when modifications occur in the notebook. The toc window can be moved and resized, the table of contents can be collapsed or the window can be completely hidden. The position, dimensions, and states (that is 'collapsed' and 'hidden' states) are remembered (actually stored in the notebook's metadata) and restored on the next session. The floating window also provides two links in its header for further functionalities:
+#### Second demo:
+![](demo2.gif)
+
+The table of contents is automatically updated when modifications occur in the notebook. The toc window can be moved and resized. It can be docked as a sidebar or dragged from the sidebar into a floating window. The table of contents can be collapsed or the window can be completely hidden. The navigation menu can be enabled/disabled via the nbextensions configuration utility. It can also be resized. The position, dimensions, and states (that is 'collapsed' and 'hidden' states) are remembered (actually stored in the notebook's metadata) and restored on the next session. The toc window also provides two links in its header for further functionalities:
- the "n" link toggles automatic numerotation of all header lines
- the "t" link toggles a toc cell in the notebook, which contains the actual table of contents, possibly with the numerotation of the different sections.
@@ -16,11 +20,30 @@ The state of these two toggles is memorized and restored on reload.
![](image.png)
## Configuration
-The initial configuration can be given using the IPython-contrib nbextensions facility. The maximum depth of headers to display on toc can be precised there (with a default of 4), as well as the state of the toc cell (default: false, ie not present) and the numbering of headers (true by default). The differents states and position of the floating window have reasonable defaults and can be modfied per notebook).
-
-
+The initial configuration can be given using the IPython-contrib nbextensions facility. It includes:
+
+- The toc initial mode (floating or sidebar)
+- The maximum depth of headers to display on toc (with a default of 6)
+- The state of the toc cell (default: false, ie not present)
+- The numbering of headers (true by default).
+
+The differents states and position of the floating window have reasonable defaults and can be modfied per notebook).
+
+## Export
+It is possible to export (most of) table of contents functionalities to html. The idea is to link a relevant part of the javascript
+extension and the css, and add a small script in the html file. This is done using a template by
+```
+jupyter nbconvert FILE.ipynb --template toc
+```
+or
+```
+jupyter nbconvert FILE.ipynb --template toc2
+```
+For the first template (toc), the files toc2.js and main.css (originally located in JUPYTER_DATA_DIR/nbextensions/usability/toc2) must reside in the same directory as intended for the html file. In the second template, these files are linked to the IPython-notebook-extensions github website. Export configuration (parameters) shall be edited directly in the template files (in templates directory JUPYTER_DATA_DIR/templates). An option "Save as HTML (with toc)" is also provided in the File menu and enable to directly convert the actual notebook. This option requires the IPython kernel and is not present with other kernels.
+
+
# Testing
-- At loading of the notebook, configuration and initial rendering of the table of contents were fired on the event "notebook_loaded.Notebook". Curiously, it happened that this event was either not always fired or detected. Thus I rely here on a combination of "notebook_loaded.Notebook" and "kernel_ready.Kernel" instead.
+- At loading of the notebook, configuration and initial rendering of the table of contents were fired on the event "notebook_loaded.Notebook". It happens that the extension is sometimes loaded *after* this event. I now rely on a direct rendering at load and on a combination of "notebook_loaded.Notebook" and "kernel_ready.Kernel".
- This extension also includes a quick workaround as described in https://github.com/ipython-contrib/IPython-notebook-extensions/issues/429
@@ -33,6 +56,8 @@ from https://gist.github.com/magican/5574556
- @JanSchulz, enable maths in headers links
- @jfbercher december 06, 2015 -- Big update: automatic numbering, toc cell, window dragging, configuration parameters
- @jfbercher december 24, 2015 -- nested numbering in toc-window, following the fix by [@paulovn](https://github.com/minrk/ipython_extensions/pull/53) in @minrk's repo. December 30-31, updated config in toc2.yaml to enable choosing the initial visible state of toc_window via a checkbox ; and now resizable.
-- @slonik-az february 13, 2016. rewritten toc numberings (more robust version), fixed problems with skipped heading levels, some code cleanup
+- @slonik-az february 13, 2016. Rewritten toc numberings (more robust version), fixed problems with skipped heading levels, some code cleanup
- @jfbercher february 21, 2016. Fixed some issues when resizing the toc window. Now avoid overflows, clip the text and add a scrollbar.
- @jfbercher february 22, 2016. Add current toc number to headings anchors. This enable to get unique anchors for recurring headings with the same text. An anchor with the original ID is still created and can be used (but toc uses the new ones!). It is also possible to directly add an html anchor within the heading text. This is taken into account when building toc links (see comments in code).
+- @jfbercher april 29, 2016. Triggered by @cqcn1991, cf [discussion here](https://github.com/ipython-contrib/IPython-notebook-extensions/issues/532), add a sidebar option. The floating toc window can be dragged and docked as a left sidebar. The sidebar can be dragged out as a floating window. These different states are stored and restored when reloading the notebook. Add html export capability via templates toc.tpl and toc2.tpl (see above).
+- - @jfbercher may 04, 2016. Added a "Save as HTML with toc" menu. Added a new "Navigate" menu with presents the contents of the toc. Changed default styling for links in tocs.
diff --git a/nbextensions/usability/toc2/demo.gif b/nbextensions/usability/toc2/demo.gif
new file mode 100644
index 000000000..5ea0bdb1d
Binary files /dev/null and b/nbextensions/usability/toc2/demo.gif differ
diff --git a/nbextensions/usability/toc2/demo2.gif b/nbextensions/usability/toc2/demo2.gif
new file mode 100644
index 000000000..302fc5420
Binary files /dev/null and b/nbextensions/usability/toc2/demo2.gif differ
diff --git a/nbextensions/usability/toc2/main.css b/nbextensions/usability/toc2/main.css
index 96eebed92..ae2005d6a 100644
--- a/nbextensions/usability/toc2/main.css
+++ b/nbextensions/usability/toc2/main.css
@@ -1,18 +1,70 @@
/*extracted from https://gist.github.com/magican/5574556*/
-#toc {
+
+#toc-level0 li > a:hover { display: block; background-color: #DAA520}
+#toc-level0 a {color: #333333; text-decoration: none;}
+#navigate_menu li a:hover {background-color: #f1f1f1}
+
+/*#Navigate_menu {
+ list-style-type: none;
+ max-width: 500px;
+ min-width: 100px;
+ width: 250px;
+ overflow: auto;
+}
+
+#navigate_menu {
+ overflow: hidden;
+}
+
+#Navigate_menu {
+ list-style-type: none;
+ overflow: auto;
+}*/
+
+#navigate_menu {
+ /*display: block;*/
+ list-style-type: none;
+ max-width: 800px;
+ min-width: 100px;
+ width: 250px;
+ overflow: auto;
+}
+
+
+#navigate_menu a {
+ list-style-type: none;
+ color: #333333;
+ text-decoration: none;
+}
+
+#navigate_menu li {
+ padding-left: 2px;
+ clear: both;
+ list-style-type: none;
+}
+
+#navigate_menu-level0 {padding-left: 10px;}
+#navigate_menu-level0 ul {padding-left: 10px;}
+
+
+.toc {
max-height: 500px;
padding: 0px;
overflow-y: auto;
+ font-weight: normal;
+ color: #333333;
+ white-space: nowrap;
+ overflow-x: auto;
}
-#toc ol.toc-item {
+.toc ol.toc-item {
counter-reset: item;
list-style: none;
padding: 0.1em;
}
-#toc ol.toc-item li {
+.toc ol.toc-item li {
display: block;
}
@@ -22,7 +74,7 @@
margin: 0;
}
-#toc ol.toc-item li:before {
+.toc ol.toc-item li:before {
font-size: 90%;
font-family: Georgia, Times New Roman, Times, serif;
counter-increment: item;
@@ -30,8 +82,8 @@
}
-#toc-wrapper {
- position: fixed;
+.float-wrapper {
+ position: fixed !important;
top: 120px;
max-width:600px;
right: 20px;
@@ -44,10 +96,29 @@
overflow: hidden;
}
+.sidebar-wrapper {
+ height: 100%;
+ left: 5px;
+ padding: 10px;
+ position: fixed !important;
+ width: 212px;
+ max-width: 28%;
+ background-color: #fff;
+ border-style: solid;
+ border-color: #eeeeee;
+ opacity: .99;
+ overflow: hidden;
+}
+
+
+.col-md-9 {
+ overflow:hidden;
+ margin-left: 14%;
+ width: 80%}
+
#toc-wrapper.closed {
min-width: 100px;
width: auto;
- height:auto;
transition: width;
}
#toc-wrapper:hover{
@@ -100,4 +171,3 @@
.lev6 {margin-left: 180px}
.lev7 {margin-left: 200px}
.lev8 {margin-left: 220px}
-
diff --git a/nbextensions/usability/toc2/main.js b/nbextensions/usability/toc2/main.js
index 89f6c851d..ae7d0a5e9 100644
--- a/nbextensions/usability/toc2/main.js
+++ b/nbextensions/usability/toc2/main.js
@@ -3,414 +3,97 @@
// See the history of contributions in README.md
+//define(["require", "jquery", "base/js/namespace", 'services/config',
+// 'base/js/utils', "nbextensions/usability/toc2/toc2"], function(require, $, IPython, configmod, utils, toc2) {
+
define(["require", "jquery", "base/js/namespace", 'services/config',
- 'base/js/utils'], function (require, $, IPython, configmod, utils) {
+ 'base/js/utils', "nbextensions/usability/toc2/toc2"], function(require, $, IPython, configmod, utils, toc2 ) {
"use strict";
+
// ...........Parameters configuration......................
// define default values for config parameters if they were not present in general settings (notebook.json)
- var cfg={'toc_threshold':6,
- 'toc_number_sections':true,
+ var cfg={'threshold':6,
+ 'number_sections':true,
'toc_cell':false,
- 'toc_window_display':false}
- var threshold = cfg['toc_threshold']
- var toc_cell=cfg['toc_cell'];
- var number_sections = cfg['toc_number_sections'];
-
- function read_config() { // read after nb is loaded
- // create config object to load parameters
- var base_url = utils.get_body_data("baseUrl");
- var config = new configmod.ConfigSection('notebook', {base_url: base_url});
-
- // update params with any specified in the server's config file
- var update_params = function() {
- for (var key in cfg) {
- if (config.data.hasOwnProperty(key)){
- cfg[key] = config.data[key];
-}
- }
- IPython.notebook.metadata.toc = cfg; //save in present nb metadata (then can be modified per document)
- threshold = cfg['toc_threshold'];
- toc_cell=cfg['toc_cell'];
- number_sections = cfg['toc_number_sections'];
- //$('#toc-wrapper').css('display',cfg['toc-wrapper_display']) //ensure display is done as noted in config
- $('#toc-wrapper').css('display',cfg['toc_window_display'] ? 'block' : 'none') //ensure display is done as noted in config
- };
+ 'toc_window_display':false,
+ "toc_section_display": "block",
+ 'sideBar':true,
+ 'navigate_menu':true}
- // config may be specified at system level or at document level.
- if (IPython.notebook.metadata.toc !== undefined){ //configuration saved in nb
- console.log("config stored in nb")
- cfg = IPython.notebook.metadata.toc;
- threshold = cfg['toc_threshold'];
- toc_cell=cfg['toc_cell'];
- number_sections = cfg['toc_number_sections'];
- } else {
- console.log("config stored in system")
- config.load();
- config.loaded.then(function() {update_params() })
- }
- config_loaded = true;
-}
//.....................global variables....
- var rendering_toc_cell = false;
- var config_loaded = false;
- var extension_initialized=false;
+ var liveNotebook = !(typeof IPython == "undefined")
-//......... utilitary functions............
+ var st={}
+ st.rendering_toc_cell = false;
+ st.config_loaded = false;
+ st.extension_initialized=false;
- function incr_lbl(ary, h_idx){//increment heading label w/ h_idx (zero based)
- ary[h_idx]++;
- for(var j= h_idx+1; j < ary.length; j++){ ary[j]= 0; }
- return ary.slice(0, h_idx+1);
- }
+ st.nbcontainer_marginleft = $('#notebook-container').css('margin-left')
+ st.nbcontainer_marginright = $('#notebook-container').css('margin-right')
+ st.nbcontainer_width = $('#notebook-container').css('width')
+ st.oldTocHeight = undefined
+ st.cell_toc = undefined;
+ st.toc_index=0;
- var make_link = function (h, num_lbl) {
- var a = $("");
- a.attr("href", '#' + h.attr('id'));
- // get the text *excluding* the link text, whatever it may be
- var hclone = h.clone();
- if( num_lbl ){ hclone.prepend(num_lbl); }
- hclone.children().last().remove(); // remove the last child (that is the automatic anchor)
- hclone.find("a[name]").remove(); //remove all named anchors
- a.html(hclone.html());
- a.on('click',function(){setTimeout(function(){ $.ajax()}, 100) }) //workaround for https://github.com/jupyter/notebook/issues/699
- //as suggested by @jhamrick
- //console.log("h",h.children)
- return a;
- };
+ function read_config(cfg, callback) { // read after nb is loaded
+ // create config object to load parameters
+ var base_url = utils.get_body_data("baseUrl");
+ var config = new configmod.ConfigSection('notebook', {base_url: base_url});
- var make_link_originalid = function (h, num_lbl) {
- var a = $("");
- a.attr("href", '#' + h.attr('saveid'));
- // get the text *excluding* the link text, whatever it may be
- var hclone = h.clone();
- if( num_lbl ){ hclone.prepend(num_lbl); }
- hclone.children().last().remove(); // remove the last child (that is the automatic anchor)
- hclone.find("a[name]").remove(); //remove all named anchors
- a.html(hclone.html());
- a.on('click',function(){setTimeout(function(){ $.ajax()}, 100) }) //workaround for https://github.com/jupyter/notebook/issues/699
- return a;
+ // update params with any specified in the server's config file
+ var update_params = function(cfg) {
+ for (var key in cfg) {
+ if (config.data.hasOwnProperty(key)){
+ cfg[key] = config.data[key];
}
-
- var ol_depth = function (element) {
- // get depth of nested ol
- var d = 0;
- while (element.prop("tagName").toLowerCase() == 'ol') {
- d += 1;
- element = element.parent();
- }
- return d;
- };
-
- var create_toc_div = function () {
- var toc_wrapper = $('
')
- .append(
- $("")
- .addClass("header")
- .text("Contents ")
- .append(
- $("")
- .attr("href", "#")
- .addClass("hide-btn")
- .attr('title', 'Hide ToC')
- .text("[-]")
- .click( function(){
- $('#toc').slideToggle({'complete': function(){
- IPython.notebook.metadata.toc['toc_section_display']=$('#toc').css('display');
- IPython.notebook.set_dirty();}}
- );
- $('#toc-wrapper').toggleClass('closed');
- if ($('#toc-wrapper').hasClass('closed')){
- $('#toc-wrapper').css({height: 40});
- $('#toc-wrapper .hide-btn')
- .text('[+]')
- .attr('title', 'Show ToC');
- } else {
- $('#toc-wrapper').css({height: IPython.notebook.metadata.toc_position['height']});
- $('#toc').css({height: IPython.notebook.metadata.toc_position['height']});
- $('#toc-wrapper .hide-btn')
- .text('[-]')
- .attr('title', 'Hide ToC');
- }
- return false;
- })
- ).append(
- $("")
- .attr("href", "#")
- .addClass("reload-btn")
- .text(" \u21BB")
- .attr('title', 'Reload ToC')
- .click( function(){
- table_of_contents();
- return false;
- })
- ).append(
- $("")
- .html("  ")
- ).append(
- $("")
- .attr("href", "#")
- .addClass("number_sections-btn")
- .text("n")
- .attr('title', 'Number text sections')
- .click( function(){
- number_sections=!(number_sections);
- IPython.notebook.metadata.toc['toc_number_sections']=number_sections;
- IPython.notebook.set_dirty();
- table_of_contents();
- return false;
- })
- ).append(
- $("")
- .html(" ")
- ).append(
- $("")
- .attr("href", "#")
- .addClass("toc_cell_sections-btn")
- .html("t")
- .attr('title', 'Add a toc section in Notebook')
- .click( function(){
- toc_cell=!(toc_cell);
- IPython.notebook.metadata.toc['toc_cell']=toc_cell;
- IPython.notebook.set_dirty();
- table_of_contents();
- return false;
- })
- )
- ).append(
- $("").attr("id", "toc")
- )
-
- $("body").append(toc_wrapper);
-
- // enable dragging and save position on stop moving
- $('#toc-wrapper').draggable({
- start : function(event, ui) {
- $(this).width($(this).width());
- },
- stop : function (event,ui){ // on save, store toc position
- var oldHeight = IPython.notebook.metadata['toc_position']['height'];
- IPython.notebook.metadata['toc_position']={
- 'left':$('#toc-wrapper').css('left'),
- 'top':$('#toc-wrapper').css('top'),
- 'width':$('#toc-wrapper').css('width'),
- 'right':$('#toc-wrapper').css('right')};
- if (!$('#toc-wrapper').hasClass('closed')){
- IPython.notebook.metadata['toc_position']['height']=$('#toc-wrapper').css('height');
- }
- else {
- IPython.notebook.metadata['toc_position']['height']=oldHeight;
+ }
+ if (typeof cfg.sideBar == "undefined") {
+ console.log("Updating sidebar")
+ cfg.sideBar=true;
}
- IPython.notebook.set_dirty();
- },
- });
-
- $('#toc-wrapper').resizable({
- start : function(event, ui) {
- $(this).width($(this).width());
- },
- stop : function (event,ui){ // on save, store toc position
- IPython.notebook.metadata['toc_position']={
- 'left':$('#toc-wrapper').css('left'),
- 'top':$('#toc-wrapper').css('top'),
- 'width':$('#toc-wrapper').css('width'),
- 'height':$('#toc-wrapper').css('height'),
- 'right':$('#toc-wrapper').css('right')};
- $('#toc').css('height', $('#toc-wrapper').height()-30)
- IPython.notebook.set_dirty();
- },
- });
-
+ };
- // restore toc position at load
- if (IPython.notebook.metadata['toc_position'] !== undefined){
- $('#toc-wrapper').css(IPython.notebook.metadata['toc_position']);
-}
- // Ensure position is fixed
- $('#toc-wrapper').css('position', 'fixed');
- // Restore toc display
- if (IPython.notebook.metadata.toc !== undefined) {
- if (IPython.notebook.metadata.toc['toc_section_display']!==undefined) {
- $('#toc').css('display',IPython.notebook.metadata.toc['toc_section_display'])
- $('#toc').css('height', $('#toc-wrapper').height()-30)
- if (IPython.notebook.metadata.toc['toc_section_display']=='none'){
- $('#toc-wrapper').addClass('closed');
- $('#toc-wrapper').css({height: 40});
- $('#toc-wrapper .hide-btn')
- .text('[+]')
- .attr('title', 'Show ToC');
+ // config may be specified at system level or at document level.
+ // (1) loads system config
+ // (2) updates it with what is in nb
+ console.log("loading config stored in system")
+ config.load();
+ config.loaded.then(function() {
+ update_params(cfg);
+ if (typeof IPython.notebook.metadata.toc != "undefined"){
+ console.log("loading config stored in nb")
+ for (var key in cfg) {
+ if (typeof IPython.notebook.metadata.toc[key] != "undefined"){
+ cfg[key] = IPython.notebook.metadata.toc[key]
}
+ }
}
- if (IPython.notebook.metadata.toc['toc_window_display']!==undefined) {
- console.log("******Restoring toc display");
- $('#toc-wrapper').css('display',IPython.notebook.metadata.toc['toc_window_display'] ? 'block' : 'none');
- //$('#toc').css('overflow','auto')
- }
- }
-
- // if toc-wrapper is undefined (first run(?), then hide it)
- if ($('#toc-wrapper').css('display')==undefined) $('#toc-wrapper').css('display',"none") //block
- };
-
-// var table_of_contents = function (threshold) { //small bug because being called on an event, the passed threshold was actually an event
-// now threshold is a global variable
-var table_of_contents = function () {
- if(rendering_toc_cell) { // if toc_cell is rendering, do not call table_of_contents,
- rendering_toc_cell=false; // otherwise it will loop
- return}
-
- var toc_wrapper = $("#toc-wrapper");
- var toc_index=0;
- if (toc_wrapper.length === 0) {
- create_toc_div();
- }
- var segments = [];
- var ul = $("
").addClass("toc-item");
- $("#toc").empty().append(ul);
-
- // TOC CELL -- if toc_cell=true, add and update a toc cell in the notebook.
- // This cell, initially at the very beginning, can be moved.
- // Its contents are automatically updated.
- // Optionnaly, the sections in the toc can be numbered.
-
- var cell_toc = undefined;
- function look_for_cell_toc(callb){ // look for a possible toc cell
- var cells = IPython.notebook.get_cells();
- var lcells=cells.length;
- for (var i = 0; i < lcells; i++) {
- if (cells[i].metadata.toc=="true") {
- cell_toc=cells[i];
- toc_index=i;
- //console.log("Found a cell_toc",i);
- break;}
- }
- callb && callb(i);
- }
- // then process the toc cell:
- function proces_cell_toc(i){
- //if toc_cell=true, we want a cell_toc.
- // If it does not exist, create it at the beginning of the notebook
- //if toc_cell=false, we do not want a cell-toc.
- // If one exists, delete it
- if(toc_cell) {
- if (cell_toc == undefined) {
- rendering_toc_cell = true;
- //console.log("********* Toc undefined - Inserting toc_cell");
- cell_toc = IPython.notebook.select(0).insert_cell_above("markdown");
- cell_toc.metadata.toc="true";
- }
- }
- else{
- if (cell_toc !== undefined) IPython.notebook.delete_cell(toc_index);
- rendering_toc_cell=false;
- }
- }
- look_for_cell_toc(proces_cell_toc);
- //proces_cell_toc();
-
- var cell_toc_text = "# Table of Contents\n
";
- var depth = 1; //var depth = ol_depth(ol);
- var li= ul;//yes, initialize li with ul!
- var all_headers= $("#notebook").find(":header");
- var min_lvl=1, lbl_ary= [];
- for(; min_lvl <= 6; min_lvl++){ if(all_headers.is('h'+min_lvl)){break;} }
- for(var i= min_lvl; i <= 6; i++){ lbl_ary[i - min_lvl]= 0; }
-
- //loop over all headers
- all_headers.each(function (i, h) {
- var level = parseInt(h.tagName.slice(1), 10) - min_lvl + 1;
- // skip below threshold
- if (level > threshold){ return; }
- // skip headings with no ID to link to
- if (!h.id){ return; }
- // skip toc cell if present
- if (h.id=="Table-of-Contents"){ return; }
- //If h had already a number, remove it
- $(h).find(".toc-item-num").remove();
- var num_str= incr_lbl(lbl_ary,level-1).join('.');// numbered heading labels
- var num_lbl= $("").addClass("toc-item-num")
- .text(num_str).append(' ').append(' ');
-
- // walk down levels
- for(var elm=li; depth < level; depth++) {
- var new_ul = $("
").addClass("toc-item");
- elm.append(new_ul);
- elm= ul= new_ul;
- }
- // walk up levels
- for(; depth > level; depth--) {
- // up twice: the enclosing and
it was inserted in
- ul= ul.parent();
- while(!ul.is('ul')){ ul= ul.parent(); }
- }
- // Change link id -- append current num_str so as to get a kind of unique anchor
- // A drawback of this approach is that anchors are subject to change and thus external links can fail if toc changes
- // Anyway, one can always add a in the heading and refer to that anchor, eg [link](#myanchor)
- // This anchor is automatically removed when building toc links. The original id is also preserved and an anchor is created
- // using it.
- // Finally a heading line can be linked to by [link](#initialID), or [link](#initialID-num_str) or [link](#myanchor)
- if (!$(h).attr("saveid")) {$(h).attr("saveid", h.id)} //save original id
- h.id=$(h).attr("saveid")+'-'+num_str; // change the id to be "unique" and toc links to it
- var saveid = $(h).attr('saveid')
- //escape special chars: http://stackoverflow.com/questions/3115150/
- var saveid_search=saveid.replace(/[-[\]{}():\/!;&@=$£%§<>%"'*+?.,~\\^$|#\s]/g, "\\$&");
- if ($(h).find("a[name="+saveid_search+"]").length==0){ //add an anchor with original id (if it doesnt't already exists)
- $(h).prepend($("").attr("name",saveid)); }
+ IPython.notebook.metadata.toc = cfg; //save in present nb metadata (then can be modified per document)
+ //$('#toc-wrapper').css('display',cfg['toc_window_display'] ? 'block' : 'none') //ensure display is done as noted in config
+ callback && callback();
+ })
+ st.config_loaded = true;
+ return cfg
+}
-
- // Create toc entry, append
tag to the current . Prepend numbered-labels to headings.
- li=$("").append( make_link( $(h), num_lbl));
- ul.append(li);
- if( number_sections ){ $(h).prepend(num_lbl); }
- //toc_cell:
- if(toc_cell) {
- var tabs = function(level) {
- var tabs = '';
- for (var j = 0; j < level -1; j++) {
- tabs += "\t";}
- return tabs}
- var leves='
'
- var lnk=make_link_originalid($(h))
- cell_toc_text += leves + $('
').append(lnk).html()+'
';
- //workaround for https://github.com/jupyter/notebook/issues/699 as suggested by @jhamrick
- lnk.on('click',function(){setTimeout(function(){console.log('clicked'); $.ajax()}, 100) })
- }
- });
+// **********************************************************************
- if(toc_cell) {
- rendering_toc_cell = true;
- //IPython.notebook.to_markdown(toc_index);
- cell_toc.set_text(cell_toc_text);
- cell_toc.render();
- };
+//***********************************************************************
+// ----------------------------------------------------------------------
- $(window).resize(function(){
- $('#toc').css({maxHeight: $(window).height() - 100});
- });
+ function toggleToc() {
+ toggle_toc(cfg,st)
+ }
- $(window).trigger('resize');
-};
-
- var toggle_toc = function () {
- // toggle draw (first because of first-click behavior)
- $("#toc-wrapper").toggle({'complete':function(){
- IPython.notebook.metadata.toc['toc_window_display']=$('#toc-wrapper').css('display')=='block';
- }
- });
- IPython.notebook.set_dirty();
- // recompute:
- rendering_toc_cell = false;
- table_of_contents();
- };
-
var toc_button = function () {
if (!IPython.toolbar) {
$([IPython.events]).on("app_initialized.NotebookApp", toc_button);
@@ -421,7 +104,7 @@ var table_of_contents = function () {
{
'label' : 'Table of Contents',
'icon' : 'fa-list',
- 'callback': toggle_toc,
+ 'callback': toggleToc,
'id' : 'toc_button'
}
]);
@@ -436,29 +119,44 @@ var table_of_contents = function () {
document.getElementsByTagName("head")[0].appendChild(link);
};
+
var load_ipython_extension = function () {
- load_css();
- toc_button();
- // render toc on load
- $([IPython.events]).on("notebook_loaded.Notebook", function(){ // curiously, the event is not always fired or detected
- // thus I rely on kernel_ready.Kernel to read the initial config
- // and render the first table of contents
- read_config();
- table_of_contents();
- // render toc for each markdown cell modification
- //$([IPython.events]).on("rendered.MarkdownCell", table_of_contents);
- $([IPython.events]).on("rendered.MarkdownCell", function(){table_of_contents();});
- console.log("toc2 initialized (via notebook_loaded)")
- extension_initialized=true ; // flag to indicate that initialization was done
+ load_css(); //console.log("Loading css")
+ toc_button(); //console.log("Adding toc_button")
+
+ // read configuration, then call toc
+ cfg = read_config(cfg,
+ function(){table_of_contents(cfg,st);} // called after config is stable
+ );
+
+ // render toc for each markdown cell modification
+ $([IPython.events]).on("rendered.MarkdownCell",
+ function(){
+ table_of_contents(cfg,st);
+ });
+ console.log("toc2 initialized")
+
+ // add a save as HTML with toc included
+ addSaveAsWithToc();
+
+ // render toc on load
+ $([IPython.events]).on("notebook_loaded.Notebook", function(){
+ table_of_contents(cfg,st);
+ console.log("toc2 initialized (via notebook_loaded)")
})
+ // render toc if kernel_ready and add/remove a menu
$([IPython.events]).on("kernel_ready.Kernel", function(){
- if (!extension_initialized){
- read_config();
- table_of_contents();
- // render toc for each markdown cell modification
- $([IPython.events]).on("rendered.MarkdownCell", function(){table_of_contents();});
- console.log("toc2 initialized (via kernel_ready)")
+ console.log("kernel_ready.Kernel")
+ table_of_contents(cfg,st);
+ console.log("toc2 initialized (via kernel_ready)")
+ // If kernel has been restarted, or changed, check if save_html_with_toc has to be included or removed
+ var IPythonKernel=(IPython.notebook.kernel.name == "python2" || IPython.notebook.kernel.name == "python3")
+ if (!IPythonKernel) {
+ $('#save_html_with_toc').remove()
+ }
+ else{
+ if ($('#save_html_with_toc').length==0) addSaveAsWithToc();
}
});
diff --git a/nbextensions/usability/toc2/presentation.md b/nbextensions/usability/toc2/presentation.md
deleted file mode 100644
index 42d001803..000000000
--- a/nbextensions/usability/toc2/presentation.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Table of Contents (2)
-
-This is an update of "Table of Contents" extension, with the following new features:
-- The toc window can now be moved on screen. The position and states (that is 'collapsed' and 'hidden' states) are remembered (actually stored in the notebook's metadata) and restored on the next session. The toc window can also be resized as needed and the new size is restored on the newt session.
-- Numerotation - The different headers in the notebook can be automatically numbered (with automatic update)
-- Toc cell - A "toc cell" can be created at top of the notebook and its contents automatically updated. This cell can be moved elsewhere in the notebook andd its position will be remembered and restored.
-
-Two links have been added to the toc window in order to toggle numerotation and toc cell.
- - the "n" link toggles automatic numerotation of all header lines
- - the "t" link toggles a toc cell in the notebook, which contains the actual table of contents, possibly with the numerotation of the different sections.
-
-- Inital configuration (applied when creating a new notebook) can be specified in the /nbextensions tab. Thenafter, configuration will be stored in the notebook itself.
-
-# Testing
-- At loading of the notebook, loading of the configuration and initial rendering of the table of contents was fired on the event "notebook_loaded.Notebook". Curiously, it happened that this event was either not always fired or detected. Thus I rely here on a combination of "notebook_loaded.Notebook" and "kernel_ready.Kernel" instead. Because of that, I propose to not overwrite toc and I put the extension in "testing" to avoid any possible/unknown side effect, though it works nicely for me.
-- The present extension also includes a small workaround for https://github.com/ipython-contrib/IPython-notebook-extensions/issues/429
diff --git a/nbextensions/usability/toc2/toc2.js b/nbextensions/usability/toc2/toc2.js
new file mode 100644
index 000000000..a2e3974a0
--- /dev/null
+++ b/nbextensions/usability/toc2/toc2.js
@@ -0,0 +1,577 @@
+//---------------------------------------------------------------------
+
+//......... utilitary functions............
+
+var liveNotebook = !(typeof IPython == "undefined")
+
+ function incr_lbl(ary, h_idx){//increment heading label w/ h_idx (zero based)
+ ary[h_idx]++;
+ for(var j= h_idx+1; j < ary.length; j++){ ary[j]= 0; }
+ return ary.slice(0, h_idx+1);
+ }
+
+
+ var make_link = function (h, num_lbl) {
+ var a = $("");
+ a.attr("href", '#' + h.attr('id'));
+ // get the text *excluding* the link text, whatever it may be
+ var hclone = h.clone();
+ if( num_lbl ){ hclone.prepend(num_lbl); }
+ hclone.children().last().remove(); // remove the last child (that is the automatic anchor)
+ hclone.find("a[name]").remove(); //remove all named anchors
+ a.html(hclone.html());
+ a.on('click',function(){setTimeout(function(){ $.ajax()}, 100) }) //workaround for https://github.com/jupyter/notebook/issues/699
+ //as suggested by @jhamrick
+ //console.log("h",h.children)
+ return a;
+ };
+
+
+ var make_link_originalid = function (h, num_lbl) {
+ var a = $("");
+ a.attr("href", '#' + h.attr('saveid'));
+ // get the text *excluding* the link text, whatever it may be
+ var hclone = h.clone();
+ if( num_lbl ){ hclone.prepend(num_lbl); }
+ hclone.children().last().remove(); // remove the last child (that is the automatic anchor)
+ hclone.find("a[name]").remove(); //remove all named anchors
+ a.html(hclone.html());
+ a.on('click',function(){setTimeout(function(){ $.ajax()}, 100) }) //workaround for https://github.com/jupyter/notebook/issues/699
+ return a;
+}
+
+ var ol_depth = function (element) {
+ // get depth of nested ol
+ var d = 0;
+ while (element.prop("tagName").toLowerCase() == 'ol') {
+ d += 1;
+ element = element.parent();
+ }
+ return d;
+ };
+
+ // extra download as html with toc menu (needs IPython kernel)
+ function addSaveAsWithToc() {
+ var saveAsWithToc = $('#save_html_with_toc').length == 0
+ var IPythonKernel = (IPython.notebook.kernel.name == "python2" || IPython.notebook.kernel.name == "python3")
+ if (IPythonKernel) {
+ $('#save_checkpoint').after("")
+ $('#save_html_with_toc').append($('').text('Save as HTML (with toc)').attr("href", "#"))
+ $('#save_html_with_toc').click(function() {
+ var IPythonKernel = (IPython.notebook.kernel.name == "python2" || IPython.notebook.kernel.name == "python3")
+ if (IPythonKernel) {
+ var code = "!jupyter nbconvert '" + IPython.notebook.notebook_name + "' --template toc3"
+ console.log(code)
+ IPython.notebook.kernel.execute(code)
+ } else {
+ alert("Sorry; this only works with a IPython kernel");
+ $('#save_html_with_toc').remove();
+ }
+ })
+ }
+ }
+
+
+ var create_navigate_menu = function(callback) {
+ $('#kernel_menu').parent().after('')
+ $('#Navigate').addClass('dropdown').append($('').attr('href', '#').attr('id', 'Navigate_sub'))
+ $('#Navigate_sub').text('Navigate').addClass('dropdown-toggle').attr('data-toggle', 'dropdown')
+ $('#Navigate').append($('
').attr('id', 'Navigate_menu').addClass('dropdown-menu')
+ .append($("").attr("id", "navigate_menu").addClass('toc')))
+
+ if (typeof IPython.notebook.metadata['nav_menu'] != "undefined") {
+ $('#Navigate_menu').css(IPython.notebook.metadata['nav_menu'])
+ $('#navigate_menu').css('width', $('#Navigate_menu').css('width'))
+ $('#navigate_menu').css('height', $('#Navigate_menu').height())
+ } else {
+ IPython.notebook.metadata.nav_menu = {}
+ }
+
+ $('#Navigate_menu').resizable({
+ resize: function(event, ui) {
+ $('#navigate_menu').css('width', $('#Navigate_menu').css('width'))
+ $('#navigate_menu').css('height', $('#Navigate_menu').height())
+ },
+ stop: function(event, ui) {
+ IPython.notebook.metadata.nav_menu['width'] = $('#Navigate_menu').css('width')
+ IPython.notebook.metadata.nav_menu['height'] = $('#Navigate_menu').css('height')
+ }
+ })
+
+ callback && callback();
+ }
+
+
+
+ var create_toc_div = function (cfg,st) {
+ var toc_wrapper = $('')
+ .append(
+ $('')
+ .addClass("header")
+ .text("Contents ")
+ .append(
+ $("")
+ .attr("href", "#")
+ .addClass("hide-btn")
+ .attr('title', 'Hide ToC')
+ .text("[-]")
+ .click( function(){
+ $('#toc').slideToggle({'complete': function(){ if(liveNotebook){
+ IPython.notebook.metadata.toc['toc_section_display']=$('#toc').css('display');
+ IPython.notebook.set_dirty();}}
+ });
+ $('#toc-wrapper').toggleClass('closed');
+ if ($('#toc-wrapper').hasClass('closed')){
+ st.oldTocHeight = $('#toc-wrapper').css('height');
+ $('#toc-wrapper').css({height: 40});
+ $('#toc-wrapper .hide-btn')
+ .text('[+]')
+ .attr('title', 'Show ToC');
+ } else {
+ // $('#toc-wrapper').css({height: IPython.notebook.metadata.toc_position['height']});
+ // $('#toc').css({height: IPython.notebook.metadata.toc_position['height']});
+ $('#toc-wrapper').css({height: st.oldTocHeight});
+ $('#toc').css({height: st.oldTocHeight});
+ $('#toc-wrapper .hide-btn')
+ .text('[-]')
+ .attr('title', 'Hide ToC');
+ }
+ return false;
+ })
+ ).append(
+ $("")
+ .attr("href", "#")
+ .addClass("reload-btn")
+ .text(" \u21BB")
+ .attr('title', 'Reload ToC')
+ .click( function(){
+ table_of_contents(cfg,st);
+ return false;
+ })
+ ).append(
+ $("")
+ .html("  ")
+ ).append(
+ $("")
+ .attr("href", "#")
+ .addClass("number_sections-btn")
+ .text("n")
+ .attr('title', 'Number text sections')
+ .click( function(){
+ cfg.number_sections=!(cfg.number_sections);
+ if(liveNotebook){
+ IPython.notebook.metadata.toc['number_sections']=cfg.number_sections;
+
+ IPython.notebook.set_dirty();}
+ //$('.toc-item-num').toggle();
+ cfg.number_sections ? $('.toc-item-num').show() : $('.toc-item-num').hide()
+ //table_of_contents();
+ return false;
+ })
+ ).append(
+ $("")
+ .html(" ")
+ ).append(
+ $("")
+ .attr("href", "#")
+ .addClass("toc_cell_sections-btn")
+ .html("t")
+ .attr('title', 'Add a toc section in Notebook')
+ .click( function(){
+ cfg.toc_cell=!(cfg.toc_cell);
+ if(liveNotebook){
+ IPython.notebook.metadata.toc['toc_cell']=cfg.toc_cell;
+ IPython.notebook.set_dirty();}
+ table_of_contents(cfg,st);
+ return false;
+ })
+ )
+ ).append(
+ $("").attr("id", "toc").addClass('toc')
+ )
+
+ $("body").append(toc_wrapper);
+
+
+ // enable dragging and save position on stop moving
+ $('#toc-wrapper').draggable({
+
+ drag: function( event, ui ) {
+
+ // If dragging to the left side, then transforms in sidebar
+ if ((ui.position.left<=0) && (cfg.sideBar==false)){
+ cfg.sideBar = true;
+ st.oldTocHeight = $('#toc-wrapper').css('height');
+ if(liveNotebook){
+ IPython.notebook.metadata.toc['sideBar']=true;
+ IPython.notebook.set_dirty();}
+ //$('#toc-wrapper').css('height','');
+ toc_wrapper.removeClass('float-wrapper').addClass('sidebar-wrapper');
+ $('#notebook-container').css('margin-left',$('#toc-wrapper').width()+30);
+ $('#notebook-container').css('width',$('#notebook').width()-$('#toc-wrapper').width()-30);
+ ui.position.top = liveNotebook ? $('#header').height() : 0;
+ ui.position.left = 0;
+ if(liveNotebook){
+ $('#toc-wrapper').css('height',$('#site').height());}
+ else{
+ $('#toc-wrapper').css('height','96%');}
+ $('#toc').css('height', $('#toc-wrapper').height()-$('#toc-header').height());
+ }
+ if (ui.position.left<=0) {
+ ui.position.left = 0;
+ ui.position.top = liveNotebook ? $('#header').height() : 0;
+ }
+ if ((ui.position.left>0) && (cfg.sideBar==true)) {
+ cfg.sideBar = false;
+ if(liveNotebook){
+ IPython.notebook.metadata.toc['sideBar']=false;
+ IPython.notebook.set_dirty(); }
+ if (st.oldTocHeight==undefined) st.oldTocHeight=Math.max($('#site').height()/2,200)
+ $('#toc-wrapper').css('height',st.oldTocHeight);
+ toc_wrapper.removeClass('sidebar-wrapper').addClass('float-wrapper');
+ $('#notebook-container').css('margin-left',st.nbcontainer_marginleft);
+ $('#notebook-container').css('width',st.nbcontainer_width);
+ $('#toc').css('height', $('#toc-wrapper').height()-$('#toc-header').height()); //redraw at begin of of drag (after resizinh height)
+
+ }
+ }, //end of drag function
+ start : function(event, ui) {
+ $(this).width($(this).width());
+ },
+ stop : function (event,ui){ // on save, store toc position
+ if(liveNotebook){
+ IPython.notebook.metadata['toc_position']={
+ 'left':$('#toc-wrapper').css('left'),
+ 'top':$('#toc-wrapper').css('top'),
+ 'width':$('#toc-wrapper').css('width'),
+ 'height':$('#toc-wrapper').css('height'),
+ 'right':$('#toc-wrapper').css('right')};
+ IPython.notebook.set_dirty();}
+ // Ensure position is fixed (again)
+ $('#toc-wrapper').css('position', 'fixed');
+ },
+ });
+
+ $('#toc-wrapper').resizable({
+ resize : function(event,ui){
+ if (cfg.sideBar){
+ $('#notebook-container').css('margin-left',$('#toc-wrapper').width()+30)
+ $('#notebook-container').css('width',$('#notebook').width()-$('#toc-wrapper').width()-30)
+ }
+ else {
+ $('#toc').css('height', $('#toc-wrapper').height()-$('#toc-header').height());
+ }
+ },
+ start : function(event, ui) {
+ $(this).width($(this).width());
+ //$(this).css('position', 'fixed');
+ },
+ stop : function (event,ui){ // on save, store toc position
+ if(liveNotebook){
+ IPython.notebook.metadata['toc_position']={
+ 'left':$('#toc-wrapper').css('left'),
+ 'top':$('#toc-wrapper').css('top'),
+ 'height':$('#toc-wrapper').css('height'),
+ 'width':$('#toc-wrapper').css('width'),
+ 'right':$('#toc-wrapper').css('right')};
+ $('#toc').css('height', $('#toc-wrapper').height()-$('#toc-header').height())
+ IPython.notebook.set_dirty();
+ }
+ // Ensure position is fixed (again)
+ //$(this).css('position', 'fixed');
+ }
+ })
+
+
+ // restore toc position at load
+ if(liveNotebook){
+ if (IPython.notebook.metadata['toc_position'] !== undefined){
+ $('#toc-wrapper').css(IPython.notebook.metadata['toc_position']);
+ }
+ }
+ // Ensure position is fixed
+ $('#toc-wrapper').css('position', 'fixed');
+
+ // Restore toc display
+ if(liveNotebook){
+ if (IPython.notebook.metadata.toc !== undefined) {
+ if (IPython.notebook.metadata.toc['toc_section_display']!==undefined) {
+ $('#toc').css('display',IPython.notebook.metadata.toc['toc_section_display'])
+ $('#toc').css('height', $('#toc-wrapper').height()-$('#toc-header').height())
+ if (IPython.notebook.metadata.toc['toc_section_display']=='none'){
+ $('#toc-wrapper').addClass('closed');
+ $('#toc-wrapper').css({height: 40});
+ $('#toc-wrapper .hide-btn')
+ .text('[+]')
+ .attr('title', 'Show ToC');
+ }
+ }
+ if (IPython.notebook.metadata.toc['toc_window_display']!==undefined) {
+ console.log("******Restoring toc display");
+ $('#toc-wrapper').css('display',IPython.notebook.metadata.toc['toc_window_display'] ? 'block' : 'none');
+ //$('#toc').css('overflow','auto')
+ }
+ }
+ }
+
+ // if toc-wrapper is undefined (first run(?), then hide it)
+ if ($('#toc-wrapper').css('display')==undefined) $('#toc-wrapper').css('display',"none") //block
+ //};
+
+ $('#site').bind('siteHeight', function() {
+ if (cfg.sideBar) $('#toc-wrapper').css('height',$('#site').height());})
+
+ $('#site').trigger('siteHeight');
+
+ // Initial style
+ ///sideBar = cfg['sideBar']
+ if (cfg.sideBar) {
+ $('#toc-wrapper').addClass('sidebar-wrapper');
+ if (!liveNotebook) {
+ $('#toc-wrapper').css('width', '202px');
+ $('#notebook-container').css('margin-left', '212px');
+ $('#toc-wrapper').css('height', '96%');
+ $('#toc').css('height', $('#toc-wrapper').height() - $('#toc-header').height())
+ } else {
+ if (cfg.toc_window_display) {
+ setTimeout(function() {
+ $('#notebook-container').css('width', $('#notebook').width() - $('#toc-wrapper').width() - 30);
+ $('#notebook-container').css('margin-left', $('#toc-wrapper').width() + 30);
+ }, 500)
+ }
+ setTimeout(function() {
+ $('#toc-wrapper').css('height', $('#site').height());
+ $('#toc').css('height', $('#toc-wrapper').height() - $('#toc-header').height())
+ }, 500)
+ }
+ setTimeout(function() { $('#toc-wrapper').css('top', liveNotebook ? $('#header').height() : 0); }, 500) //wait a bit
+ $('#toc-wrapper').css('left', 0);
+
+ }
+
+ else {
+ toc_wrapper.addClass('float-wrapper');
+ }
+}
+
+//------------------------------------------------------------------
+ // TOC CELL -- if cfg.toc_cell=true, add and update a toc cell in the notebook.
+ // This cell, initially at the very beginning, can be moved.
+ // Its contents are automatically updated.
+ // Optionnaly, the sections in the toc can be numbered.
+
+
+ function look_for_cell_toc(callb){ // look for a possible toc cell
+ var cells = IPython.notebook.get_cells();
+ var lcells=cells.length;
+ for (var i = 0; i < lcells; i++) {
+ if (cells[i].metadata.toc=="true") {
+ cell_toc=cells[i];
+ toc_index=i;
+ //console.log("Found a cell_toc",i);
+ break;}
+ }
+ callb && callb(i);
+ }
+ // then process the toc cell:
+
+ function proces_cell_toc(cfg,st){
+ // look for a possible toc cell
+ var cells = IPython.notebook.get_cells();
+ var lcells=cells.length;
+ for (var i = 0; i < lcells; i++) {
+ if (cells[i].metadata.toc=="true") {
+ st.cell_toc=cells[i];
+ st.toc_index=i;
+ //console.log("Found a cell_toc",i);
+ break;}
+ }
+ //if toc_cell=true, we want a cell_toc.
+ // If it does not exist, create it at the beginning of the notebook
+ //if toc_cell=false, we do not want a cell-toc.
+ // If one exists, delete it
+ if(cfg.toc_cell) {
+ if (st.cell_toc == undefined) {
+ st.rendering_toc_cell = true;
+ //console.log("********* Toc undefined - Inserting toc_cell");
+ st.cell_toc = IPython.notebook.select(0).insert_cell_above("markdown");
+ st.cell_toc.metadata.toc="true";
+ }
+ }
+ else{
+ if (st.cell_toc !== undefined) IPython.notebook.delete_cell(st.toc_index);
+ st.rendering_toc_cell=false;
+ }
+ } //end function process_cell_toc --------------------------
+
+// Table of Contents =================================================================
+var table_of_contents = function (cfg,st) {
+
+ if(st.rendering_toc_cell) { // if toc_cell is rendering, do not call table_of_contents,
+ st.rendering_toc_cell=false; // otherwise it will loop
+ return}
+
+
+ var toc_wrapper = $("#toc-wrapper");
+ // var toc_index=0;
+ if (toc_wrapper.length === 0) {
+ create_toc_div(cfg,st);
+ }
+ var segments = [];
+ var ul = $("
").addClass("toc-item").attr('id','toc-level0');
+
+ // update toc element
+ $("#toc").empty().append(ul);
+
+
+ st.cell_toc = undefined;
+ // if cfg.toc_cell=true, add and update a toc cell in the notebook.
+
+ if(liveNotebook){
+ ///look_for_cell_toc(proces_cell_toc);
+ proces_cell_toc(cfg,st);
+ }
+ //proces_cell_toc();
+
+ var cell_toc_text = "# Table of Contents\n
";
+ var depth = 1; //var depth = ol_depth(ol);
+ var li= ul;//yes, initialize li with ul!
+ var all_headers= $("#notebook").find(":header");
+ var min_lvl=1, lbl_ary= [];
+ for(; min_lvl <= 6; min_lvl++){ if(all_headers.is('h'+min_lvl)){break;} }
+ for(var i= min_lvl; i <= 6; i++){ lbl_ary[i - min_lvl]= 0; }
+
+ //loop over all headers
+ all_headers.each(function (i, h) {
+ var level = parseInt(h.tagName.slice(1), 10) - min_lvl + 1;
+ // skip below threshold
+ if (level > cfg.threshold){ return; }
+ // skip headings with no ID to link to
+ if (!h.id){ return; }
+ // skip toc cell if present
+ if (h.id=="Table-of-Contents"){ return; }
+ //If h had already a number, remove it
+ $(h).find(".toc-item-num").remove();
+ var num_str= incr_lbl(lbl_ary,level-1).join('.');// numbered heading labels
+ var num_lbl= $("").addClass("toc-item-num")
+ .text(num_str).append(' ').append(' ');
+
+ // walk down levels
+ for(var elm=li; depth < level; depth++) {
+ var new_ul = $("
").addClass("toc-item");
+ elm.append(new_ul);
+ elm= ul= new_ul;
+ }
+ // walk up levels
+ for(; depth > level; depth--) {
+ // up twice: the enclosing and
it was inserted in
+ ul= ul.parent();
+ while(!ul.is('ul')){ ul= ul.parent(); }
+ }
+ // Change link id -- append current num_str so as to get a kind of unique anchor
+ // A drawback of this approach is that anchors are subject to change and thus external links can fail if toc changes
+ // Anyway, one can always add a in the heading and refer to that anchor, eg [link](#myanchor)
+ // This anchor is automatically removed when building toc links. The original id is also preserved and an anchor is created
+ // using it.
+ // Finally a heading line can be linked to by [link](#initialID), or [link](#initialID-num_str) or [link](#myanchor)
+ if (!$(h).attr("saveid")) {$(h).attr("saveid", h.id)} //save original id
+ h.id=$(h).attr("saveid")+'-'+num_str; // change the id to be "unique" and toc links to it
+ var saveid = $(h).attr('saveid')
+ //escape special chars: http://stackoverflow.com/questions/3115150/
+ var saveid_search=saveid.replace(/[-[\]{}():\/!;&@=$£%§<>%"'*+?.,~\\^$|#\s]/g, "\\$&");
+ if ($(h).find("a[name="+saveid_search+"]").length==0){ //add an anchor with original id (if it doesnt't already exists)
+ $(h).prepend($("").attr("name",saveid)); }
+
+
+ // Create toc entry, append
tag to the current . Prepend numbered-labels to headings.
+ li=$("").append( make_link( $(h), num_lbl));
+
+ ul.append(li);
+ $(h).prepend(num_lbl);
+
+
+ //toc_cell:
+ if(cfg.toc_cell) {
+ var tabs = function(level) {
+ var tabs = '';
+ for (var j = 0; j < level -1; j++) {
+ tabs += "\t";}
+ return tabs}
+
+ var leves='
'
+ var lnk=make_link_originalid($(h))
+ cell_toc_text += leves + $('
').append(lnk).html()+'
';
+ //workaround for https://github.com/jupyter/notebook/issues/699 as suggested by @jhamrick
+ lnk.on('click',function(){setTimeout(function(){$.ajax()}, 100) })
+ }
+ });
+
+
+
+ // update navigation menu
+ if (cfg.navigate_menu) {
+ var pop_nav = function() { //callback for create_nav_menu
+ //$('#Navigate_menu').empty().append($("").attr("id", "navigate_menu").addClass('toc').append(ul.clone().attr('id', 'navigate_menu-level0')))
+ $('#navigate_menu').empty().append($('#toc-level0').clone().attr('id', 'navigate_menu-level0'))
+ }
+ if ($('#Navigate_menu').length == 0) {
+ create_navigate_menu(pop_nav);
+ } else {
+ pop_nav()
+ }
+ } else { // If navigate_menu is false but the menu already exists, then remove it
+ if ($('#Navigate_menu').length > 0) $('#Navigate_sub').remove()
+ }
+
+
+
+ if(cfg.toc_cell) {
+ st.rendering_toc_cell = true;
+ //IPython.notebook.to_markdown(toc_index);
+ st.cell_toc.set_text(cell_toc_text);
+ st.cell_toc.render();
+ };
+
+ // Show section numbers if enabled
+ cfg.number_sections ? $('.toc-item-num').show() : $('.toc-item-num').hide()
+
+ $(window).resize(function(){
+ $('#toc').css({maxHeight: $(window).height() - 30});
+ $('#toc-wrapper').css({maxHeight: $(window).height() - 10});
+ });
+
+ $(window).trigger('resize');
+
+};
+
+ var toggle_toc = function (cfg,st) {
+ // toggle draw (first because of first-click behavior)
+ //$("#toc-wrapper").toggle({'complete':function(){
+ $("#toc-wrapper").toggle({
+ 'progress':function(){
+ if (cfg.sideBar==true) {
+ if ($('#toc-wrapper').css('display')!='block'){
+ $('#notebook-container').css('margin-left',st.nbcontainer_marginleft);
+ $('#notebook-container').css('width',st.nbcontainer_width);
+ }
+ else{
+ $('#notebook-container').css('margin-left',$('#toc-wrapper').width()+30)
+ $('#notebook-container').css('width',$('#notebook').width()-$('#toc-wrapper').width()-30)
+ }
+ }
+ },
+ 'complete': function(){
+ if(liveNotebook){
+ IPython.notebook.metadata.toc['toc_window_display']=$('#toc-wrapper').css('display')=='block';
+ IPython.notebook.set_dirty();
+ }
+ // recompute:
+ st.rendering_toc_cell = false;
+ table_of_contents(cfg,st);
+ }
+ });
+
+ };
+
+//var out=$.ajax({url:"/nbextensions/usability/toc2/toc2.js", async:false})
+//eval(out.responseText)
diff --git a/nbextensions/usability/toc2/toc2.yaml b/nbextensions/usability/toc2/toc2.yaml
index eb2bb1378..d3cce2cf0 100644
--- a/nbextensions/usability/toc2/toc2.yaml
+++ b/nbextensions/usability/toc2/toc2.yaml
@@ -1,28 +1,36 @@
Type: IPython Notebook Extension
Name: Table of Contents (2)
-Description: The ToC2 extension displays a floating (draggable) table of contents of the notebooks headers. Optionally, it also allows to automatically number all notebook's sections, and to add a table of Contents cell at the top of the notebook.
+Description: The toc2 extension enables to collect all running headers and display them in a floating window, as a sidebar or with a navigation menu. The extension is also draggable, resizable, collapsable, dockable and features automatic numerotation with unique links ids, and an optional toc cell.
Link: README.md
Icon: icon.png
Main: main.js
Compatibility: 4.x
Parameters:
-- name: toc_number_sections
+- name: number_sections
description: Automatically number notebook's sections
input_type: checkbox
default: true
-- name: toc_threshold
+- name: threshold
description: Maximum level of nested sections to display on the tables of contents
input_type: number
min: -1
step: 1
default: 4
- name: toc_cell
- description: Add a Table of Contents at the top of the notebook
+ description: Add a Table of Contents cell at the top of the notebook
input_type: checkbox
default: false
- name: toc_window_display
description: Display toc window at startup
input_type: checkbox
default: false
-
+- name: sideBar
+ description: Display Table of Contents as a sidebar (otherwise as a floating window)
+ input_type: checkbox
+ default: true
+- name: navigate_menu
+ description: Display Table of Contents as a navigation menu
+ input_type: checkbox
+ default: false
+
diff --git a/templates/toc.tpl b/templates/toc.tpl
new file mode 100644
index 000000000..59cd00866
--- /dev/null
+++ b/templates/toc.tpl
@@ -0,0 +1,43 @@
+{%- extends 'full.tpl' -%}
+
+
+{%- block header -%}
+{{ super() }}
+
+
+
+
+
+
+
+
+
+
+
+
+{%- endblock header -%}
diff --git a/templates/toc2.tpl b/templates/toc2.tpl
new file mode 100644
index 000000000..3bef7fd76
--- /dev/null
+++ b/templates/toc2.tpl
@@ -0,0 +1,44 @@
+{%- extends 'full.tpl' -%}
+
+
+{%- block header -%}
+{{ super() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+{%- endblock header -%}
diff --git a/templates/toc3.tpl b/templates/toc3.tpl
new file mode 100644
index 000000000..818859d9f
--- /dev/null
+++ b/templates/toc3.tpl
@@ -0,0 +1,44 @@
+{%- extends 'full.tpl' -%}
+
+
+{%- block header -%}
+{{ super() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+{%- endblock header -%}