{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Write notebook extensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note: This document is a draft."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO: this document doesn't (and won't) contain code... Thus it has no reason to be a notebook and should be converted to a regular RST document."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This document contains generic notes describing how to personalize and extend Jupyter notebooks.\n",
    "It doesn't contain instructions for writing/installing a specific extension.\n",
    "\n",
    "My wish-list/todolist of extensions is [there](https://github.com/jdhp-docs/notebooks/blob/master/notebook_snippets.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## How can the notebook be extended?\n",
    "\n",
    "### Extensions\n",
    "\n",
    "The Jupyter's frontend (*Notebook client*) and backend (*server application*) are both customizable.\n",
    "\n",
    "Their behavior can be extended by creating, respectively:\n",
    "\n",
    "* *nbextension*: a frontend extension written in Javascript, CSS, ...\n",
    "    * a Javascript [AMD module](https://en.wikipedia.org/wiki/Asynchronous_module_definition) that exports a function `load_ipython_extension`\n",
    "* *server extension*: a backend extension written in Python\n",
    "    * a Python module that implements `load_jupyter_server_extension`\n",
    "* *bundler extension*: a backend extension written in Python\n",
    "    * a Python module that implements `bundle`\n",
    "\n",
    "To display the list of installed extensions:\n",
    "\n",
    "``\n",
    "jupyter nbextension list\n",
    "jupyter serverextension list\n",
    "jupyter bundlerextension list\n",
    "``\n",
    "\n",
    "[Source](http://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.html#How-can-the-notebook-be-extended?)\n",
    "\n",
    "### Templates (does it only concern nbconvert ?)\n",
    "\n",
    "**TODO**: WHAT ABOUT:\n",
    "* templates (Jinja templates) (.tpl/.tplx) ?\n",
    "    * Does it only concern `nbconvert` ? \n",
    "    * See http://nbviewer.jupyter.org/github/jupyter/nbconvert/blob/master/docs/source/customizing.ipynb\n",
    "    * See http://jupyter-contrib-nbextensions.readthedocs.io/en/latest/exporting.html\n",
    "\n",
    "**TODO**: for nbconvert, see also:\n",
    "* http://nbconvert.readthedocs.io/en/latest/external_exporters.html\n",
    "* http://nbconvert.readthedocs.io/en/stable/customizing.html\n",
    "\n",
    "### Themes\n",
    "\n",
    "**TODO**: WHAT ABOUT:\n",
    "* themes ?\n",
    "    * It seems to be a simple customized CSS and JS files stored in:\n",
    "        * ~/.jupyter/custom/custom.css\n",
    "        * ~/.jupyter/custom/custom.js    **(TODO: <- test it)** see http://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/JavaScript%20Notebook%20Extensions.html#custom.js\n",
    "        * Procedure:\n",
    "            * Make the ~/.jupyter/custom/ directory if it doesn't exist then write your custom CSS in ~/.jupyter/custom/custom.css\n",
    "            * To see the effect of this files, you have to restart your Jupyter kernels ; you may have to clear your browser's cache too\n",
    "            * To \"uninstall\" a template, remove the ~/.jupyter/custom/custom.css file may not be enough as it is probably cached in the browser, thus you should empty the browser's cache too (see http://stackoverflow.com/questions/38944204/jupyter-custom-css-removal)\n",
    "    * http://stackoverflow.com/questions/32156248/how-do-i-set-custom-css-for-my-ipython-ihaskell-jupyter-notebook\n",
    "    * See e.g. https://github.com/powerpak/jupyter-dark-theme\n",
    "    * Does it affect document generated by `nbconvert` too ?\n",
    "    * See http://sherifsoliman.com/2016/01/11/theming-ipython-jupyter-notebook/\n",
    "\n",
    "### Configuration files\n",
    "\n",
    "**TODO** Frontend configuration file: `~/.jupyter/jupyter_notebook_config.py`\n",
    "\n",
    "This file is generated with the following command:\n",
    "\n",
    "```\n",
    "jupyter notebook --generate-config\n",
    "```\n",
    "\n",
    "See http://stackoverflow.com/questions/32071672/where-should-i-place-my-settings-and-profiles-for-use-with-ipython-jupyter-4-0\n",
    "\n",
    "It seems there are one configuration file for the frontend part (see the previous link) and another one for the backend part. **TODO: mention both...**\n",
    "\n",
    "**TODO** there is also a configuration file for nbconvert: ~/.jupyter/jupyter_nbconvert_config.py\n",
    "\n",
    "See https://jupyter.readthedocs.io/en/latest/migrating.html#finding-the-location-of-important-files"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Frontend extensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Embeded Javascript in cells"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note: This is not an actual extension, just an useful trick."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is the simplest solution as there is nothing to install or configure.\n",
    "HTML/Javascript code is directly written in documents cells.\n",
    "\n",
    "This is can be satisfactory for small \"one-time\" functionalities, but this is clearly not convenient in most cases.\n",
    "\n",
    "This hack has an advantage: the \"extension\" is contained within the document and follow it everywhere without additional installation on execution platforms."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "%%html\n",
    "<div id=\"toc\"></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "var toc = document.getElementById(\"toc\");\n",
       "toc.innerHTML = \"Table of contents:\";\n",
       "toc.innerHTML += \"<ol>\"\n",
       "\n",
       "var h2_list = document.getElementsByTagName(\"h2\");\n",
       "for (var i = 0; i < h2_list.length; i++) {\n",
       "    var h2 = h2_list[i];\n",
       "    var h2_str = h2.textContent.slice(0, -1);  // \"slice(0, -1)\" remove the last character \n",
       "    toc.innerHTML += \"<li><a href=\\\"#\" + h2_str.replace(/\\s+/g, '-') + \"\\\">\" + h2_str + \"</a></li>\";\n",
       "}\n",
       "\n",
       "toc.innerHTML += \"</ol>\""
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "var toc = document.getElementById(\"toc\");\n",
    "toc.innerHTML = \"Table of contents:\";\n",
    "toc.innerHTML += \"<ol>\"\n",
    "\n",
    "var h2_list = document.getElementsByTagName(\"h2\");\n",
    "for (var i = 0; i < h2_list.length; i++) {\n",
    "    var h2 = h2_list[i];\n",
    "    var h2_str = h2.textContent.slice(0, -1);  // \"slice(0, -1)\" remove the last character \n",
    "    toc.innerHTML += \"<li><a href=\\\"#\" + h2_str.replace(/\\s+/g, '-') + \"\\\">\" + h2_str + \"</a></li>\";\n",
    "}\n",
    "\n",
    "toc.innerHTML += \"</ol>\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configuring the notebook frontend"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note: This is not an actual extension but it can be useful.\n",
    "\n",
    "Notebook frontend and its underlying libraries can be configured through Javascript statements.\n",
    "Those statements can be ... persistent ...\n",
    "\n",
    "For instance, Jupyter use *Codemirror* (https://codemirror.net) to edit notebooks cells. This library can be configured ...\n",
    "\n",
    "Codemirror's options list is there: https://codemirror.net/doc/manual.html#commands.\n",
    "\n",
    "See http://jupyter-notebook.readthedocs.io/en/latest/frontend_config.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Nbextensions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lets write a tiny nbextension.\n",
    "\n",
    "Create a `hello` directory wherever you want in your file system and write the following file in `hello/main.js`:\n",
    "\n",
    "```js\n",
    "define([\n",
    "        'base/js/namespace',\n",
    "        'base/js/events'\n",
    "        ],\n",
    "        function(Jupyter, events) {\n",
    "            var load_ipython_extension = function () {\n",
    "                alert(\"Hello, world!\");\n",
    "            };\n",
    "\n",
    "            return {\n",
    "                load_ipython_extension : load_ipython_extension\n",
    "            };\n",
    "        });\n",
    "```\n",
    "\n",
    "Write the following file in `hello/hello.yaml`:\n",
    "\n",
    "```yaml\n",
    "Type: IPython Notebook Extension\n",
    "Name: Hello\n",
    "Link: README.md\n",
    "Description: This is an minimal nbextension example.\n",
    "Main: main.js\n",
    "Compatibility: 4.x\n",
    "```\n",
    "\n",
    "Then install it with the following command:\n",
    "\n",
    "```\n",
    "jupyter nbextension install hello --user\n",
    "```\n",
    "\n",
    "On my system, it copy files in `~/Library/Jupyter/nbextensions/`.\n",
    "\n",
    "To be usable, the extension have to be activated with the following command:\n",
    "\n",
    "```\n",
    "jupyter nbextension enable hello/main --user\n",
    "```\n",
    "\n",
    "On my system, it update the following file: `~/.jupyter/nbconfig/notebook.json`.\n",
    "\n",
    "Et voilà!\n",
    "Now you can open or reload a notebook.\n",
    "\n",
    "You can also check its installation with:\n",
    "\n",
    "```\n",
    "jupyter nbextension list --user\n",
    "```\n",
    "\n",
    "To remove this extension, first disable it:\n",
    "\n",
    "```\n",
    "jupyter nbextension disable hello/main --user\n",
    "```\n",
    "\n",
    "then uninstall it:\n",
    "\n",
    "```\n",
    "jupyter nbextension uninstall hello --user\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "References (FYI their examples doesn't seems to work):\n",
    "* http://jupyter-notebook.readthedocs.io/en/latest/extending/frontend_extensions.html\n",
    "* http://jupyter-contrib-nbextensions.readthedocs.io/en/latest/internals.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Packages\n",
    "\n",
    "> \"Since it is rare to have a server extension that does not have any frontend components (an nbextension), for  convenience and consistency, all these client and server extensions with their assets can be packaged and versioned together as a Python package with a few simple commands. This makes installing the package of extensions easier and less error-prone for the user.\"\n",
    "\n",
    "[Source](http://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.html#Why-create-a-Python-package-for-Jupyter-extensions?)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## TODO"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- First, get a clear view of all possible extension mechanisms in notebook (python extensions, embedded JS, html export, templates, magics, themes, ...) and make snippets for each"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Useful links"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* http://jupyter-notebook.readthedocs.io/en/latest/extending/\n",
    "* http://jupyter-notebook.readthedocs.io/en/latest/frontend_config.html\n",
    "* http://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.html\n",
    "* https://carreau.gitbooks.io/jupyter-book/content/notebook-extensions.html\n",
    "* http://blog.rtwilson.com/an-easy-way-to-install-jupyter-notebook-extensions/comment-page-1/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Extension examples"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* https://github.com/tkf/ipython-tempmagic : a simple (only 1 small python file), useful and easy to understand extension...\n",
    "* https://github.com/ipython-contrib/jupyter_contrib_nbextensions\n",
    "* https://github.com/ipython/ipython/wiki/Extensions-Index"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Zoom on a very useful extension example: TOC (table of contents)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* https://github.com/minrk/ipython_extensions\n",
    "* https://github.com/minrk/ipython_extensions/blob/master/nbextensions/toc.js\n",
    "* http://stackoverflow.com/questions/21188698/what-happend-to-the-toc-extension-for-ipython-notebook/33051798#33051798\n",
    "* http://stackoverflow.com/questions/21151450/how-can-i-add-a-table-of-contents-to-an-ipython-notebook"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [default]",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
