diff --git a/docs/Makefile b/docs/Makefile index 66550b6d2..2be882e1f 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -19,7 +19,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) sou # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext ipynb2rst help: @echo "Please use \`make ' where is one of" @@ -52,7 +52,7 @@ clean: rm -rf $(BUILDDIR)/* rm -rf source/config.rst -html: source/config.rst +html: source/config.rst ipynb2rst $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." @@ -61,6 +61,10 @@ source/config.rst: python3 autogen_config.py @echo "Created docs for config options" +ipynb2rst: + jupyter nbconvert --to rst source/examples/Notebook/*.ipynb --template=source/template --FilesWriter.build_directory=source/examples/Notebook + @echo "Converted notebooks to rst" + dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo diff --git a/examples/Notebook/Configuring the Notebook and Server.ipynb b/docs/source/examples/Notebook/Configuring the Notebook and Server.ipynb similarity index 100% rename from examples/Notebook/Configuring the Notebook and Server.ipynb rename to docs/source/examples/Notebook/Configuring the Notebook and Server.ipynb diff --git a/docs/source/examples/Notebook/Configuring the Notebook and Server.rst b/docs/source/examples/Notebook/Configuring the Notebook and Server.rst new file mode 100644 index 000000000..790a0faf7 --- /dev/null +++ b/docs/source/examples/Notebook/Configuring the Notebook and Server.rst @@ -0,0 +1,188 @@ + +`View the original notebook on nbviewer `__ + +Configuring the Notebook and Server +=================================== + +Configuring the Jupyter Notebook +-------------------------------- + +The notebook web server can also be configured using Jupyter profiles +and configuration files. The Notebook web server configuration options +are set in a file named ``jupyter_notebook_config.py`` in your Jupyter +directory, which itself is usually ``.jupyter`` in your home directory. + +The default version of ``jupyter_notebook_config.py`` lists all of the +options available along with documentation for each. Changes made to +that file will affect all notebook servers run under that profile. +Command line options always override those set in configuration files. + +You can create a new config: + +.. code:: python + + !jupyter notebook --generate-config + +More details about Jupyter configuration files and profiles can be found +`here `__. + +Securing the notebook server +---------------------------- + +The Jupyter Notebook allows arbitrary code execution on the computer +running it. Thus, the notebook web server should never be run on the +open internet without first securing it. By default, the notebook server +only listens on local network interface (``127.0.0.1``) There are two +steps required to secure the notebook server: + +1. Setting a password +2. Encrypt network traffic using SSL + +Setting a password +~~~~~~~~~~~~~~~~~~ + +You can protect your notebook server with a simple single password by +setting the ``NotebookApp.password`` configurable. You can prepare a +hashed password using the function ``IPython.lib.passwd``: + +.. code:: python + + from IPython.lib import passwd + password = passwd("secret") + password + +You can then add this to your ``jupyter_notebook_config.py``: + +.. code:: python + + # Password to use for web authentication + c = get_config() + c.NotebookApp.password = + u'sha1:6c2164fc2b22:ed55ecf07fc0f985ab46561483c0e888e8964ae6' + +Using SSL/HTTPS +~~~~~~~~~~~~~~~ + +When using a password, it is a good idea to also use SSL, so that your +password is not sent unencrypted by your browser to the web server. When +running the notebook on the public internet this is absolutely required. + +The first step is to generate an SSL certificate. A self-signed +certificate can be generated with ``openssl``. For example, the +following command will create a certificate valid for 365 days with both +the key and certificate data written to the same file: + +:: + + openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem + +In most cases, you should run this command in your profile directory, +which will make it easy to use the generated key and certificate. + +When you connect to a notebook server over HTTPS using a self-signed +certificate, your browser will warn you of a dangerous certificate +because it is self-signed. If you want to have a fully compliant +certificate that will not raise warnings, it is possible (but rather +involved) to obtain one, as explained in detail in `this +tutorial `__ + +When you enable SSL support, you will need to access the notebook server +over ``https://``, rather than plain ``http://``. The startup message +from the notebook server prints the correct URL, but it is easy to +overlook and think the server is for some reason non-responsive. + +Once you have generated the key and certificate, you can configure the +notebook server to use them, by adding the following to +``jupyter_notebook_config.py``: + +.. code:: python + + # The full path to an SSL/TLS certificate file. + c.NotebookApp.certfile = u'/Users/bgranger/.jupyter/mycert.crt' + + # The full path to a private key file for usage with SSL/TLS. + c.NotebookApp.keyfile = u'/Users/bgranger/.jupyter/mycert.key' + +Running a public notebook server +-------------------------------- + +.. raw:: html + +
+ +Don't run a public notebook server unless you first secure it with a +password and SSL/HTTPS as described above + +.. raw:: html + +
+ +By default the notebook server only listens on the +``localhost/127.0.0.1`` network interface. If you want to connect to the +notebook from another computers, or over the internet, you need to +configure the notebook server to listen on all network interfaces and +not open the browser. You will often also want to disable the automatic +launching of the web browser. + +This can be accomplished by passing a command line options. + +:: + + jupyter notebook --ip=* --no-browser + +You can also add the following to your ``jupyter_notebook_config.py`` +file: + +.. code:: python + + c.NotebookApp.ip = '*' + c.NotebookApp.open_browser = False + +Running with a different URL prefix +----------------------------------- + +The notebook dashboard typically lives at the URL +``http://localhost:8888/tree``. If you prefer that it lives, together +with the rest of the notebook web application, under a base URL prefix, +such as ``http://localhost:8888/ipython/tree``, you can do so by adding +the following lines to your ``jupyter_notebook_config.py`` file. + +.. code:: python + + c.NotebookApp.base_url = '/ipython/' + c.NotebookApp.webapp_settings = {'static_url_prefix':'/ipython/static/'} + +Using a different notebook store +-------------------------------- + +By default, the notebook server stores the notebook documents that it +saves as files in the working directory of the notebook server, also +known as the ``notebook_dir``. This logic is implemented in the +``FileNotebookManager`` class. However, the server can be configured to +use a different notebook manager class, which can store the notebooks in +a different format. + +The `bookstore `__ package +currently allows users to store notebooks on Rackspace CloudFiles or +OpenStack Swift based object stores. + +Writing a notebook manager is as simple as extending the base class +``NotebookManager``. The +`simple\_notebook\_manager `__ +provides a great example of an in memory notebook manager, created +solely for the purpose of illustrating the notebook manager API. + +Known issues +------------ + +When behind a proxy, especially if your system or browser is set to +autodetect the proxy, the notebook web application might fail to connect +to the server's websockets, and present you with a warning at startup. +In this case, you need to configure your system not to use the proxy for +the server's address. + +For example, in Firefox, go to the Preferences panel, Advanced section, +Network tab, click 'Settings...', and add the address of the notebook +server to the 'No proxy for' field. + +`View the original notebook on nbviewer `__ diff --git a/examples/Notebook/Connecting with the Qt Console.ipynb b/docs/source/examples/Notebook/Connecting with the Qt Console.ipynb similarity index 100% rename from examples/Notebook/Connecting with the Qt Console.ipynb rename to docs/source/examples/Notebook/Connecting with the Qt Console.ipynb diff --git a/docs/source/examples/Notebook/Connecting with the Qt Console.rst b/docs/source/examples/Notebook/Connecting with the Qt Console.rst new file mode 100644 index 000000000..6849733b2 --- /dev/null +++ b/docs/source/examples/Notebook/Connecting with the Qt Console.rst @@ -0,0 +1,67 @@ + +`View the original notebook on nbviewer `__ + +Connecting to an existing IPython kernel using the Qt Console +============================================================= + +The Frontend/Kernel Model +------------------------- + +The traditional IPython (``ipython``) consists of a single process that +combines a terminal based UI with the process that runs the users code. + +While this traditional application still exists, the modern Jupyter +consists of two processes: + +- Kernel: this is the process that runs the users code. +- Frontend: this is the process that provides the user interface where + the user types code and sees results. + +Jupyter currently has 3 frontends: + +- Terminal Console (``ipython console``) +- Qt Console (``ipython qtconsole``) +- Notebook (``ipython notebook``) + +The Kernel and Frontend communicate over a ZeroMQ/JSON based messaging +protocol, which allows multiple Frontends (even of different types) to +communicate with a single Kernel. This opens the door for all sorts of +interesting things, such as connecting a Console or Qt Console to a +Notebook's Kernel. For example, you may want to connect a Qt console to +your Notebook's Kernel and use it as a help browser, calling ``??`` on +objects in the Qt console (whose pager is more flexible than the one in +the notebook). + +This Notebook describes how you would connect another Frontend to a +Kernel that is associated with a Notebook. + +Manual connection +----------------- + +To connect another Frontend to a Kernel manually, you first need to find +out the connection information for the Kernel using the +``%connect_info`` magic: + +.. code:: python + + %connect_info + +You can see that this magic displays everything you need to connect to +this Notebook's Kernel. + +Automatic connection using a new Qt Console +------------------------------------------- + +You can also start a new Qt Console connected to your current Kernel by +using the ``%qtconsole`` magic. This will detect the necessary +connection information and start the Qt Console for you automatically. + +.. code:: python + + a = 10 + +.. code:: python + + %qtconsole + +`View the original notebook on nbviewer `__ diff --git a/examples/Notebook/Custom Keyboard Shortcuts.ipynb b/docs/source/examples/Notebook/Custom Keyboard Shortcuts.ipynb similarity index 100% rename from examples/Notebook/Custom Keyboard Shortcuts.ipynb rename to docs/source/examples/Notebook/Custom Keyboard Shortcuts.ipynb diff --git a/docs/source/examples/Notebook/Custom Keyboard Shortcuts.rst b/docs/source/examples/Notebook/Custom Keyboard Shortcuts.rst new file mode 100644 index 000000000..b916830b0 --- /dev/null +++ b/docs/source/examples/Notebook/Custom Keyboard Shortcuts.rst @@ -0,0 +1,71 @@ + +`View the original notebook on nbviewer `__ + +Keyboard Shortcut Customization +=============================== + +Starting with IPython 2.0 keyboard shortcuts in command and edit mode +are fully customizable. These customizations are made using the Jupyter +JavaScript API. Here is an example that makes the ``r`` key available +for running a cell: + +.. code:: python + + %%javascript + + Jupyter.keyboard_manager.command_shortcuts.add_shortcut('r', { + help : 'run cell', + help_index : 'zz', + handler : function (event) { + IPython.notebook.execute_cell(); + return false; + }} + ); + +"By default the keypress ``r``, while in command mode, changes the type +of the selected cell to ``raw``. This shortcut is overridden by the code +in the previous cell, and thus the action no longer be available via the +keypress ``r``." + +There are a couple of points to mention about this API: + +- The ``help_index`` field is used to sort the shortcuts in the + Keyboard Shortcuts help dialog. It defaults to ``zz``. +- When a handler returns ``false`` it indicates that the event should + stop propagating and the default action should not be performed. For + further details about the ``event`` object or event handling, see the + jQuery docs. +- If you don't need a ``help`` or ``help_index`` field, you can simply + pass a function as the second argument to ``add_shortcut``. + +.. code:: python + + %%javascript + + Jupyter.keyboard_manager.command_shortcuts.add_shortcut('r', function (event) { + IPython.notebook.execute_cell(); + return false; + }); + +Likewise, to remove a shortcut, use ``remove_shortcut``: + +.. code:: python + + %%javascript + + Jupyter.keyboard_manager.command_shortcuts.remove_shortcut('r'); + +If you want your keyboard shortcuts to be active for all of your +notebooks, put the above API calls into your ``custom.js`` file. + +Of course we provide name for majority of existing action so that you do +not have to re-write everything, here is for example how to bind ``r`` +back to it's initial behavior: + +.. code:: python + + %%javascript + + Jupyter.keyboard_manager.command_shortcuts.add_shortcut('r', 'ipython.change-selected-cell-to-raw-cell'); + +`View the original notebook on nbviewer `__ diff --git a/examples/Notebook/Index.ipynb b/docs/source/examples/Notebook/Examples and Tutorials Index.ipynb similarity index 79% rename from examples/Notebook/Index.ipynb rename to docs/source/examples/Notebook/Examples and Tutorials Index.ipynb index 96a2e283c..322fc121d 100644 --- a/examples/Notebook/Index.ipynb +++ b/docs/source/examples/Notebook/Examples and Tutorials Index.ipynb @@ -11,14 +11,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Notebook" + "# Examples and Tutorials" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The Jupyter Notebook is a web-based interactive computing system that enables users to author documents that include live code, narrative text, LaTeX equations, HTML, images and video. These documents contain a full record of a computation and its results and can be shared on email, [Dropbox](http://dropbox.com), version control systems (like git/[GitHub](http://github.com)) or [nbviewer.jupyter.org](http://nbviewer.jupyter.org)." + "This portion of the documentation was generated from notebook files. You can download the original interactive notebook files using the links at the tops and bottoms of the pages." ] }, { diff --git a/docs/source/examples/Notebook/Examples and Tutorials Index.rst b/docs/source/examples/Notebook/Examples and Tutorials Index.rst new file mode 100644 index 000000000..0486ec30f --- /dev/null +++ b/docs/source/examples/Notebook/Examples and Tutorials Index.rst @@ -0,0 +1,36 @@ + +`View the original notebook on nbviewer `__ + + + +Examples and Tutorials +====================== + +This portion of the documentation was generated from notebook files. You +can download the original interactive notebook files using the links at +the tops and bottoms of the pages. + +Tutorials +--------- + +- `What is the Jupyter + Notebook `__ +- `Notebook Basics `__ +- `Running Code `__ +- `Working With Markdown + Cells `__ +- `Configuring the Notebook and + Server `__ +- `Custom Keyboard Shortcuts `__ +- `JavaScript Notebook + Extensions `__ + +Examples +-------- + +- `Importing Notebooks `__ +- `Connecting with the Qt + Console `__ +- `Typesetting Equations `__ + +`View the original notebook on nbviewer `__ diff --git a/examples/Notebook/Importing Notebooks.ipynb b/docs/source/examples/Notebook/Importing Notebooks.ipynb similarity index 100% rename from examples/Notebook/Importing Notebooks.ipynb rename to docs/source/examples/Notebook/Importing Notebooks.ipynb diff --git a/docs/source/examples/Notebook/Importing Notebooks.rst b/docs/source/examples/Notebook/Importing Notebooks.rst new file mode 100644 index 000000000..23850d722 --- /dev/null +++ b/docs/source/examples/Notebook/Importing Notebooks.rst @@ -0,0 +1,286 @@ + +`View the original notebook on nbviewer `__ + +Importing Jupyter Notebooks as Modules +====================================== + +It is a common problem that people want to import code from Jupyter +Notebooks. This is made difficult by the fact that Notebooks are not +plain Python files, and thus cannot be imported by the regular Python +machinery. + +Fortunately, Python provides some fairly sophisticated +`hooks `__ into the import +machinery, so we can actually make Jupyter notebooks importable without +much difficulty, and only using public APIs. + +.. code:: python + + import io, os, sys, types + +.. code:: python + + from IPython import get_ipython + from IPython.nbformat import current + from IPython.core.interactiveshell import InteractiveShell + +Import hooks typically take the form of two objects: + +1. a Module **Loader**, which takes a module name (e.g. + ``'IPython.display'``), and returns a Module +2. a Module **Finder**, which figures out whether a module might exist, + and tells Python what **Loader** to use + +.. code:: python + + def find_notebook(fullname, path=None): + """find a notebook, given its fully qualified name and an optional path + + This turns "foo.bar" into "foo/bar.ipynb" + and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar + does not exist. + """ + name = fullname.rsplit('.', 1)[-1] + if not path: + path = [''] + for d in path: + nb_path = os.path.join(d, name + ".ipynb") + if os.path.isfile(nb_path): + return nb_path + # let import Notebook_Name find "Notebook Name.ipynb" + nb_path = nb_path.replace("_", " ") + if os.path.isfile(nb_path): + return nb_path + + +Notebook Loader +--------------- + +Here we have our Notebook Loader. It's actually quite simple - once we +figure out the filename of the module, all it does is: + +1. load the notebook document into memory +2. create an empty Module +3. execute every cell in the Module namespace + +Since IPython cells can have extended syntax, the IPython transform is +applied to turn each of these cells into their pure-Python counterparts +before executing them. If all of your notebook cells are pure-Python, +this step is unnecessary. + +.. code:: python + + class NotebookLoader(object): + """Module Loader for Jupyter Notebooks""" + def __init__(self, path=None): + self.shell = InteractiveShell.instance() + self.path = path + + def load_module(self, fullname): + """import a notebook as a module""" + path = find_notebook(fullname, self.path) + + print ("importing Jupyter notebook from %s" % path) + + # load the notebook object + with io.open(path, 'r', encoding='utf-8') as f: + nb = current.read(f, 'json') + + + # create the module and add it to sys.modules + # if name in sys.modules: + # return sys.modules[name] + mod = types.ModuleType(fullname) + mod.__file__ = path + mod.__loader__ = self + mod.__dict__['get_ipython'] = get_ipython + sys.modules[fullname] = mod + + # extra work to ensure that magics that would affect the user_ns + # actually affect the notebook module's ns + save_user_ns = self.shell.user_ns + self.shell.user_ns = mod.__dict__ + + try: + for cell in nb.worksheets[0].cells: + if cell.cell_type == 'code' and cell.language == 'python': + # transform the input to executable Python + code = self.shell.input_transformer_manager.transform_cell(cell.input) + # run the code in themodule + exec(code, mod.__dict__) + finally: + self.shell.user_ns = save_user_ns + return mod + + +The Module Finder +----------------- + +The finder is a simple object that tells you whether a name can be +imported, and returns the appropriate loader. All this one does is +check, when you do: + +.. code:: python + + import mynotebook + +it checks whether ``mynotebook.ipynb`` exists. If a notebook is found, +then it returns a NotebookLoader. + +Any extra logic is just for resolving paths within packages. + +.. code:: python + + class NotebookFinder(object): + """Module finder that locates Jupyter Notebooks""" + def __init__(self): + self.loaders = {} + + def find_module(self, fullname, path=None): + nb_path = find_notebook(fullname, path) + if not nb_path: + return + + key = path + if path: + # lists aren't hashable + key = os.path.sep.join(path) + + if key not in self.loaders: + self.loaders[key] = NotebookLoader(path) + return self.loaders[key] + + +Register the hook +----------------- + +Now we register the ``NotebookFinder`` with ``sys.meta_path`` + +.. code:: python + + sys.meta_path.append(NotebookFinder()) + +After this point, my notebooks should be importable. + +Let's look at what we have in the CWD: + +.. code:: python + + ls nbpackage + +So I should be able to ``import nbimp.mynotebook``. + +Aside: displaying notebooks +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here is some simple code to display the contents of a notebook with +syntax highlighting, etc. + +.. code:: python + + from pygments import highlight + from pygments.lexers import PythonLexer + from pygments.formatters import HtmlFormatter + + from IPython.display import display, HTML + + formatter = HtmlFormatter() + lexer = PythonLexer() + + # publish the CSS for pygments highlighting + display(HTML(""" + + """ % formatter.get_style_defs() + )) + +.. code:: python + + def show_notebook(fname): + """display a short summary of the cells of a notebook""" + with io.open(fname, 'r', encoding='utf-8') as f: + nb = current.read(f, 'json') + html = [] + for cell in nb.worksheets[0].cells: + html.append("

%s cell

" % cell.cell_type) + if cell.cell_type == 'code': + html.append(highlight(cell.input, lexer, formatter)) + else: + html.append("
%s
" % cell.source) + display(HTML('\n'.join(html))) + + show_notebook(os.path.join("nbpackage", "mynotebook.ipynb")) + +So my notebook has a heading cell and some code cells, one of which +contains some IPython syntax. + +Let's see what happens when we import it + +.. code:: python + + from nbpackage import mynotebook + +Hooray, it imported! Does it work? + +.. code:: python + + mynotebook.foo() + +Hooray again! + +Even the function that contains IPython syntax works: + +.. code:: python + + mynotebook.has_ip_syntax() + +Notebooks in packages +--------------------- + +We also have a notebook inside the ``nb`` package, so let's make sure +that works as well. + +.. code:: python + + ls nbpackage/nbs + +Note that the ``__init__.py`` is necessary for ``nb`` to be considered a +package, just like usual. + +.. code:: python + + show_notebook(os.path.join("nbpackage", "nbs", "other.ipynb")) + +.. code:: python + + from nbpackage.nbs import other + other.bar(5) + +So now we have importable notebooks, from both the local directory and +inside packages. + +I can even put a notebook inside IPython, to further demonstrate that +this is working properly: + +.. code:: python + + import shutil + from IPython.utils.path import get_ipython_package_dir + + utils = os.path.join(get_ipython_package_dir(), 'utils') + shutil.copy(os.path.join("nbpackage", "mynotebook.ipynb"), + os.path.join(utils, "inside_ipython.ipynb") + ) + +and import the notebook from ``IPython.utils`` + +.. code:: python + + from IPython.utils import inside_ipython + inside_ipython.whatsmyname() + +This approach can even import functions and classes that are defined in +a notebook using the ``%%cython`` magic. + +`View the original notebook on nbviewer `__ diff --git a/examples/Notebook/JavaScript Notebook Extensions.ipynb b/docs/source/examples/Notebook/JavaScript Notebook Extensions.ipynb similarity index 100% rename from examples/Notebook/JavaScript Notebook Extensions.ipynb rename to docs/source/examples/Notebook/JavaScript Notebook Extensions.ipynb diff --git a/docs/source/examples/Notebook/JavaScript Notebook Extensions.rst b/docs/source/examples/Notebook/JavaScript Notebook Extensions.rst new file mode 100644 index 000000000..255bae2ac --- /dev/null +++ b/docs/source/examples/Notebook/JavaScript Notebook Extensions.rst @@ -0,0 +1,389 @@ + +`View the original notebook on nbviewer `__ + +Embrasing web standards +======================= + +One of the main reason that allowed us to developp the current notebook +web application was to embrase the web technology. + +By beeing a pure web application using HTML, Javascript and CSS, the +Notebook can get all the web technology improvement for free. Thus, as +browsers support for different media extend, The notebook web app should +be able to be compatible without modification. + +This is also true with performance of the User Interface as the speed of +javascript VM increase. + +The other advantage of using only web technology is that the code of the +interface is fully accessible to the end user, and modifiable live. Even +if this task is not always easy, we strive to keep our code as +accessible and reusable as possible. This should allow with minimum +effort to develop small extensions that customize the behavior of the +web interface. + +Tempering with Notebook app +--------------------------- + +The first tool that is availlable to you and that you shoudl be aware of +are browser "developpers tool". The exact naming can change across +browser, and might require the installation of extensions. But basically +they can allow you to inspect/modify the DOM, and interact with the +javascript code that run the frontend. + +- In Chrome and safari Developper tools are in the menu [Put mmenu name + in english here] +- In firefox you might need to install + `Firebug `__ +- others ? + +Those will be your best friends to debug and try different approach for +your extensions. + +Injecting JS +~~~~~~~~~~~~ + +using magics +^^^^^^^^^^^^ + +Above tools can be tedious to edit long javascipt files. Hopefully we +provide the ``%%javascript`` magic. This allows you to quickly inject +javascript into the notebook. Still the javascript injected this way +will not survive reloading. Hence it is a good tool for testing an +refinig a script. + +You might see here and there people modifying css and injecting js into +notebook by reading file and publishing them into the notebook. Not only +this often break the flow of the notebook and make the re-execution of +the notebook broken, but it also mean that you need to execute those +cells on all the notebook every time you need to update the code. + +This can still be usefull in some cases, like the ``%autosave`` magic +that allows to control the time between each save. But this can be +replaced by a Javascript dropdown menu to select save interval. + +.. code:: python + + ## you can inspect the autosave code to see what it does. + %autosave?? + +custom.js +^^^^^^^^^ + +To inject Javascript we provide an entry point: ``custom.js`` that allow +the user to execute and load other resources into the notebook. +Javascript code in ``custom.js`` will be executed when the notebook app +start and can then be used to customise almost anything in the UI and in +the behavior of the notebook. + +``custom.js`` can be found in the Jupyter dir. You can share your +custom.js with others. + +Back to theory +'''''''''''''' + +.. code:: python + + import os.path + profile_dir = '~/.jupyter' + profile_dir = os.path.expanduser(profile_dir) + profile_dir + +and custom js is in + +.. code:: python + + import os.path + custom_js_path = os.path.join(profile_dir,'custom','custom.js') + +.. code:: python + + # my custom js + if os.path.isfile(custom_js_path): + with open(custom_js_path) as f: + for l in f: + print(l) + else: + print("You don't have a custom.js file") + +Note that ``custom.js`` is ment to be modified by user, when writing a +script, you can define it in a separate file and add a line of +configuration into ``custom.js`` that will fetch and execute the file. + +**Warning** : even if modification of ``custom.js`` take effect +immediately after browser refresh (except if browser cache is +aggressive), *creating* a file in ``static/`` directory need a **server +restart**. + +Exercise : +---------- + +- Create a ``custom.js`` in the right location with the following + content: + + .. code:: javascript + + alert("hello world from custom.js") + +- Restart your server and open any notebook. +- Be greeted by custom.js + +Have a look at `default +custom.js `__, +to see it's content and some more explanation. + +For the quick ones : +^^^^^^^^^^^^^^^^^^^^ + +We've seen above that you can change the autosave rate by using a magic. +This is typically something I don't want to type everytime, and that I +don't like to embed into my workwlow and documents. (reader don't care +what my autosave time is), let's build an extension that allow to do it. + +Create a dropdow elemement in the toolbar (DOM +``Jupyter.toolbar.element``), you will need + +- ``IPython.notebook.set_autosave_interval(miliseconds)`` +- know that 1min = 60 sec, and 1 sec = 1000 ms + +.. code:: javascript + + + var label = jQuery('