diff --git a/README.md b/README.md index 28c7f5f..9ad332a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ Documentation ============= In the 4.x Jupyter repository, all extensions that are maintained and active have a markdown readme file for -documentation and a yaml file to allow them being configured using the 'nbextensions' server extension. +documentation and a yaml file to allow them being configured using the +[`jupyter_nbextensions_configurator`](https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator) +server extension.  diff --git a/configure_nbextensions.py b/configure_nbextensions.py index c6c8584..d386764 100644 --- a/configure_nbextensions.py +++ b/configure_nbextensions.py @@ -4,6 +4,7 @@ from __future__ import print_function from jupyter_core.paths import jupyter_config_dir, jupyter_data_dir from notebook import version_info +from jupyter_nbextensions_configurator.application import main as jnc_app_main from traitlets.config.loader import Config, JSONFileConfigLoader import os import sys @@ -153,12 +154,8 @@ update_config(py_config) json_file = 'jupyter_notebook_config.json' config = load_json_config(json_file) -# Add server extension of /nbextension/ configuration tool and template path +# Add template path newconfig = Config() -if version_info[1] > 1: - newconfig.NotebookApp.nbserver_extensions = {"nbextensions": True} -else: - newconfig.NotebookApp.server_extensions = ["nbextensions"] newconfig.NotebookApp.extra_template_paths = [os.path.join(jupyter_data_dir(),'templates') ] config.merge(newconfig) config.version = 1 @@ -167,3 +164,6 @@ save_json_config(json_file, config) # Update notebook PY configuration py_config = os.path.join(jupyter_config_dir(), 'jupyter_notebook_config.py') update_config(py_config) + +# Configure the jupyter_nbextensions_configurator serverextension +jnc_app_main(['enable', '--user', '--debug']) diff --git a/extensions/nbextensions.py b/extensions/nbextensions.py deleted file mode 100644 index 6564635..0000000 --- a/extensions/nbextensions.py +++ /dev/null @@ -1,189 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) IPython-Contrib Team. - -"""Notebook Server Extension to activate, deactivate and configure javascript -notebook extensions""" - -from __future__ import unicode_literals - -import json -import os.path -import posixpath -import re -import subprocess -import sys - -import yaml -from notebook.base.handlers import IPythonHandler -from notebook.notebookapp import NotebookApp -from notebook.utils import url_path_join as ujoin -from notebook.utils import path2url -from tornado import web -from traitlets.config.configurable import MultipleInstanceError -from yaml.scanner import ScannerError - -# attempt to use LibYaml if available -try: - from yaml import CSafeLoader as SafeLoader -except ImportError: - from yaml import SafeLoader - -absolute_url_re = re.compile(r'^(f|ht)tps?://') - - -def get_nbextensions_path(): - """ - Return the nbextensions search path - - gets the search path for - - the current NotebookApp instance - or, if we can't instantiate one - - the default config used, found by spawning a subprocess - """ - # Attempt to get the path for the currently-running config, or the default - # if one isn't running. - # If there's already a non-NotebookApp traitlets app running, e.g. when - # we're inside an IPython kernel there's a KernelApp instantiated, then - # attempting to get a NotebookApp instance will raise a - # MultipleInstanceError. - try: - return NotebookApp.instance().nbextensions_path - except MultipleInstanceError: - # So, we spawn a new python process to get paths for the default config - cmd = "from {0} import {1}; [print(p) for p in {1}()]".format( - get_nbextensions_path.__module__, get_nbextensions_path.__name__) - - return subprocess.check_output([ - sys.executable, '-c', cmd - ]).decode(sys.stdout.encoding).split('\n') - - -def get_configurable_nbextensions( - nbextension_dirs=None, exclude_dirs=['mathjax'], log=None): - """Build a list of configurable nbextensions based on YAML descriptor files - - descriptor files must: - - be located under one of nbextension_dirs - - have the extension '.yaml' - - containing (at minimum) the following keys: - - Type: must be 'IPython Notebook Extension' or - 'Jupyter Notebook Extension' - - Main: url of the nbextension's main javascript file, relative to yaml - """ - - if nbextension_dirs is None: - nbextension_dirs = get_nbextensions_path() - - extension_list = [] - required_keys = {'Type', 'Main'} - valid_types = {'IPython Notebook Extension', 'Jupyter Notebook Extension'} - do_log = (log is not None) - # Traverse through nbextension subdirectories to find all yaml files - for root_nbext_dir in nbextension_dirs: - if do_log: - log.debug( - 'Looking for nbextension yaml descriptor files in {}'.format( - root_nbext_dir)) - for direct, dirs, files in os.walk(root_nbext_dir, followlinks=True): - # filter to exclude directories - dirs[:] = [d for d in dirs if d not in exclude_dirs] - for filename in files: - if not filename.endswith('.yaml'): - continue - yaml_path = os.path.join(direct, filename) - yaml_relpath = os.path.relpath(yaml_path, root_nbext_dir) - with open(yaml_path, 'r') as stream: - try: - extension = yaml.load(stream, Loader=SafeLoader) - except ScannerError: - if do_log: - log.warning( - 'Failed to load yaml file {}'.format( - yaml_relpath)) - continue - if not isinstance(extension, dict): - continue - if any(key not in extension for key in required_keys): - continue - if extension['Type'].strip() not in valid_types: - continue - extension.setdefault('Compatibility', '?.x') - extension.setdefault('Section', 'notebook') - - # generate relative URLs within the nbextensions namespace, - # from urls relative to the yaml file - yaml_dir_url = path2url(os.path.dirname(yaml_relpath)) - key_map = [ - ('Link', 'readme'), - ('Icon', 'icon'), - ('Main', 'require'), - ] - for from_key, to_key in key_map: - # str needed in python 3, otherwise it ends up bytes - from_val = str(extension.get(from_key, '')) - if not from_val: - continue - if absolute_url_re.match(from_val): - extension[to_key] = from_val - else: - extension[to_key] = posixpath.normpath( - ujoin(yaml_dir_url, from_val)) - # strip .js extension in require path - extension['require'] = os.path.splitext( - extension['require'])[0] - - if do_log: - log.debug( - 'Found nbextension {!r} in {}'.format( - extension.setdefault('Name', extension['require']), - yaml_relpath, - ) - ) - - extension_list.append(extension) - return extension_list - - -class NBExtensionHandler(IPythonHandler): - """Render the notebook extension configuration interface.""" - - @web.authenticated - def get(self): - extension_list = get_configurable_nbextensions(log=self.log) - # dump to JSON, replacing any single quotes with HTML representation - extension_list_json = json.dumps(extension_list).replace("'", "'") - - self.write(self.render_template( - 'nbextensions.html', - base_url=self.base_url, - extension_list=extension_list_json, - page_title="Notebook Extension Configuration" - )) - - -class RenderExtensionHandler(IPythonHandler): - """Render given markdown file""" - - @web.authenticated - def get(self, path): - if not path.endswith('.md'): - # for all non-markdown items, we redirect to the actual file - return self.redirect(self.base_url + path) - self.write(self.render_template( - 'rendermd.html', - base_url=self.base_url, - md_url=path, - page_title=path, - )) - - -def load_jupyter_server_extension(nbapp): - webapp = nbapp.web_app - base_url = webapp.settings['base_url'] - - webapp.add_handlers(".*$", [ - (ujoin(base_url, r"/nbextensions"), NBExtensionHandler), - (ujoin(base_url, r"/nbextensions/"), NBExtensionHandler), - (ujoin(base_url, r"/nbextensions/config/rendermd/(.*)"), - RenderExtensionHandler), - ]) diff --git a/nbextensions/config/config_menu/config_menu.yaml b/nbextensions/config/config_menu/config_menu.yaml deleted file mode 100644 index 573abf6..0000000 --- a/nbextensions/config/config_menu/config_menu.yaml +++ /dev/null @@ -1,6 +0,0 @@ -Type: IPython Notebook Extension -Name: NbExtensions menu item -Description: Add an edit-menu item to open the NbExtensions config page -Main: main.js -Compatibility: 4.x -Icon: icon.png diff --git a/nbextensions/config/config_menu/icon.png b/nbextensions/config/config_menu/icon.png deleted file mode 100644 index 5b4a3f8..0000000 Binary files a/nbextensions/config/config_menu/icon.png and /dev/null differ diff --git a/nbextensions/config/config_menu/main.js b/nbextensions/config/config_menu/main.js deleted file mode 100644 index 6f44f3c..0000000 --- a/nbextensions/config/config_menu/main.js +++ /dev/null @@ -1,29 +0,0 @@ -// extension by jcb91 -// Tiny extension to add an edit-menu item to open the NbExtensions config page - -define(['jquery', 'base/js/namespace'], function ($, Jupyter) { - "use strict"; - - var load_ipython_extension = function () { - var menu_item = $('
').append( - $('', { - 'target' : '_blank', - 'title' : 'Opens in a new window', - 'href' : Jupyter.notebook.base_url + 'nbextensions/', - }) - .append(' ') - .append($('', {'class' : 'fa fa-cogs menu-icon pull-right'})) - .append($('').html('nbextensions config')) - ); - - var edit_menu = $('#edit_menu'); - edit_menu.append($('').addClass('divider')); - edit_menu.append(menu_item); - }; - - // export the extension so it can be loaded correctly - var extension = { - load_ipython_extension : load_ipython_extension - }; - return extension; -}); diff --git a/nbextensions/config/icon.png b/nbextensions/config/icon.png deleted file mode 100644 index 87b7d17..0000000 Binary files a/nbextensions/config/icon.png and /dev/null differ diff --git a/nbextensions/config/kse_components.js b/nbextensions/config/kse_components.js deleted file mode 100644 index 7ecaed7..0000000 --- a/nbextensions/config/kse_components.js +++ /dev/null @@ -1,364 +0,0 @@ -define([ - 'bootstrap', // for modals - 'jquery', - 'base/js/dialog', - 'base/js/utils', - 'base/js/keyboard', - 'notebook/js/quickhelp', - './quickhelp_shim' -], function( - bs, - $, - dialog, - utils, - keyboard, - quickhelp, - quickhelp_shim -){ - "use strict"; - - function only_modifier_event (event) { - // adapted from base/js/keyboard - /** - * Return `true` if the event only contains modifiers keys, false - * otherwise - */ - var key = keyboard.inv_keycodes[event.which]; - return ((event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) && - (key === 'alt'|| key === 'ctrl'|| key === 'meta'|| key === 'shift')); - } - - function editor_build () { - var editor = $('#kse-editor'); - if (editor.length > 0) { - return editor; - } - - editor = $('') - .addClass('kse-editor') - .attr('id', 'kse-editor') - .data({ - 'kse_sequence': [], - 'kse_info': {}, - 'kse_mode': 'command', - 'kse_undefined_key': false - }); - - var form = $('') - .addClass('form') - .appendTo(editor); - - $('') - .addClass('form-group') - .appendTo(form); - - var form_group = $('') - .addClass('form-group has-feedback') - .appendTo(form); - - var input_group = $('') - .addClass('input-group') - .addClass('kse-input-group') - .appendTo(form_group); - - // reset button - var btn = $('') - .addClass('btn btn-default') - .addClass('kse-input-group-reset') - .attr({ - 'title': 'Restart', - 'type': 'button' - }) - .append( - $('') - .addClass('fa fa-repeat') - ) - .on('click', function () { - editor.data({ - 'kse_sequence': [], - 'kse_undefined_key': false - }); - editor_update_input_group(editor); - $(this).blur(); - textcontrol.focus(); - }); - $('') - .addClass('input-group-btn') - .append(btn) - .appendTo(input_group); - - // pretty-displayed shortcut - $('') - .addClass('input-group-addon') - .addClass('kse-input-group-pretty') - .addClass('kse-editor-to') - .appendTo(input_group); - - var textcontrol = $('') - .addClass('form-control') - .addClass('kse-input-group-input') - .attr({ - 'type': 'text', - 'placeholder': 'click here to edit the shortcut' - }) - .on('keydown', editor_handle_shortcut_keydown) - .on('focus', function (evt) { - $(this).attr('placeholder', 'press keys to add to the shortcut'); - }) - .on('blur', function (evt) { - $(this).attr('placeholder', 'click here to edit the shortcut'); - }) - .appendTo(input_group); - - // feedback icon - var form_fdbck = $('') - .addClass('fa fa-lg'); - $('') - .addClass('form-control-feedback') - .append(form_fdbck) - .appendTo(form_group); - - // help for input group - $('') - .addClass('help-block') - .appendTo(form_group); - - return editor; - } - - function editor_update_input_group (editor, seq) { - seq = seq || editor.data('kse_sequence'); - var shortcut = seq.join(','); - var mode = editor.data('kse_mode'); - var have_seq = seq.length > 0; - var valid = have_seq; - - // empty help block - var feedback = editor.find('.form-group.has-feedback:first'); - var help_block = feedback.find('.help-block'); - help_block.empty(); - - var ii; - var has_comma = false; - for (ii = 0; !has_comma && (ii < seq.length); ii++) { - has_comma = seq[ii].indexOf(',') >= 0; - } - - if (has_comma) { - valid = false; - // use HTML Unicode escape for a comma, to get it to look right in the pretty version - shortcut = $.map(seq, function (elem, idx) { - return elem.replace(',', ','); - }).join(','); - - $('') - .html( - 'Unfortunately, Jupyter\'s handling of shortcuts containing ' + - 'commas (,) is fundamentally flawed, ' + - 'as the comma is used as the key-separator character ☹. ' + - 'Please try something else for your rebind!' - ) - .appendTo(help_block); - } - else if (have_seq) { - var conflicts = {}; - var tree; - - // get existing shortcuts - if (Jupyter.keyboard_manager !== undefined) { - var startkey = seq.slice(0, 1)[0]; - if (mode === 'command') { - tree = Jupyter.keyboard_manager.command_shortcuts.get_shortcut(startkey); - } - else { - tree = Jupyter.keyboard_manager.edit_shortcuts.get_shortcut(startkey); - // deal with codemirror shortcuts specially, since they're not included in kbm - for (var jj = 0; jj < quickhelp.cm_shortcuts.length; jj++) { - var cm_shrt = quickhelp.cm_shortcuts[jj]; - if (keyboard.normalize_shortcut(cm_shrt.shortcut) === startkey) { - tree = cm_shrt.help; - break; - } - } - } - } - - // check for conflicting shortcuts. - // Start at 1 because we got tree from startkey - for (ii = 1; (ii < seq.length) && (tree !== undefined); ii++) { - // check for exsiting definitions at current specificity - if (typeof(tree) === 'string') { - valid = false; - conflicts[seq.slice(0, ii).join(',')] = tree; - break; - } - tree = tree[seq[ii]]; - } - - // check whether any more-specific shortcuts were defined - if ((ii === seq.length) && (tree !== undefined)) { - valid = false; - var flatten_conflict_tree = function flatten_conflict_tree (obj, key) { - if (typeof(obj) === 'string') { - conflicts[key] = obj; - } - else for (var subkey in obj) { - if (obj.hasOwnProperty(subkey)) { - flatten_conflict_tree(obj[key], [key, subkey].join(',')); - } - } - }; - flatten_conflict_tree(tree, seq.join(',')); - } - - if (!valid) { - var plural = Object.keys(conflicts).length != 1; - $('') - .append(quickhelp.humanize_sequence(seq.join(','))) - .append( - ' conflicts with the' + (plural ? ' following' : '') + - ' existing shortcut' + (plural ? 's' : '') + ':' - ) - .appendTo(help_block); - - for (var conflicting_shortcut in conflicts) { - if (conflicts.hasOwnProperty(conflicting_shortcut)) { - $('') - .append(quickhelp.humanize_sequence(conflicting_shortcut)) - .append($('').text(conflicts[conflicting_shortcut]))
- .appendTo(help_block);
- }
- }
- }
- }
-
- if (editor.data('kse_undefined_key')) {
- var warning = $('')
- .addClass('form-group has-feedback has-warning kse-undefined')
- .append(
- $('')
- .addClass('help-block')
- .append(
- $('').text('Unrecognised key! (code ' + editor.data('kse_undefined_key' ) + ')')
- )
- );
-
- var existing = editor.find('.kse-undefined');
- if (existing.length > 0) {
- existing.replaceWith(warning);
- }
- else {
- warning.insertAfter(feedback);
- }
- setTimeout(function () {
- warning.remove();
- }, 2000);
- }
-
- // disable reset button if no sequence
- editor.find('.kse-input-group-reset')
- .toggleClass('disabled', !have_seq);
-
- editor.find('.kse-input-group-pretty')
- .html(shortcut ? quickhelp.humanize_sequence(shortcut) : '<new shortcut>');
-
- feedback
- .toggleClass('has-error', !valid && have_seq)
- .toggleClass('has-success', valid && have_seq)
- .find('.form-control-feedback .fa')
- .toggleClass('fa-remove', !valid && have_seq)
- .toggleClass('fa-check', valid && have_seq);
- }
-
- function editor_handle_shortcut_keydown (evt) {
- var elem = $(evt.delegateTarget);
- if (!only_modifier_event(evt)) {
- var shortcut = keyboard.normalize_shortcut(keyboard.event_to_shortcut(evt));
- var editor = elem.closest('#kse-editor');
- var seq = editor.data('kse_sequence');
- var has_undefined_key = (shortcut.toLowerCase().indexOf('undefined') !== -1);
- editor.data('kse_undefined_key', has_undefined_key);
- if (has_undefined_key) {
- // deal with things like ~ appearing on apple alt-n, or ¨ on alt-u
- editor.find('.kse-input-group-input').val('');
- editor.data('kse_undefined_key', evt.which || true);
- }
- else {
- seq.push(shortcut);
- }
- editor_update_input_group(editor, seq);
- }
- }
-
- function modal_build (editor, modal_options) {
- var modal = $('#kse-editor-modal');
- if (modal.length > 0) {
- return modal;
- }
-
- var default_modal_options = {
- 'destroy': false,
- 'show': false,
- 'title': 'Edit keyboard shortcut',
- 'body': editor,
- 'buttons': {
- 'OK': {'class': 'btn-primary'},
- 'Cancel': {}
- },
- 'open': function (evt) {
- $(this).find('.kse-input-group-input').focus();
- }
- };
- if (Jupyter.notebook !== undefined) {
- default_modal_options.notebook = Jupyter.notebook;
- }
- if (Jupyter.keyboard_manager !== undefined) {
- default_modal_options.keyboard_manager= Jupyter.keyboard_manager;
- }
- modal_options = $.extend({}, default_modal_options, modal_options);
-
- modal = dialog.modal(modal_options);
-
- modal
- .addClass('modal_stretch')
- .attr('id', 'kse-editor-modal');
-
- // Add a data-target attribute to ensure buttons only target the editor modal
- modal.find('.close,.modal-footer button')
- .attr('data-target', '#kse-editor-modal');
-
- return modal;
- }
-
- /**
- * Pass it an option dictionary with any of the bootstrap or base/js/dialog
- * modal options, plus the following optional properties:
- * - description: html for the form group preceding the editor group,
- * useful as a description
- */
- function KSE_modal (modal_options) {
- var editor = editor_build();
- editor.data({'kse_sequence': [], 'kse_undefined_key': false});
- editor_update_input_group(editor);
- var modal = modal_build(editor, modal_options);
-
- editor.on('keydown', '.kse-input-group-input', function (evt) {
- event.preventDefault();
- event.stopPropagation();
- return false;
- });
-
- if (modal_options.description) {
- modal.find('.modal-body .form-group:first').html(modal_options.description);
- }
-
- return modal;
- }
-
- return {
- editor_build : editor_build,
- editor_update_input_group: editor_update_input_group,
- modal_build : modal_build,
- KSE_modal : KSE_modal
- };
-});
diff --git a/nbextensions/config/main.css b/nbextensions/config/main.css
deleted file mode 100644
index d56bcda..0000000
--- a/nbextensions/config/main.css
+++ /dev/null
@@ -1,157 +0,0 @@
-/* nbext-page-title-wrap and nbext-page-title
- are styled to match the notebook save widget */
-.nbext-page-title-wrap {
- margin-top: 6px;
-}
-
-.nbext-page-title {
- height: 1em;
- line-height: 1em;
- padding: 3px;
- margin-left: 16px;
- border: none;
- font-size: 146.5%;
- border-radius: 2px;
-}
-
-.nbext-showhide-incompat {
- padding-bottom: 0.5em;
- /* the selector is hidden by default, revealed by js if any incompatible
- extensions are found */
- display: none;
-}
-
-/* styles for the extension-selector nav links */
-
-.nbext-selector {
- border-bottom: 1px solid #888;
- padding-bottom: 1em;
-}
-
-.nbext-selector > nav > .nav > li > a {
- padding: 2px 6px;
-}
-
-.nbext-selector > nav > .nav > li > a > .nbext-active-toggle {
- padding-right: 0.5em;
- display: inline-block;
-}
-
-.nbext-selector > nav > .nav > li > a > .nbext-active-toggle:before {
- /* fa-square-o */
- content: "\f096";
-}
-
-.nbext-selector > nav > .nav > li > a > .nbext-activated:before {
- /* fa-check-square-o */
- content: "\f046";
-}
-
-.nbext-selector > nav > .nav > li.disabled > a > .nbext-active-toggle {
- width: auto;
-}
-
-.nbext-selector > nav > .nav > li.disabled > a > .nbext-active-toggle:before {
- /* fa-ban */
- content: "\f05e (incompatible)";
-}
-
-.nbext-selector > nav > .nav > li.disabled > a > .nbext-activated:before {
- /* fa-exclamation-circle */
- content: "\f06a (incompatible, enabled)";
-}
-
-/* bugfix for using .container with padding: we need 15px extra to width. */
-@media (min-width: 768px) and (max-width: 783px) {
- .container {
- width: auto;
- }
-}
-/* styles for the readme */
-
-.nbext-readme > .nbext-readme-contents {
- /* padding
- top 0 to ensure box shadow of child is hidden (with overflow-y: hidden)
- side & bottom sufficient to show box shadow of child */
- padding: 0 12px 12px;
- overflow-y: hidden;
-}
-
-.nbext-readme > h3 {
- padding: 0 12px; /* to match nbext-readme-contents */
-}
-
-.nbext-readme > .nbext-readme-contents:not(:empty) {
- margin-top: 0.5em;
- border-top: 1px solid #ccc;
-}
-
-.nbext-readme > .nbext-readme-contents > .rendered_html {
- padding: 0.5em 1em;
- /* z-index & margin-top to clip box shadow behind parent */
- margin-top: 0;
- z-index: -10;
- -webkit-box-shadow: 0 0 12px 1px rgba(87, 87, 87, 0.2);
- -moz-box-shadow: 0 0 12px 1px rgba(87, 87, 87, 0.2);
- box-shadow: 0 0 12px 1px rgba(87, 87, 87, 0.2);
-}
-
-/* styles for elements in an extension's UI */
-
-.nbext-activate-btns .btn {
- border-radius: 4px;
-}
-
-.nbext-activate-btns .btn[disabled] {
- color: #cccccc;
-}
-
-.nbext-icon {
- margin-left: 8px;
- text-align: center;
-}
-
-.nbext-icon img {
- max-height: 120px;
- max-width: 100%;
-}
-
-.nbext-icon,
-.nbext-desc,
-.nbext-compat-div,
-.nbext-activate-btns,
-.nbext-params {
- margin-bottom: 8px;
-}
-
-.nbext-params > .panel-heading {
- font-size: 110%;
-}
-
-/* styles for list inputs */
-
-.nbext-list-element,
-.nbext-list-element-placeholder {
- margin-bottom: 2px;
-}
-
-.nbext-list-element-placeholder {
- height: 32px;
- width: 100%;
- border: 1px solid #fcefa1;
- background-color: #fbf9ee;
- color: #363636;
-}
-
-.nbext-list-element:only-child > .handle {
- display: none;
-}
-
-.nbext-list-element:only-child > .handle + .form-control {
- border-top-left-radius: 2px;
- border-bottom-left-radius: 2px;
-}
-
-.input-group .nbext-list-btn-add {
- font-size: inherit;
-}
diff --git a/nbextensions/config/main.js b/nbextensions/config/main.js
deleted file mode 100644
index bf3f5e7..0000000
--- a/nbextensions/config/main.js
+++ /dev/null
@@ -1,972 +0,0 @@
-// Copyright (c) IPython-Contrib Team.
-// Distributed under the terms of the Modified BSD License.
-
-// Show notebook extension configuration
-
-define([
- 'jqueryui',
- 'require',
- 'base/js/namespace',
- 'base/js/page',
- 'base/js/utils',
- 'services/config',
- 'base/js/events',
- 'notebook/js/quickhelp',
- 'nbextensions/config/render/render',
- 'nbextensions/config/kse_components'
-], function(
- $,
- require,
- Jupyter,
- page,
- utils,
- configmod,
- events,
- quickhelp,
- rendermd,
- kse_comp
-) {
- 'use strict';
-
- var base_url = utils.get_body_data('baseUrl');
- var first_load_done = false; // flag used to not push history on first load
- var extensions_dict = {}; // dictionary storing extensions by their 'require' value
-
- /**
- * create configs var from json files on server.
- * we still need to call configs[].load later to actually fetch them though!
- */
- var configs = {
- 'notebook' : new configmod.ConfigSection('notebook', {base_url: base_url}),
- 'edit' : new configmod.ConfigSection('edit', {base_url: base_url}),
- 'tree' : new configmod.ConfigSection('tree', {base_url: base_url}),
- 'common' : new configmod.ConfigSection('common', {base_url: base_url}),
- };
-
- // the prefix added to all parameter input id's
- var param_id_prefix = 'input_';
-
- /**
- * check whether a dot-notation key exists in a given ConfigSection object
- *
- * @param {ConfigSection} conf - the config section to query
- * @param {string} key - the (dot-notation) key to check for
- * @return {Boolean} - `true` if the key exists, `false` otherwise
- */
- function conf_dot_key_exists(conf, key) {
- var obj = conf.data;
- key = key.split('.');
- while (key.length > 0) {
- var partkey = key.shift();
- if (!obj.hasOwnProperty(partkey)) {
- return false;
- }
- obj = obj[partkey];
- }
- return true;
- }
-
- /**
- * get the value for a dot-notation key in a given ConfigSection object
- *
- * @param {ConfigSection} conf - the config section to query
- * @param {string} key - the (dot-notation) key to get the value of
- * @return - the value associated with the given key
- */
- function conf_dot_get (conf, key) {
- var obj = conf.data;
- key = key.split('.');
- while (key.length > 0) {
- obj = obj[key.shift()];
- }
- return obj;
- }
-
- /**
- * update the value for a dot-notation key in a given ConfigSection object
- *
- * @param {ConfigSection} conf - the config section to update
- * @param {string} key - the (dot-notation) key to update the value of
- * @param value - the new value to set. null results in removal of the key
- * @return - the return value of the ConfigSection.update call
- */
- function conf_dot_update (conf, key, value) {
- key = key.split('.');
- var root = {};
- var curr = root;
- while (key.length > 1) {
- curr = curr[key.shift()] = {};
- }
- curr[key.shift()] = value;
- return conf.update(root);
- }
-
- /**
- * Update server's json config file to reflect changed activate state
- */
- function set_config_active (extension, state) {
- state = state === undefined ? true : state;
- console.log('Notebook extension "' + extension.Name + '"', state ? 'enabled' : 'disabled');
- var to_load = {};
- to_load[extension.require] = (state ? true : null);
- configs[extension.Section].update({load_extensions: to_load});
- }
-
- /**
- * Update buttons to reflect changed activate state
- */
- function set_buttons_active (extension, state) {
- state = (state === true);
-
- extension.selector_link.find('.nbext-active-toggle').toggleClass('nbext-activated', state);
-
- var btns = $(extension.ui).find('.nbext-activate-btns').children();
- btns.eq(0)
- .prop('disabled', state)
- .toggleClass('btn-default disabled', state)
- .toggleClass('btn-primary', !state);
- btns.eq(1)
- .prop('disabled', !state)
- .toggleClass('btn-default disabled', !state)
- .toggleClass('btn-primary', state);
- }
-
- /**
- * Handle button click event to activate/deactivate extension
- */
- function handle_buttons_click (evt) {
- var btn = $(evt.target);
- var state = btn.is(':first-child');
- var extension = btn.closest('.nbext-ext-row').data('extension');
- set_buttons_active(extension, state);
- set_config_active(extension, state);
- }
-
- /*
- * Get the useful value (dependent on element type) from an input element
- */
- function get_input_value (input) {
- input = $(input);
- var input_type = input.data('param_type');
-
- switch (input_type) {
- case 'hotkey':
- return input.find('.hotkey').data('pre-humanized');
- case 'list':
- var val = [];
- input.find('.nbext-list-element').children().not('a').each(
- function () {
- // "this" is the current child element of input in the loop
- val.push(get_input_value(this));
- }
- );
- return val;
- case 'checkbox':
- return input.prop('checked') ? true : false;
- default:
- return input.val();
- }
- }
-
- /*
- * Set the useful value (dependent on element type) from a js value
- */
- function set_input_value (input, new_value) {
- input = $(input);
- var input_type = input.data('param_type');
- switch (input_type) {
- case 'hotkey':
- input.find('.hotkey')
- .html(quickhelp.humanize_sequence(new_value))
- .data('pre-humanized', new_value);
- break;
- case 'list':
- var ul = input.children('ul');
- ul.empty();
- var list_element_param = input.data('list_element_param');
- for (var ii = 0; ii < new_value.length; ii++) {
- var list_element_input = build_param_input(list_element_param);
- list_element_input.on('change', handle_input);
- set_input_value(list_element_input, new_value[ii]);
- ul.append(wrap_list_input(list_element_input));
- }
- break;
- case 'checkbox':
- input.prop('checked', new_value ? true : false);
- break;
- default:
- input.val(new_value);
- }
- }
-
- /**
- * handle form input for extension parameters, updating parameters in
- * server's json config file
- */
- function handle_input (evt) {
- var input = $(evt.target);
-
- // list elements should alter their parent's config
- if (input.closest('.nbext-list-wrap').length > 0) {
- input = input.closest('.nbext-list-wrap');
- }
- // hotkeys need to find the correct tag
- else if (input.hasClass('hotkey')) {
- input = input.closest('.input-group');
- }
-
- // get param name by cutting off prefix
- var configkey = input.attr('id').substring(param_id_prefix.length);
- var configval = get_input_value(input);
- var configsection = input.data('section');
- console.log(configsection + '.' + configkey, '->', configval);
- conf_dot_update(configs[configsection], configkey, configval);
- return configval;
- }
-
- /**
- * wrap a single list-element input with the | Extension name | -
|---|
and blocks and snippets #}
-
-
-{% endblock %}
-
-{% block script %}
-
- {{super()}}
-
-
-
-{% endblock %}