Merge pull request #644 from jcb91/split_configurator

replace config extension
This commit is contained in:
Josh Barnes
2016-06-28 22:46:53 +01:00
committed by GitHub
18 changed files with 9 additions and 2696 deletions
+3 -1
View File
@@ -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.
![Extensions](nbextensions/config/icon.png)
+5 -5
View File
@@ -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'])
-189
View File
@@ -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),
])
@@ -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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

-29
View File
@@ -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 = $('<li/>').append(
$('<a/>', {
'target' : '_blank',
'title' : 'Opens in a new window',
'href' : Jupyter.notebook.base_url + 'nbextensions/',
})
.append(' ')
.append($('<i/>', {'class' : 'fa fa-cogs menu-icon pull-right'}))
.append($('<span/>').html('nbextensions config'))
);
var edit_menu = $('#edit_menu');
edit_menu.append($('<li/>').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;
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 KiB

-364
View File
@@ -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 = $('<div/>')
.addClass('kse-editor')
.attr('id', 'kse-editor')
.data({
'kse_sequence': [],
'kse_info': {},
'kse_mode': 'command',
'kse_undefined_key': false
});
var form = $('<form/>')
.addClass('form')
.appendTo(editor);
$('<div/>')
.addClass('form-group')
.appendTo(form);
var form_group = $('<div/>')
.addClass('form-group has-feedback')
.appendTo(form);
var input_group = $('<div/>')
.addClass('input-group')
.addClass('kse-input-group')
.appendTo(form_group);
// reset button
var btn = $('<a/>')
.addClass('btn btn-default')
.addClass('kse-input-group-reset')
.attr({
'title': 'Restart',
'type': 'button'
})
.append(
$('<i/>')
.addClass('fa fa-repeat')
)
.on('click', function () {
editor.data({
'kse_sequence': [],
'kse_undefined_key': false
});
editor_update_input_group(editor);
$(this).blur();
textcontrol.focus();
});
$('<div/>')
.addClass('input-group-btn')
.append(btn)
.appendTo(input_group);
// pretty-displayed shortcut
$('<div/>')
.addClass('input-group-addon')
.addClass('kse-input-group-pretty')
.addClass('kse-editor-to')
.appendTo(input_group);
var textcontrol = $('<input/>')
.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 = $('<i/>')
.addClass('fa fa-lg');
$('<span/>')
.addClass('form-control-feedback')
.append(form_fdbck)
.appendTo(form_group);
// help for input group
$('<span/>')
.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(',', '&#44;');
}).join(',');
$('<p/>')
.html(
'Unfortunately, Jupyter\'s handling of shortcuts containing ' +
'commas (<kbd>,</kbd>) is fundamentally flawed, ' +
'as the comma is used as the key-separator character &#9785;. ' +
'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;
$('<p/>')
.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)) {
$('<p/>')
.append(quickhelp.humanize_sequence(conflicting_shortcut))
.append($('<code/>').text(conflicts[conflicting_shortcut]))
.appendTo(help_block);
}
}
}
}
if (editor.data('kse_undefined_key')) {
var warning = $('<span/>')
.addClass('form-group has-feedback has-warning kse-undefined')
.append(
$('<span/>')
.addClass('help-block')
.append(
$('<p/>').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) : '&lt;new shortcut&gt;');
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
};
});
-157
View File
@@ -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;
}
-972
View File
@@ -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 <li>, and move/remove buttons
*/
function wrap_list_input (list_input) {
var btn_remove = $('<a/>', {'class': 'btn btn-default input-group-addon nbext-list-el-btn-remove'});
btn_remove.append($('<i/>', {'class': 'fa fa-fw fa-trash'}));
btn_remove.on('click', function () {
var list_el = $(this).closest('li');
var list_input = list_el.closest('.nbext-list-wrap');
list_el.remove();
list_input.change(); // trigger change event
});
return $('<li/>', {'class' : 'nbext-list-element input-group'}).append(
$('<a class="btn btn-default input-group-addon handle"/>').append(
$('<i class="fa fa-fw fa-arrows-v"/>')
),
[list_input, btn_remove]);
}
/**
* Build and return an element used to edit a parameter
*/
function build_param_input (param) {
var input_type = (param.input_type || 'text').toLowerCase();
var input;
switch (input_type) {
case 'hotkey':
input = $('<div class="input-group"/>');
input.append(
$('<span class="form-control form-control-static hotkey"/>')
.css(utils.platform === 'MasOS' ? {'letter-spacing': '1px'} : {})
);
input.append($('<div class="input-group-btn"/>').append(
$('<div class="btn-group"/>').append(
$('<a/>', {
type: 'button',
class: 'btn btn-primary',
text: 'Change'
}).on('click', function() {
var description = 'Change ' +
param.description +
' from ' +
quickhelp.humanize_sequence(get_input_value(input)) +
' to:';
var modal = kse_comp.KSE_modal({
'description': description,
'buttons': {
'OK': {
'class': 'btn-primary',
'click': function () {
var editor = $(this).find('#kse-editor');
var new_value = (editor.data('kse_sequence') || []).join(',');
set_input_value(input, new_value);
// trigger write to config
input.find('.hotkey').change();
}
},
'Cancel': {}
},
});
modal.modal('show');
})
)
));
break;
case 'list':
input = $('<div/>', {'class' : 'nbext-list-wrap'});
input.append(
$('<ul/>', {'class': 'list-unstyled'})
.sortable({
handle: '.handle',
containment: 'window',
placeholder: 'nbext-list-element-placeholder',
update: function(event, ui) {
ui.item.closest('.nbext-list-wrap').change();
}
})
);
var list_element_param = param.list_element || {};
// add the requested list param type to the list element using
// jquery data api
input.data('list_element_param', list_element_param);
// add a button to add list elements
var add_button = $('<a/>')
.addClass('btn btn-default input-group-btn nbext-list-btn-add')
.append($('<i/>', {'class': 'fa fa-plus'}).text(' new item'))
.on('click', function () {
$(this).parent().siblings('ul').append(
wrap_list_input(
build_param_input(list_element_param)
.on('change', handle_input)
)
).closest('.nbext-list-wrap').change();
});
input.append($('<div class="input-group"/>').append(add_button));
break;
case 'textarea':
input = $('<textarea/>');
break;
case 'number':
input = $('<input/>', {'type': input_type});
if (param.step !== undefined) input.attr('step', param.step);
if (param.min !== undefined) input.attr('min', param.min);
if (param.max !== undefined) input.attr('max', param.max);
break;
default:
// detect html5 input tag support using scheme from
// http://diveintohtml5.info/detect.html#input-types
// If the browser supports the requested particular input type,
// the type property will retain the value you set.
// If the browser does not support the requested input type,
// it will ignore the value you set
// and the type property will still be "text".
input = document.createElement('input');
input.setAttribute('type', input_type);
// wrap in jquery
input = $(input);
}
// add the param type to the element using jquery data api
input.data('param_type', input_type);
input.data('section', param.section);
var non_form_control_input_types = ['checkbox', 'list', 'hotkey'];
if (non_form_control_input_types.indexOf(input_type) < 0) {
input.addClass('form-control');
}
return input;
}
/*
* Build and return a div containing the buttons to activate/deactivate an
* extension with the given id.
*/
function build_activate_buttons () {
var div_buttons = $('<div class="btn-group nbext-activate-btns"/>');
var btn_activate = $('<button/>', {
'type': 'button',
'class': 'btn btn-primary'
}).text('Activate').on('click', handle_buttons_click);
btn_activate.appendTo(div_buttons);
var btn_deactivate = $('<button/>', {
'type': 'button',
'class': 'btn btn-default'
}).text('Deactivate').on('click', handle_buttons_click);
btn_deactivate.appendTo(div_buttons);
btn_deactivate.prop('disabled', true);
return div_buttons;
}
/**
* show/hide compatibility text, along with en/disabling the nav link
*/
function set_hide_incompat (hide_incompat) {
$('.nbext-compat-div').toggle(!hide_incompat);
$('.nbext-selector .nbext-incompatible')
.toggleClass('disabled', hide_incompat)
.attr('title', hide_incompat ? 'possibly incompatible' : '');
set_input_value(
$('#' + param_id_prefix + 'nbext_hide_incompat'), hide_incompat);
var selector = $('.nbext-selector');
if (selector.find('li.active').first().hasClass('disabled')) {
selector.find('li:not(.disabled) a').first().click();
}
}
/**
* if the extension's readme is a relative url with extension .md,
* render the referenced markdown file
* otherwise
* add an anchor element to the extension's description
*/
function load_readme (extension) {
var readme_div = $('.nbext-readme .nbext-readme-contents').empty();
var readme_title = $('.nbext-readme > h3').empty();
if (!extension.readme) return;
var url = extension.readme;
var is_absolute = /^(f|ht)tps?:\/\//i.test(url);
if (is_absolute || (utils.splitext(url)[1] !== '.md')) {
// provide a link only
var desc = extension.ui.find('.nbext-desc');
var link = desc.find('.nbext-readme-more-link');
if (link.length === 0) {
desc.append(' ');
link = $('<a/>')
.addClass('nbext-readme-more-link')
.text('more...')
.attr('href', url)
.appendTo(desc);
}
return;
}
// relative urls are in nbextensions namespace
url = require.toUrl(utils.url_path_join(base_url, 'nbextensions', url));
readme_title.text(url);
// add rendered markdown to readme_div. Use pre-fetched if present
if (extension.readme_content) {
rendermd.render_markdown(extension.readme_content, url)
.addClass('rendered_html')
.appendTo(readme_div);
return;
}
$.ajax({
url: url,
dataType: 'text',
success: function (md_contents) {
rendermd.render_markdown(md_contents, url)
.addClass('rendered_html')
.appendTo(readme_div);
// We can't rely on picking up the rendered html,
// since render_markdown returns
// before the actual rendering work is complete
extension.readme_content = md_contents;
// attempt to scroll to a location hash, if there is one.
var hash = window.location.hash.replace(/^#/, '');
if (hash) {
// Allow time for markdown to render
setTimeout( function () {
// use filter to avoid breaking jQuery selector syntax with weird id
var hdr = readme_div.find(':header').filter(function (idx, elem) {
return elem.id === hash;
});
if (hdr.length > 0) {
var site = $('#site');
var adjust = hdr.offset().top - site.offset().top;
if (adjust > 0) {
site.animate(
{scrollTop: site.scrollTop() + adjust},
undefined, // time
undefined, // easing function
function () {
if (hdr.effect !== undefined) {
hdr.effect('highlight', {color: '#faf2cc'});
}
}
);
}
}
}, 100);
}
},
error: function (jqXHR, textStatus, errorThrown) {
var error_div = $('<div class="text-danger bg-danger"/>')
.text(textStatus + ' : ' + jqXHR.status + ' ' + errorThrown)
.appendTo(readme_div);
if (jqXHR.status === 404) {
$('<p/>')
.text('no markdown file at ' + url)
.appendTo(error_div);
}
}
});
}
/**
* open the user interface the extension corresponding to the given
* link
* @param extension the extension
* @param opts options for the reveal animation
*/
function open_ext_ui (extension, opts) {
var default_opts = {duration: 100};
opts = $.extend(true, {}, default_opts, opts);
/**
* Set window search string to allow reloading settings for a given
* extension.
* Use history.pushState if available, to avoid reloading the page
*/
var new_search = '?nbextension=' + encodeURIComponent(extension.require).replace(/%2F/g, '/');
if (first_load_done) {
if (window.history.pushState) {
window.history.pushState(extension.require, undefined, new_search);
}
else {
window.location.search = new_search;
}
}
first_load_done = true;
// ensure extension.ui exists
if (extension.ui === undefined) {
// use display: none since hide(0) doesn't do anything
// for elements that aren't yet part of the DOM
extension.ui = build_extension_ui(extension)
.css('display', 'none')
.insertBefore('.nbext-readme');
var ext_active = extension.selector_link.find('.nbext-active-toggle').hasClass('nbext-activated');
set_buttons_active(extension, ext_active);
}
$('.nbext-selector li')
.removeClass('active');
extension.selector_link.closest('li').addClass('active');
$('.nbext-ext-row')
.not(extension.ui)
.slideUp(default_opts);
extension.ui.slideDown(opts);
load_readme(extension);
}
/**
* Callback for the nav links
* open the user interface the extension corresponding to the clicked
* link, and scroll it into view
*/
function selector_nav_link_callback (evt) {
evt.preventDefault();
evt.stopPropagation();
var a = $(evt.currentTarget);
var extension = a.data('extension');
if (a.closest('li').hasClass('disabled')) {
return;
}
open_ext_ui(extension, {
complete: function () {
// scroll to ensure at least title is visible
var site = $('#site');
var title = extension.ui.children('h3:first');
var adjust = (title.offset().top - site.offset().top) + (2 * title.outerHeight(true) - site.innerHeight());
if (adjust > 0) {
site.animate({scrollTop: site.scrollTop() + adjust});
}
}
});
}
/**
* Callback for the nav links' activation checkboxes
*/
function selector_checkbox_callback (evt) {
evt.preventDefault();
evt.stopPropagation();
var a = $(evt.currentTarget).closest('a');
if (!a.closest('li').hasClass('disabled')) {
var extension = a.data('extension');
var state = !$(evt.currentTarget).hasClass('nbext-activated');
set_buttons_active(extension, state);
set_config_active(extension, state);
open_ext_ui(extension);
}
}
/**
* build and return UI elements for a set of parameters
*/
function build_params_ui (params) {
// Assemble and add params
var div_param_list = $('<div/>')
.addClass('list-group');
for (var pp in params) {
var param = params[pp];
var param_name = param.name;
if (!param_name) {
console.error('nbext param: unnamed parameter declared!');
continue;
}
var param_div = $('<div/>')
.addClass('form-group list-group-item')
.appendTo(div_param_list);
var param_id = param_id_prefix + param_name;
// use param name / description as label
$('<label/>')
.attr('for', param_id)
.html(
param.hasOwnProperty('description') ? param.description : param_name
)
.appendTo(param_div);
// input to configure the param
var input = build_param_input(param);
input.on('change', handle_input);
input.attr('id', param_id);
var prepend_input_types = ['checkbox'];
if (prepend_input_types.indexOf(param.input_type) < 0) {
param_div.append(input);
}
else {
param_div.prepend(' ');
param_div.prepend(input);
}
// set input value from config or default, if poss
if (conf_dot_key_exists(configs[param.section], param_name)) {
var configval = conf_dot_get(configs[param.section], param_name);
console.log(
'nbext param:', param_name,
'from config:', configval
);
set_input_value(input, configval);
}
else if (param.hasOwnProperty('default')) {
set_input_value(input, param.default);
console.log(
'nbext param:', param_name,
'default:', param.default
);
}
else {
console.log('nbext param:', param_name);
}
}
return div_param_list;
}
/**
* build and return UI elements for a single extension
*/
function build_extension_ui (extension) {
var ext_row = $('<div/>')
.data('extension', extension)
.addClass('row nbext-ext-row');
try {
/**
* Name.
* Take advantage of column wrapping by using the col-xs-12 class
* to ensure the name takes up a whole row-width on its own,
* so that the subsequent columns wrap onto a new line.
*/
var ext_name_head = $('<h3>')
.addClass('col-xs-12')
.html(extension.Name)
.appendTo(ext_row);
/**
* Columns
*/
var col_left = $('<div/>')
.addClass('col-xs-12')
.appendTo(ext_row);
// Icon
if (extension.icon) {
col_left
.addClass('col-sm-8 col-sm-pull-4 col-md-6 col-md-pull-6');
// right precedes left in markup, so that it appears first when
// the columns are wrapped each onto a single line.
// The push and pull CSS classes are then used to get them to
// be left/right correctly when next to each other
var col_right = $('<div>')
.addClass('col-xs-12 col-sm-4 col-sm-push-8 col-md-6 col-md-push-6')
.insertBefore(col_left);
$('<div/>')
.addClass('nbext-icon')
.append(
$('<img>')
.attr({
// extension.icon is in nbextensions namespace
'src': utils.url_path_join(base_url, 'nbextensions', extension.icon),
'alt': extension.Name + ' icon'
})
)
.appendTo(col_right);
}
// Description
var div_desc = $('<div/>')
.addClass('nbext-desc')
.appendTo(col_left);
if (extension.hasOwnProperty('Description')) {
$('<p/>')
.html(extension.Description)
.appendTo(div_desc);
}
// Compatibility
var compat_txt = extension.Compatibility || '?.x';
var compat_idx = compat_txt.toLowerCase().indexOf(
Jupyter.version.substring(0, 2) + 'x');
if (!extension.is_compatible) {
ext_row.addClass('nbext-incompatible');
compat_txt = $('<span/>')
.addClass('bg-danger text-danger')
.text(compat_txt);
}
else {
compat_txt = $('<span/>')
.append(
compat_txt.substring(0, compat_idx)
)
.append(
$('<span/>')
.addClass('bg-success text-success')
.text(compat_txt.substring(compat_idx, compat_idx + 3))
)
.append(compat_txt.substring(compat_idx + 3, compat_txt.length));
}
$('<div/>')
.addClass('nbext-compat-div')
.text('compatibility: ')
.append(compat_txt)
.appendTo(col_left);
// Activate/Deactivate buttons
build_activate_buttons().appendTo(col_left);
// Parameters
if (extension.Parameters.length > 0) {
for (var ii = 0; ii < extension.Parameters.length; ii++) {
extension.Parameters[ii].section = extension.Section;
}
$('<div/>')
.addClass('panel panel-default nbext-params col-xs-12')
.append(
$('<div/>')
.addClass('panel-heading')
.text('Parameters')
)
.append(
build_params_ui(extension.Parameters)
)
.appendTo(ext_row);
}
}
catch (err) {
console.error('nbext error loading extension', extension.Name);
console.error(err);
$('<div/>')
.addClass('alert alert-warning')
.css('margin-top', '5px')
.append(
$('<p/>')
.text('error loading extension ' + extension.Name)
)
.appendTo(ext_row);
}
finally {
return ext_row;
}
}
/**
* build html body listing all extensions.
*/
function build_page () {
add_css('./main.css');
var nbext_config_page = new page.Page();
// prepare for rendermd usage
rendermd.add_markdown_css();
build_param_input({
input_type: 'checkbox',
section: 'common'
})
.attr('id', param_id_prefix + 'nbext_hide_incompat')
.on('change', function (evt) {
set_hide_incompat(handle_input(evt));
})
.prependTo('.nbext-showhide-incompat');
nbext_config_page.show_header();
events.trigger('resize-header.Page');
var config_promises = [];
for (var section in configs) {
config_promises.push(configs[section].loaded);
configs[section].load();
}
Promise.all(config_promises).then(function () {
build_extension_list();
nbext_config_page.show();
});
return nbext_config_page;
}
/**
* Callback for the window.popstate event, used to handle switching to the
* correct selected extension
*/
function popstateCallback (evt) {
var require_url;
if (evt === undefined) {
// attempt to select an extension specified by a URL search parameter
var queries = window.location.search.replace(/^\?/, '').split('&');
for (var ii = 0; ii < queries.length; ii++) {
var keyValuePair = queries[ii].split('=');
if (decodeURIComponent(keyValuePair[0]) === 'nbextension') {
require_url = decodeURIComponent(keyValuePair[1]);
break;
}
}
}
else if (evt.state === null) {
return; // as a result of setting window.location.hash
}
else {
require_url = evt.state;
}
var selected_link;
if (extensions_dict[require_url] === undefined || extensions_dict[require_url].selector_link.hasClass('disabled')) {
selected_link = $('.nbext-selector').find('li:not(.disabled)').last().children('a');
}
else {
selected_link = extensions_dict[require_url].selector_link;
}
selected_link.click();
}
/**
* build html body listing all extensions.
*
* Since this function uses the contents of config.data,
* it should only be called after config.load() has been executed
*/
function build_extension_list () {
// get list of extensions from body data supplied by the python backend
var extension_list = $('body').data('extension-list') || [];
// add enabled-but-unconfigurable extensions to the list
// construct a set of enabled extension urls from the configs
// this is used later to add unconfigurable extensions to the list
var unconfigurable_enabled_extensions = {};
var section;
for (section in configs) {
unconfigurable_enabled_extensions[section] = $.extend({}, configs[section].data.load_extensions);
}
var i, extension;
for (i = 0; i < extension_list.length; i++) {
extension = extension_list[i];
extension.Section = (extension.Section || 'notebook').toString();
extension.Name = (extension.Name || (extension.Section + ':' + extension.require)).toString();
// extension *is* configurable
delete unconfigurable_enabled_extensions[extension.Section][extension.require];
}
// add any remaining unconfigurable extensions as stubs
for (section in configs) {
for (var require_url in unconfigurable_enabled_extensions[section]) {
extension_list.push({
Name: section + ' : ' + require_url,
Description: 'This extension is enabled in the ' + section + ' json config, ' +
"but doesn't provide a yaml file to tell us how to configure it. " +
"You can disable it from here, but if you do, it won't show up in " +
'this list again after you reload the page.',
Section: section,
require: require_url,
unconfigurable: true,
});
}
}
var container = $('#site > .container');
var selector = $('.nbext-selector');
var cols = selector.find('ul');
// sort extensions alphabetically
extension_list.sort(function (a, b) {
var an = (a.Name || '').toLowerCase();
var bn = (b.Name || '').toLowerCase();
if (an < bn) return -1;
if (an > bn) return 1;
return 0;
});
// fill the columns with nav links
var col_length = Math.ceil(extension_list.length / cols.length);
for (i = 0; i < extension_list.length; i++) {
extension = extension_list[i];
extensions_dict[extension.require] = extension;
console.log('Notebook extension "' + extension.Name + '" found');
extension.is_compatible = (extension.Compatibility || '?.x').toLowerCase().indexOf(
Jupyter.version.substring(0, 2) + 'x') >= 0;
extension.Parameters = extension.Parameters || [];
if (!extension.is_compatible) {
// reveal the checkbox since we've found an incompatible nbext
$('.nbext-showhide-incompat').show();
}
extension.selector_link = $('<a/>')
.data('extension', extension)
.html(extension.Name)
.toggleClass('text-warning bg-warning', extension.unconfigurable === true)
.prepend(
$('<i>')
.addClass('fa fa-fw nbext-active-toggle')
);
$('<li/>')
.toggleClass('nbext-incompatible', !extension.is_compatible)
.append(extension.selector_link)
.appendTo(cols[Math.floor(i / col_length)]);
var ext_active = false;
var conf = configs[extension.Section];
if (conf === undefined) {
console.error("nbextension '" + extension.Name + "' specifies unknown Section of '" + extension.Section + "'. Can't determine active status.");
}
else if (conf.data.hasOwnProperty('load_extensions')) {
ext_active = (conf.data.load_extensions[extension.require] === true);
}
set_buttons_active(extension, ext_active);
}
// attach click handlers
$('.nbext-active-toggle')
.on('click', selector_checkbox_callback)
.closest('a')
.on('click', selector_nav_link_callback);
// en/disable incompatible extensions
var hide_incompat = true;
if (configs['common'].data.hasOwnProperty('nbext_hide_incompat')) {
hide_incompat = configs['common'].data.nbext_hide_incompat;
console.log(
'nbext_hide_incompat loaded from config as: ',
hide_incompat
);
}
set_hide_incompat(hide_incompat);
window.addEventListener('popstate', popstateCallback);
setTimeout(popstateCallback, 0);
}
/**
* Add CSS file to page
*
* @param name filename
*/
function add_css (name) {
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = require.toUrl(name);
document.getElementsByTagName('head')[0].appendChild(link);
}
return {
build_page: build_page
};
});
-208
View File
@@ -1,208 +0,0 @@
define([
'underscore',
'jquery',
'base/js/utils',
'notebook/js/quickhelp',
'codemirror/lib/codemirror'
], function(
_,
$,
utils,
quickhelp,
CodeMirror
){
"use strict";
// This is essentially a duplicate of the quickhelp module, used to
// patch the existing quickhelp with definitions which aren't exported.
var platform = utils.platform;
var cmd_ctrl = 'Ctrl-';
var platform_specific;
if (platform === 'MacOS') {
// Mac OS X specific
cmd_ctrl = 'Cmd-';
platform_specific = [
{ shortcut: "Cmd-Up", help:"go to cell start" },
{ shortcut: "Cmd-Down", help:"go to cell end" },
{ shortcut: "Alt-Left", help:"go one word left" },
{ shortcut: "Alt-Right", help:"go one word right" },
{ shortcut: "Alt-Backspace", help:"delete word before" },
{ shortcut: "Alt-Delete", help:"delete word after" },
];
} else {
// PC specific
platform_specific = [
{ shortcut: "Ctrl-Home", help:"go to cell start" },
{ shortcut: "Ctrl-Up", help:"go to cell start" },
{ shortcut: "Ctrl-End", help:"go to cell end" },
{ shortcut: "Ctrl-Down", help:"go to cell end" },
{ shortcut: "Ctrl-Left", help:"go one word left" },
{ shortcut: "Ctrl-Right", help:"go one word right" },
{ shortcut: "Ctrl-Backspace", help:"delete word before" },
{ shortcut: "Ctrl-Delete", help:"delete word after" },
];
}
var cm_shortcuts = [
{ shortcut:"Tab", help:"code completion or indent" },
{ shortcut:"Shift-Tab", help:"tooltip" },
{ shortcut: cmd_ctrl + "]", help:"indent" },
{ shortcut: cmd_ctrl + "[", help:"dedent" },
{ shortcut: cmd_ctrl + "a", help:"select all" },
{ shortcut: cmd_ctrl + "z", help:"undo" },
{ shortcut: cmd_ctrl + "Shift-z", help:"redo" },
{ shortcut: cmd_ctrl + "y", help:"redo" },
].concat( platform_specific );
var mac_humanize_map = {
// all these are unicode, will probably display badly on anything except macs.
// these are the standard symbol that are used in MacOS native menus
// cf http://apple.stackexchange.com/questions/55727/
// for htmlentities and/or unicode value
'cmd': '&#8984;',
'shift': '&#8679;',
'alt': '&#8997;',
'up': '&#8593;',
'down': '&#8595;',
'left': '&#8592;',
'right': '&#8594;',
'eject': '&#9167;',
'tab': '&#8677;',
'backtab': '&#8676;',
'capslock': '&#8682',
'esc': 'esc',
'altesc': '&#9099;',
'ctrl': '&#8963;',
'enter': '&#8617;',
'pageup': '&#8670;',
'pagedown': '&#8671;',
'home': '&#8598;',
'end': '&#8600;',
'altenter': '&#8996;',
'space': '&#9251;',
'delete': '&#8998;',
'backspace': '&#9003;',
'apple': '&#63743;',
};
var default_humanize_map = {
'shift': 'Shift',
'alt': 'Alt',
'up': 'Up',
'down': 'Down',
'left': 'Left',
'right': 'Right',
'tab': 'Tab',
'capslock': 'Caps Lock',
'esc': 'Esc',
'ctrl': 'Ctrl',
'enter': 'Enter',
'pageup': 'Page Up',
'pagedown': 'Page Down',
'home': 'Home',
'end': 'End',
'space': 'Space',
'backspace': 'Backspace',
};
var humanize_map;
if (platform === 'MacOS'){
humanize_map = mac_humanize_map;
} else {
humanize_map = default_humanize_map;
}
var special_case = { pageup: "PageUp", pagedown: "Page Down", 'minus': '-' };
function humanize_key(key){
if (key.length === 1){
return key.toUpperCase();
}
key = humanize_map[key.toLowerCase()]||key;
if (key.indexOf(',') === -1){
return ( special_case[key] ? special_case[key] : key.charAt(0).toUpperCase() + key.slice(1) );
}
}
// return an **html** string of the keyboard shortcut
// for human eyes consumption.
// the sequence is a string, comma sepparated linkt of shortcut,
// where the shortcut is a list of dash-joined keys.
// Each shortcut will be wrapped in <kbd> tag, and joined by comma is in a
// sequence.
//
// Depending on the platform each shortcut will be normalized, with or without dashes.
// and replace with the corresponding unicode symbol for modifier if necessary.
function humanize_sequence(sequence){
var joinchar = ',';
var hum = _.map(sequence.replace(/meta/g, 'cmd').split(','), humanize_shortcut).join(joinchar);
return hum;
}
function humanize_shortcut(shortcut){
var joinchar = '-';
if (platform === 'MacOS'){
joinchar = '';
}
var sh = _.map(shortcut.split('-'), humanize_key ).join(joinchar);
return '<kbd>'+sh+'</kbd>';
}
var build_one = function (s) {
var help = s.help;
var shortcut = '';
if(s.shortcut){
shortcut = humanize_sequence(s.shortcut);
}
return $('<div>').addClass('quickhelp').
append($('<span/>').addClass('shortcut_key').append($(shortcut))).
append($('<span/>').addClass('shortcut_descr').text(' : ' + help));
};
var build_div = function (title, shortcuts) {
// Remove jupyter-notebook:ignore shortcuts.
shortcuts = shortcuts.filter(function(shortcut) {
if (shortcut.help === 'ignore') {
return false;
} else {
return true;
}
});
var i, half, n;
var div = $('<div/>').append($(title));
var sub_div = $('<div/>').addClass('container-fluid');
var col1 = $('<div/>').addClass('col-md-6');
var col2 = $('<div/>').addClass('col-md-6');
n = shortcuts.length;
half = ~~(n/2); // Truncate :)
for (i=0; i<half; i++) { col1.append( build_one(shortcuts[i]) ); }
for (i=half; i<n; i++) { col2.append( build_one(shortcuts[i]) ); }
sub_div.append(col1).append(col2);
div.append(sub_div);
return div;
};
var quickhelp_shiv = {
cmd_ctrl : cmd_ctrl,
platform_specific : platform_specific,
cm_shortcuts : cm_shortcuts,
mac_humanize_map : mac_humanize_map,
default_humanize_map : default_humanize_map,
humanize_map : humanize_map,
special_case : special_case,
humanize_key : humanize_key,
humanize_sequence : humanize_sequence,
humanize_shortcut : humanize_shortcut,
build_one : build_one,
build_div : build_div
};
_.defaults(quickhelp, quickhelp_shiv);
});
-272
View File
@@ -1,272 +0,0 @@
Introduction
============
This extension provides a web page
(which you can find by going to the '/nbextensions' URL)
which allows you to activate or deactivate installed notebook extensions,
if they provide a `YAML` file description.
Activating an extension means it is loaded automatically when working with a
notebook document.
If you encounter problems with this config page, please create an issue at the
[ipython-contrib](https://github.com/ipython-contrib/IPython-notebook-extensions)
repository.
![](icon.png)
The config page is realized using a notebook server extension, new in IPython 3.x.
In order to work, this extension (`nbextensions/config`) needs to be installed.
In addition, any notebook extensions it will configure will require a YAML
description file under the `nbextensions` directory
(see installation notes, below) in order to be found.
You can see a video of the config extension in action on youtube:
<div class="embed-responsive embed-responsive-4by3">
![config extension on youtube](https://i.ytimg.com/vi_webp/h9DEfxZSz2M/maxresdefault.webp)
[](https://youtu.be/h9DEfxZSz2M)
</div>
Setup procedure
===============
The most recent version
can be found here:
[master.zip](https://github.com/ipython-contrib/IPython-notebook-extensions/archive/master.zip)
https://binstar.org/juhasch/nbextensions
1. Installation
---------------
There are several ways to install the IPython-contrib extensions package:
a) Using `setup.py` 777
After downloading the most recent version from GitHub [master.zip](https://github.com/ipython-contrib/IPython-notebook-extensions/archive/master.zip),
unpack the archive and run `python setup.py install`. This will copy all extensions to your local Jupyter installation
and configure the extensions to work.
b) Installing the Anaconda package
When using the Anaconda Python installation (highly recommended), you can install the package using the
`conda install -c https://conda.binstar.org/juhasch nbextensions` command. Alternatively, you can download the latest master
version and do a `conda build IPython-notebook-extensions` yourself to build the Anaconda package.
c) Manual installation (see below)
The easiest way is to do a) and install the extensions using the `python setup.py install` command.
Having restarted the server after the installation, you should be able to see the configuration page by going to the URL `/nbextensions`.
Otherwise, you can use the detailed instructions below - good luck!
**Note**: The notebook server is not allowed to run during installation.
1a. Manual Installation
-----------------------
* copy the `nbextensions` folder to `~/Library/Jupyter` (on MacOs, for help locating Jupyter data dir on other OS look in 3.)
* copy the `extensions` folder to `~/Library/Jupyter`
* copy the `templates` folder to `~/Library/Jupyter`
2. Configuration
----------------
There are two configuration steps required:
1. Edit your `.jupyter/jupyter_notebook_config.py` file (on MacOs, for help locating the Jupyter config dir on other OS look in 3.):
```
from jupyter_core.paths import jupyter_config_dir, jupyter_data_dir
import os
import sys
sys.path.append(os.path.join(jupyter_data_dir(), 'extensions'))
c = get_config()
c.NotebookApp.extra_template_paths = [os.path.join(jupyter_data_dir(),'templates') ]
```
And `.jupyter/jupyter_notebook_config.py`
```
from jupyter_core.paths import jupyter_config_dir, jupyter_data_dir
import os
import sys
sys.path.append(os.path.join(jupyter_data_dir(), 'extensions'))
c = get_config()
c.Exporter.template_path = [os.path.join(jupyter_data_dir(), 'templates') ]
```
This makes sure the Python extensions are found in the `~/Library/Jupyter/extensions` directory and the
additional templates are found in `~/Library/Jupyter/templates`
2. Configure the server extension, and pre-/postprocessessors:
Edit the `.jupyter/jupyter_notebook.json` to look like this
```
{
"Exporter": {
"preprocessors": [
"pre_codefolding.CodeFoldingPreprocessor",
"pre_pymarkdown.PyMarkdownPreprocessor"
]
},
"NbConvertApp": {
"postprocessor_class": "post_embedhtml.EmbedPostProcessor"
},
"NotebookApp": {
"server_extensions": [
"nbextensions"
]
},
"version": 1
}
```
Edit the `.jupyter/jupyter_nbconvert.json` to look like this:
```
{
"Exporter": {
"preprocessors": [
"pre_codefolding.CodeFoldingPreprocessor",
"pre_pymarkdown.PyMarkdownPreprocessor"
]
},
"NbConvertApp": {
"postprocessor_class": "post_embedhtml.EmbedPostProcessor"
},
"version": 1
}
```
This will load the `nbextensions` server extension in the notebook, and add several pre/- and postprocessors that
are required by notebook extensions to export notebooks using `jupter nbconvert`
(namely Codefolding, Python Mmarkdown, Runtools).
3. Help with locating files
---------------------------
If you're having problems with where the different files are supposed to go,
here's an attempt at an explanation.
Jupyter/IPython 4.x works differently than IPython 3.x:
* The notebook was split from IPython. You need to install both to run the
notebook with IPython now.
The easiest way is to use Anaconda and do a `conda install jupyter`
* There are no profiles anymore.
You can specify environment variables to change the default, see
[Jupyter ML](https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!topic/jupyter/7q02jjksvFU).
* The configuration has moved to a new place. To find out where, see below.
* There is a kind of automatic upgrade of the configuration files from IPython 3.x to Jupyter.
### So where are all the config files now?
To find where the configuration files are, start IPython and run the following:
```Python
from __future__ import print_function
from jupyter_core.paths import jupyter_config_dir, jupyter_config_path
print(jupyter_config_dir())
print(jupyter_config_path())
```
`jupyter_config_dir()` shows you where your *local* configuration files are,
`jupyter_config_path()` shows you where Jupyter will look for *global* configuration files.
For the notebook, there are two files that will be used:
`jupyter_notebook_config.py` and `jupyter_notebook_config.json`.
The `nbextensions` directory has moved to a different location and can be found
in one of these directories:
```Python
from __future__ import print_function
from jupyter_core.paths import jupyter_data_dir, jupyter_path
print(jupyter_data_dir())
print(jupyter_path())
```
Internals
=========
The configuration for which nbextensions are enabled is stored in `jupyter_config_dir()/nbconfig/notebook.json`.
You can generate a table of currently activated extensions by executing the following in a notebook cell:
```Python
from IPython.html.services.config import ConfigManager
from IPython.display import HTML
ip = get_ipython()
cm = ConfigManager(parent=ip, profile_dir=ip.profile_dir.location)
extensions =cm.get('notebook')
table = ""
for ext in extensions['load_extensions']:
table += "<tr><td>%s</td>\n" % (ext)
top = """
<table border="1">
<tr>
<th>Extension name</th>
</tr>
"""
bottom = """
</table>
"""
HTML(top + table + bottom)
```
If you reload the notebook after enabling a notebook extension, the extension
should be loaded. You can also check the Javascript console to confirm.
YAML file format
----------------
A notebook extensions is found, when a special YAML file describing the extensions is found.
The YAML file can have any name with the extension `YAML`, and describes the notebook extension. Note that keys (in bold) are case-sensitive.
* **Type** - identifier, must be 'IPython Notebook Extension'
* **Name** - unique name of the extension
* **Description** - short explanation of the extension
* **Link** - a url for more documentation
* **Icon** - a url for a small icon (rendered 120px high, should preferably end up 400px wide. Recall HDPI displays may benefit from a 2x resolution icon).
* **Main** - main javascript file that is loaded, typically 'main.js'
* **Compatibility** - IPython version compatibility, e.g. '3.x' or '4.x' or '3.x 4.x'
* **Parameters** - Optional list of configuration parameters. Each item is a dictionary with (some of) the following keys:
* **name** - (mandatory) this is the name used to store the configuration variable in the config json, so should be unique among all extensions
* **description** - description of the configuration parameter
* **default** - a default value used to populate the tag on the nbextensions config page. Note that this is more of a hint to the user than anything functional - since it's only set in the yaml file, the javascript implementing the extension in question might actually use a different default, depending on the implementation.
* **input_type** - controls the type of html tag used to render the parameter on the configuration page. Valid values include 'text', 'textarea', 'checkbox', [html5 input tags such as 'number', 'url', 'color', ...], plus a final type of 'list'
* **list_element** - for parameters with input_type 'list', this is used in place of 'input_type' to render each element of the list
* finally, extras such as **min** **step** **max** may be used by 'number' tags for validation
Example:
```yaml
Type: IPython Notebook Extension
Name: Limit Output
Description: This extension limits the number of characters that can be printed below a codecell
Link: https://github.com/ipython-contrib/IPython-notebook-extensions/wiki/limit-output
Icon: icon.png
Main: main.js
Compatibility: 3.x 4.x
Parameters:
- name: limit_output
description: Number of characters to limit output to
input_type: number
default: 10000
step: 1
min: 0
```
Troubleshooting
===============
If an extension doesn't work, here are some ways you can check what is wrong:
1. Clear your browser cache or start a private browser tab.
2. Verify the extension can be loaded by the IPython notebook, for example,
load the javascript file directly:
`http://127.0.0.1:8888/nbextensions/usability/runtools/main.js`
3. Check for error messages in the JavaScript console of the browser.
4. Check for any error messages in the server output logs
-255
View File
@@ -1,255 +0,0 @@
// Copyright (c) IPython-Contrib Team.
// Distributed under the terms of the Modified BSD License.
// Render markdown url
define([
'require',
'jquery',
'base/js/utils',
'base/js/page',
'base/js/security',
'notebook/js/mathjaxutils',
'notebook/js/codemirror-ipythongfm',
'codemirror/lib/codemirror',
'codemirror/mode/gfm/gfm',
'codemirror/addon/runmode/runmode',
'components/marked/lib/marked'
], function(
require,
$,
utils,
page,
security,
mathjaxutils,
ipgfm,
CodeMirror,
gfm,
runMode,
marked
){
/**
* Custom marked options,
* lifted from notebook/js/notebook
*/
var custom_marked_options = {
gfm : true,
tables: true,
// FIXME: probably want central config for CodeMirror theme when we have js config
langPrefix: "cm-s-ipython language-",
highlight: function(code, lang, callback) {
if (!lang) {
// no language, no highlight
if (callback) {
callback(null, code);
return;
} else {
return code;
}
}
var magic_match;
if (lang.toLowerCase() === 'jupyter') {
var magic_specs = {
'javascript' : /^%%javascript/,
'perl' : /^%%perl/,
'ruby' : /^%%ruby/,
'python' : /^%%python3?/,
'shell' : /^%%bash/,
'r' : /^%%R/,
'text/x-cython' : /^%%cython/,
'latex' : /^%%latex/
};
for (var mode in magic_specs) {
magic_match = code.match(magic_specs[mode]);
if (magic_match !== null) {
magic_match = magic_match[0];
lang = mode;
code = code.substr(magic_match.length);
break;
}
}
}
utils.requireCodeMirrorMode(lang, function (spec) {
var el = document.createElement("div");
var mode = CodeMirror.getMode({}, spec);
if (!mode) {
console.log("No CodeMirror mode: " + lang);
callback(null, code);
return;
}
try {
CodeMirror.runMode(code, spec, el);
if (magic_match) {
$(el).prepend($('<span/>').text(magic_match));
}
callback(null, el.innerHTML);
} catch (err) {
console.log("Failed to highlight " + lang + " code", err);
callback(err, code);
}
}, function (err) {
console.log("No CodeMirror mode: " + lang);
callback(err, code);
});
}
};
/**
* return a URL constructed by joining together each relative URL given as an argument, applying '..'
*/
var join_relative_urls = function () {
var url = [], root = '';
for (var i in arguments) {
var url_parts = arguments[i];
// reset url if we encounter a relative URL starting at domain root (i.e. beginning with '/')
if (url_parts.length > 0 && url_parts[0] === '/') {
url = [];
root = '/';
}
url_parts = url_parts.split('/');
url.pop(); // relative urls don't include the resource, so pop it
for (var j in url_parts) {
switch (url_parts[j]) {
case '':
case '.':
continue;
case '..':
url.pop();
break;
default:
url.push(url_parts[j]);
}
}
}
return root + url.join('/');
};
/**
* Render given markdown into html, returning as a jquery element.
* Optionally absolutify relative href/src attributes using the parameter relative_url_root
*/
var render_markdown = function(md_contents, relative_url_root) {
var div = $('<div>');
// the bulk of this functon is adapted from
// notebook/js/textcell.Markdowncell.render
// with the addition of code to absolutify relative href/src attributes
if (md_contents) {
var text_and_math = mathjaxutils.remove_math(md_contents);
var text = text_and_math[0];
var math = text_and_math[1];
var options = custom_marked_options;
if (relative_url_root) {
// patch the renderer to fix relative paths to be absolute
var renderer = new marked.Renderer();
var base_renderer_link = renderer.link;
renderer.link = function (href, title, text) {
if (!/^#|mailto:|(f|ht)tps?:\/\//i.test(href)) {
href = join_relative_urls(relative_url_root, href);
}
return base_renderer_link.call(this, href, title, text);
};
base_renderer_image = renderer.image;
renderer.image = function (href, title, text) {
if (!/^(f|ht)tps?:\/\//i.test(href)) {
href = join_relative_urls(relative_url_root, href);
}
return base_renderer_image.call(this, href, title, text);
};
options = $.extend(custom_marked_options, {renderer: renderer});
}
marked(text, options, function (err, html) {
html = mathjaxutils.replace_math(html, math);
html = security.sanitize_html(html);
html = $($.parseHTML(html));
// add anchors to headings
html.find(":header").addBack(":header").each(function (i, h) {
h = $(h);
var hash = h.text().replace(/ /g, '-');
h.attr('id', hash);
h.append(
$('<a/>')
.addClass('anchor-link')
.attr('href', '#' + hash)
.text('¶')
);
});
// links in markdown cells should open in new tabs
html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
div.html(html);
});
}
return div;
};
var render_markdown_page = function() {
// add css first so hopefully it'll be loaded in time
add_markdown_css();
page = new page.Page();
page.show_header();
var base_url = utils.get_body_data('baseUrl');
var md_url = $('body').data('md-url');
var url = base_url + md_url;
$.ajax({
url: url,
dataType: 'text', // or 'html', 'xml', 'more'
success: function(md_contents) {
$("#render-container").append(render_markdown(md_contents));
},
error: function(jqXHR, textStatus, errorThrown) {
$(".nbext-page-title-wrap").append(
$('<span class="nbext-page-title text-danger"/>').text(
textStatus + ' : ' + jqXHR.status + ' ' + errorThrown
)
);
$("#render-container").addClass("text-danger bg-danger");
var body_txt = "";
switch (jqXHR.status) {
case 404:
body_txt = 'no markdown file at ' + url;
break;
}
$("#render-container").append(body_txt);
},
complete: function(jqXHR, textStatus) {
page.show();
// See http://stackoverflow.com/questions/13735912
var el = $(window.location.hash);
if (el.length > 0) el[0].scrollIntoView();
}
});
};
/**
* Add CSS file to page
*
* @param url where to get css from. Will be wrapped by require.toUrl
*/
var add_css = function (url) {
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = require.toUrl(url);
document.getElementsByTagName("head")[0].appendChild(link);
};
/**
* Add the specific markdown CSS file to page
*/
var add_markdown_css = function () {
add_css('./rendermd.css');
};
// expose functions
return {
add_markdown_css : add_markdown_css,
custom_marked_options : custom_marked_options,
join_relative_urls : join_relative_urls,
render_markdown : render_markdown,
render_markdown_page : render_markdown_page
};
});
-80
View File
@@ -1,80 +0,0 @@
/* CSS overrides for the rendermd page */
/* the render container should match the notebook container*/
#render-container {
padding: 15px;
-moz-box-shadow: 0 0 12px 1px rgba(87, 87, 87, 0.2);
-webkit-box-shadow: 0 0 12px 1px rgba(87, 87, 87, 0.2);
box-shadow: 0 0 12px 1px rgba(87, 87, 87, 0.2);
min-height: calc(100% - 30px);
}
/* rendermd-page-title-wrap and rendermd-page-title
are styled to match the notebook save widget */
.rendermd-page-title-wrap {
margin-top: 6px;
}
.rendermd-page-title {
height: 1em;
line-height: 1em;
padding: 3px;
margin-left: 16px;
border: none;
font-size: 146.5%;
border-radius: 2px;
}
.rendered_html pre {
background-color: #f5f5f5;
padding: 4px;
}
.rendered_html code {
background-color: #f9f2f4;
padding: 2px 4px;
}
.rendered_html pre code {
background-color: transparent;
padding: 0;
}
.rendered_html ol li,
.rendered_html ul li {
margin-bottom: 0.5em;
}
* + .embed-responsive {
margin-top: 1em;
}
/* bootstrap's embed-responsive-item class, as applied to images */
.embed-responsive > img {
position: absolute;
top: 0;
left: 0;
bottom: 0;
height: 100%;
width: 100%;
border: 0;
}
.embed-responsive > a {
content: "";
position: absolute;
top: 37.5%;
left: 37.5%;
right: 37.5%;
bottom: 37.5%;
z-index: 100;
background: transparent url(video_play.svg) no-repeat center;
background-size: contain;
}
.embed-responsive > a:hover {
top: 35%;
left: 35%;
right: 35%;
bottom: 35%;
}
-29
View File
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="512px"
height="512px"
viewBox="0 0 512 512"
enable-background="new 0 0 512 512"
xml:space="preserve"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><path
style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke-width:1.77165353;stroke-miterlimit:4;stroke-dasharray:none;stroke:none"
d="M 256,2e-6 A 256,256 0 0 0 0,256 256,256 0 0 0 256,512 256,256 0 0 0 512,256 256,256 0 0 0 256,2e-6 Z m 0,42 A 214,214 0 0 1 470,256 214,214 0 0 1 256,470 214,214 0 0 1 42,256 214,214 0 0 1 256,42.000002 Z"
id="path4225" /><path
style="opacity:0.5;fill:#000000;fill-opacity:1;stroke-width:1.77165353;stroke-miterlimit:4;stroke-dasharray:none;stroke:none"
d="M 256 42 A 214 214 0 0 0 42 256 A 214 214 0 0 0 256 470 A 214 214 0 0 0 470 256 A 214 214 0 0 0 256 42 z M 176 120 L 378 255.49414 L 176 392 L 176 120 z "
id="path4227" /><path
style="opacity:0.5;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 176,392 0,-272 202,135.49438 z"
id="path4229" /></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

+1
View File
@@ -92,6 +92,7 @@ setup(name='Python-contrib-nbextensions',
license='BSD',
install_requires=[
'jupyter_core',
'jupyter_nbextensions_configurator',
'nbconvert',
'notebook',
'psutil >= 2.2.1',
-74
View File
@@ -1,74 +0,0 @@
{% extends "page.html" %}
{% block title %}{{page_title}}{% endblock %}
{% block stylesheet %}
{{super()}}
{% endblock %}
{% block params %}
data-base-url='{{base_url}}'
data-extension-list='{{extension_list}}'
{% endblock %}
{% block headercontainer %}
<div class="pull-left nbext-page-title-wrap">
<span class="nbext-page-title">
Configuration for Notebook Extensions
(<a href="{{base_url}}nbextensions/config/rendermd/nbextensions/config/readme.md">more information</a>)
</span>
</div>
{% endblock %}
{% block header %}
{% endblock %}
{% block site %}
<div id="notebook-container" class="container">
<div class="row nbext-row container-fluid nbext-selector">
<h4>Configurable extensions</h4>
<div class="nbext-showhide-incompat">
disable configuration for extensions without explicit compatibility
(they may break your notebook environment, but can be useful to show for extension development)
</div>
<nav class="row">
<ul class="nav nav-pills nav-stacked col-md-3"></ul>
<ul class="nav nav-pills nav-stacked col-md-3"></ul>
<ul class="nav nav-pills nav-stacked col-md-3"></ul>
<ul class="nav nav-pills nav-stacked col-md-3"></ul>
</nav>
</div>
<div class="row nbext-readme">
<h3></h3>
<div class="nbext-readme-contents"></div>
</div>
</div>
{% endblock %}
{% block script %}
{{super()}}
<script type="text/javascript" charset="utf-8">
require(['jquery'], function (jq) {
// hack to fix notebook 4.2.1
// see https://github.com/jupyter/notebook/pull/1399
if (jq === undefined) {
require.undef('jquery');
require.undef('jquery-ui');
require.undef('jqueryui');
require.undef('bootstrap');
}
require(['nbextensions/config/main'], function (nbext_config_module) {
nbext_config_module.build_page();
});
});
</script>
{% endblock %}
-55
View File
@@ -1,55 +0,0 @@
{% extends "page.html" %}
{% block title %}{{page_title}}{% endblock %}
{% block stylesheet %}
{{super()}}
{% endblock %}
{% block params %}
data-base-url='{{base_url}}'
data-md-url='{{md_url}}'
{% endblock %}
{% block headercontainer %}
<div class="pull-left rendermd-page-title-wrap">
<span class="rendermd-page-title">{{ page_title }}</span>
</div>
{% endblock %}
{% block header %}
{% endblock %}
{% block site %}
{# the "rendered_html" class, will make everything look like notebook-formatted markdown cells. The custom css loaded by render.js is used to add backgrounds for <code> and <pre> blocks and snippets #}
<div id="render-container" class="container rendered_html"></div>
{% endblock %}
{% block script %}
{{super()}}
<script type="text/javascript" charset="utf-8">
require(['jquery'], function (jq) {
// hack to fix notebook 4.2.1
// see https://github.com/jupyter/notebook/pull/1399
if (jq === undefined) {
require.undef('jquery');
require.undef('jquery-ui');
require.undef('jqueryui');
require.undef('bootstrap');
}
require(["nbextensions/config/render/render"], function(rendermd_module) {
rendermd_module.render_markdown_page();
});
});
</script>
{% endblock %}