Merge branch '6.5.x' into selenium_test_updates

pull/6484/head
Eric Gentry 4 years ago committed by GitHub
commit 446d02eb12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +0,0 @@
{
"directory": "notebook/static/components"
}

@ -61,6 +61,7 @@ jobs:
uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
version_spec: 100.100.100
- name: Check Links
if: ${{ matrix.group == 'link_check' }}
uses: jupyter-server/jupyter_releaser/.github/actions/check-links@v1

@ -1,3 +1,6 @@
# Pull requests to this repo should have a triage label,
# this workflow ensures that this is the case.
name: Enforce PR label
on:

@ -1,65 +0,0 @@
name: Linux JS Tests
on:
push:
branches: '*'
pull_request:
branches: '*'
jobs:
build:
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu, macos]
group: [notebook, base, services]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Set up Node
uses: actions/setup-node@v1
with:
node-version: '12.x'
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Cache pip on Linux
uses: actions/cache@v1
if: startsWith(runner.os, 'Linux')
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python }}-${{ hashFiles('**/requirements.txt', 'setup.py') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python }}
- name: Temporary workaround for sanitizer loading in JS Tests
run: |
cp tools/security_deprecated.js notebook/static/base/js/security.js
- name: Install dependencies
run: |
pip install --upgrade pip
pip install --upgrade setuptools wheel
npm install
npm install -g casperjs@1.1.3 phantomjs-prebuilt@2.1.7
pip install .[test]
- name: Run Tests
run: |
python -m notebook.jstest ${{ matrix.group }}

@ -22,7 +22,7 @@ jobs:
architecture: 'x64'
- name: Upgrade packaging dependencies
run: |
pip install --upgrade pip setuptools wheel --user
python -m pip install --upgrade pip setuptools wheel --user
- name: Get pip cache dir
id: pip-cache
run: |

@ -71,7 +71,7 @@ If you are working in development mode, you will see that your version of Jupyte
Troubleshooting the Installation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you do not see that your Jupyter Notebook is not running on dev mode, it's possible that you are
If you do not see that your Jupyter Notebook is running on dev mode, it's possible that you are
running other instances of Jupyter Notebook. You can try the following steps:
1. Uninstall all instances of the notebook package. These include any installations you made using

@ -3,13 +3,10 @@ include CONTRIBUTING.rst
include README.md
include CHANGELOG.md
include package.json
include bower.json
include .bowerrc
include pyproject.toml
include setup.py
include setupbase.py
include Dockerfile
include *.js
graft tools
graft notebook/tests

@ -1,30 +0,0 @@
{
"name": "jupyter-notebook-deps",
"version": "0.0.1",
"dependencies": {
"backbone": "components/backbone#~1.2",
"bootstrap": "bootstrap#~3.4",
"bootstrap-tour": "0.9.0",
"codemirror": "components/codemirror#5.56.0+components1",
"create-react-class": "https://cdn.jsdelivr.net/npm/create-react-class@15.6.3/create-react-class.min.js",
"es6-promise": "~1.0",
"font-awesome": "components/font-awesome#~4.7.0",
"google-caja": "5669",
"jed": "~1.1.1",
"jquery": "components/jquery#~3.5.0",
"jquery-typeahead": "~2.10.6",
"jquery-ui": "components/jqueryui#~1.12",
"marked": "~0.7",
"MathJax": "^2.7.4",
"moment": "~2.19.3",
"react": "~16.0.0",
"requirejs": "~2.2",
"requirejs-text": "~2.0.15",
"requirejs-plugins": "~1.0.3",
"text-encoding": "~0.1",
"underscore": "components/underscore#~1.8.3",
"xterm.js": "https://unpkg.com/xterm@~3.1.0/dist/xterm.js",
"xterm.js-css": "https://unpkg.com/xterm@~3.1.0/dist/xterm.css",
"xterm.js-fit": "https://unpkg.com/xterm@~3.1.0/dist/addons/fit/fit.js"
}
}

@ -13,6 +13,7 @@ dependencies:
- sphinx
- terminado
- myst-parser
- nbclassic>=0.4.0
- pip:
- nbsphinx
- Send2Trash

@ -134,7 +134,7 @@
"\n",
"> Of course, in addition to the files listed, there are number of other files one needs to build a proper package. Here are some good resources:\n",
"- [The Hitchhiker's Guide to Packaging](https://the-hitchhikers-guide-to-packaging.readthedocs.io/en/latest/quickstart.html)\n",
"- [Repository Structure and Python](https://kenreitz.org/essays/2013/01/27/repository-structure-and-python) by Kenneth Reitz\n",
"- [Structure of the Repository](https://docs.python-guide.org/writing/structure/) by Kenneth Reitz and Real Python\n",
"\n",
"> How you distribute them, too, is important:\n",
"- [Packaging and Distributing Projects](https://python-packaging-user-guide.readthedocs.io/tutorials/distributing-packages/)\n",

@ -1,8 +1,6 @@
"""The Jupyter HTML Notebook"""
import os
# Packagers: modify this line if you store the notebook static files elsewhere
DEFAULT_STATIC_FILES_PATH = os.path.join(os.path.dirname(__file__), "static")
# Packagers: modify the next line if you store the notebook template files
# elsewhere

@ -14,7 +14,3 @@ parts = [int(match[part]) for part in ['major', 'minor', 'patch']]
if match['rest']:
parts.append(match['rest'])
version_info = tuple(parts)
# Downstream maintainer, when running `python.setup.py jsversion`,
# the version string is propagated to the JavaScript files, do not forget to
# patch the JavaScript files in `.postN` release done by distributions.

@ -66,11 +66,12 @@ if not sys.platform.startswith('win'):
from notebook import (
DEFAULT_NOTEBOOK_PORT,
DEFAULT_STATIC_FILES_PATH,
DEFAULT_TEMPLATE_PATH_LIST,
__version__,
)
import nbclassic
# Packagers: modify this line if you store the notebook static files elsewhere
DEFAULT_STATIC_FILES_PATH = os.path.join(os.path.dirname(nbclassic.__file__), "static")
from .base.handlers import Template404, RedirectWithParams
from .log import log_request

@ -1,61 +0,0 @@
//This is file created for overwriting some of bootstrap element color in order to satisfiy the color contrast greater than 4.5:1.
.btn-danger{
color: #ffffff;
background-color: #df0404;
border-color: #df0404;
}
.btn-warning{
color: #ffffff;
background-color:#b46102;
border-color: #b46102;
}
@link-color: #296eaa;
.close {
float: right;
font-size: (@font-size-base * 1.5);
font-weight: @close-font-weight;
line-height: 1;
color: @close-color;
text-shadow: @close-text-shadow;
.opacity(.6);
&:hover,
&:focus {
color: @close-color;
text-decoration: none;
cursor: pointer;
.opacity(1.0);
} button& {
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
appearance: none;
}
}
.navbar-nav {
> li > a {
color: @navbar-default-link-color;
// To make keyboard focus clearly visible on the Controls(File,Edit,View,Insert,Cell...etc)present on the menubar.
//&:hover, //To use browser's hover default value
&:focus {
/* -webkit-focus-ring-color = '#5B9DD9' */
outline: -webkit-focus-ring-color auto 5px;
// color: @navbar-default-link-hover-color;
// background-color: @navbar-default-link-hover-bg;
}
}
}
.menu_focus_highlight{
a:focus {
outline: -webkit-focus-ring-color auto 5px;
}
}

@ -1,8 +0,0 @@
/*This file contains any manual css for this page that needs to override the global styles.
This is only required when different pages style the same element differently. This is just
a hack to deal with our current css styles and no new styling should be added in this file.*/
#ipython-main-app {
padding-top: 50px;
text-align: center;
}

@ -1,12 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['jquery', 'base/js/namespace', 'base/js/page'], function($, IPython, page) {
function login_main() {
var page_instance = new page.Page('div#header', 'div#site');
page_instance.show();
$('input#password_input').focus();
IPython.page = page_instance;
}
return login_main;
});

@ -1,38 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
], function($, utils){
"use strict";
var LoginWidget = function (selector, options) {
options = options || {};
this.base_url = options.base_url || utils.get_body_data("baseUrl");
this.selector = selector;
if (this.selector !== undefined) {
this.element = $(selector);
this.bind_events();
}
};
LoginWidget.prototype.bind_events = function () {
var that = this;
this.element.find("button#logout").click(function () {
window.location = utils.url_path_join(
that.base_url,
"logout"
);
});
this.element.find("button#login").click(function () {
window.location = utils.url_path_join(
that.base_url,
"login"
);
});
};
return {'LoginWidget': LoginWidget};
});

@ -1,12 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['base/js/namespace', 'base/js/page'], function(IPython, page) {
function logout_main() {
var page_instance = new page.Page('div#header', 'div#site');
page_instance.show();
IPython.page = page_instance;
}
return logout_main;
});

@ -1,12 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['./loginmain', './logoutmain', 'bidi/bidi'], function (login_main, logout_main, bidi) {
if(bidi.isMirroringEnabled()){
$("body").attr("dir","rtl");
}
return {
login_main: login_main,
logout_main: logout_main
};
});

@ -1,23 +0,0 @@
// Custom styles for login.html
.center-nav {
display: inline-block;
// pull the lower margin back
margin-bottom: -4px;
}
[dir="rtl"] .center-nav {
form.pull-left {
.pull-right();
}
.navbar-text {
float: right;
}
}
[dir="rtl"] .navbar-inner {
text-align: right;
}
[dir="rtl"] div.text-left {
.text-right();
}

@ -1,2 +0,0 @@
// Custom styles for logout.html

@ -1,7 +0,0 @@
/*!
*
* IPython auth
*
*/
@import "login.less";
@import "logout.less";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

@ -1,433 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['jquery',
'codemirror/lib/codemirror',
'bootstrap',
'base/js/i18n'],
function($, CodeMirror, bs, i18n) {
"use strict";
/**
* A wrapper around bootstrap modal for easier use
* Pass it an option dictionary with the following properties:
*
* - body : <string> or <DOM node>, main content of the dialog
* if pass a <string> it will be wrapped in a p tag and
* html element escaped, unless you specify sanitize=false
* option.
* - title : Dialog title, default to empty string.
* - buttons : dict of btn_options who keys are button label.
* see btn_options below for description
* - open : callback to trigger on dialog open.
* - destroy:
* - notebook : notebook instance
* - keyboard_manager: keyboard manager instance.
*
* Unlike bootstrap modals, the backdrop options is set by default
* to 'static'.
*
* The rest of the options are passed as is to bootstrap modals.
*
* btn_options: dict with the following property:
*
* - click : callback to trigger on click
* - class : css classes to add to button.
*
*
*
**/
var modal = function (options) {
var modal = $("<div/>")
.addClass("modal")
.addClass("fade")
.attr("role", "dialog");
var dialog = $("<div/>")
.addClass("modal-dialog")
.appendTo(modal);
var dialog_content = $("<div/>")
.addClass("modal-content")
.appendTo(dialog);
if(typeof(options.body) === 'string' && options.sanitize !== false){
options.body = $("<p/>").text(options.body);
}
dialog_content.append(
$("<div/>")
.addClass("modal-header")
.mousedown(function() {
$(".modal").draggable({handle: '.modal-header'});
})
.append($("<button>")
.attr("type", "button")
.attr("aria-label", i18n.msg._("close"))
.addClass("close")
.attr("data-dismiss", "modal")
.attr("aria-hidden", "true")
.html("&times;")
).append(
$("<h4/>")
.addClass('modal-title')
.text(options.title || "")
)
).append(
$("<div/>")
.addClass("modal-body")
.append(
options.body || $("<p/>")
)
);
var footer = $("<div/>").addClass("modal-footer");
var default_button;
for (var label in options.buttons) {
var btn_opts = options.buttons[label];
var button = $("<button/>")
.addClass("btn btn-default btn-sm")
.attr("data-dismiss", "modal")
.text(i18n.msg.translate(label).fetch());
if (btn_opts.id) {
button.attr('id', btn_opts.id);
}
if (btn_opts.click) {
button.click($.proxy(btn_opts.click, dialog_content));
}
if (btn_opts.class) {
button.addClass(btn_opts.class);
}
footer.append(button);
if (options.default_button && label === options.default_button) {
default_button = button;
}
}
if (!options.default_button) {
default_button = footer.find("button").last();
}
dialog_content.append(footer);
// hook up on-open event
modal.on("shown.bs.modal", function () {
setTimeout(function () {
default_button.focus();
if (options.open) {
$.proxy(options.open, modal)();
}
}, 0);
});
// destroy modal on hide, unless explicitly asked not to
if (options.destroy === undefined || options.destroy) {
modal.on("hidden.bs.modal", function () {
modal.remove();
});
}
modal.on("hidden.bs.modal", function () {
if (options.notebook) {
var cell = options.notebook.get_selected_cell();
if (cell) cell.select();
}
if (options.keyboard_manager) {
options.keyboard_manager.enable();
options.keyboard_manager.command_mode();
}
if (options.focus_button) {
$(options.focus_button).focus();
}
});
if (options.keyboard_manager) {
options.keyboard_manager.disable();
}
if(options.backdrop === undefined){
options.backdrop = 'static';
}
return modal.modal(options);
};
var kernel_modal = function (options) {
/**
* only one kernel dialog should be open at a time -- but
* other modal dialogs can still be open
*/
$('.kernel-modal').modal('hide');
var dialog = modal(options);
dialog.addClass('kernel-modal');
return dialog;
};
var edit_metadata = function (options) {
options.name = options.name || "Cell";
var error_div = $('<div/>').css('color', 'red');
var message_cell =
i18n.msg._("Manually edit the JSON below to manipulate the metadata for this cell.");
var message_notebook =
i18n.msg._("Manually edit the JSON below to manipulate the metadata for this notebook.");
var message_end =
i18n.msg._(" We recommend putting custom metadata attributes in an appropriately named substructure," +
" so they don't conflict with those of others.");
var message;
if (options.name === 'Notebook') {
message = message_notebook + message_end;
} else {
message = message_cell + message_end;
}
var textarea = $('<textarea/>')
.attr('rows', '13')
.attr('cols', '80')
.attr('name', 'metadata')
.text(JSON.stringify(options.md || {}, null, 2));
var dialogform = $('<div/>').attr('title', i18n.msg._('Edit the metadata'))
.append(
$('<form/>').append(
$('<fieldset/>').append(
$('<label/>')
.attr('for','metadata')
.text(message)
)
.append(error_div)
.append($('<br/>'))
.append(textarea)
)
);
var editor = CodeMirror.fromTextArea(textarea[0], {
lineNumbers: true,
matchBrackets: true,
indentUnit: 2,
autoIndent: true,
mode: 'application/json',
});
var title_msg;
if (options.name === "Notebook") {
title_msg = i18n.msg._("Edit Notebook Metadata");
} else {
title_msg = i18n.msg._("Edit Cell Metadata");
}
// This statement is used simply so that message extraction
// will pick up the strings.
var button_labels = [ i18n.msg._("Cancel"), i18n.msg._("Edit"), i18n.msg._("OK"), i18n.msg._("Apply")];
var modal_obj = modal({
title: title_msg,
body: dialogform,
default_button: "Cancel",
buttons: {
Cancel: {},
Edit: { class : "btn-primary",
click: function() {
/**
* validate json and set it
*/
var new_md;
try {
new_md = JSON.parse(editor.getValue());
} catch(e) {
console.log(e);
error_div.text(i18n.msg._('WARNING: Could not save invalid JSON.'));
return false;
}
options.callback(new_md);
options.notebook.apply_directionality();
}
}
},
notebook: options.notebook,
keyboard_manager: options.keyboard_manager,
});
modal_obj.on('shown.bs.modal', function(){ editor.refresh(); });
modal_obj.on('hide.bs.modal', function(){
options.edit_metadata_button ? options.edit_metadata_button.focus() : "";});
};
var edit_attachments = function (options) {
// This shows the Edit Attachments dialog. This dialog allows the
// user to delete attachments. We show a list of attachments to
// the user and he can mark some of them for deletion. The deletion
// is applied when the 'Apply' button of this dialog is pressed.
var message;
var attachments_list;
if (Object.keys(options.attachments).length == 0) {
message = i18n.msg._("There are no attachments for this cell.");
attachments_list = $('<div>');
} else {
message = i18n.msg._("Current cell attachments");
attachments_list = $('<div>')
.addClass('list_container')
.append(
$('<div>')
.addClass('row list_header')
.append(
$('<div>')
.text(i18n.msg._('Attachments'))
)
);
// This is a set containing keys of attachments to be deleted when
// the Apply button is clicked
var to_delete = {};
var refresh_attachments_list = function() {
$(attachments_list).find('.row').remove();
for (var key in options.attachments) {
var mime = Object.keys(options.attachments[key])[0];
var deleted = key in to_delete;
// This ensures the current value of key is captured since
// javascript only has function scope
var btn;
// Trash/restore button
(function(){
var _key = key;
btn = $('<button>')
.addClass('btn btn-default btn-xs')
.css('display', 'inline-block');
if (deleted) {
btn.attr('title', i18n.msg._('Restore'))
.append(
$('<i>')
.addClass('fa fa-plus')
);
btn.click(function() {
delete to_delete[_key];
refresh_attachments_list();
});
} else {
btn.attr('title', i18n.msg._('Delete'))
.addClass('btn-danger')
.append(
$('<i>')
.addClass('fa fa-trash')
);
btn.click(function() {
to_delete[_key] = true;
refresh_attachments_list();
});
}
return btn;
})();
var row = $('<div>')
.addClass('col-md-12 att_row')
.append(
$('<div>')
.addClass('row')
.append(
$('<div>')
.addClass('att-name col-xs-4')
.text(key)
)
.append(
$('<div>')
.addClass('col-xs-4 text-muted')
.text(mime)
)
.append(
$('<div>')
.addClass('item-buttons pull-right')
.append(btn)
)
);
if (deleted) {
row.find('.att-name')
.css('text-decoration', 'line-through');
}
attachments_list.append($('<div>')
.addClass('list_item row')
.append(row)
);
}
};
refresh_attachments_list();
}
var dialogform = $('<div/>')
.attr('title', i18n.msg._('Edit attachments'))
.append(message)
.append('<br />')
.append(attachments_list);
var title_msg;
if ( options.name === "Notebook" ) {
title_msg = i18n.msg._("Edit Notebook Attachments");
} else {
title_msg = i18n.msg._("Edit Cell Attachments");
}
var modal_obj = modal({
title: title_msg,
body: dialogform,
buttons: {
Apply: { class : "btn-primary",
click: function() {
for (var key in to_delete) {
delete options.attachments[key];
}
options.callback(options.attachments);
}
},
Cancel: {}
},
notebook: options.notebook,
keyboard_manager: options.keyboard_manager,
});
};
var insert_image = function (options) {
var message =
i18n.msg._("Select a file to insert.");
var file_input = $('<input/>')
.attr('type', 'file')
.attr('accept', 'image/*')
.attr('name', 'file')
.on('change', function(file) {
var $btn = $(modal_obj).find('#btn_ok');
if (this.files.length > 0) {
$btn.removeClass('disabled');
} else {
$btn.addClass('disabled');
}
});
var dialogform = $('<div/>').attr('title', i18n.msg._('Edit attachments'))
.append(
$('<form id="insert-image-form" />').append(
$('<fieldset/>').append(
$('<label/>')
.attr('for','file')
.text(message)
)
.append($('<br/>'))
.append(file_input)
)
);
var modal_obj = modal({
title: i18n.msg._("Select a file"),
body: dialogform,
buttons: {
OK: {
id : 'btn_ok',
class : "btn-primary disabled",
click: function() {
options.callback(file_input[0].files[0]);
}
},
Cancel: {}
},
notebook: options.notebook,
keyboard_manager: options.keyboard_manager,
});
};
var dialog = {
modal : modal,
kernel_modal : kernel_modal,
edit_metadata : edit_metadata,
edit_attachments : edit_attachments,
insert_image : insert_image
};
return dialog;
});

@ -1,37 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// Give us an object to bind all events to. This object should be created
// before all other objects so it exists when others register event handlers.
// To register an event handler:
//
// requirejs(['base/js/events'], function (events) {
// events.on("event.Namespace", function () { do_stuff(); });
// });
define(['jquery', 'base/js/namespace'], function($, Jupyter) {
"use strict";
// Events singleton
if (!window._Events) {
window._Events = function () {};
window._events = new window._Events();
}
// Backwards compatibility.
Jupyter.Events = window._Events;
Jupyter.events = window._events;
var events = $([window._events]);
// catch and log errors in triggered events
events._original_trigger = events.trigger;
events.trigger = function (name, data) {
try {
this._original_trigger.apply(this, arguments);
} catch (e) {
console.error("Exception in event handler for " + name, e, arguments);
}
}
return events;
});

@ -1,16 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// Module to handle i18n ( Internationalization ) and translated UI
define([
'jed'
], function(Jed) {
"use strict";
var i18n = new Jed(document.nbjs_translations);
i18n._ = i18n.gettext;
i18n.msg = i18n; // Just a place holder until the init promise resolves.
return i18n;
});

@ -1,576 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
/**
*
*
* @module keyboard
* @namespace keyboard
* @class ShortcutManager
*/
define([
'jquery',
'base/js/utils',
'underscore',
], function($, utils, _) {
"use strict";
/**
* Setup global keycodes and inverse keycodes.
*
* See http://unixpapa.com/js/key.html for a complete description. The short of
* it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
* and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
* but have minor differences.
**/
// These apply to Firefox, (Webkit and IE)
// This does work **only** on US keyboard.
var _keycodes = {
'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
'1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
'7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
'[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
'\\ |': 220, '\' "': 222,
'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
'f8': 119, 'f9': 120, 'f10': 121, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
'insert': 45, 'delete': 46, 'numlock': 144,
};
// These apply to Firefox and Opera
var _mozilla_keycodes = {
'; :': 59, '= +': 61, '- _': 173, 'meta': 224, 'minus':173
};
// This apply to Webkit and IE
var _ie_keycodes = {
'; :': 186, '= +': 187, '- _': 189, 'minus':189
};
var browser = utils.browser[0];
var platform = utils.platform;
if (browser === 'Firefox' || browser === 'Opera' || browser === 'Netscape') {
$.extend(_keycodes, _mozilla_keycodes);
} else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
$.extend(_keycodes, _ie_keycodes);
}
var keycodes = {};
var inv_keycodes = {};
for (var name in _keycodes) {
var names = name.split(' ');
if (names.length === 1) {
var n = names[0];
keycodes[n] = _keycodes[n];
inv_keycodes[_keycodes[n]] = n;
} else {
var primary = names[0];
var secondary = names[1];
keycodes[primary] = _keycodes[name];
keycodes[secondary] = _keycodes[name];
inv_keycodes[_keycodes[name]] = primary;
}
}
var normalize_key = function (key) {
return inv_keycodes[keycodes[key]];
};
var normalize_shortcut = function (shortcut) {
/**
* @function _normalize_shortcut
* @private
* return a dict containing the normalized shortcut and the number of time it should be pressed:
*
* Put a shortcut into normalized form:
* 1. Make lowercase
* 2. Replace cmd by meta
* 3. Sort '-' separated modifiers into the order alt-ctrl-meta-shift
* 4. Normalize keys
**/
if (platform === 'MacOS') {
shortcut = shortcut.toLowerCase().replace('cmdtrl-', 'cmd-');
} else {
shortcut = shortcut.toLowerCase().replace('cmdtrl-', 'ctrl-');
}
shortcut = shortcut.toLowerCase().replace('cmd', 'meta');
shortcut = shortcut.replace(/-$/, 'minus'); // catch shortcuts using '-' key
shortcut = shortcut.replace(/,$/, 'comma'); // catch shortcuts using '-' key
if(shortcut.indexOf(',') !== -1){
var sht = shortcut.split(',');
sht = _.map(sht, normalize_shortcut);
return shortcut;
}
shortcut = shortcut.replace(/comma/g, ','); // catch shortcuts using '-' key
var values = shortcut.split("-");
if (values.length === 1) {
return normalize_key(values[0]);
} else {
var modifiers = values.slice(0,-1);
var key = normalize_key(values[values.length-1]);
modifiers.sort();
return modifiers.join('-') + '-' + key;
}
};
var shortcut_to_event = function (shortcut, type) {
/**
* Convert a shortcut (shift-r) to a jQuery Event object
**/
type = type || 'keydown';
shortcut = normalize_shortcut(shortcut);
shortcut = shortcut.replace(/-$/, 'minus'); // catch shortcuts using '-' key
var values = shortcut.split("-");
var modifiers = values.slice(0,-1);
var key = values[values.length-1];
var opts = {which: keycodes[key]};
if (modifiers.indexOf('alt') !== -1) {opts.altKey = true;}
if (modifiers.indexOf('ctrl') !== -1) {opts.ctrlKey = true;}
if (modifiers.indexOf('meta') !== -1) {opts.metaKey = true;}
if (modifiers.indexOf('shift') !== -1) {opts.shiftKey = true;}
return $.Event(type, opts);
};
var only_modifier_event = function(event){
/**
* Return `true` if the event only contains modifiers keys.
* false otherwise
**/
var key = inv_keycodes[event.which];
return ((event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) &&
(key === 'alt'|| key === 'ctrl'|| key === 'meta'|| key === 'shift'));
};
var event_to_shortcut = function (event) {
/**
* Convert a jQuery Event object to a normalized shortcut string (shift-r)
**/
var shortcut = '';
var key = inv_keycodes[event.which];
if (event.altKey && key !== 'alt') {shortcut += 'alt-';}
if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl-';}
if (event.metaKey && key !== 'meta') {shortcut += 'meta-';}
if (event.shiftKey && key !== 'shift') {shortcut += 'shift-';}
shortcut += key;
return shortcut;
};
// Shortcut manager class
var ShortcutManager = function (delay, events, actions, env, config, mode) {
/**
* A class to deal with keyboard event and shortcut
*
* @class ShortcutManager
* @constructor
*
* :config: configobjet on which to call `update(....)` to persist the config.
* :mode: mode of this shortcut manager where to persist config.
*/
mode = mode || 'command';
this._shortcuts = {};
this._defaults_bindings = [];
this.delay = delay || 800; // delay in milliseconds
this.events = events;
this.actions = actions;
this.actions.extend_env(env);
this._queue = [];
this._cleartimeout = null;
this._config = config;
this._mode = mode;
Object.seal(this);
};
ShortcutManager.prototype.clearsoon = function(){
/**
* Clear the pending shortcut soon, and cancel previous clearing
* that might be registered.
**/
var that = this;
clearTimeout(this._cleartimeout);
this._cleartimeout = setTimeout(function(){that.clearqueue();}, this.delay);
};
ShortcutManager.prototype.clearqueue = function(){
/**
* clear the pending shortcut sequence now.
**/
this._queue = [];
clearTimeout(this._cleartimeout);
};
var flatten_shorttree = function(tree){
/**
* Flatten a tree of shortcut sequences.
* use full to iterate over all the key/values of available shortcuts.
**/
var dct = {};
_.forEach(tree, function(value, key) {
if(typeof(value) === 'string'){
dct[key] = value;
} else {
var ftree=flatten_shorttree(value);
_.forEach(ftree, function(v2, subkey) {
dct[key+','+subkey] = ftree[subkey];
});
}
});
return dct;
};
ShortcutManager.prototype.get_action_shortcuts = function(name){
var ftree = flatten_shorttree(this._shortcuts);
var res = [];
_.forEach(ftree, function(value, key) {
if(value === name){
res.push(key);
}
});
return res;
};
ShortcutManager.prototype.get_action_shortcut = function(name){
var matches = this.get_action_shortcuts(name);
if (matches.length > 0) {
return matches[0];
}
return undefined;
};
ShortcutManager.prototype.help = function () {
var that = this;
var help = [];
var ftree = flatten_shorttree(this._shortcuts);
_.forEach(ftree, function(value, key) {
var action = that.actions.get(value);
var help_string = action.help||'== no help ==';
var help_index = action.help_index;
if (help_string) {
var shortstring = (action.shortstring||key);
help.push({
shortcut: shortstring,
help: help_string,
help_index: help_index}
);
}
});
help.sort(function (a, b) {
if (a.help_index === b.help_index) {
if (a.shortcut === b.shortcut) {
return 0;
}
if (a.shortcut > b.shortcut) {
return 1;
}
return -1;
}
if (a.help_index === undefined || a.help_index > b.help_index){
return 1;
}
return -1;
});
return help;
};
ShortcutManager.prototype.clear_shortcuts = function () {
this._shortcuts = {};
};
ShortcutManager.prototype.get_shortcut = function (shortcut){
/**
* return a node of the shortcut tree which an action name (string) if leaf,
* and an object with `object.subtree===true`
**/
if(typeof(shortcut) === 'string'){
shortcut = shortcut.split(',');
}
return this._get_leaf(shortcut, this._shortcuts);
};
ShortcutManager.prototype._get_leaf = function(shortcut_array, tree){
/**
* @private
* find a leaf/node in a subtree of the keyboard shortcut
*
**/
if(shortcut_array.length === 1){
return tree[shortcut_array[0]];
} else if( typeof(tree[shortcut_array[0]]) !== 'string'){
return this._get_leaf(shortcut_array.slice(1), tree[shortcut_array[0]]);
}
return null;
};
ShortcutManager.prototype.set_shortcut = function( shortcut, action_name){
if( typeof(action_name) !== 'string'){throw new Error('action is not a string', action_name);}
if( typeof(shortcut) === 'string'){
shortcut = shortcut.split(',');
}
return this._set_leaf(shortcut, action_name, this._shortcuts);
};
ShortcutManager.prototype._is_leaf = function(shortcut_array, tree){
if(shortcut_array.length === 1){
return(typeof(tree[shortcut_array[0]]) === 'string');
} else {
var subtree = tree[shortcut_array[0]];
return this._is_leaf(shortcut_array.slice(1), subtree );
}
};
ShortcutManager.prototype._remove_leaf = function(shortcut_array, tree, allow_node){
if(shortcut_array.length === 1){
var current_node = tree[shortcut_array[0]];
if(typeof(current_node) === 'string'){
delete tree[shortcut_array[0]];
} else {
throw new Error('try to delete non-leaf');
}
} else {
this._remove_leaf(shortcut_array.slice(1), tree[shortcut_array[0]], allow_node);
if(_.keys(tree[shortcut_array[0]]).length === 0){
delete tree[shortcut_array[0]];
}
}
};
ShortcutManager.prototype.is_available_shortcut = function(shortcut){
var shortcut_array = shortcut.split(',');
return this._is_available_shortcut(shortcut_array, this._shortcuts);
};
ShortcutManager.prototype._is_available_shortcut = function(shortcut_array, tree){
var current_node = tree[shortcut_array[0]];
if(!shortcut_array[0]){
return false;
}
if(current_node === undefined){
return true;
} else {
if (typeof(current_node) === 'string'){
return false;
} else { // assume is a sub-shortcut tree
return this._is_available_shortcut(shortcut_array.slice(1), current_node);
}
}
};
ShortcutManager.prototype._set_leaf = function(shortcut_array, action_name, tree){
var current_node = tree[shortcut_array[0]];
if(shortcut_array.length === 1){
if(current_node !== undefined && typeof(current_node) !== 'string'){
console.warn('[warning], you are overriting a long shortcut with a shorter one');
}
tree[shortcut_array[0]] = action_name;
return true;
} else {
if(typeof(current_node) === 'string'){
console.warn('you are trying to set a shortcut that will be shadowed'+
'by a more specific one. Aborting for :', action_name, 'the following '+
'will take precedence', current_node);
return false;
} else {
tree[shortcut_array[0]] = tree[shortcut_array[0]]||{};
}
this._set_leaf(shortcut_array.slice(1), action_name, tree[shortcut_array[0]]);
return true;
}
};
ShortcutManager.prototype._persist_shortcut = function(shortcut, data) {
/**
* add a shortcut to this manager and persist it to the config file.
**/
shortcut = shortcut.toLowerCase();
this.add_shortcut(shortcut, data);
var patch = {keys:{}};
patch.keys[this._mode] = {bind:{}};
patch.keys[this._mode].bind[shortcut] = data;
this._config.update(patch);
};
ShortcutManager.prototype._persist_remove_shortcut = function(shortcut){
/**
* Remove a shortcut from this manager and persist its removal.
*/
shortcut = shortcut.toLowerCase();
this.remove_shortcut(shortcut);
var patch = {keys: {}};
patch.keys[this._mode] = {bind:{}};
patch.keys[this._mode].bind[shortcut] = null;
this._config.update(patch);
// if the shortcut we unbind is a default one, we add it to the list of
// things to unbind at startup
if( this._defaults_bindings.indexOf(shortcut) !== -1 ){
var cnf = (this._config.data.keys || {})[this._mode];
var unbind_array = cnf.unbind || [];
// unless it's already there (like if we have remapped a default
// shortcut to another command): unbind it)
if(unbind_array.indexOf(shortcut) === -1){
var _parray = unbind_array.concat(shortcut);
var unbind_patch = {keys:{}};
unbind_patch.keys[this._mode] = {unbind:_parray};
console.warn('up:', unbind_patch);
this._config.update(unbind_patch);
}
}
};
ShortcutManager.prototype.add_shortcut = function (shortcut, data, suppress_help_update) {
/**
* Add an action to be handled by shortcut manager.
*
* - `shortcut` should be a `Shortcut Sequence` of the for `Ctrl-Alt-C,Meta-X`...
* - `data` could be an `action name`, an `action` or a `function`.
* if a `function` is passed it will be converted to an anonymous `action`.
*
**/
var action_name = this.actions.get_name(data);
if (! action_name){
if (typeof data === 'string') {
// If we have an action name, allow it to be bound anyway.
console.log("Unknown action '" + data + "' for shortcut " + shortcut
+ "; it may be defined by an extension which is not yet loaded.");
action_name = data;
} else {
throw new Error('does not know how to deal with : ' + data);
}
}
var _shortcut = normalize_shortcut(shortcut);
this.set_shortcut(_shortcut, action_name);
if (!suppress_help_update) {
// update the keyboard shortcuts notebook help
this.events.trigger('rebuild.QuickHelp');
}
};
ShortcutManager.prototype.add_shortcuts = function (data) {
/**
* Convenient methods to call `add_shortcut(key, value)` on several items
*
* data : Dict of the form {key:value, ...}
**/
var that = this;
_.forEach(data, function(value, key) {
that.add_shortcut(key, value, true);
});
// update the keyboard shortcuts notebook help
this.events.trigger('rebuild.QuickHelp');
};
ShortcutManager.prototype._add_default_shortcuts = function (data) {
/**
* same as add_shortcuts, but register them as "default" that if persistently unbound, with
* persist_remove_shortcut, need to be on the "unbind" list.
**/
this._defaults_bindings = this._defaults_bindings.concat(Object.keys(data));
this.add_shortcuts(data);
};
ShortcutManager.prototype.remove_shortcut = function (shortcut, suppress_help_update) {
/**
* Remove the binding of shortcut `shortcut` with its action.
* throw an error if trying to remove a non-exiting shortcut
**/
if(!shortcut){
console.warn('trying to remove empty shortcut');
return;
}
shortcut = normalize_shortcut(shortcut);
if( typeof(shortcut) === 'string'){
shortcut = shortcut.split(',');
}
/*
* The shortcut error should be explicit here, because it will be
* seen by users.
*/
try {
this._remove_leaf(shortcut, this._shortcuts);
if (!suppress_help_update) {
// update the keyboard shortcuts notebook help
this.events.trigger('rebuild.QuickHelp');
}
} catch (ex) {
throw new Error('trying to remove a non-existent shortcut', shortcut, typeof shortcut);
}
};
ShortcutManager.prototype.call_handler = function (event) {
/**
* Call the corresponding shortcut handler for a keyboard event
* @method call_handler
* @return {Boolean} `true|false`, `false` if no handler was found, otherwise the value return by the handler.
* @param event {event}
*
* given an event, call the corresponding shortcut.
* return false is event wan handled, true otherwise
* in any case returning false stop event propagation
**/
this.clearsoon();
if(only_modifier_event(event)){
return true;
}
var shortcut = event_to_shortcut(event);
this._queue.push(shortcut);
var action_name = this.get_shortcut(this._queue);
if (typeof(action_name) === 'undefined'|| action_name === null){
this.clearqueue();
return true;
}
if (this.actions.exists(action_name)) {
event.preventDefault();
this.clearqueue();
return this.actions.call(action_name, event);
}
return false;
};
ShortcutManager.prototype.handles = function (event) {
var shortcut = event_to_shortcut(event);
var action_name = this.get_shortcut(this._queue.concat(shortcut));
return (typeof(action_name) !== 'undefined');
};
return {
keycodes : keycodes,
inv_keycodes : inv_keycodes,
ShortcutManager : ShortcutManager,
normalize_key : normalize_key,
normalize_shortcut : normalize_shortcut,
shortcut_to_event : shortcut_to_event,
event_to_shortcut : event_to_shortcut,
};
});

@ -1,117 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/mathjaxutils',
'base/js/security',
'components/marked/lib/marked',
'codemirror/lib/codemirror',
], function($, utils, mathjaxutils, security, marked, CodeMirror){
"use strict";
marked.setOptions({
gfm : true,
tables: true,
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;
}
}
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);
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);
console.log("Require CodeMirror mode error: " + err);
callback(null, code);
});
}
});
var mathjax_init_done = false;
function ensure_mathjax_init() {
if(!mathjax_init_done) {
mathjax_init_done = true;
mathjaxutils.init();
}
}
function render(markdown, options, callback) {
/**
* Find a readme in the current directory. Look for files with
* a name like 'readme.md' (case insensitive) or similar and
* mimetype 'text/markdown'.
*
* @param markdown: the markdown text to parse
* @param options
* Object with parameters:
* with_math: the markdown can contain mathematics
* clean_tables: prevent default inline styles for table cells
* sanitize: remove dangerous html (like <script>)
* @param callback
* A function with two arguments (err, html)
* err: null or the error if there was one
* html: the rendered html string, or if {sanitize: true} was used
* a sanitized jQuery object
*/
options = $.extend({
// Apply mathjax transformation
with_math: false,
// Prevent marked from returning inline styles for table cells
clean_tables: false,
// Apply sanitation, this will return a jQuery object.
sanitize: false,
}, options);
var renderer = new marked.Renderer();
if(options.clean_tables) {
renderer.tablecell = function (content, flags) {
var type = flags.header ? 'th' : 'td';
var style = flags.align == null ? '': ' style="text-align: ' + flags.align + '"';
var start_tag = '<' + type + style + '>';
var end_tag = '</' + type + '>\n';
return start_tag + content + end_tag;
};
}
var text = markdown;
var math = null;
if(options.with_math) {
ensure_mathjax_init();
var text_and_math = mathjaxutils.remove_math(markdown);
text = text_and_math[0];
math = text_and_math[1];
}
marked(text, { renderer: renderer }, function (err, html) {
if(!err) {
if(options.with_math) {
html = mathjaxutils.replace_math(html, math);
}
if(options.sanitize) {
html = $(security.sanitize_html_and_parse(html, true));
}
}
callback(err, html);
});
}
return {'render': render};
});

@ -1,247 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'base/js/dialog',
], function($, utils, i18n, dialog) {
"use strict";
var init = function () {
if (window.MathJax) {
// MathJax loaded
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true,
processEnvironments: true
},
TeX: {
extensions: ['newcommand.js', 'begingroup.js'] // For \gdef
},
MathML: {
extensions: ['content-mathml.js']
},
// Center justify equations in code and markdown cells. Elsewhere
// we use CSS to left justify single line equations in code cells.
displayAlign: 'center',
"HTML-CSS": {
availableFonts: [],
imageFont: null,
preferredFont: null,
webFont: "STIX-Web",
styles: {'.MathJax_Display': {"margin": 0}},
linebreaks: { automatic: true }
},
});
MathJax.Hub.Configured();
} else if (window.mathjax_url !== "") {
// This statement is used simply so that message extraction
// will pick up the strings. The actual setting of the text
// for the button is in dialog.js.
var button_labels = [ i18n.msg._("OK") ];
// Don't have MathJax, but should. Show dialog.
dialog.modal({
title : i18n.msg.sprintf(i18n.msg._("Failed to retrieve MathJax from '%s'",window.mathjax_url)),
body : $("<p/>").addClass('dialog').text(
i18n.msg._("Math/LaTeX rendering will be disabled.")
),
buttons : {
OK : {class: "btn-danger"}
}
});
}
};
// Some magic for deferring mathematical expressions to MathJax
// by hiding them from the Markdown parser.
// Some of the code here is adapted with permission from Davide Cervone
// under the terms of the Apache2 license governing the MathJax project.
// Other minor modifications are also due to StackExchange and are used with
// permission.
// MATHSPLIT contains the pattern for math delimiters and special symbols
// needed for searching for math in the text input.
var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[{}$]|[{}]|(?:\n\s*)+|@@\d+@@|\\\\(?:\(|\)|\[|\]))/i;
// The math is in blocks i through j, so
// collect it into one block and clear the others.
// Replace &, <, and > by named entities.
// For IE, put <br> at the ends of comments since IE removes \n.
// Clear the current math positions and store the index of the
// math, then push the math string onto the storage array.
// The preProcess function is called on all blocks if it has been passed in
var process_math = function (i, j, pre_process, math, blocks) {
var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
.replace(/</g, "&lt;") // use HTML entity for <
.replace(/>/g, "&gt;") // use HTML entity for >
;
if (utils.browser === 'msie') {
block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n");
}
while (j > i) {
blocks[j] = "";
j--;
}
blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
if (pre_process){
block = pre_process(block);
}
math.push(block);
return blocks;
};
// Break up the text into its component parts and search
// through them for math delimiters, braces, linebreaks, etc.
// Math delimiters must match and braces must balance.
// Don't allow math to pass through a double linebreak
// (which will be a paragraph).
//
var remove_math = function (text) {
var math = []; // stores math strings for later
var start;
var end;
var last;
var braces;
// Except for extreme edge cases, this should catch precisely those pieces of the markdown
// source that will later be turned into code spans. While MathJax will not TeXify code spans,
// we still have to consider them at this point; the following issue has happened several times:
//
// `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
var hasCodeSpans = /`/.test(text),
de_tilde;
if (hasCodeSpans) {
var tilde = function (wholematch) {
return wholematch.replace(/\$/g, "~D");
}
text = text.replace(/~/g, "~T")
.replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, tilde)
.replace(/^\s{0,3}(`{3,})(.|\n)*?\1/gm, tilde);
de_tilde = function (text) {
return text.replace(/~([TD])/g, function (wholematch, character) {
return { T: "~", D: "$" }[character];
});
};
} else {
de_tilde = function (text) { return text; };
}
var blocks = utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
for (var i = 1, m = blocks.length; i < m; i += 2) {
var block = blocks[i];
if (block.charAt(0) === "@") {
//
// Things that look like our math markers will get
// stored and then retrieved along with the math.
//
blocks[i] = "@@" + math.length + "@@";
math.push(block);
}
else if (start) {
//
// If we are in math, look for the end delimiter,
// but don't go past double line breaks, and
// and balance braces within the math.
//
if (block === end) {
if (braces) {
last = i;
}
else {
blocks = process_math(start, i, de_tilde, math, blocks);
start = null;
end = null;
last = null;
}
}
else if (block.match(/\n.*\n/)) {
if (last) {
i = last;
blocks = process_math(start, i, de_tilde, math, blocks);
}
start = null;
end = null;
last = null;
braces = 0;
}
else if (block === "{") {
braces++;
}
else if (block === "}" && braces) {
braces--;
}
}
else {
//
// Look for math start delimiters and when
// found, set up the end delimiter.
//
if (block === "$" || block === "$$") {
start = i;
end = block;
braces = 0;
}
else if (block === "\\\\\(" || block === "\\\\\[") {
start = i;
end = block.slice(-1) === "(" ? "\\\\\)" : "\\\\\]";
braces = 0;
}
else if (block.substr(1, 5) === "begin") {
start = i;
end = "\\end" + block.substr(6);
braces = 0;
}
}
}
if (last) {
blocks = process_math(start, last, de_tilde, math, blocks);
start = null;
end = null;
last = null;
}
return [de_tilde(blocks.join("")), math];
};
//
// Put back the math strings that were saved,
// and clear the math array (no need to keep it around).
//
var replace_math = function (text, math) {
//
// Replaces a math placeholder with its corresponding group.
// The math delimiters "\\(", "\\[", "\\)" and "\\]" are replaced
// removing one backslash in order to be interpreted correctly by MathJax.
//
var math_group_process = function (match, n) {
var math_group = math[n];
if (math_group.substr(0, 3) === "\\\\\(" && math_group.substr(math_group.length - 3) === "\\\\\)") {
math_group = "\\\(" + math_group.substring(3, math_group.length - 3) + "\\\)";
} else if (math_group.substr(0, 3) === "\\\\\[" && math_group.substr(math_group.length - 3) === "\\\\\]") {
math_group = "\\\[" + math_group.substring(3, math_group.length - 3) + "\\\]";
}
return math_group;
};
// Replace all the math group placeholders in the text
// with the saved strings.
text = text.replace(/@@(\d+)@@/g, math_group_process);
return text;
};
var mathjaxutils = {
init : init,
remove_math : remove_math,
replace_math : replace_math
};
return mathjaxutils;
});

@ -1,83 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
var Jupyter = Jupyter || {};
var jprop = function(name, module_path){
Object.defineProperty(Jupyter, name, {
get: function() {
console.warn('accessing `'+name+'` is deprecated. Use `requirejs("'+module_path+'")`');
return requirejs(module_path);
},
enumerable: true,
configurable: false
});
}
var jglobal = function(name, module_path){
Object.defineProperty(Jupyter, name, {
get: function() {
console.warn('accessing `'+name+'` is deprecated. Use `requirejs("'+module_path+'").'+name+'`');
return requirejs(module_path)[name];
},
enumerable: true,
configurable: false
});
}
define(function(){
"use strict";
// expose modules
jprop('utils','base/js/utils')
jprop('mathjaxutils','base/js/mathjaxutils');
//Jupyter.load_extensions = Jupyter.utils.load_extensions;
//
jprop('security','base/js/security');
jprop('keyboard','base/js/keyboard');
jprop('dialog','base/js/dialog');
//// exposed constructors
jglobal('CommManager','services/kernels/comm')
jglobal('Comm','services/kernels/comm')
jglobal('NotificationWidget','base/js/notificationwidget');
jglobal('Kernel','services/kernels/kernel');
jglobal('Session','services/sessions/session');
jglobal('LoginWidget','auth/js/loginwidget');
jglobal('Page','base/js/page');
// notebook
jglobal('TextCell','notebook/js/textcell');
jglobal('OutputArea','notebook/js/outputarea');
jglobal('KeyboardManager','notebook/js/keyboardmanager');
jglobal('Completer','notebook/js/completer');
jglobal('Notebook','notebook/js/notebook');
jglobal('Tooltip','notebook/js/tooltip');
jglobal('Toolbar','notebook/js/toolbar');
jglobal('SaveWidget','notebook/js/savewidget');
jglobal('Pager','notebook/js/pager');
jglobal('QuickHelp','notebook/js/quickhelp');
jglobal('MarkdownCell','notebook/js/textcell');
jglobal('RawCell','notebook/js/textcell');
jglobal('Cell','notebook/js/cell');
jglobal('MainToolBar','notebook/js/maintoolbar');
jglobal('NotebookNotificationArea','notebook/js/notificationarea');
jglobal('NotebookTour', 'notebook/js/tour');
jglobal('MenuBar', 'notebook/js/menubar');
// tree
jglobal('SessionList','tree/js/sessionlist');
Jupyter.version = "6.4.13.dev0";
Jupyter._target = '_blank';
return Jupyter;
});
// deprecated since 4.0, remove in 5+
var IPython = Jupyter;

@ -1,83 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/notificationwidget',
], function($, notificationwidget) {
"use strict";
// store reference to the NotificationWidget class
var NotificationWidget = notificationwidget.NotificationWidget;
/**
* Construct the NotificationArea object. Options are:
* events: $(Events) instance
* save_widget: SaveWidget instance
* notebook: Notebook instance
* keyboard_manager: KeyboardManager instance
*
* @constructor
* @param {string} selector - a jQuery selector string for the
* notification area element
* @param {Object} [options] - a dictionary of keyword arguments.
*/
var NotificationArea = function (selector, options) {
this.selector = selector;
this.events = options.events;
if (this.selector !== undefined) {
this.element = $(selector);
}
this.widget_dict = {};
};
/**
* Get a widget by name, creating it if it doesn't exist.
*
* @method widget
* @param {string} name - the widget name
*/
NotificationArea.prototype.widget = function (name) {
if (this.widget_dict[name] === undefined) {
return this.new_notification_widget(name);
}
return this.get_widget(name);
};
/**
* Get a widget by name, throwing an error if it doesn't exist.
*
* @method get_widget
* @param {string} name - the widget name
*/
NotificationArea.prototype.get_widget = function (name) {
if(this.widget_dict[name] === undefined) {
throw new Error('no widgets with this name');
}
return this.widget_dict[name];
};
/**
* Create a new notification widget with the given name. The
* widget must not already exist.
*
* @method new_notification_widget
* @param {string} name - the widget name
*/
NotificationArea.prototype.new_notification_widget = function (name) {
if (this.widget_dict[name] !== undefined) {
throw new Error('widget with that name already exists!');
}
// create the element for the notification widget and add it
// to the notification aread element
var div = $('<div/>').attr('id', 'notification_' + name);
$(this.selector).append(div);
// create the widget object and return it
this.widget_dict[name] = new NotificationWidget('#notification_' + name);
return this.widget_dict[name];
};
return {'NotificationArea': NotificationArea};
});

@ -1,168 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['jquery'], function($) {
"use strict";
/**
* Construct a NotificationWidget object.
*
* @constructor
* @param {string} selector - a jQuery selector string for the
* notification widget element
*/
var NotificationWidget = function (selector) {
this.selector = selector;
this.timeout = null;
this.busy = false;
if (this.selector !== undefined) {
this.element = $(selector);
this.style();
}
this.element.hide();
this.inner = $('<span/>');
this.element.append(this.inner);
};
/**
* Add the 'notification_widget' CSS class to the widget element.
*
* @method style
*/
NotificationWidget.prototype.style = function () {
// use explicit bootstrap classes here,
// because multiple inheritance in LESS doesn't work
// for this particular combination
this.element.addClass('notification_widget btn btn-xs navbar-btn');
};
/**
* hide the widget and empty the text
**/
NotificationWidget.prototype.hide = function () {
var that = this;
this.element.fadeOut(100, function(){that.inner.text('');});
};
/**
* Set the notification widget message to display for a certain
* amount of time (timeout). The widget will be shown forever if
* timeout is <= 0 or undefined. If the widget is clicked while it
* is still displayed, execute an optional callback
* (click_callback). If the callback returns false, it will
* prevent the notification from being dismissed.
*
* Options:
* class - CSS class name for styling
* icon - CSS class name for the widget icon
* title - HTML title attribute for the widget
*
* @method set_message
* @param {string} msg - The notification to display
* @param {integer} [timeout] - The amount of time in milliseconds to display the widget
* @param {function} [click_callback] - The function to run when the widget is clicked
* @param {Object} [options] - Additional options
*/
NotificationWidget.prototype.set_message = function (msg, timeout, click_callback, options) {
options = options || {};
// unbind potential previous callback
this.element.unbind('click');
this.inner.attr('class', options.icon);
this.inner.attr('title', options.title);
this.inner.text(msg);
this.element.fadeIn(100);
// reset previous set style
this.element.removeClass();
this.style();
if (options.class) {
this.element.addClass(options.class);
}
// clear previous timer
if (this.timeout !== null) {
clearTimeout(this.timeout);
this.timeout = null;
}
// set the timer if a timeout is given
var that = this;
if (timeout !== undefined && timeout >= 0) {
this.timeout = setTimeout(function () {
that.element.fadeOut(100, function () {that.inner.text('');});
that.element.unbind('click');
that.timeout = null;
}, timeout);
}
// if no click callback assume we will just dismiss the notification
if (click_callback === undefined) {
click_callback = function(){return true};
}
// on click, remove widget if click callback say so
// and unbind click event.
this.element.click(function () {
if (click_callback() !== false) {
that.element.fadeOut(100, function () {that.inner.text('');});
that.element.unbind('click');
}
if (that.timeout !== null) {
clearTimeout(that.timeout);
that.timeout = null;
}
});
};
/**
* Display an information message (styled with the 'info'
* class). Arguments are the same as in set_message. Default
* timeout is 3500 milliseconds.
*
* @method info
*/
NotificationWidget.prototype.info = function (msg, timeout, click_callback, options) {
options = options || {};
options.class = options.class + ' info';
timeout = timeout || 3500;
this.set_message(msg, timeout, click_callback, options);
};
/**
* Display a warning message (styled with the 'warning'
* class). Arguments are the same as in set_message. Messages are
* sticky by default.
*
* @method warning
*/
NotificationWidget.prototype.warning = function (msg, timeout, click_callback, options) {
options = options || {};
options.class = options.class + ' warning';
this.set_message(msg, timeout, click_callback, options);
};
/**
* Display a danger message (styled with the 'danger'
* class). Arguments are the same as in set_message. Messages are
* sticky by default.
*
* @method danger
*/
NotificationWidget.prototype.danger = function (msg, timeout, click_callback, options) {
options = options || {};
options.class = options.class + ' danger';
this.set_message(msg, timeout, click_callback, options);
};
/**
* Get the text of the widget message.
*
* @method get_message
* @return {string} - the message text
*/
NotificationWidget.prototype.get_message = function () {
return this.inner.html();
};
return {'NotificationWidget': NotificationWidget};
});

@ -1,78 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/events',
], function($, events){
"use strict";
var Page = function (header_div_selector, site_div_selector) {
/**
* Constructor
*
* Parameters
* header_div_selector: string
* site_div_selector: string
*/
this.header_div_element = $(header_div_selector || 'div#header');
this.site_div_element = $(site_div_selector || 'div#site');
this.bind_events();
};
Page.prototype.bind_events = function () {
// resize site on:
// - window resize
// - header change
// - page load
var _handle_resize = $.proxy(this._resize_site, this);
$(window).resize(_handle_resize);
// On document ready, resize codemirror.
$(document).ready(_handle_resize);
events.on('resize-header.Page', _handle_resize);
};
Page.prototype.show = function () {
/**
* The header and site divs start out hidden to prevent FLOUC.
* Main scripts should call this method after styling everything.
*/
this.show_header();
this.show_site();
};
Page.prototype.show_header = function () {
/**
* The header and site divs start out hidden to prevent FLOUC.
* Main scripts should call this method after styling everything.
*/
this.header_div_element.css('display','block');
};
Page.prototype.show_site = function () {
/**
* The header and site divs start out hidden to prevent FLOUC.
* Main scripts should call this method after styling everything.
*/
this.site_div_element.css('display', 'block');
this._resize_site();
};
Page.prototype._resize_site = function(e) {
/**
* Update the site's size.
*/
// In the case an event is passed in, only trigger if the event does
// *not* have a target DOM node (i.e., it is not bubbling up). See
// https://bugs.jquery.com/ticket/9841#comment:8
if (!(e && e.target && e.target.tagName)) {
$('div#site').height($(window).height() - $('#header').height());
}
};
return {'Page': Page};
});

@ -1,26 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// Define an object to attach promises to for one-time events.
define(['base/js/events', 'base/js/namespace'], function(events, Jupyter) {
"use strict";
// Promise to be resolved when the application is initialized.
// The value is the name of the app on the current page.
var app_initialized = new Promise(function(resolve, reject) {
events.on('app_initialized.NotebookApp', function() {
resolve('NotebookApp');
});
events.on('app_initialized.DashboardApp', function() {
resolve('DashboardApp');
});
});
var promises = {
app_initialized: app_initialized
};
Jupyter.promises = promises;
return promises;
});

@ -1,51 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'components/sanitizer/index',
], function($, sanitizer) {
"use strict";
var noop = function (x) { return x; };
var defaultSanitizer = sanitizer.defaultSanitizer;
var sanitize_html = function (html, allow_css) {
/**
* sanitize HTML
* if allow_css is true (default: false), CSS is sanitized as well.
* otherwise, CSS elements and attributes are simply removed.
*/
const options = {};
if (!allow_css) {
options.allowedStyles = {};
}
return defaultSanitizer.sanitize(html, options);
};
var sanitize_html_and_parse = function (html, allow_css) {
/**
* Sanitize HTML and parse it safely using jQuery.
*
* This disable's jQuery's html 'prefilter', which can make invalid
* HTML valid after the sanitizer has checked it.
*
* Returns an array of DOM nodes.
*/
var sanitized_html = sanitize_html(html, allow_css);
var prev_htmlPrefilter = $.htmlPrefilter;
$.htmlPrefilter = function(html) {return html;}; // Don't modify HTML
try {
return $.parseHTML(sanitized_html);
} finally {
$.htmlPrefilter = prev_htmlPrefilter; // Set it back again
}
};
var security = {
sanitize_html_and_parse: sanitize_html_and_parse,
sanitize_html: sanitize_html
};
return security;
});

File diff suppressed because it is too large Load Diff

@ -1,24 +0,0 @@
div.error {
margin: 2em;
text-align: center;
}
div.error > h1 {
font-size: 500%;
line-height: normal;
}
div.error > p {
font-size: 200%;
line-height: normal;
}
div.traceback-wrapper {
text-align: left;
max-width: 800px;
margin: auto;
pre.traceback {
max-height: 600px;
overflow: auto;
}
}

@ -1,269 +0,0 @@
/* Flexible box model classes */
/* Taken from Alex Russell http://infrequently.org/2009/08/css-3-progress/ */
/* This file is a compatibility layer. It allows the usage of flexible box
model layouts accross multiple browsers, including older browsers. The newest,
universal implementation of the flexible box model is used when available (see
`Modern browsers` comments below). Browsers that are known to implement this
new spec completely include:
Firefox 28.0+
Chrome 29.0+
Internet Explorer 11+
Opera 17.0+
Browsers not listed, including Safari, are supported via the styling under the
`Old browsers` comments below.
*/
.hbox {
/* Old browsers */
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: stretch;
display: -moz-box;
-moz-box-orient: horizontal;
-moz-box-align: stretch;
display: box;
box-orient: horizontal;
box-align: stretch;
/* Modern browsers */
display: flex;
flex-direction: row;
align-items: stretch;
}
.hbox > * {
/* Old browsers */
-webkit-box-flex: 0;
-moz-box-flex: 0;
box-flex: 0;
/* Modern browsers */
flex: none;
}
.vbox {
/* Old browsers */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-align: stretch;
display: -moz-box;
-moz-box-orient: vertical;
-moz-box-align: stretch;
display: box;
box-orient: vertical;
box-align: stretch;
/* Modern browsers */
display: flex;
flex-direction: column;
align-items: stretch;
}
.vbox > * {
/* Old browsers */
-webkit-box-flex: 0;
-moz-box-flex: 0;
box-flex: 0;
/* Modern browsers */
flex: none;
}
.hbox.reverse,
.vbox.reverse,
.reverse {
/* Old browsers */
-webkit-box-direction: reverse;
-moz-box-direction: reverse;
box-direction: reverse;
/* Modern browsers */
flex-direction: row-reverse;
}
.hbox.box-flex0,
.vbox.box-flex0,
.box-flex0 {
/* Old browsers */
-webkit-box-flex: 0;
-moz-box-flex: 0;
box-flex: 0;
/* Modern browsers */
flex: none;
width: auto;
}
.hbox.box-flex1,
.vbox.box-flex1,
.box-flex1 {
/* Old browsers */
-webkit-box-flex: 1;
-moz-box-flex: 1;
box-flex: 1;
/* Modern browsers */
flex: 1;
}
.hbox.box-flex,
.vbox.box-flex,
.box-flex {
/* Old browsers */
.box-flex1();
}
.hbox.box-flex2,
.vbox.box-flex2,
.box-flex2 {
/* Old browsers */
-webkit-box-flex: 2;
-moz-box-flex: 2;
box-flex: 2;
/* Modern browsers */
flex: 2;
}
.box-group1 {
/* Deprecated */
-webkit-box-flex-group: 1;
-moz-box-flex-group: 1;
box-flex-group: 1;
}
.box-group2 {
/* Deprecated */
-webkit-box-flex-group: 2;
-moz-box-flex-group: 2;
box-flex-group: 2;
}
.hbox.start,
.vbox.start,
.start {
/* Old browsers */
-webkit-box-pack: start;
-moz-box-pack: start;
box-pack: start;
/* Modern browsers */
justify-content: flex-start;
}
.hbox.end,
.vbox.end,
.end {
/* Old browsers */
-webkit-box-pack: end;
-moz-box-pack: end;
box-pack: end;
/* Modern browsers */
justify-content: flex-end;
}
.hbox.center,
.vbox.center,
.center {
/* Old browsers */
-webkit-box-pack: center;
-moz-box-pack: center;
box-pack: center;
/* Modern browsers */
justify-content: center;
}
.hbox.baseline,
.vbox.baseline,
.baseline {
/* Old browsers */
-webkit-box-pack: baseline;
-moz-box-pack: baseline;
box-pack: baseline;
/* Modern browsers */
justify-content: baseline;
}
.hbox.stretch,
.vbox.stretch,
.stretch {
/* Old browsers */
-webkit-box-pack: stretch;
-moz-box-pack: stretch;
box-pack: stretch;
/* Modern browsers */
justify-content: stretch;
}
.hbox.align-start,
.vbox.align-start,
.align-start {
/* Old browsers */
-webkit-box-align: start;
-moz-box-align: start;
box-align: start;
/* Modern browsers */
align-items: flex-start;
}
.hbox.align-end,
.vbox.align-end,
.align-end {
/* Old browsers */
-webkit-box-align: end;
-moz-box-align: end;
box-align: end;
/* Modern browsers */
align-items: flex-end;
}
.hbox.align-center,
.vbox.align-center,
.align-center {
/* Old browsers */
-webkit-box-align: center;
-moz-box-align: center;
box-align: center;
/* Modern browsers */
align-items: center;
}
.hbox.align-baseline,
.vbox.align-baseline,
.align-baseline {
/* Old browsers */
-webkit-box-align: baseline;
-moz-box-align: baseline;
box-align: baseline;
/* Modern browsers */
align-items: baseline;
}
.hbox.align-stretch,
.vbox.align-stretch,
.align-stretch {
/* Old browsers */
-webkit-box-align: stretch;
-moz-box-align: stretch;
box-align: stretch;
/* Modern browsers */
align-items: stretch;
}

@ -1,19 +0,0 @@
// Mixin CSS classes
.border-box-sizing {
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.corner-all {
border-radius: @border-radius-base;
}
.border-radius(@radius) {
border-radius: @radius;
}
.no-padding {
padding: 0px;
}

@ -1,188 +0,0 @@
/**
* Primary styles
*
* Author: Jupyter Development Team
*/
body {
background-color: @body-bg;
/* This makes sure that the body covers the entire window and needs to
be in a different element than the display: box in wrapper below */
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
overflow: visible;
}
body > #header {
/* Initially hidden to prevent FLOUC */
display: none;
background-color: @body-bg;
/* Display over codemirror */
position: relative;
z-index: 100;
#header-container {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 5px;
padding-bottom: 5px;
padding-top: 5px;
.border-box-sizing();
}
.header-bar {
width: 100%;
height: 1px;
background: @navbar-default-border;
margin-bottom: -1px;
}
@media print {
display: none !important;
}
}
#header-spacer {
width: 100%;
visibility: hidden;
@media print {
display: none;
}
}
#ipython_notebook {
padding-left: 0px;
padding-top: (@navbar-height - @logo_height) / 2;
padding-bottom: (@navbar-height - @logo_height) / 2;
}
[dir="rtl"] #ipython_notebook {
margin-right: 10px;
margin-left: 0;
}
[dir="rtl"] #ipython_notebook.pull-left {
.pull-right();
}
.flex-spacer {
flex: 1;
}
#noscript {
width: auto;
padding-top: 16px;
padding-bottom: 16px;
text-align: center;
font-size: 22px;
color: red;
font-weight: bold;
}
#ipython_notebook img {
height: @logo_height;
}
#site {
width: 100%;
display: none;
.border-box-sizing();
overflow: auto;
@media print {
// force auto-height on print (overrides manual resizing in live view)
height: auto !important;
}
}
/* Smaller buttons */
.ui-button .ui-button-text {
padding: 0.2em 0.8em;
font-size: 77%;
}
input.ui-button {
padding: 0.3em 0.9em;
}
span#kernel_logo_widget {
margin: 0 10px;
}
span#login_widget {
float: right;
}
[dir="rtl"] span#login_widget {
float: left;
}
span#login_widget > .button,
#logout, #shutdown
{
.btn-default();
margin-left: 10px;
}
.nav-header {
text-transform: none;
}
#header > span {
margin-top: 10px;
}
// class for stretching dialogs to fill the screen
.modal_stretch .modal-dialog {
.vbox();
min-height: 80vh;
.modal-body {
// ~"foo" is to avoid less turning this into a weird value
max-height: calc(~"100vh - 200px");
overflow: auto;
flex: 1;
}
}
.modal-header {
cursor: move;
}
@media (min-width: @screen-sm-min) {
.modal .modal-dialog {
width: 700px;
}
}
// less mixin to be sure to add the right class to get icons with font awesome.
.icon(@ico){
.fa();
content: @ico;
}
@media (min-width: @screen-sm-min) {
select.form-control {
margin-left: @padding-base-horizontal;
margin-right: @padding-base-horizontal;
}
}
/* rtl fixes for the error, connecting, and renaming window */
[dir="rtl"] .modal-footer {
text-align : left !important;
}
[dir="rtl"] .close {
float : left;
}
[dir="rtl"] .fa-step-forward::before {
content: "\f048";
}

@ -1,9 +0,0 @@
/*!
*
* IPython base
*
*/
@import "variables.less";
@import "mixins.less";
@import "flexbox.less";
@import "error.less";

@ -1,62 +0,0 @@
// Our customizations to bootstrap go here.
@black: #000;
@text-color: @black;
@font-size-base: 13px;
@font-family-monospace: monospace; // to allow user to customize their fonts
@navbar-height: 30px;
@breadcrumb-color: darken(@border_color, 30%);
@blockquote-font-size: inherit;
@modal-inner-padding: 15px;
@grid-float-breakpoint: 541px;
@screen-xs: 540px;
@logo_height: 28px;
@border-radius-small: 1px;
@border-radius-base: 2px;
@border-radius-large: 3px;
@grid-gutter-width: 0px;
@kbd-color: #888;
@kbd-bg: transparent;
@icon-font-path: "../components/bootstrap/fonts/";
// Disable modal slide-in from top animation.
.modal {
&.fade .modal-dialog {
.translate(0, 0);
}
}
// Set the default code color.
code {
color: @black; // default code color in bootstrap is #d14 (crimson / amaranth)
}
// Override bootstrap pre element styling.
pre {
// bootstrap has pre defaults that we don't want to inherit.
// start pre tag defaults based on the surrounding context instead.
font-size: inherit;
line-height: inherit;
}
// Disable bold labels in BS3
label {
font-weight: normal;
}
// Our own global variables for all pages go here
@global-shadow: 0px 0px 12px 1px rgba(87, 87, 87, 0.2);
@global-shadow-dark: 0px 0px 12px 1px rgba(87, 87, 87, 0.4);
@page-header-padding: 20px;
/* Make the page background atleast 100% the height of the view port */
@page-backdrop-height: 100vh;
/* Make the page itself atleast 70% the height of the view port */
@page-min-height: 0;
@page-backdrop-color: #EEE;
@page-color: @body-bg;
@page-padding: 15px;
// preven container size to jump from 768px to 720px
// when window width go from 768 to 769+
@container-sm : @screen-sm-min;

@ -1,45 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['bidi/numericshaping'], function(numericshaping) {
'use strict';
var shaperType = '';
var _uiLang = function() {
return navigator.language.toLowerCase();
};
var _loadLocale = function() {
if (_isMirroringEnabled()) {
document.body.dir = 'rtl';
}
requirejs(['moment'], function (moment) {
console.log('Loaded moment locale', moment.locale(_uiLang()));
});
shaperType = _uiLang().split('-')[0] == 'ar' ? 'national' : 'defaultNumeral';
};
var _isMirroringEnabled = function() {
return new RegExp('^(ar|ara|arc|ae|ave|egy|he|heb|nqo|pal|phn|sam|syc|syr|fa|per|fas|ckb|ur|urd)').test(_uiLang());
};
/**
* @param value : the string to apply the bidi-support on it.
* @param flag :indicates the type of bidi-support (Numeric-shaping ,Base-text-dir ).
*/
var _applyBidi = function(value /*, flag*/) {
value = numericshaping.shapeNumerals(value, shaperType);
return value;
};
var bidi = {
applyBidi: _applyBidi,
isMirroringEnabled: _isMirroringEnabled,
loadLocale: _loadLocale,
};
return bidi;
});

@ -1,42 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([],
function(bidi) {
"use strict";
var regex = /([0-9])|([\u0660-\u0669])|([\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE]+)|([^0-9\u0660-\u0669\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE\u0600-\u0607\u0609-\u060A\u060C\u060E-\u061A\u064B-\u066C\u0670\u06D6-\u06E4\u06E7-\u06ED\u06F0-\u06F9\u08E4-\u08FF\uFD3E-\uFD3F\uFDD0-\uFDEF\uFDFD\uFEFF\u0000-\u0040\u005B-\u0060\u007B-\u007F\u0080-\u00A9\u00AB-\u00B4\u00B6-\u00B9\u00BB-\u00BF\u00D7\u00F7\u02B9-\u02BA\u02C2-\u02CF\u02D2-\u02DF\u02E5-\u02ED\u02EF-\u02FF\u2070\u2074-\u207E\u2080-\u208E\u2100-\u2101\u2103-\u2106\u2108-\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A-\u213B\u2140-\u2144\u214A-\u214D\u2150-\u215F\u2189\uA720-\uA721\uA788\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFFE0-\uFFE6\uFFE8-\uFFEE]+)/g;
var shape = function(text, shaperType) {
text = text.toString();
if (!text) {
return text;
}
switch (shaperType) {
case "defaultNumeral":
return _shapeEuropean(text);
case "national":
return _shapeArabic(text);
default:
return text;
}
};
var _shapeEuropean = function(text) {
return text.replace(/[\u0660-\u0669]/g, function(c) {
return c.charCodeAt(0) - 1632;
});
};
var _shapeArabic = function(text) {
return text.replace(/[0-9]/g, function(c) {
return String.fromCharCode(parseInt(c) + 1632);
});
};
var numericshaping = {
shapeNumerals : shape
};
return numericshaping;
});

@ -1,8 +0,0 @@
/*
Placeholder for custom user CSS
mainly to be overridden in profile/static/custom/custom.css
This will always be an empty file
*/

@ -1,74 +0,0 @@
// leave at least 2 line with only a star on it below, or doc generation fails
/**
*
*
* Placeholder for custom user javascript
* mainly to be overridden in profile/static/custom/custom.js
* This will always be an empty file in IPython
*
* User could add any javascript in the `profile/static/custom/custom.js` file.
* It will be executed by the ipython notebook at load time.
*
* Same thing with `profile/static/custom/custom.css` to inject custom css into the notebook.
*
*
* The object available at load time depend on the version of IPython in use.
* there is no guaranties of API stability.
*
* The example below explain the principle, and might not be valid.
*
* Instances are created after the loading of this file and might need to be accessed using events:
* define([
* 'base/js/namespace',
* 'base/js/promises'
* ], function(IPython, promises) {
* promises.app_initialized.then(function (appName) {
* if (appName !== 'NotebookApp') return;
* IPython.keyboard_manager....
* });
* });
*
* __Example 1:__
*
* Create a custom button in toolbar that execute `%qtconsole` in kernel
* and hence open a qtconsole attached to the same kernel as the current notebook
*
* define([
* 'base/js/namespace',
* 'base/js/promises'
* ], function(IPython, promises) {
* promises.app_initialized.then(function (appName) {
* if (appName !== 'NotebookApp') return;
* IPython.toolbar.add_buttons_group([
* {
* 'label' : 'run qtconsole',
* 'icon' : 'icon-terminal', // select your icon from http://fortawesome.github.io/Font-Awesome/icons
* 'callback': function () {
* IPython.notebook.kernel.execute('%qtconsole')
* }
* }
* // add more button here if needed.
* ]);
* });
* });
*
* __Example 2:__
*
* At the completion of the dashboard loading, load an unofficial javascript extension
* that is installed in profile/static/custom/
*
* define([
* 'base/js/events'
* ], function(events) {
* events.on('app_initialized.DashboardApp', function(){
* requirejs(['custom/unofficial_extension.js'])
* });
* });
*
*
*
* @module IPython
* @namespace IPython
* @class customjs
* @static
*/

@ -1,370 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'base/js/dialog',
'codemirror/lib/codemirror',
'codemirror/mode/meta',
'codemirror/addon/comment/comment',
'codemirror/addon/dialog/dialog',
'codemirror/addon/edit/closebrackets',
'codemirror/addon/edit/matchbrackets',
'codemirror/addon/search/searchcursor',
'codemirror/addon/search/search',
'codemirror/keymap/emacs',
'codemirror/keymap/sublime',
'codemirror/keymap/vim',
],
function(
$,
utils,
i18n,
dialog,
CodeMirror
) {
"use strict";
var Editor = function(selector, options) {
var that = this;
this.selector = selector;
this.clean = false;
this.contents = options.contents;
this.events = options.events;
this.base_url = options.base_url;
this.file_path = options.file_path;
this.config = options.config;
this.file_extension_modes = options.file_extension_modes || {};
this.last_modified = null;
this._changed_on_disk_dialog = null;
this.codemirror = new CodeMirror($(this.selector)[0]);
this.codemirror.on('changes', function(cm, changes){
that._clean_state();
});
this.generation = -1;
// It appears we have to set commands on the CodeMirror class, not the
// instance. I'd like to be wrong, but since there should only be one CM
// instance on the page, this is good enough for now.
CodeMirror.commands.save = $.proxy(this.save, this);
this.save_enabled = false;
this.config.loaded.then(function () {
// load codemirror config
var cfg = that.config.data.Editor || {};
var cmopts = $.extend(true, {}, // true = recursive copy
Editor.default_codemirror_options,
cfg.codemirror_options || {}
);
that._set_codemirror_options(cmopts);
that.events.trigger('config_changed.Editor', {config: that.config});
if (cfg.file_extension_modes) {
// check for file extension in user preferences
var modename = cfg.file_extension_modes[that._get_file_extension()];
if (modename) {
var modeinfo = CodeMirror.findModeByName(modename);
if (modeinfo) {
that.set_codemirror_mode(modeinfo);
}
}
}
that._clean_state();
});
this.clean_sel = $('<div/>');
$('.last_modified').before(this.clean_sel);
this.clean_sel.addClass('dirty-indicator-dirty');
};
// default CodeMirror options
Editor.default_codemirror_options = {
extraKeys: {
"Cmd-Right": "goLineRight",
"End": "goLineRight",
"Cmd-Left": "goLineLeft",
"Tab": "indentMore",
"Shift-Tab" : "indentLess",
"Cmd-/" : "toggleComment",
"Ctrl-/" : "toggleComment",
},
indentUnit: 4,
theme: "ipython",
lineNumbers: true,
lineWrapping: true
};
Editor.prototype.load = function() {
/** load the file */
var that = this;
var cm = this.codemirror;
return this.contents.get(this.file_path, {type: 'file', format: 'text'})
.then(function(model) {
cm.setValue(model.content);
// Setting the file's initial value creates a history entry,
// which we don't want.
cm.clearHistory();
that._set_mode_for_model(model);
that.save_enabled = true;
that.generation = cm.changeGeneration();
that.events.trigger("file_loaded.Editor", model);
that._clean_state();
that.last_modified = new Date(model.last_modified);
}).catch(
function(error) {
that.events.trigger("file_load_failed.Editor", error);
console.warn('Error loading: ', error);
cm.setValue("Error! " + error.message +
"\nSaving disabled.\nSee Console for more details.");
cm.setOption('readOnly','nocursor');
that.save_enabled = false;
}
);
};
Editor.prototype._set_mode_for_model = function (model) {
/** Set the CodeMirror mode based on the file model */
// Find and load the highlighting mode,
// first by mime-type, then by file extension
var modeinfo;
var ext = this._get_file_extension();
if (ext) {
// check if a mode has been remembered for this extension
var modename = this.file_extension_modes[ext];
if (modename) {
modeinfo = CodeMirror.findModeByName(modename);
}
}
// prioritize CodeMirror's filename identification
if (!modeinfo || modeinfo.mode === "null") {
modeinfo = CodeMirror.findModeByFileName(model.name);
// codemirror's filename identification is case-sensitive.
// try once more with lowercase extension
if (!modeinfo && ext) {
// CodeMirror wants lowercase ext without leading '.'
modeinfo = CodeMirror.findModeByExtension(ext.slice(1).toLowerCase());
}
}
if (model.mimetype && (!modeinfo || modeinfo.mode === "null")) {
// mimetype is not set on file rename
modeinfo = CodeMirror.findModeByMIME(model.mimetype);
}
if (modeinfo) {
this.set_codemirror_mode(modeinfo);
}
};
Editor.prototype.set_codemirror_mode = function (modeinfo) {
/** set the codemirror mode from a modeinfo struct */
var that = this;
utils.requireCodeMirrorMode(modeinfo, function () {
that.codemirror.setOption('mode', modeinfo.mime);
that.events.trigger("mode_changed.Editor", modeinfo);
}, function(err) {
console.log('Error getting CodeMirror mode: ' + err);
});
};
Editor.prototype.save_codemirror_mode = function (modeinfo) {
/** save the selected codemirror mode for the current extension in config */
var update_mode_map = {};
var ext = this._get_file_extension();
// no extension, nothing to save
// TODO: allow remembering no-extension things like Makefile?
if (!ext) return;
update_mode_map[ext] = modeinfo.name;
return this.config.update({
Editor: {
file_extension_modes: update_mode_map,
}
});
};
Editor.prototype.get_filename = function () {
return utils.url_path_split(this.file_path)[1];
};
Editor.prototype._get_file_extension = function () {
/** return file extension *including* .
Returns undefined if no extension is found.
*/
var filename = this.get_filename();
var ext_idx = filename.lastIndexOf('.');
if (ext_idx < 0) {
return;
} else {
return filename.slice(ext_idx);
}
};
/**
* Rename the file.
* @param {string} new_name
* @return {Promise} promise that resolves when the file is renamed.
*/
Editor.prototype.rename = function (new_name) {
/** rename the file */
var that = this;
var parent = utils.url_path_split(this.file_path)[0];
var new_path = utils.url_path_join(parent, new_name);
return this.contents.rename(this.file_path, new_path).then(
function (model) {
that.file_path = model.path;
that.events.trigger('file_renamed.Editor', model);
that.last_modified = new Date(model.last_modified);
that._set_mode_for_model(model);
that._clean_state();
}
);
};
/**
* Save this file on the server.
*
* @param {boolean} check_last_modified - checks if file has been modified on disk
* @return {Promise} - promise that resolves when the notebook is saved.
*/
Editor.prototype.save = function (check_last_modified) {
/** save the file */
if (!this.save_enabled) {
console.log("Not saving, save disabled");
return;
}
// used to check for last modified saves
if (check_last_modified === undefined) {
check_last_modified = true;
}
var model = {
path: this.file_path,
type: 'file',
format: 'text',
content: this.codemirror.getValue(),
};
var that = this;
var _save = function () {
that.events.trigger("file_saving.Editor");
return that.contents.save(that.file_path, model).then(function(data) {
// record change generation for isClean
that.generation = that.codemirror.changeGeneration();
that.events.trigger("file_saved.Editor", data);
that.last_modified = new Date(data.last_modified);
that._clean_state();
});
};
/*
* Gets the current working file, and checks if the file has been modified on disk. If so, it
* creates & opens a modal that issues the user a warning and prompts them to overwrite the file.
*
* If it can't get the working file, it builds a new file and saves.
*/
if (check_last_modified) {
return this.contents.get(that.file_path, {content: false}).then(
function check_if_modified(data) {
var last_modified = new Date(data.last_modified);
// We want to check last_modified (disk) > that.last_modified (our last save)
// In some cases the filesystem reports an inconsistent time,
// so we allow 0.5 seconds difference before complaining.
if ((last_modified.getTime() - that.last_modified.getTime()) > 500) { // 500 ms
console.warn("Last saving was done on `"+that.last_modified+"`("+that._last_modified+"), "+
"while the current file seem to have been saved on `"+data.last_modified+"`");
if (that._changed_on_disk_dialog !== null) {
// since the modal's event bindings are removed when destroyed, we reinstate
// save & reload callbacks on the confirmation & reload buttons
that._changed_on_disk_dialog.find('.save-confirm-btn').click(_save);
that._changed_on_disk_dialog.find('.btn-warning').click(function () {window.location.reload()});
// redisplay existing dialog
that._changed_on_disk_dialog.modal('show');
} else {
// create new dialog
that._changed_on_disk_dialog = dialog.modal({
keyboard_manager: that.keyboard_manager,
title: i18n.msg._("File changed"),
body: i18n.msg._("The file has changed on disk since the last time we opened or saved it. "
+ "Do you want to overwrite the file on disk with the version open here, or load "
+ "the version on disk (reload the page)?"),
buttons: {
Reload: {
class: 'btn-warning',
click: function () {
window.location.reload();
}
},
Cancel: {},
Overwrite: {
class: 'btn-danger save-confirm-btn',
click: function () {
_save();
}
},
}
});
}
} else {
return _save();
}
}, function (error) {
console.log(error);
// maybe it has been deleted or renamed? Go ahead and save.
return _save();
})
} else {
return _save();
}
};
Editor.prototype._clean_state = function(){
var clean = this.codemirror.isClean(this.generation);
if (clean === this.clean){
return;
} else {
this.clean = clean;
}
if(clean){
this.events.trigger("save_status_clean.Editor");
this.clean_sel.attr('class','dirty-indicator-clean').attr('title','No changes to save');
} else {
this.events.trigger("save_status_dirty.Editor");
this.clean_sel.attr('class','dirty-indicator-dirty').attr('title','Unsaved changes');
}
};
Editor.prototype._set_codemirror_options = function (options) {
// update codemirror options from a dict
var codemirror = this.codemirror;
$.map(options, function (value, opt) {
if (value === null) {
value = CodeMirror.defaults[opt];
}
codemirror.setOption(opt, value);
});
var that = this;
};
Editor.prototype.update_codemirror_options = function (options) {
/** update codemirror options locally and save changes in config */
var that = this;
this._set_codemirror_options(options);
return this.config.update({
Editor: {
codemirror_options: options
}
}).then(
that.events.trigger('config_changed.Editor', {config: that.config})
);
};
return {Editor: Editor};
});

@ -1,113 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
requirejs([
'jquery',
'contents',
'base/js/namespace',
'base/js/utils',
'base/js/page',
'base/js/events',
'services/config',
'edit/js/editor',
'edit/js/menubar',
'edit/js/savewidget',
'edit/js/notificationarea',
'bidi/bidi',
'auth/js/loginwidget',
], function(
$,
contents_service,
IPython,
utils,
page,
events,
configmod,
editmod,
menubar,
savewidget,
notificationarea,
bidi,
loginwidget,
){
"use strict";
try {
requirejs(['custom/custom'], function() {});
bidi.loadLocale();
} catch(err) {
console.log("Error loading custom.js from edition service. Continuing and logging");
console.warn(err);
}
page = new page.Page('div#header', 'div#site');
var base_url = utils.get_body_data('baseUrl');
var file_path = utils.get_body_data('filePath');
// This enables logout
var login_widget = new loginwidget.LoginWidget('#login_widget', {
base_url: base_url
});
var config = new configmod.ConfigSection('edit', {base_url: base_url});
config.load();
var common_config = new configmod.ConfigSection('common', {base_url: base_url});
common_config.load();
var contents = new contents_service.Contents({
base_url: base_url,
common_config: common_config
});
var editor = new editmod.Editor('#texteditor-container', {
base_url: base_url,
events: events,
contents: contents,
file_path: file_path,
config: config,
});
// Make it available for debugging
IPython.editor = editor;
var save_widget = new savewidget.SaveWidget('span#save_widget', {
editor: editor,
events: events,
});
var menus = new menubar.MenuBar('#menubar', {
base_url: base_url,
editor: editor,
events: events,
save_widget: save_widget,
});
var notification_area = new notificationarea.EditorNotificationArea(
'#notification_area', {
events: events,
});
editor.notification_area = notification_area;
notification_area.init_notification_widgets();
utils.load_extensions_from_config(config);
utils.load_extensions_from_config(common_config);
editor.load();
page.show();
window.onbeforeunload = function () {
if (editor.save_enabled && !editor.codemirror.isClean(editor.generation)) {
return "Unsaved changes will be lost. Close anyway?";
}
};
// Make sure the codemirror editor is sized appropriately.
var _handle_resize = function() {
var backdrop = $("#texteditor-backdrop");
// account for padding on the backdrop wrapper
var padding = backdrop.outerHeight(true) - backdrop.height();
$('div.CodeMirror').height($("#site").height() - padding);
};
$(window).resize(_handle_resize);
// On document ready, resize codemirror.
$(document).ready(_handle_resize);
});

@ -1,167 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/namespace',
'base/js/utils',
'base/js/dialog',
'codemirror/lib/codemirror',
'codemirror/mode/meta',
], function($, IPython, utils, dialog, CodeMirror) {
"use strict";
var MenuBar = function (selector, options) {
/**
* Constructor
*
* A MenuBar Class to generate the menubar of IPython notebook
*
* Parameters:
* selector: string
* options: dictionary
* Dictionary of keyword arguments.
* codemirror: CodeMirror instance
* contents: ContentManager instance
* events: $(Events) instance
* base_url : string
* file_path : string
*/
options = options || {};
this.base_url = options.base_url || utils.get_body_data("baseUrl");
this.selector = selector;
this.editor = options.editor;
this.events = options.events;
this.save_widget = options.save_widget;
if (this.selector !== undefined) {
this.element = $(selector);
this.bind_events();
}
this._load_mode_menu();
Object.seal(this);
};
MenuBar.prototype.bind_events = function () {
var that = this;
var editor = that.editor;
// File
this.element.find('#new-file').click(function () {
var w = window.open(undefined, IPython._target);
// Create a new file in the current directory
var parent = utils.url_path_split(editor.file_path)[0];
editor.contents.new_untitled(parent, {type: "file"}).then(
function (data) {
w.location = utils.url_path_join(
that.base_url, 'edit', utils.encode_uri_components(data.path)
);
},
function(error) {
w.close();
dialog.modal({
title : 'Creating New File Failed',
body : "The error was: " + error.message,
buttons : {'OK' : {'class' : 'btn-primary'}}
});
}
);
});
this.element.find('#save-file').click(function () {
editor.save();
});
this.element.find('#rename-file').click(function () {
that.save_widget.rename();
});
this.element.find('#download-file').click(function () {
window.open(utils.url_path_join(
that.base_url, 'files',
utils.encode_uri_components(that.editor.file_path)
) + '?download=1');
});
// Edit
this.element.find('#menu-find').click(function () {
editor.codemirror.execCommand("find");
});
this.element.find('#menu-replace').click(function () {
editor.codemirror.execCommand("replace");
});
this.element.find('#menu-keymap-default').click(function () {
editor.update_codemirror_options({
vimMode: false,
keyMap: 'default'
});
});
this.element.find('#menu-keymap-sublime').click(function () {
editor.update_codemirror_options({
vimMode: false,
keyMap: 'sublime'
});
});
this.element.find('#menu-keymap-emacs').click(function () {
editor.update_codemirror_options({
vimMode: false,
keyMap: 'emacs'
});
});
this.element.find('#menu-keymap-vim').click(function () {
editor.update_codemirror_options({
vimMode: true,
keyMap: 'vim'
});
});
// View
this.element.find('#toggle_header').click(function (){
$("#header-container").toggle();
});
this.element.find('#menu-line-numbers').click(function () {
var current = editor.codemirror.getOption('lineNumbers');
var value = Boolean(1-current);
editor.update_codemirror_options({lineNumbers: value});
});
this.events.on("config_changed.Editor", function () {
var keyMap = editor.codemirror.getOption('keyMap') || 'default';
that.element.find(".selected-keymap").removeClass("selected-keymap");
that.element.find("#menu-keymap-" + keyMap).addClass("selected-keymap");
});
this.events.on("mode_changed.Editor", function (evt, modeinfo) {
that.element.find("#current-mode")
.text(modeinfo.name)
.attr(
'title',
"The current language is " + modeinfo.name
);
});
};
MenuBar.prototype._load_mode_menu = function () {
var list = this.element.find("#mode-menu");
var editor = this.editor;
function make_set_mode(info) {
return function () {
editor.set_codemirror_mode(info);
// save codemirror mode for extension when explicitly selected
editor.save_codemirror_mode(info);
};
}
for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
var info = CodeMirror.modeInfo[i];
list.append($("<li>").append(
$("<a>").attr("href", "#")
.text(info.name)
.click(make_set_mode(info))
.attr('title',
"Set language to " + info.name
)
));
}
};
return {'MenuBar': MenuBar};
});

@ -1,29 +0,0 @@
define([
'base/js/notificationarea'
], function(notificationarea) {
"use strict";
var NotificationArea = notificationarea.NotificationArea;
var EditorNotificationArea = function(selector, options) {
NotificationArea.apply(this, [selector, options]);
}
EditorNotificationArea.prototype = Object.create(NotificationArea.prototype);
/**
* Initialize the default set of notification widgets.
*
* @method init_notification_widgets
*/
EditorNotificationArea.prototype.init_notification_widgets = function () {
var that = this;
var savew = this.new_notification_widget('save');
this.events.on("file_saved.Editor", function() {
savew.set_message("File saved", 2000);
});
};
return {EditorNotificationArea: EditorNotificationArea};
});

@ -1,192 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/dialog',
'base/js/keyboard',
'moment',
'bidi/bidi',
], function($, utils, dialog, keyboard, moment, bidi) {
"use strict";
var SaveWidget = function (selector, options) {
this.editor = undefined;
this.selector = selector;
this.events = options.events;
this.editor = options.editor;
this._last_modified = undefined;
this._filename = undefined;
this.keyboard_manager = options.keyboard_manager;
if (this.selector !== undefined) {
this.element = $(selector);
this.bind_events();
}
};
SaveWidget.prototype.bind_events = function () {
var that = this;
this.element.find('span.filename').click(function () {
that.rename();
});
this.events.on('save_status_clean.Editor', function (evt) {
that.update_document_title();
});
this.events.on('save_status_dirty.Editor', function (evt) {
that.update_document_title(undefined, true);
});
this.events.on('file_loaded.Editor', function (evt, model) {
that.update_filename(model.name);
that.update_document_title(model.name);
that.update_last_modified(model.last_modified);
});
this.events.on('file_saved.Editor', function (evt, model) {
that.update_filename(model.name);
that.update_document_title(model.name);
that.update_last_modified(model.last_modified);
});
this.events.on('file_renamed.Editor', function (evt, model) {
that.update_filename(model.name);
that.update_document_title(model.name);
that.update_address_bar(model.path);
});
this.events.on('file_save_failed.Editor', function () {
that.set_save_status('Save Failed!');
});
};
SaveWidget.prototype.rename = function (options) {
options = options || {};
var that = this;
var dialog_body = $('<div/>').append(
$("<p/>").addClass("rename-message")
.text('Enter a new filename:')
).append(
$("<br/>")
).append(
$('<input/>').attr('type','text').attr('size','25').addClass('form-control')
.val(that.editor.get_filename())
);
var d = dialog.modal({
title: "Rename File",
body: dialog_body,
default_button: "Cancel",
buttons : {
"Cancel": {},
"OK": {
class: "btn-primary",
click: function () {
var new_name = d.find('input').val();
if (!new_name) {
// Reset the message
d.find('.rename-message').text("Enter a new filename:");
return false;
}
d.find('.rename-message').text("Renaming...");
d.find('input[type="text"]').prop('disabled', true);
that.editor.rename(new_name).then(
function () {
d.modal('hide');
}, function (error) {
d.find('.rename-message').text(error.message || 'Unknown error');
d.find('input[type="text"]').prop('disabled', false).focus().select();
}
);
return false;
}
}
},
open : function () {
// Upon ENTER, click the OK button.
d.find('input[type="text"]').keydown(function (event) {
if (event.which === keyboard.keycodes.enter) {
d.find('.btn-primary').first().click();
return false;
}
});
d.find('input[type="text"]').focus().select();
}
});
};
SaveWidget.prototype.update_filename = function (filename) {
filename = bidi.applyBidi(filename);
this.element.find('span.filename').text(filename);
};
SaveWidget.prototype.update_document_title = function (filename, dirty) {
if(filename){
this._filename = filename;
}
document.title = (dirty ? '*' : '') + this._filename + ' - Jupyter Text Editor';
};
SaveWidget.prototype.update_address_bar = function (path) {
var state = {path : path};
window.history.replaceState(state, "", utils.url_path_join(
this.editor.base_url,
"edit",
utils.encode_uri_components(path)
));
};
SaveWidget.prototype.update_last_modified = function (last_modified) {
if (last_modified) {
this._last_modified = new Date(last_modified);
} else {
this._last_modified = null;
}
this._render_last_modified();
};
SaveWidget.prototype._render_last_modified = function () {
/** actually set the text in the element, from our _last_modified value
called directly, and periodically in timeouts.
*/
this._schedule_render_last_modified();
var el = this.element.find('span.last_modified');
if (!this._last_modified) {
el.text('').attr('title', 'never saved');
return;
}
var chkd = moment(this._last_modified);
var long_date = chkd.format('llll');
var human_date;
var tdelta = Math.ceil(new Date() - this._last_modified);
if (tdelta < utils.time.milliseconds.d){
// less than 24 hours old, use relative date
human_date = chkd.fromNow();
} else {
// otherwise show calendar
// <Today | yesterday|...> at hh,mm,ss
human_date = chkd.calendar();
}
el.text(human_date).attr('title', long_date);
};
SaveWidget.prototype._schedule_render_last_modified = function () {
/** schedule the next update to relative date
periodically updated, so short values like 'a few seconds ago' don't get stale.
*/
if (!this._last_modified) {
return;
}
if ((this._last_modified_timeout)) {
clearTimeout(this._last_modified_timeout);
}
var dt = Math.ceil(new Date() - this._last_modified);
this._last_modified_timeout = setTimeout(
$.proxy(this._render_last_modified, this),
utils.time.timeout_from_dt(dt)
);
};
return {'SaveWidget': SaveWidget};
});

@ -1,55 +0,0 @@
.dirty-indicator{
.fa();
width:20px;
}
.dirty-indicator-dirty{
.dirty-indicator();
}
.dirty-indicator-clean{
.dirty-indicator();
&:before{
.icon(@fa-var-check);
}
}
#filename {
font-size: 16pt;
display: table;
padding: 0px 5px;
}
#current-mode{
padding-left: 5px;
padding-right: 5px;
}
#texteditor-backdrop {
padding-top: @page-header-padding;
padding-bottom: @page-header-padding;
@media not print{
background-color: @page-backdrop-color;
}
#texteditor-container {
.CodeMirror-gutter, .CodeMirror-gutters {
@media print {
background-color: @body-bg;
}
@media not print {
background-color: @page-color;
}
}
@media not print{
padding: 0px;
background-color : @page-color;
.box-shadow(@global-shadow);
}
}
}
.CodeMirror-dialog {
background-color: @page-color;
}

@ -1,26 +0,0 @@
.selected-keymap {
i.fa {
padding: 0px 5px;
}
i.fa:before {
content: @fa-var-check;
}
}
#mode-menu {
// truncate mode-menu, so it doesn't get longer than the screen
overflow: auto;
max-height: 20em;
}
.edit_app {
#header {
.box-shadow(@global-shadow);
}
#menubar .navbar {
/* Use a negative 1 bottom margin, so the border overlaps the border of the
header */
margin-bottom: -1px;
}
}

@ -1,7 +0,0 @@
/*!
*
* IPython text editor webapp
*
*/
@import "menubar.less";
@import "edit.less";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

@ -1,7 +0,0 @@
/*This file contains any manual css for this page that needs to override the global styles.
This is only required when different pages style the same element differently. This is just
a hack to deal with our current css styles and no new styling should be added in this file.*/
#ipython-main-app {
position: relative;
}

@ -1,50 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
requirejs([
'jquery',
'base/js/dialog',
'base/js/i18n',
'underscore',
'base/js/namespace'
], function ($, dialog, i18n, _, IPython) {
'use strict';
$('#notebook_about').click(function () {
// use underscore template to auto html escape
if (sys_info) {
var text = i18n.msg._('You are using Jupyter notebook.');
text = text + '<br/><br/>';
text = text + i18n.msg._('The version of the notebook server is: ');
text = text + _.template('<b><%- version %></b>')({ version: sys_info.notebook_version });
if (sys_info.commit_hash) {
text = text + _.template('-<%- hash %>')({ hash: sys_info.commit_hash });
}
text = text + '<br/>';
text = text + i18n.msg._('The server is running on this version of Python:');
text = text + _.template('<br/><pre>Python <%- pyver %></pre>')({
pyver: sys_info.sys_version });
var kinfo = $('<div/>').attr('id', '#about-kinfo').text(i18n.msg._('Waiting for kernel to be available...'));
var body = $('<div/>');
body.append($('<h4/>').text(i18n.msg._('Server Information:')));
body.append($('<p/>').html(text));
body.append($('<h4/>').text(i18n.msg._('Current Kernel Information:')));
body.append(kinfo);
} else {
var text = i18n.msg._('Could not access sys_info variable for version information.');
var body = $('<div/>');
body.append($('<h4/>').text(i18n.msg._('Cannot find sys_info!')));
body.append($('<p/>').html(text));
}
dialog.modal({
title: i18n.msg._('About Jupyter Notebook'),
body: body,
buttons: { 'OK': {} }
});
try {
IPython.notebook.session.kernel.kernel_info(function (data) {
kinfo.html($('<pre/>').text(data.content.banner));
});
} catch (e) {
kinfo.html($('<p/>').text(i18n.msg._('unable to contact kernel')));
}
});
});

File diff suppressed because it is too large Load Diff

@ -1,878 +0,0 @@
// Distributed under the terms of the Modified BSD License.
/**
*
*
* @module cell
* @namespace cell
* @class Cell
*/
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'codemirror/lib/codemirror',
'codemirror/addon/edit/matchbrackets',
'codemirror/addon/edit/closebrackets',
'codemirror/addon/comment/comment',
'services/config',
], function($, utils, i18n, CodeMirror, cm_match, cm_closeb, cm_comment, configmod) {
"use strict";
function is_single_cursor(dict1, dict2) {
return ((dict1.line == dict2.line) && (dict1.ch == dict2.ch));
};
var overlayHack = CodeMirror.scrollbarModel.native.prototype.overlayHack;
CodeMirror.scrollbarModel.native.prototype.overlayHack = function () {
overlayHack.apply(this, arguments);
// Reverse `min-height: 18px` scrollbar hack on OS X
// which causes a dead area, making it impossible to click on the last line
// when there is horizontal scrolling to do and the "show scrollbar only when scrolling" behavior
// is enabled.
// This, in turn, has the undesirable behavior of never showing the horizontal scrollbar,
// even when it should, which is less problematic, at least.
if (/Mac/.test(navigator.platform)) {
this.horiz.style.minHeight = "";
}
};
var Cell = function (options) {
/* Constructor
*
* The Base `Cell` class from which to inherit.
* @constructor
* @param:
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* config: dictionary
* keyboard_manager: KeyboardManager instance
*/
options = options || {};
this.keyboard_manager = options.keyboard_manager;
this.events = options.events;
var config = options.config;
// superclass default overwrite our default
this.selected = false;
this.anchor = false;
this.rendered = false;
this.mode = 'command';
// Metadata property
var that = this;
this._metadata = {};
Object.defineProperty(this, 'metadata', {
get: function() { return that._metadata; },
set: function(value) {
that._metadata = value;
if (that.celltoolbar) {
that.celltoolbar.rebuild();
}
}
});
// backward compat.
Object.defineProperty(this, 'cm_config', {
get: function() {
console.warn(i18n.msg._("Warning: accessing Cell.cm_config directly is deprecated."));
return that._options.cm_config;
},
});
// load this from metadata later ?
this.user_highlight = 'auto';
// merge my class-specific config data with general cell-level config
var class_config_data = {};
if (this.class_config) {
class_config_data = this.class_config.get_sync();
}
var cell_config = new configmod.ConfigWithDefaults(options.config,
Cell.options_default, 'Cell');
var cell_config_data = cell_config.get_sync();
// this._options is a merge of SomeCell and Cell config data:
this._options = utils.mergeopt({}, cell_config_data, class_config_data);
this.placeholder = this._options.placeholder || '';
this.cell_id = utils.uuid();
// For JS VM engines optimization, attributes should be all set (even
// to null) in the constructor, and if possible, if different subclass
// have new attributes with same name, they should be created in the
// same order. Easiest is to create and set to null in parent class.
this.element = null;
this.cell_type = this.cell_type || null;
this.code_mirror = null;
// The nbformat only specifies attachments for textcell, but to avoid
// data loss when switching between cell types in the UI, all cells
// have an attachments property here. It is only saved to disk
// for textcell though (in toJSON)
this.attachments = {};
this.create_element();
if (this.element !== null) {
this.element.data("cell", this);
this.bind_events();
this.init_classes();
}
};
Cell.options_default = {
cm_config : {
indentUnit : 4,
readOnly: false,
theme: "default",
extraKeys: {
"Cmd-Right": "goLineRight",
"End": "goLineRight",
"Cmd-Left": "goLineLeft",
"Tab": "indentMore",
"Shift-Tab" : "indentLess",
"Cmd-/" : "toggleComment",
"Ctrl-/" : "toggleComment",
}
}
};
// FIXME: Workaround CM Bug #332 (Safari segfault on drag)
// by disabling drag/drop altogether on Safari
// https://github.com/codemirror/CodeMirror/issues/332
if (utils.browser[0] == "Safari") {
Cell.options_default.cm_config.dragDrop = false;
}
/**
* Empty. Subclasses must implement create_element.
* This should contain all the code to create the DOM element in notebook
* and will be called by Base Class constructor.
* @method create_element
*/
Cell.prototype.create_element = function () {
};
Cell.prototype.init_classes = function () {
/**
* Call after this.element exists to initialize the css classes
* related to selected, rendered and mode.
*/
if (this.selected) {
this.element.addClass('selected');
} else {
this.element.addClass('unselected');
}
if (this.rendered) {
this.element.addClass('rendered');
} else {
this.element.addClass('unrendered');
}
};
/**
* trigger on focus and on click to bubble up to the notebook and
* potentially extend the selection if shift-click, contract the selection
* if just codemirror focus (so edit mode).
* We **might** be able to move that to notebook `handle_edit_mode`.
*/
Cell.prototype._on_click = function (event) {
if (!this.selected) {
this.events.trigger('select.Cell', {'cell':this, 'extendSelection':event.shiftKey});
} else {
// I'm already part of the selection; contract selection to just me
this.events.trigger('select.Cell', {'cell': this});
}
};
/**
* Subclasses can implement override bind_events.
* Be careful to call the parent method when overwriting as it fires event.
* this will be triggered after create_element in constructor.
* @method bind_events
*/
Cell.prototype.bind_events = function () {
var that = this;
// We trigger events so that Cell doesn't have to depend on Notebook.
that.element.click(function (event) {
that._on_click(event);
});
if (this.code_mirror) {
this.code_mirror.on("change", function(cm, change) {
that.events.trigger("change.Cell", {cell: that, change: change});
that.events.trigger("set_dirty.Notebook", {value: true});
});
}
if (this.code_mirror) {
this.code_mirror.on('focus', function(cm, change) {
if (!that.selected) {
that.events.trigger('select.Cell', {'cell':that});
}
that.events.trigger('edit_mode.Cell', {cell: that});
});
}
if (this.code_mirror) {
this.code_mirror.on('blur', function(cm, change) {
that.events.trigger('command_mode.Cell', {cell: that});
});
}
this.element.dblclick(function () {
if (that.selected === false) {
this.events.trigger('select.Cell', {'cell':that});
}
});
};
/**
* This method gets called in CodeMirror's onKeyDown/onKeyPress
* handlers and is used to provide custom key handling.
*
* To have custom handling, subclasses should override this method, but still call it
* in order to process the Edit mode keyboard shortcuts.
*
* @method handle_codemirror_keyevent
* @param {CodeMirror} editor - The codemirror instance bound to the cell
* @param {event} event - key press event which either should or should not be handled by CodeMirror
* @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
*/
Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
var shortcuts = this.keyboard_manager.edit_shortcuts;
var cur = editor.getCursor();
if((cur.line !== 0 || cur.ch !==0) && event.keyCode === 38){
event._ipkmIgnore = true;
}
var nLastLine = editor.lastLine();
if ((event.keyCode === 40) &&
((cur.line !== nLastLine) ||
(cur.ch !== editor.getLineHandle(nLastLine).text.length))
) {
event._ipkmIgnore = true;
}
// if this is an edit_shortcuts shortcut, the global keyboard/shortcut
// manager will handle it
if (shortcuts.handles(event)) {
return true;
}
return false;
};
/**
* Triger typesetting of math by mathjax on current cell element
* @method typeset
*/
Cell.prototype.typeset = function () {
utils.typeset(this.element);
};
/**
* handle cell level logic when a cell is selected
* @method select
* @return is the action being taken
*/
Cell.prototype.select = function (moveanchor) {
// if anchor is true, set the move the anchor
moveanchor = (moveanchor === undefined)? true:moveanchor;
if(moveanchor){
this.anchor=true;
}
if (!this.selected) {
this.element.addClass('selected');
this.element.removeClass('unselected');
this.selected = true;
// disable 'insert image' menu item (specific cell types will enable
// it in their override select())
this.notebook.set_insert_image_enabled(false);
return true;
} else {
return false;
}
};
/**
* handle cell level logic when the cell is unselected
* @method unselect
* @return is the action being taken
*/
Cell.prototype.unselect = function (moveanchor) {
// if anchor is true, remove also the anchor
moveanchor = (moveanchor === undefined)? true:moveanchor;
if (moveanchor){
this.anchor = false;
}
if (this.selected) {
this.element.addClass('unselected');
this.element.removeClass('selected');
this.selected = false;
return true;
} else {
return false;
}
};
/**
* should be overwritten by subclass
* @method execute
*/
Cell.prototype.execute = function () {
return;
};
/**
* handle cell level logic when a cell is rendered
* @method render
* @return is the action being taken
*/
Cell.prototype.render = function () {
if (!this.rendered) {
this.element.addClass('rendered');
this.element.removeClass('unrendered');
this.rendered = true;
return true;
} else {
return false;
}
};
/**
* handle cell level logic when a cell is unrendered
* @method unrender
* @return is the action being taken
*/
Cell.prototype.unrender = function () {
if (this.rendered) {
this.element.addClass('unrendered');
this.element.removeClass('rendered');
this.rendered = false;
return true;
} else {
return false;
}
};
/**
* Delegates keyboard shortcut handling to either Jupyter keyboard
* manager when in command mode, or CodeMirror when in edit mode
*
* @method handle_keyevent
* @param {CodeMirror} editor - The codemirror instance bound to the cell
* @param {event} - key event to be handled
* @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
*/
Cell.prototype.handle_keyevent = function (editor, event) {
if (this.mode === 'command') {
return true;
} else if (this.mode === 'edit') {
return this.handle_codemirror_keyevent(editor, event);
}
};
/**
* @method at_top
* @return {Boolean}
*/
Cell.prototype.at_top = function () {
var cm = this.code_mirror;
var cursor = cm.getCursor();
if (cursor.line === 0 && cursor.ch === 0) {
return true;
}
return false;
};
/**
* @method at_bottom
* @return {Boolean}
* */
Cell.prototype.at_bottom = function () {
var cm = this.code_mirror;
var cursor = cm.getCursor();
if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
return true;
}
return false;
};
/**
* enter the command mode for the cell
* @method command_mode
* @return is the action being taken
*/
Cell.prototype.command_mode = function () {
if (this.mode !== 'command') {
this.mode = 'command';
return true;
} else {
return false;
}
};
/**
* enter the edit mode for the cell
* @method command_mode
* @return is the action being taken
*/
Cell.prototype.edit_mode = function () {
if (this.mode !== 'edit') {
this.mode = 'edit';
return true;
} else {
return false;
}
};
Cell.prototype.ensure_focused = function() {
if(this.element !== document.activeElement && !this.code_mirror.hasFocus()){
this.focus_cell();
}
};
/**
* Focus the cell in the DOM sense
* @method focus_cell
*/
Cell.prototype.focus_cell = function () {
this.element.focus();
this._on_click({});
};
/**
* Focus the editor area so a user can type
*
* NOTE: If codemirror is focused via a mouse click event, you don't want to
* call this because it will cause a page jump.
* @method focus_editor
*/
Cell.prototype.focus_editor = function () {
this.refresh();
this.code_mirror.focus();
};
/**
* Refresh codemirror instance
* @method refresh
*/
Cell.prototype.refresh = function () {
if (this.code_mirror) {
this.code_mirror.refresh();
}
};
/**
* should be overwritten by subclass
* @method get_text
*/
Cell.prototype.get_text = function () {
};
/**
* should be overwritten by subclass
* @method set_text
* @param {string} text
*/
Cell.prototype.set_text = function (text) {
};
/**
* should be overwritten by subclass
* serialise cell to json.
* @method toJSON
**/
Cell.prototype.toJSON = function () {
var data = {};
// deepcopy the metadata so copied cells don't share the same object
data.metadata = JSON.parse(JSON.stringify(this.metadata));
if (this.id !== undefined) {
data.id = this.id;
}
if (data.metadata.deletable) {
delete data.metadata.deletable;
}
if (data.metadata.editable) {
delete data.metadata.editable;
}
if (data.metadata.collapsed === false) {
delete data.metadata.collapsed;
}
data.cell_type = this.cell_type;
return data;
};
/**
* should be overwritten by subclass
* @method fromJSON
**/
Cell.prototype.fromJSON = function (data) {
if (data.metadata !== undefined) {
this.metadata = data.metadata;
}
if (data.id !== undefined) {
this.id = data.id;
}
};
/**
* can the cell be split into two cells (false if not deletable)
*
* @method is_splittable
**/
Cell.prototype.is_splittable = function () {
return this.is_deletable();
};
/**
* can the cell be merged with other cells (false if not deletable)
* @method is_mergeable
**/
Cell.prototype.is_mergeable = function () {
return this.is_deletable();
};
/**
* is the cell edtitable? only false (readonly) if
* metadata.editable is explicitly false -- everything else
* counts as true
*
* @method is_editable
**/
Cell.prototype.is_editable = function () {
if (this.metadata.editable === false) {
return false;
}
return true;
};
/**
* is the cell deletable? only false (undeletable) if
* metadata.deletable is explicitly false or if the cell is not
* editable -- everything else counts as true
*
* @method is_deletable
**/
Cell.prototype.is_deletable = function () {
if (this.metadata.deletable === false || !this.is_editable()) {
return false;
}
return true;
};
/**
* @return {String} - the text before the cursor
* @method get_pre_cursor
**/
Cell.prototype.get_pre_cursor = function () {
var cursor = this.code_mirror.getCursor();
var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
text = text.replace(/^\n+/, '').replace(/\n+$/, '');
return text;
};
/**
* @return {String} - the text after the cursor
* @method get_post_cursor
**/
Cell.prototype.get_post_cursor = function () {
var cursor = this.code_mirror.getCursor();
var last_line_num = this.code_mirror.lineCount()-1;
var last_line_len = this.code_mirror.getLine(last_line_num).length;
var end = {line:last_line_num, ch:last_line_len};
var text = this.code_mirror.getRange(cursor, end);
text = text.replace(/^\n+/, '').replace(/\n+$/, '');
return text;
};
/**
* @return {Array} - the text between cursors and within selections (multicursor/sorted)
* @method get_split_text
**/
Cell.prototype.get_split_text = function () {
var start = {line:0, ch:0};
var last_line_num = this.code_mirror.lineCount()-1;
var last_line_len = this.code_mirror.getLine(last_line_num).length;
var end = {line:last_line_num, ch:last_line_len};
var flag_empty_cell = is_single_cursor(start, end);
var flag_first_position = false;
var flag_last_position = false;
var flag_all_select = false;
var ranges = this.code_mirror.listSelections();
var cursors = [start];
for (var i = 0; i < ranges.length; i++) {
// append both to handle selections
// ranges[i].head.sticky is null if ctrl-a select
if ((ranges[i].head.sticky == 'before') || (ranges[i].head.sticky === null )) {
cursors.push(ranges[i].anchor);
cursors.push(ranges[i].head);
if (is_single_cursor(ranges[i].anchor, start) &&
is_single_cursor(ranges[i].head, end)) {
flag_all_select = true;
}
} else {
cursors.push(ranges[i].head);
cursors.push(ranges[i].anchor);
if (is_single_cursor(ranges[i].head, start) &&
is_single_cursor(ranges[i].anchor, end)) {
flag_all_select = true;
}
}
// single cursor at beginning or end of cell
if (is_single_cursor(ranges[i].head, ranges[i].anchor)) {
if (is_single_cursor(ranges[i].head, start)) flag_first_position = true;
if (is_single_cursor(ranges[i].head, end)) flag_last_position = true;
}
}
cursors.push(end);
// Cursors is now sorted, but likely has duplicates due to anchor and head being the same for cursors
var locations = [cursors[0]];
for (var i = 1; i < cursors.length; i++) {
var last = locations[locations.length-1];
var current = cursors[i];
if ((last.line != current.line) || (last.ch != current.ch)) {
locations.push(cursors[i]);
}
}
// Split text
var text_list = [];
// Split single cursors at first position
if (flag_empty_cell || flag_first_position) text_list.push('');
for (var i = 1; i < locations.length; i++) {
var text = this.code_mirror.getRange(locations[i-1], locations[i]);
text = text.replace(/^\n+/, '').replace(/\n+$/, ''); // removes newlines at beginning and end
text_list.push(text);
}
// Split single cursors at last position
if (flag_last_position) text_list.push('');
// Duplicate cell if full cell is selected
if ((text_list.length == 1) && flag_all_select && !flag_empty_cell) {
text_list = text_list.concat(text_list);
}
return text_list;
};
/**
* Show/Hide CodeMirror LineNumber
* @method show_line_numbers
*
* @param value {Bool} show (true), or hide (false) the line number in CodeMirror
**/
Cell.prototype.show_line_numbers = function (value) {
this.code_mirror.setOption('lineNumbers', value);
this.code_mirror.refresh();
};
/**
* Toggle CodeMirror LineNumber
* @method toggle_line_numbers
**/
Cell.prototype.toggle_line_numbers = function () {
var val = this.code_mirror.getOption('lineNumbers');
this.show_line_numbers(!val);
};
/**
* Force codemirror highlight mode
* @method force_highlight
* @param {object} - CodeMirror mode
**/
Cell.prototype.force_highlight = function(mode) {
this.user_highlight = mode;
this.auto_highlight();
};
/**
* Trigger autodetection of highlight scheme for current cell
* @method auto_highlight
*/
Cell.prototype.auto_highlight = function () {
this._auto_highlight(this.class_config.get_sync('highlight_modes'));
};
/**
* Try to autodetect cell highlight mode, or use selected mode
* @methods _auto_highlight
* @private
* @param {String|object|undefined} - CodeMirror mode | 'auto'
**/
Cell.prototype._auto_highlight = function (modes) {
/**
*Here we handle manually selected modes
*/
var that = this;
var mode;
if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
{
mode = this.user_highlight;
CodeMirror.autoLoadMode(this.code_mirror, mode);
this.code_mirror.setOption('mode', mode);
return;
}
var current_mode = this.code_mirror.getOption('mode', mode);
var first_line = this.code_mirror.getLine(0);
// loop on every pairs
for(mode in modes) {
var regs = modes[mode].reg;
// only one key every time but regexp can't be keys...
for(var i=0; i<regs.length; i++) {
// here we handle non magic_modes.
// TODO :
// On 3.0 and below, these things were regex.
// But now should be string for json-able config.
// We should get rid of assuming they might be already
// in a later version of Jupyter.
var re = regs[i];
if(typeof(re) === 'string'){
re = new RegExp(re);
}
if(first_line.match(re) !== null) {
if(current_mode == mode){
return;
}
if (mode.search('magic_') !== 0) {
utils.requireCodeMirrorMode(mode, function (spec) {
that.code_mirror.setOption('mode', spec);
});
return;
}
var magic_mode = mode;
mode = magic_mode.substr(6);
if(current_mode == magic_mode){
return;
}
utils.requireCodeMirrorMode(mode, function (spec) {
// Add an overlay mode to recognize the first line as "magic" instead
// of the mode used for the rest of the cell.
CodeMirror.defineMode(magic_mode, function(config) {
var magicOverlay = {
startState: function() {
return {firstMatched : false, inMagicLine: false};
},
token: function(stream, state) {
if(!state.firstMatched) {
state.firstMatched = true;
if (stream.match("%%", false)) {
state.inMagicLine = true;
}
}
if (state.inMagicLine) {
stream.eat(function any(ch) { return true; });
if (stream.eol()) {
state.inMagicLine = false;
}
return "magic";
}
stream.skipToEnd();
return null;
}
};
return CodeMirror.overlayMode(CodeMirror.getMode(config, spec), magicOverlay);
});
that.code_mirror.setOption('mode', magic_mode);
});
return;
}
}
}
// fallback on default
var default_mode;
try {
default_mode = this._options.cm_config.mode;
} catch(e) {
default_mode = 'text/plain';
}
if( current_mode === default_mode){
return;
}
this.code_mirror.setOption('mode', default_mode);
};
var UnrecognizedCell = function (options) {
/** Constructor for unrecognized cells */
Cell.apply(this, arguments);
this.cell_type = 'unrecognized';
this.celltoolbar = null;
this.data = {};
Object.seal(this);
};
UnrecognizedCell.prototype = Object.create(Cell.prototype);
// cannot merge or split unrecognized cells
UnrecognizedCell.prototype.is_mergeable = function () {
return false;
};
UnrecognizedCell.prototype.is_splittable = function () {
return false;
};
UnrecognizedCell.prototype.toJSON = function () {
/**
* deepcopy the metadata so copied cells don't share the same object
*/
return JSON.parse(JSON.stringify(this.data));
};
UnrecognizedCell.prototype.fromJSON = function (data) {
this.data = data;
if (data.metadata !== undefined) {
this.metadata = data.metadata;
} else {
data.metadata = this.metadata;
}
this.element.find('.inner_cell').find("a").text(i18n.msg.sprintf(i18n.msg._("Unrecognized cell type: %s"), data.cell_type));
};
UnrecognizedCell.prototype.create_element = function () {
Cell.prototype.create_element.apply(this, arguments);
var cell = this.element = $("<div>").addClass('cell unrecognized_cell');
cell.attr('tabindex','2');
var prompt = $('<div/>').addClass('prompt input_prompt');
cell.append(prompt);
var inner_cell = $('<div/>').addClass('inner_cell');
inner_cell.append(
$("<a>")
.attr("href", "#")
.text(i18n.msg._("Unrecognized cell type"))
);
cell.append(inner_cell);
this.element = cell;
};
UnrecognizedCell.prototype.bind_events = function () {
Cell.prototype.bind_events.apply(this, arguments);
var cell = this;
this.element.find('.inner_cell').find("a").click(function () {
cell.events.trigger('unrecognized_cell.Cell', {cell: cell});
});
};
return {
Cell: Cell,
UnrecognizedCell: UnrecognizedCell
};
});

@ -1,470 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/namespace',
'base/js/events',
'base/js/i18n'
], function($, IPython, events, i18n) {
"use strict";
var CellToolbar = function (options) {
/**
* Constructor
*
* Parameters:
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* cell: Cell instance
* notebook: Notebook instance
*
* TODO: This leaks, when cell are deleted
* There is still a reference to each celltoolbars.
*/
CellToolbar._instances.push(this);
this.notebook = options.notebook;
this.cell = options.cell;
this.create_element();
this.rebuild();
return this;
};
CellToolbar.prototype.create_element = function () {
this.inner_element = $('<div/>').addClass('celltoolbar');
this.element = $('<div/>').addClass('ctb_hideshow')
.append(this.inner_element);
};
// The default css style for the outer celltoolbar div
// (ctb_hideshow) is display: none.
// To show the cell toolbar, *both* of the following conditions must be met:
// - A parent container has class `ctb_global_show`
// - The celltoolbar has the class `ctb_show`
// This allows global show/hide, as well as per-cell show/hide.
CellToolbar.global_hide = function () {
$('body').removeClass('ctb_global_show');
};
CellToolbar.global_show = function () {
$('body').addClass('ctb_global_show');
};
CellToolbar.prototype.hide = function () {
this.element.removeClass('ctb_show');
};
CellToolbar.prototype.show = function () {
this.element.addClass('ctb_show');
};
/**
* Class variable that should contain a dict of all available callback
* we need to think of wether or not we allow nested namespace
* @property _callback_dict
* @private
* @static
* @type Dict
*/
CellToolbar._callback_dict = {};
/**
* Class variable that should contain the reverse order list of the button
* to add to the toolbar of each cell
* @property _ui_controls_list
* @private
* @static
* @type List
*/
CellToolbar._ui_controls_list = [];
/**
* Class variable that should contain the CellToolbar instances for each
* cell of the notebook
*
* @private
* @property _instances
* @static
* @type List
*/
CellToolbar._instances = [];
/**
* keep a list of all the available presets for the toolbar
* @private
* @property _presets
* @static
* @type Dict
*/
CellToolbar._presets = {};
// this is by design not a prototype.
/**
* Register a callback to create an UI element in a cell toolbar.
* @method register_callback
* @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
* for easier sorting and avoid collision
* @param callback {function(div, cell)} callback that will be called to generate the ui element
* @param [cell_types] {List_of_String|undefined} optional list of cell types. If present the UI element
* will be added only to cells of types in the list.
*
*
* The callback will receive the following element :
*
* * a div in which to add element.
* * the cell it is responsible from
*
* @example
*
* Example that create callback for a button that toggle between `true` and `false` label,
* with the metadata under the key 'foo' to reflect the status of the button.
*
* // first param reference to a DOM div
* // second param reference to the cell.
* var toggle = function(div, cell) {
* var button_container = $(div)
*
* // let's create a button that show the current value of the metadata
* var button = $('<div/>').button({label:String(cell.metadata.foo)});
*
* // On click, change the metadata value and update the button label
* button.click(function(){
* var v = cell.metadata.foo;
* cell.metadata.foo = !v;
* button.button("option", "label", String(!v));
* })
*
* // add the button to the DOM div.
* button_container.append(button);
* }
*
* // now we register the callback under the name `foo` to give the
* // user the ability to use it later
* CellToolbar.register_callback('foo', toggle);
*/
CellToolbar.register_callback = function(name, callback, cell_types) {
// Overwrite if it already exists.
CellToolbar._callback_dict[name] = cell_types ? {callback: callback, cell_types: cell_types} : callback;
};
/**
* Register a preset of UI element in a cell toolbar.
* Not supported Yet.
* @method register_preset
* @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
* for easier sorting and avoid collision
* @param preset_list {List_of_String} reverse order of the button in the toolbar. Each String of the list
* should correspond to a name of a registered callback.
*
* @private
* @example
*
* CellToolbar.register_callback('foo.c1', function(div, cell){...});
* CellToolbar.register_callback('foo.c2', function(div, cell){...});
* CellToolbar.register_callback('foo.c3', function(div, cell){...});
* CellToolbar.register_callback('foo.c4', function(div, cell){...});
* CellToolbar.register_callback('foo.c5', function(div, cell){...});
*
* CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])
* CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])
*/
CellToolbar.register_preset = function(name, preset_list, notebook) {
CellToolbar._presets[name] = preset_list;
events.trigger('preset_added.CellToolbar', {name: name});
// When "register_callback" is called by a custom extension, it may be executed after notebook is loaded.
// In that case, activate the preset if needed.
if (notebook && notebook.metadata && notebook.metadata.celltoolbar === name){
CellToolbar.activate_preset(name);
}
};
/**
* unregister the selected preset,
*
* return true if preset successfully unregistered
* false otherwise
*
**/
CellToolbar.unregister_preset = function(name){
if(CellToolbar._presets[name]){
delete CellToolbar._presets[name];
events.trigger('unregistered_preset.CellToolbar', {name: name});
return true
}
return false
}
/**
* List the names of the presets that are currently registered.
*
* @method list_presets
* @static
*/
CellToolbar.list_presets = function() {
var keys = [];
for (var k in CellToolbar._presets) {
keys.push(k);
}
return keys;
};
/**
* Activate an UI preset from `register_preset`
*
* This does not update the selection UI.
*
* @method activate_preset
* @param preset_name {String} string corresponding to the preset name
*
* @static
* @private
* @example
*
* CellToolbar.activate_preset('foo.foo_preset1');
*/
CellToolbar.activate_preset = function(preset_name){
var preset = CellToolbar._presets[preset_name];
if(preset !== undefined){
CellToolbar._ui_controls_list = preset;
CellToolbar.rebuild_all();
}
events.trigger('preset_activated.CellToolbar', {name: preset_name});
};
/**
* This should be called on the class and not on a instance as it will trigger
* rebuild of all the instances.
* @method rebuild_all
* @static
*
*/
CellToolbar.rebuild_all = function(){
for(var i=0; i < CellToolbar._instances.length; i++){
CellToolbar._instances[i].rebuild();
}
};
/**
* Rebuild all the button on the toolbar to update its state.
* @method rebuild
*/
CellToolbar.prototype.rebuild = function(){
/**
* strip everything from the div
* which is probably inner_element
* or this.element.
*/
this.inner_element.empty();
this.ui_controls_list = [];
var callbacks = CellToolbar._callback_dict;
var preset = CellToolbar._ui_controls_list;
// Yes we iterate on the class variable, not the instance one.
for (var i=0; i < preset.length; i++) {
var key = preset[i];
var callback = callbacks[key];
if (!callback) continue;
if (typeof callback === 'object') {
if (callback.cell_types.indexOf(this.cell.cell_type) === -1) continue;
callback = callback.callback;
}
var local_div = $('<div/>').addClass('button_container');
try {
callback(local_div, this.cell, this);
this.ui_controls_list.push(key);
} catch (e) {
console.log(i18n.msg.sprintf(i18n.msg._("Error in cell toolbar callback %s"), key), e);
continue;
}
// only append if callback succeeded.
this.inner_element.append(local_div);
}
// If there are no controls or the cell is a rendered TextCell hide the toolbar.
if (!this.ui_controls_list.length) {
this.hide();
} else {
this.show();
}
};
CellToolbar.utils = {};
/**
* A utility function to generate bindings between a checkbox and cell/metadata
* @method utils.checkbox_ui_generator
* @static
*
* @param name {string} Label in front of the checkbox
* @param setter {function( cell, newValue )}
* A setter method to set the newValue
* @param getter {function( cell )}
* A getter methods which return the current value.
*
* @return callback {function( div, cell )} Callback to be passed to `register_callback`
*
* @example
*
* An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
*
* var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',
* // setter
* function(cell, value){
* // we check that the slideshow namespace exist and create it if needed
* if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
* // set the value
* cell.metadata.slideshow.isSectionStart = value
* },
* // getter
* function(cell){ var ns = cell.metadata.slideshow;
* // if the slideshow namespace does not exist return `undefined`
* // (will be interpreted as `false` by checkbox) otherwise
* // return the value
* return (ns == undefined)? undefined: ns.isSectionStart
* }
* );
*
* CellToolbar.register_callback('newSlide', newSlide);
*
*/
CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){
return function(div, cell, celltoolbar) {
var button_container = $(div);
var chkb = $('<input/>').attr('type', 'checkbox');
var lbl = $('<label/>').append($('<span/>').text(name));
chkb.attr("checked", getter(cell));
chkb.click(function(){
var v = getter(cell);
setter(cell, !v);
chkb.attr("checked", !v);
});
button_container.append($('<span/>').append(lbl).append(chkb));
};
};
/**
* A utility function to generate bindings between a input field and cell/metadata
* @method utils.input_ui_generator
* @static
*
* @param name {string} Label in front of the input field
* @param setter {function( cell, newValue )}
* A setter method to set the newValue
* @param getter {function( cell )}
* A getter methods which return the current value.
*
* @return callback {function( div, cell )} Callback to be passed to `register_callback`
*
*/
CellToolbar.utils.input_ui_generator = function(name, setter, getter){
return function(div, cell, celltoolbar) {
var button_container = $(div);
var text = $('<input/>').attr('type', 'text');
var lbl = $('<label/>').append($('<span/>').text(name));
text.attr("value", getter(cell));
text.keyup(function(){
setter(cell, text.val());
});
button_container.append($('<span/>').append(lbl).append(text));
IPython.keyboard_manager.register_events(text);
};
};
/**
* A utility function to generate bindings between a dropdown list cell
* @method utils.select_ui_generator
* @static
*
* @param list_list {list_of_sublist} List of sublist of metadata value and name in the dropdown list.
* sublist should contain 2 element each, first a string that would be displayed in the dropdown list,
* and second the corresponding value to be passed to setter/return by getter. the corresponding value
* should not be "undefined" or behavior can be unexpected.
* @param setter {function( cell, newValue )}
* A setter method to set the newValue
* @param getter {function( cell )}
* A getter methods which return the current value of the metadata.
* @param [label=""] {String} optional label for the dropdown menu
*
* @return callback {function( div, cell )} Callback to be passed to `register_callback`
*
* @example
*
* var select_type = CellToolbar.utils.select_ui_generator([
* ["<None>" , "None" ],
* ["Header Slide" , "header_slide" ],
* ["Slide" , "slide" ],
* ["Fragment" , "fragment" ],
* ["Skip" , "skip" ],
* ],
* // setter
* function(cell, value){
* // we check that the slideshow namespace exist and create it if needed
* if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
* // set the value
* cell.metadata.slideshow.slide_type = value
* },
* // getter
* function(cell){ var ns = cell.metadata.slideshow;
* // if the slideshow namespace does not exist return `undefined`
* // (will be interpreted as `false` by checkbox) otherwise
* // return the value
* return (ns == undefined)? undefined: ns.slide_type
* }
* CellToolbar.register_callback('slideshow.select', select_type);
*
*/
CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label) {
label = label || "";
return function(div, cell, celltoolbar) {
var button_container = $(div);
var lbl = $("<label/>").append($('<span/>').text(label));
var select = $('<select/>');
if(!cell.is_editable()){
select.attr("disabled","disabled")
}
for(var i=0; i < list_list.length; i++){
var opt = $('<option/>')
.attr('value', list_list[i][1])
.text(list_list[i][0]);
select.append(opt);
}
select.val(getter(cell));
select.change(function(){
setter(cell, select.val());
});
button_container.append($('<span/>').append(lbl).append(select));
};
};
// Backwards compatibility.
IPython.CellToolbar = CellToolbar;
return {'CellToolbar': CellToolbar};
});

@ -1,50 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'notebook/js/celltoolbar',
'base/js/dialog',
'base/js/i18n'
], function(celltoolbar, dialog, i18n) {
"use strict";
var CellToolbar = celltoolbar.CellToolbar;
var edit_attachments_dialog = function(cell) {
dialog.edit_attachments({
attachments: cell.attachments,
callback: function(attachments) {
cell.attachments = attachments;
// Force cell refresh
cell.unrender();
cell.render();
},
name: 'Cell',
notebook: cell.notebook,
keyboard_manager: cell.keyboard_manager
});
};
var add_dialog_button = function(div, cell) {
var button_container = $(div);
var button = $('<button />')
.addClass('btn btn-default btn-xs')
.text(i18n.msg._('Edit Attachments'))
.click( function() {
edit_attachments_dialog(cell);
return false;
});
button_container.append(button);
};
var register = function(notebook) {
CellToolbar.register_callback('attachments.edit', add_dialog_button);
var attachments_preset = [];
attachments_preset.push('attachments.edit');
CellToolbar.register_preset(i18n.msg._('Attachments'), attachments_preset, notebook);
};
return {'register' : register};
});

@ -1,52 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'notebook/js/celltoolbar',
'base/js/dialog',
'base/js/i18n'
], function(celltoolbar, dialog, i18n) {
"use strict";
var CellToolbar = celltoolbar.CellToolbar;
var raw_edit = function (cell , edit_metadata_button) {
dialog.edit_metadata({
md: cell.metadata,
callback: function (md) {
cell.metadata = md;
},
name: i18n.msg._('Cell'),
notebook: this.notebook,
keyboard_manager: this.keyboard_manager,
edit_metadata_button: edit_metadata_button
});
};
var add_raw_edit_button = function(div, cell) {
var button_container = $(div);
var button = $('<button/>')
.addClass("btn btn-default btn-xs")
.text(i18n.msg._("Edit Metadata"))
.click( function () {
raw_edit(cell, this);
return false;
});
button_container.append(button);
};
var register = function (notebook) {
CellToolbar.register_callback('default.rawedit', add_raw_edit_button);
raw_edit = $.proxy(raw_edit, {
notebook: notebook,
keyboard_manager: notebook.keyboard_manager
});
var example_preset = [];
example_preset.push('default.rawedit');
CellToolbar.register_preset(i18n.msg._('Edit Metadata'), example_preset, notebook);
};
return {'register': register};
});

@ -1,142 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'notebook/js/celltoolbar',
], function(celltoolbar) {
"use strict";
var CellToolbar = celltoolbar.CellToolbar;
var example_preset = [];
var simple_button = function(div, cell) {
var button_container = $(div);
var button = $('<div/>').button({icons:{primary:'ui-icon-locked'}});
var fun = function(value){
try{
if(value){
cell.code_mirror.setOption('readOnly','nocursor');
button.button('option','icons',{primary:'ui-icon-locked'});
} else {
cell.code_mirror.setOption('readOnly',false);
button.button('option','icons',{primary:'ui-icon-unlocked'});
}
} catch(e){}
};
fun(cell.metadata.ro);
button.click(function(){
var v = cell.metadata.ro;
var locked = !v;
cell.metadata.ro = locked;
fun(locked);
})
.css('height','16px')
.css('width','35px');
button_container.append(button);
};
CellToolbar.register_callback('example.lock',simple_button);
example_preset.push('example.lock');
var toggle_test = function(div, cell) {
var button_container = $(div);
var button = $('<div/>')
.button({label:String(cell.metadata.foo)}).
css('width','65px');
button.click(function(){
var v = cell.metadata.foo;
cell.metadata.foo = !v;
button.button("option","label",String(!v));
});
button_container.append(button);
};
CellToolbar.register_callback('example.toggle',toggle_test);
example_preset.push('example.toggle');
var checkbox_test = CellToolbar.utils.checkbox_ui_generator('Yes/No',
// setter
function(cell, value){
// we check that the slideshow namespace exist and create it if needed
if (cell.metadata.yn_test === undefined){cell.metadata.yn_test = {};}
// set the value
cell.metadata.yn_test.value = value;
},
// getter
function(cell){ var ns = cell.metadata.yn_test;
// if the slideshow namespace does not exist return `undefined`
// (will be interpreted as `false` by checkbox) otherwise
// return the value
return (ns === undefined)? undefined: ns.value;
}
);
CellToolbar.register_callback('example.checkbox',checkbox_test);
example_preset.push('example.checkbox');
var select_test = CellToolbar.utils.select_ui_generator([
["-" ,undefined ],
["Header Slide" ,"header_slide" ],
["Slide" ,"slide" ],
["Fragment" ,"fragment" ],
["Skip" ,"skip" ],
],
// setter
function(cell,value){
// we check that the slideshow namespace exist and create it if needed
if (cell.metadata.test === undefined){cell.metadata.test = {};}
// set the value
cell.metadata.test.slide_type = value;
},
// getter
function(cell){ var ns = cell.metadata.test;
// if the slideshow namespace does not exist return `undefined`
// (will be interpreted as `false` by checkbox) otherwise
// return the value
return (ns === undefined)? undefined: ns.slide_type;
});
CellToolbar.register_callback('example.select',select_test);
example_preset.push('example.select');
var simple_dialog = function(title,text){
var dlg = $('<div/>').attr('title',title)
.append($('<p/>').text(text));
$(dlg).dialog({
autoOpen: true,
height: 300,
width: 650,
modal: true,
close: function() {
/**
*cleanup on close
*/
$(this).remove();
}
});
};
var add_simple_dialog_button = function(div, cell) {
var help_text = ["This is the Metadata editting UI.",
"It heavily rely on plugin to work ",
"and is still under development. You shouldn't wait too long before",
" seeing some customisable buttons in those toolbar."
].join('\n');
var button_container = $(div);
var button = $('<div/>').button({label:'?'})
.click(function(){simple_dialog('help',help_text); return false;});
button_container.append(button);
};
var register = function (notebook) {
CellToolbar.register_callback('example.help',add_simple_dialog_button);
example_preset.push('example.help');
CellToolbar.register_preset('Example',example_preset, notebook);
console.log('Example extension for metadata editing loaded.');
};
return {'register': register};
});

@ -1,100 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'notebook/js/celltoolbar',
'base/js/dialog',
'base/js/keyboard',
'base/js/i18n'
], function(celltoolbar, dialog, keyboard, i18n) {
"use strict";
var CellToolbar = celltoolbar.CellToolbar;
var raw_cell_preset = [];
var select_type = CellToolbar.utils.select_ui_generator([
[i18n.msg._("None"), "-"],
["LaTeX", "text/latex"],
["reST", "text/restructuredtext"],
["HTML", "text/html"],
["Markdown", "text/markdown"],
["Python", "text/x-python"],
[i18n.msg._("Custom"), "dialog"],
],
// setter
function(cell, value) {
if (value === "-") {
delete cell.metadata.raw_mimetype;
} else if (value === 'dialog') {
var message =
i18n.msg._("Set the MIME type of the raw cell:");
var mimeinput = $('<input/>')
.attr('type', 'text')
.attr('size', '25')
.attr('name', 'mimetype')
.val(cell.metadata.raw_mimetype || "-");
var dialogform = $('<div/>').attr('title', i18n.msg._("Edit MIME type"))
.append(
$('<form/>').append(
$('<fieldset/>').append(
$('<label/>')
.attr('for', 'mimetype')
.text(message)
)
.append($('<br/>'))
.append(mimeinput)
)
);
dialog.modal({
title: i18n.msg._("Raw Cell MIME Type"),
body: dialogform,
buttons : {
Cancel: {},
OK: {
class: "btn-primary",
click: function () {
console.log(cell);
cell.metadata.raw_mimetype = $(this).find('input').val();
console.log(cell.metadata);
}
}
},
notebook: cell.notebook,
keyboard_manager: cell.keyboard_manager,
open : function (event, ui) {
var that = $(this);
// Upon ENTER, click the OK button.
that.find('input[type="text"]').keydown(function (event, ui) {
if (event.which === keyboard.keycodes.enter) {
that.find('.btn-primary').first().click();
return false;
}
});
that.find('input[type="text"]').focus().select();
}
});
} else {
cell.metadata.raw_mimetype = value;
}
},
//getter
function(cell) {
return cell.metadata.raw_mimetype || "";
},
// name
i18n.msg._("Raw NBConvert Format")
);
var register = function (notebook) {
CellToolbar.register_callback('raw_cell.select', select_type, ['raw']);
raw_cell_preset.push('raw_cell.select');
CellToolbar.register_preset(i18n.msg._('Raw Cell Format'), raw_cell_preset, notebook);
};
return {'register': register};
});

@ -1,44 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'notebook/js/celltoolbar',
'base/js/i18n'
], function(celltoolbar, i18n) {
"use strict";
var CellToolbar = celltoolbar.CellToolbar;
var slideshow_preset = [];
var select_type = CellToolbar.utils.select_ui_generator([
["-" ,"-" ],
[i18n.msg._("Slide") ,"slide" ],
[i18n.msg._("Sub-Slide") ,"subslide" ],
[i18n.msg._("Fragment") ,"fragment" ],
[i18n.msg._("Skip") ,"skip" ],
[i18n.msg._("Notes") ,"notes" ],
],
// setter
function(cell, value){
// we check that the slideshow namespace exist and create it if needed
if (cell.metadata.slideshow === undefined){cell.metadata.slideshow = {};}
// set the value
cell.metadata.slideshow.slide_type = value;
},
// getter
function(cell){ var ns = cell.metadata.slideshow;
// if the slideshow namespace does not exist return `undefined`
// (will be interpreted as `false` by checkbox) otherwise
// return the value
return (ns === undefined)? undefined: ns.slide_type;
},
i18n.msg._("Slide Type"));
var register = function (notebook) {
CellToolbar.register_callback('slideshow.select',select_type);
slideshow_preset.push('slideshow.select');
CellToolbar.register_preset(i18n.msg._('Slideshow'),slideshow_preset, notebook);
};
return {'register': register};
});

@ -1,255 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'notebook/js/celltoolbar',
'base/js/dialog',
'base/js/i18n'
], function(celltoolbar, dialog, i18n) {
"use strict";
var CellToolbar = celltoolbar.CellToolbar;
var array_difference = function(a, b) {
return a.filter(function(n) {
return b.indexOf(n) === -1;
});
};
var write_tag = function(cell, name, add) {
if (add) {
// Add to metadata
if (cell.metadata.tags === undefined) {
cell.metadata.tags = [];
} else if (cell.metadata.tags.indexOf(name) !== -1) {
// Tag already exists
return false;
}
cell.metadata.tags.push(name);
} else {
// Remove from metadata
if (!cell.metadata || !cell.metadata.tags) {
// No tags to remove
return false;
}
// Remove tag from tags list
var index = cell.metadata.tags.indexOf(name);
if (index !== -1) {
cell.metadata.tags.splice(index, 1);
}
// If tags list is empty, remove it
if (cell.metadata.tags.length === 0) {
delete cell.metadata.tags;
}
}
cell.events.trigger('set_dirty.Notebook', {value: true});
return true;
};
var preprocess_input = function(input) {
// Split on whitespace + commas:
return input.split(/[,\s]+/)
};
var add_tag = function(cell, tag_container, on_remove) {
return function(name) {
if (name === '') {
// Skip empty strings
return;
}
// Write tag to metadata
var changed = write_tag(cell, name, true);
if (changed) {
// Make tag UI
var tag = make_tag(name, on_remove, cell.is_editable());
tag_container.append(tag);
var tag_map = jQuery.data(tag_container, "tag_map") || {};
tag_map[name] = tag;
jQuery.data(tag_container, 'tag_map', tag_map);
}
};
};
var remove_tag = function(cell, tag_container) {
return function(name) {
var changed = write_tag(cell, name, false);
if (changed) {
// Remove tag UI
var tag_map = jQuery.data(tag_container, "tag_map") || {};
var tag_UI = tag_map[name];
delete tag_map[name];
tag_UI.remove();
}
};
};
var init_tag_container = function(cell, tag_container, on_remove) {
var tag_list = cell.metadata.tags || [];
if (!Array.isArray(tag_list)) {
// We cannot make tags UI for this cell!
// Maybe someone else used "tags" for something?
return false; // Fail gracefully
}
var tag_map = {};
for (var i=0; i < tag_list.length; ++i) {
var tag_name = tag_list[i];
if (typeof tag_name !== 'string') {
// Unexpected type, disable toolbar for safety
return false;
}
var tag = make_tag(tag_name, on_remove, cell.is_editable());
tag_container.append(tag);
tag_map[tag_name] = tag;
}
jQuery.data(tag_container, 'tag_map', tag_map);
return true;
};
var make_tag = function(name, on_remove, is_editable) {
var tag_UI = $('<span/>')
.addClass('cell-tag')
.text(name);
if(is_editable){
var remove_button = $('<i/>')
.addClass('remove-tag-btn')
.addClass('fa fa-times')
.click(function () {
on_remove(name);
return false;
});
tag_UI.append(remove_button);
}
return tag_UI;
};
// Single edit with button to add tags
var add_tag_edit = function(div, cell, on_add, on_remove) {
var button_container = $(div);
var text = $('<input/>').attr('type', 'text');
var button = $('<button />')
.addClass('btn btn-default btn-xs')
.text(i18n.msg._('Add tag'))
.click(function() {
var tags = preprocess_input(text[0].value);
for (var i=0; i < tags.length; ++i) {
on_add(tags[i]);
}
// Clear input after adding:
text[0].value = '';
return false;
});
// Wire enter in input to button click
text.keyup(function(event){
if(event.keyCode == 13){
button.click();
}
});
var input_container = $('<span/>')
.addClass('tags-input');
add_dialog_button(input_container, cell, on_add, on_remove);
button_container.append(input_container
.append(text)
.append(button)
);
IPython.keyboard_manager.register_events(text);
};
var tag_dialog = function(cell, on_add, on_remove) {
var tag_list = cell.metadata.tags || [];
var message =
i18n.msg._("Edit the list of tags below. All whitespace " +
"is treated as tag separators.");
var textarea = $('<textarea/>')
.attr('aria-label', i18n.msg._('Edit the tags in the text area'))
.attr('rows', '13')
.attr('cols', '80')
.attr('name', 'tags')
.text(tag_list.join('\n'));
var dialogform = $('<div/>').attr('title', i18n.msg._('Edit the tags'))
.append(
$('<form/>').append(
$('<fieldset/>').append(
$('<label/>')
.attr('for','tags')
.text(message)
)
.append($('<br/>'))
.append(textarea)
)
);
var modal_obj = dialog.modal({
title: i18n.msg._("Edit Tags"),
body: dialogform,
default_button: "Cancel",
buttons: {
Cancel: {},
Edit: { class : "btn-primary",
click: function() {
var old_tags = cell.metadata.tags || [];
var new_tags = preprocess_input(textarea[0].value);
var added_tags = array_difference(new_tags, old_tags);
var removed_tags = array_difference(old_tags, new_tags);
for (var i=0; i < added_tags.length; ++i) {
on_add(added_tags[i]);
}
for (var i=0; i < removed_tags.length; ++i) {
on_remove(removed_tags[i]);
}
}
}
},
notebook: cell.notebook,
keyboard_manager: cell.keyboard_manager,
});
};
var add_dialog_button = function(container, cell, on_add, on_remove) {
var button = $('<button />')
.addClass('btn btn-default btn-xs tags-dialog-btn')
.text('...')
.click( function() {
tag_dialog(cell, on_add, on_remove);
return false;
});
container.append(button);
};
var add_tags_cellbar = function(div, cell) {
var button_container = $(div);
button_container.addClass('tags_button_container');
var tag_container = $('<span/>').
addClass('tag-container');
var on_remove = remove_tag(cell, tag_container);
var ok = init_tag_container(cell, tag_container, on_remove);
if (!ok) {
return;
}
button_container.append(tag_container);
var on_add = add_tag(cell, tag_container, on_remove);
if(cell.is_editable()){
add_tag_edit(div, cell, on_add, on_remove);
}
};
var register = function(notebook) {
CellToolbar.register_callback('tags.edit', add_tags_cellbar);
var tags_preset = [];
tags_preset.push('tags.edit');
CellToolbar.register_preset('Tags', tags_preset, notebook);
};
return {'register' : register};
});

@ -1,159 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/namespace',
'base/js/utils',
'base/js/i18n',
'base/js/dialog'
], function($, Jupyter, utils, i18n, dialog) {
var jcbprefix = '<pre class="jupyter-nb-cells-json">';
var jcbsuffix = '</pre>';
function store_json(cells, clipboard) {
// Firefox ignores application/json mime type, so put it in HTML as well.
// We also copy a text version so you can paste cell sources into a text editor
var j = JSON.stringify(cells);
var t = cells.map(function(c) {return c.source;}).join('\n\n');
clipboard.setData('text/plain', t);
clipboard.setData('text/html', jcbprefix + j + jcbsuffix);
clipboard.setData('application/json', j);
}
function load_json(clipboard) {
var s = clipboard.getData('text/html');
// System/browsers may add some more stuff before/after our content, so
// find where our prefix and suffix are.
var pix = s.indexOf(jcbprefix);
var six = s.lastIndexOf(jcbsuffix);
if (pix === -1 || six === -1) {
return null;
}
return JSON.parse(s.slice(pix + jcbprefix.length, six));
}
function isProgrammaticCopy(event) {
return (typeof(event.target.selectionStart) !== 'undefined'
&& typeof(event.target.selectionEnd) !== 'undefined'
&& ((event.target.selectionEnd - event.target.selectionStart) > 0));
}
function copy(event) {
if ((Jupyter.notebook.mode !== 'command') ||
// window.getSelection checks if text is selected, e.g. in output
!window.getSelection().isCollapsed ||
// Allow programmatic copy
isProgrammaticCopy(event)) {
return;
}
var selecn = Jupyter.notebook.get_selected_cells().map(
function(c) { return c.toJSON();});
store_json(selecn, event.clipboardData);
event.preventDefault();
}
function paste(event) {
if (Jupyter.notebook.mode !== 'command') {
return;
}
console.log(i18n.msg.sprintf(i18n.msg._('Clipboard types: %s'),event.clipboardData.types));
cells = load_json(event.clipboardData);
// console.log(cells);
// Does this JSON look like cells?
if (Array.isArray(cells) && (cells.length > 0) &&
cells[0].cell_type && cells[0].source) {
var first_inserted = null;
for (var i=0; i < cells.length; i++) {
var cell_data = cells[i];
var new_cell = Jupyter.notebook.insert_cell_above(cell_data.cell_type);
new_cell.fromJSON(cell_data);
if (first_inserted === null) {
first_inserted = new_cell;
}
}
first_inserted.focus_cell();
}
event.preventDefault();
}
function notebookOnlyEvent(callback) {
// Only call the callback to redirect the event if the notebook should be
// handling the events, at the discretion of the keyboard manager.
// If the focus is in a text widget or something (kbmanager disabled),
// allow the default event.
return function() {
if (Jupyter.keyboard_manager.enabled) {
callback.apply(this, arguments);
}
};
}
function needs_text_box_for_paste_event() {
// I know this is bad, but I don't know a better way to check this
return navigator.userAgent.indexOf('Firefox') !== -1;
}
function setup_paste_dialog() {
// Firefox only fires a paste event if the cursor is in a text input. So, on
// Ctrl-V, bring up a dialog with an invisible text box and catch the
// second Ctrl-V
var action = {
icon: 'fa-clipboard', // a font-awesome class used on buttons, etc
help : i18n.msg._('Dialog for paste from system clipboard'),
help_index : 'zz',
handler : function () {
var entry_box = $('<input type="text"/>');
entry_box.css('opacity', 0);
function paste_close_dlg(e) {
paste(e);
// There must be a better way to do this, but it's not any of:
// .hide(), .remove() or .dialog('close')
paste_dlg.find('.close').click();
document.removeEventListener('paste', paste_close_dlg);
}
document.addEventListener('paste', paste_close_dlg);
var cmdtrl = i18n.msg._('Ctrl-V');
if (utils.platform === 'MacOS') {
cmdtrl = i18n.msg._('Cmd-V');
}
var dialog_body = $("<div/>").append("<p>").append(i18n.msg.sprintf(i18n.msg._("Press %s again to paste"),cmdtrl))
.append("<br/>")
.append("<p><b>")
.append(i18n.msg._("Why is this needed? "))
.append("</b>")
.append(i18n.msg._("We can't get paste events in this browser without a text box. "))
.append(i18n.msg._("There's an invisible text box focused in this dialog."))
.append($("<form/>").append(entry_box));
var paste_dlg = dialog.modal({
notebook: Jupyter.notebook,
keyboard_manager: Jupyter.keyboard_manager,
title : i18n.msg.sprintf(i18n.msg._("%s to paste"),cmdtrl),
body : dialog_body,
open: function() {
entry_box.focus();
},
buttons : {
"Cancel" : {
// click : function() { reject("Dialog cancelled"); },
}
}
});
}
};
var full_action_name = Jupyter.actions.register(action, 'paste-dialog', 'system-clipboard');
Jupyter.keyboard_manager.command_shortcuts.add_shortcut('Cmdtrl-V', full_action_name);
}
// Set clipboard event listeners on the document.
return {setup_clipboard_events: function() {
document.addEventListener('copy', notebookOnlyEvent(copy));
if (needs_text_box_for_paste_event()) {
setup_paste_dialog();
} else {
document.addEventListener('paste', notebookOnlyEvent(paste));
}
}};
});

@ -1,620 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
/**
*
*
* @module codecell
* @namespace codecell
* @class CodeCell
*/
define([
'jquery',
'base/js/namespace',
'base/js/utils',
'base/js/i18n',
'base/js/keyboard',
'services/config',
'notebook/js/cell',
'notebook/js/outputarea',
'notebook/js/completer',
'notebook/js/celltoolbar',
'codemirror/lib/codemirror',
'codemirror/mode/python/python',
'notebook/js/codemirror-ipython'
], function(
$,
IPython,
utils,
i18n,
keyboard,
configmod,
cell,
outputarea,
completer,
celltoolbar,
CodeMirror,
cmpython,
cmip
) {
"use strict";
var Cell = cell.Cell;
/* local util for codemirror */
var posEq = function(a, b) {return a.line === b.line && a.ch === b.ch;};
/**
*
* function to delete until previous non blanking space character
* or first multiple of 4 tabstop.
* @private
*/
CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
var tabSize = cm.getOption('tabSize');
var ranges = cm.listSelections(); // handle multicursor
for (var i = ranges.length - 1; i >= 0; i--) { // iterate reverse so any deletions don't overlap
var head = ranges[i].head;
var anchor = ranges[i].anchor;
var sel = !posEq(head, anchor);
if (sel) {
// range is selection
cm.replaceRange("", anchor, head);
} else {
// range is cursor
var line = cm.getLine(head.line).substring(0, head.ch);
if (line.match(/^\ +$/) !== null){
// delete tabs
var prevTabStop = (Math.ceil(head.ch/tabSize)-1)*tabSize;
var from = CodeMirror.Pos(head.line, prevTabStop)
cm.replaceRange("", from, head);
} else {
// delete normally
var from = cm.findPosH(head, -1, 'char', false);
cm.replaceRange("", from, head);
}
}
}
};
var keycodes = keyboard.keycodes;
var CodeCell = function (kernel, options) {
/**
* Constructor
*
* A Cell conceived to write code.
*
* Parameters:
* kernel: Kernel instance
* The kernel doesn't have to be set at creation time, in that case
* it will be null and set_kernel has to be called later.
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* config: dictionary
* keyboard_manager: KeyboardManager instance
* notebook: Notebook instance
* tooltip: Tooltip instance
*/
this.kernel = kernel || null;
this.notebook = options.notebook;
this.collapsed = false;
this.events = options.events;
this.tooltip = options.tooltip;
this.config = options.config;
this.class_config = new configmod.ConfigWithDefaults(this.config,
CodeCell.options_default, 'CodeCell');
// create all attributed in constructor function
// even if null for V8 VM optimisation
this.input_prompt_number = null;
this.celltoolbar = null;
this.output_area = null;
this.last_msg_id = null;
this.completer = null;
Cell.apply(this,[{
config: options.config,
keyboard_manager: options.keyboard_manager,
events: this.events}]);
// Attributes we want to override in this subclass.
this.cell_type = "code";
var that = this;
this.element.focusout(
function() { that.auto_highlight(); }
);
};
CodeCell.options_default = {
cm_config : {
extraKeys: {
"Backspace" : "delSpaceToPrevTabStop",
},
mode: 'text',
theme: 'ipython',
matchBrackets: true,
autoCloseBrackets: true
},
highlight_modes : {
'magic_javascript' :{'reg':['^%%javascript']},
'magic_perl' :{'reg':['^%%perl']},
'magic_ruby' :{'reg':['^%%ruby']},
'magic_python' :{'reg':['^%%python3?']},
'magic_shell' :{'reg':['^%%bash']},
'magic_r' :{'reg':['^%%R']},
'magic_text/x-cython' :{'reg':['^%%cython']},
},
};
CodeCell.msg_cells = {};
CodeCell.prototype = Object.create(Cell.prototype);
/** @method create_element */
CodeCell.prototype.create_element = function () {
Cell.prototype.create_element.apply(this, arguments);
var that = this;
var cell = $('<div></div>').addClass('cell code_cell');
cell.attr('tabindex','2');
var input = $('<div></div>').addClass('input');
this.input = input;
var prompt_container = $('<div/>').addClass('prompt_container');
var run_this_cell = $('<div></div>').addClass('run_this_cell');
run_this_cell.prop('title', 'Run this cell');
run_this_cell.append('<i class="fa-step-forward fa"></i>');
run_this_cell.click(function (event) {
event.stopImmediatePropagation();
that.execute();
});
var prompt = $('<div/>').addClass('prompt input_prompt');
var inner_cell = $('<div/>').addClass('inner_cell');
this.celltoolbar = new celltoolbar.CellToolbar({
cell: this,
notebook: this.notebook});
inner_cell.append(this.celltoolbar.element);
var input_area = $('<div/>').addClass('input_area').attr("aria-label", i18n.msg._("Edit code here"));
this.code_mirror = new CodeMirror(input_area.get(0), this._options.cm_config);
// In case of bugs that put the keyboard manager into an inconsistent state,
// ensure KM is enabled when CodeMirror is focused:
this.code_mirror.on('focus', function () {
if (that.keyboard_manager) {
that.keyboard_manager.enable();
}
that.code_mirror.setOption('readOnly', !that.is_editable());
});
this.code_mirror.on('keydown', $.proxy(this.handle_keyevent,this));
$(this.code_mirror.getInputField()).attr("spellcheck", "false");
inner_cell.append(input_area);
prompt_container.append(prompt).append(run_this_cell);
input.append(prompt_container).append(inner_cell);
var output = $('<div></div>');
cell.append(input).append(output);
this.element = cell;
this.output_area = new outputarea.OutputArea({
config: this.config,
selector: output,
prompt_area: true,
events: this.events,
keyboard_manager: this.keyboard_manager,
});
this.completer = new completer.Completer(this, this.events);
};
/** @method bind_events */
CodeCell.prototype.bind_events = function () {
Cell.prototype.bind_events.apply(this, arguments);
var that = this;
this.element.focusout(
function() { that.auto_highlight(); }
);
this.events.on('kernel_restarting.Kernel', function() {
if (that.input_prompt_number === '*') {
that.set_input_prompt();
}
});
};
/**
* This method gets called in CodeMirror's onKeyDown/onKeyPress
* handlers and is used to provide custom key handling. Its return
* value is used to determine if CodeMirror should ignore the event:
* true = ignore, false = don't ignore.
* @method handle_codemirror_keyevent
*/
CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
var that = this;
// whatever key is pressed, first, cancel the tooltip request before
// they are sent, and remove tooltip if any, except for tab again
var tooltip_closed = null;
if (event.type === 'keydown' && event.which !== keycodes.tab ) {
tooltip_closed = this.tooltip.remove_and_cancel_tooltip();
}
var cur = editor.getCursor();
if (event.keyCode === keycodes.enter){
this.auto_highlight();
}
if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) {
// triger on keypress (!) otherwise inconsistent event.which depending on platform
// browser and keyboard layout !
// Pressing '(' , request tooltip, don't forget to reappend it
// The second argument says to hide the tooltip if the docstring
// is actually empty
this.tooltip.pending(that, true);
} else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
// If tooltip is active, cancel it. The call to
// remove_and_cancel_tooltip above doesn't pass, force=true.
// Because of this it won't actually close the tooltip
// if it is in sticky mode. Thus, we have to check again if it is open
// and close it with force=true.
if (!this.tooltip._hidden) {
this.tooltip.remove_and_cancel_tooltip(true);
}
// If we closed the tooltip, don't let CM or the global handlers
// handle this event.
event.codemirrorIgnore = true;
event._ipkmIgnore = true;
event.preventDefault();
return true;
} else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
if (editor.somethingSelected() || editor.getSelections().length !== 1){
var anchor = editor.getCursor("anchor");
var head = editor.getCursor("head");
if( anchor.line !== head.line){
return false;
}
}
var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
if (pre_cursor.trim() === "") {
// Don't show tooltip if the part of the line before the cursor
// is empty. In this case, let CodeMirror handle indentation.
return false;
}
this.tooltip.request(that);
event.codemirrorIgnore = true;
event.preventDefault();
return true;
} else if (event.keyCode === keycodes.tab && event.type === 'keydown') {
// Tab completion.
this.tooltip.remove_and_cancel_tooltip();
// completion does not work on multicursor, it might be possible though in some cases
if (editor.somethingSelected() || editor.getSelections().length > 1) {
return false;
}
var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
if (pre_cursor.trim() === "") {
// Don't autocomplete if the part of the line before the cursor
// is empty. In this case, let CodeMirror handle indentation.
return false;
} else {
event.codemirrorIgnore = true;
event.preventDefault();
this.completer.startCompletion();
return true;
}
}
// keyboard event wasn't one of those unique to code cells, let's see
// if it's one of the generic ones (i.e. check edit mode shortcuts)
return Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
};
// Kernel related calls.
CodeCell.prototype.set_kernel = function (kernel) {
this.kernel = kernel;
};
/**
* Execute current code cell to the kernel
* @method execute
*/
CodeCell.prototype.execute = function (stop_on_error) {
if (!this.kernel) {
console.log(i18n.msg._("Can't execute cell since kernel is not set."));
return;
}
if (stop_on_error === undefined) {
if (this.metadata !== undefined &&
this.metadata.tags !== undefined) {
if (this.metadata.tags.indexOf('raises-exception') !== -1) {
stop_on_error = false;
} else {
stop_on_error = true;
}
} else {
stop_on_error = true;
}
}
this.clear_output(false, true);
var old_msg_id = this.last_msg_id;
if (old_msg_id) {
this.kernel.clear_callbacks_for_msg(old_msg_id);
delete CodeCell.msg_cells[old_msg_id];
this.last_msg_id = null;
}
if (this.get_text().trim().length === 0) {
// nothing to do
this.set_input_prompt(null);
return;
}
this.set_input_prompt('*');
this.element.addClass("running");
var callbacks = this.get_callbacks();
this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true,
stop_on_error : stop_on_error});
CodeCell.msg_cells[this.last_msg_id] = this;
this.render();
this.events.trigger('execute.CodeCell', {cell: this});
var that = this;
function handleFinished(evt, data) {
if (that.kernel.id === data.kernel.id && that.last_msg_id === data.msg_id) {
that.events.trigger('finished_execute.CodeCell', {cell: that});
that.events.off('finished_iopub.Kernel', handleFinished);
}
}
this.events.on('finished_iopub.Kernel', handleFinished);
};
/**
* Construct the default callbacks for
* @method get_callbacks
*/
CodeCell.prototype.get_callbacks = function () {
var that = this;
return {
clear_on_done: false,
shell : {
reply : $.proxy(this._handle_execute_reply, this),
payload : {
set_next_input : $.proxy(this._handle_set_next_input, this),
page : $.proxy(this._open_with_pager, this)
}
},
iopub : {
output : function() {
that.events.trigger('set_dirty.Notebook', {value: true});
that.output_area.handle_output.apply(that.output_area, arguments);
},
clear_output : function() {
that.events.trigger('set_dirty.Notebook', {value: true});
that.output_area.handle_clear_output.apply(that.output_area, arguments);
},
},
input : $.proxy(this._handle_input_request, this),
};
};
CodeCell.prototype._open_with_pager = function (payload) {
this.events.trigger('open_with_text.Pager', payload);
};
/**
* @method _handle_execute_reply
* @private
*/
CodeCell.prototype._handle_execute_reply = function (msg) {
this.set_input_prompt(msg.content.execution_count);
this.element.removeClass("running");
this.events.trigger('set_dirty.Notebook', {value: true});
};
/**
* @method _handle_set_next_input
* @private
*/
CodeCell.prototype._handle_set_next_input = function (payload) {
var data = {
cell: this,
text: payload.text,
replace: payload.replace,
clear_output: payload.clear_output,
};
this.events.trigger('set_next_input.Notebook', data);
};
/**
* @method _handle_input_request
* @private
*/
CodeCell.prototype._handle_input_request = function (msg) {
this.output_area.append_raw_input(msg);
};
// Basic cell manipulation.
CodeCell.prototype.select = function () {
var cont = Cell.prototype.select.apply(this, arguments);
if (cont) {
this.code_mirror.refresh();
this.auto_highlight();
}
return cont;
};
CodeCell.prototype.render = function () {
var cont = Cell.prototype.render.apply(this, arguments);
// Always execute, even if we are already in the rendered state
return cont;
};
CodeCell.prototype.select_all = function () {
var start = {line: 0, ch: 0};
var nlines = this.code_mirror.lineCount();
var last_line = this.code_mirror.getLine(nlines-1);
var end = {line: nlines-1, ch: last_line.length};
this.code_mirror.setSelection(start, end);
};
CodeCell.prototype.collapse_output = function () {
this.output_area.collapse();
};
CodeCell.prototype.expand_output = function () {
this.output_area.expand();
this.output_area.unscroll_area();
};
CodeCell.prototype.scroll_output = function () {
this.output_area.expand();
this.output_area.scroll_if_long();
};
CodeCell.prototype.toggle_output = function () {
this.output_area.toggle_output();
};
CodeCell.prototype.toggle_output_scroll = function () {
this.output_area.toggle_scroll();
};
CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
var ns;
if (prompt_value === undefined || prompt_value === null) {
ns = "&nbsp;";
} else {
ns = encodeURIComponent(prompt_value);
}
return '<bdi>'+i18n.msg._('In')+'</bdi>&nbsp;[' + ns + ']:';
};
CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
for(var i=1; i < lines_number; i++) {
html.push(['...:']);
}
return html.join('<br/>');
};
CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
CodeCell.prototype.set_input_prompt = function (number) {
var nline = 1;
if (this.code_mirror !== undefined) {
nline = this.code_mirror.lineCount();
}
this.input_prompt_number = number;
var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
// This HTML call is okay because the user contents are escaped.
this.element.find('div.input_prompt').html(prompt_html);
this.events.trigger('set_dirty.Notebook', {value: true});
};
CodeCell.prototype.clear_input = function () {
this.code_mirror.setValue('');
};
CodeCell.prototype.get_text = function () {
return this.code_mirror.getValue();
};
CodeCell.prototype.set_text = function (code) {
return this.code_mirror.setValue(code);
};
CodeCell.prototype.clear_output = function (wait, ignore_queue) {
this.events.trigger('clear_output.CodeCell', {cell: this});
this.output_area.clear_output(wait, ignore_queue);
this.set_input_prompt();
};
// JSON serialization
CodeCell.prototype.fromJSON = function (data) {
Cell.prototype.fromJSON.apply(this, arguments);
if (data.cell_type === 'code') {
if (data.source !== undefined) {
this.set_text(data.source);
// make this value the starting point, so that we can only undo
// to this state, instead of a blank cell
this.code_mirror.clearHistory();
this.auto_highlight();
}
this.set_input_prompt(data.execution_count);
this.output_area.trusted = data.metadata.trusted || false;
this.output_area.fromJSON(data.outputs, data.metadata);
}
};
CodeCell.prototype.toJSON = function () {
var data = Cell.prototype.toJSON.apply(this);
data.source = this.get_text();
// is finite protect against undefined and '*' value
if (isFinite(this.input_prompt_number)) {
data.execution_count = this.input_prompt_number;
} else {
data.execution_count = null;
}
var outputs = this.output_area.toJSON();
data.outputs = outputs;
data.metadata.trusted = this.output_area.trusted;
if (this.output_area.collapsed) {
data.metadata.collapsed = this.output_area.collapsed;
} else {
delete data.metadata.collapsed;
}
if (this.output_area.scroll_state === 'auto') {
delete data.metadata.scrolled;
} else {
data.metadata.scrolled = this.output_area.scroll_state;
}
return data;
};
/**
* handle cell level logic when the cell is unselected
* @method unselect
* @return is the action being taken
*/
CodeCell.prototype.unselect = function() {
var cont = Cell.prototype.unselect.apply(this, arguments);
if (cont) {
// When a code cell is unselected, make sure that the corresponding
// tooltip and completer to that cell is closed.
this.tooltip.remove_and_cancel_tooltip(true);
if (this.completer !== null) {
this.completer.close();
}
}
return cont;
};
// Backwards compatibility.
IPython.CodeCell = CodeCell;
return {'CodeCell': CodeCell};
});

@ -1,38 +0,0 @@
// IPython mode is just a slightly altered Python Mode with `?` beeing a extra
// single operator. Here we define `ipython` mode in the require `python`
// callback to auto-load python mode, which is more likely not the best things
// to do, but at least the simple one for now.
(function(mod) {
if (typeof exports == "object" && typeof module == "object"){ // CommonJS
mod(requirejs("codemirror/lib/codemirror"),
requirejs("codemirror/mode/python/python")
);
} else if (typeof define == "function" && define.amd){ // AMD
define(["codemirror/lib/codemirror",
"codemirror/mode/python/python"], mod);
} else {// Plain browser env
mod(CodeMirror);
}
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("ipython", function(conf, parserConf) {
var pythonConf = {};
for (var prop in parserConf) {
if (parserConf.hasOwnProperty(prop)) {
pythonConf[prop] = parserConf[prop];
}
}
pythonConf.name = 'python';
pythonConf.singleOperators = new RegExp("^[\\+\\-\\*/%&|@\\^~<>!\\?]");
if (pythonConf.version === 3) {
pythonConf.identifiers = new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*");
} else if (pythonConf.version === 2) {
pythonConf.identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
}
return CodeMirror.getMode(conf, pythonConf);
}, 'python');
CodeMirror.defineMIME("text/x-ipython", "ipython");
})

@ -1,63 +0,0 @@
// IPython GFM (GitHub Flavored Markdown) mode is just a slightly altered GFM
// Mode with support for latex.
//
// Latex support was supported by Codemirror GFM as of
// https://github.com/codemirror/CodeMirror/pull/567
// But was later removed in
// https://github.com/codemirror/CodeMirror/commit/d9c9f1b1ffe984aee41307f3e927f80d1f23590c
(function(mod) {
if (typeof exports == "object" && typeof module == "object"){ // CommonJS
mod(requirejs("codemirror/lib/codemirror")
,requirejs("codemirror/addon/mode/multiplex")
,requirejs("codemirror/mode/gfm/gfm")
,requirejs("codemirror/mode/stex/stex")
);
} else if (typeof define == "function" && define.amd){ // AMD
define(["codemirror/lib/codemirror"
,"codemirror/addon/mode/multiplex"
,"codemirror/mode/python/python"
,"codemirror/mode/stex/stex"
], mod);
} else {// Plain browser env
mod(CodeMirror);
}
})( function(CodeMirror){
"use strict";
CodeMirror.defineMode("ipythongfm", function(config, parserConfig) {
var gfm_mode = CodeMirror.getMode(config, "gfm");
var tex_mode = CodeMirror.getMode(config, "stex");
return CodeMirror.multiplexingMode(
gfm_mode,
// By defining the $$ delimiter before the $ delimiter we don't run
// into the problem that $$ is interpreted as two consecutive $.
{
open: "$$", close: "$$",
mode: tex_mode,
delimStyle: "delimit"
},
{
open: "$", close: "$",
mode: tex_mode,
delimStyle: "delimit"
},
{
open: "\\(", close: "\\)",
mode: tex_mode,
delimStyle: "delimit"
},
{
open: "\\[", close: "\\]",
mode: tex_mode,
delimStyle: "delimit"
}
// .. more multiplexed styles can follow here
);
}, 'gfm');
CodeMirror.defineMIME("text/x-ipythongfm", "ipythongfm");
})

@ -1,207 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'typeahead',
'base/js/i18n',
'notebook/js/quickhelp'
],function($, typeahead, i18n, QH){
"use strict";
/**
* Humanize the action name to be consumed by user.
* internally the actions anem are of the form
* <namespace>:<description-with-dashes>
* we drop <namespace> and replace dashes for space.
*/
var humanize_action_id = function(str) {
return str.split(':')[1].replace(/-/g, ' ').replace(/_/g, '-');
};
/**
* given an action id return 'command-shortcut', 'edit-shortcut' or 'no-shortcut'
* for the action. This allows us to tag UI in order to visually distinguish
* wether an action have a keybinding or not.
**/
var get_mode_for_action_id = function(name, notebook) {
var shortcut = notebook.keyboard_manager.command_shortcuts.get_action_shortcut(name);
if (shortcut) {
return 'command-shortcut';
}
shortcut = notebook.keyboard_manager.edit_shortcuts.get_action_shortcut(name);
if (shortcut) {
return 'edit-shortcut';
}
return 'no-shortcut';
};
var CommandPalette = function(notebook) {
if(!notebook){
throw new Error("CommandPalette takes a notebook non-null mandatory argument");
}
// typeahead lib need a specific layout with specific class names.
// the following just does that
var form = $('<form/>');
var container = $('<div/>').addClass('typeahead__container');
var field = $('<div/>').addClass('typeahead__field');
var input = $('<input/>').attr('type', 'search');
field
.append(
$('<span>').addClass('typeahead__query').append(
input
)
)
.append(
$('<span/>').addClass('typeahead__button').append(
$('<button/>').attr('type', 'submit').append(
$('<span/>').addClass('typeahead__search-icon')
)
)
);
container.append(field);
form.append(container);
var mod = $('<div/>').addClass('modal cmd-palette').append(
$('<div/>').addClass('modal-dialog')
.append(
$('<div/>').addClass('modal-content').append(
$('<div/>').addClass('modal-body')
.append(
form
)
)
)
)
// end setting up right layout
.modal({show: false, backdrop:true})
.on('shown.bs.modal', function () {
// click on button trigger de-focus on mouse up.
// or something like that.
setTimeout(function(){input.focus();}, 100);
});
notebook.keyboard_manager.disable();
var before_close = function() {
// little trick to trigger early in onsubmit
// when the action called pop-up a dialog
// insure this function is only called once
if (before_close.ok) {
return;
}
var cell = notebook.get_selected_cell();
if (cell) {
cell.select();
}
if (notebook.keyboard_manager) {
notebook.keyboard_manager.enable();
notebook.keyboard_manager.command_mode();
}
before_close.ok = true; // avoid double call.
};
mod.on("hide.bs.modal", before_close);
// will be trigger when user select action
var onSubmit = function(node, query, result, resultCount) {
if (actions.indexOf(result.key) >= 0) {
before_close();
notebook.keyboard_manager.actions.call(result.key);
} else {
console.warning("No command " + result.key);
}
mod.modal('hide');
};
/* Whenever a result is rendered, if there is only one resulting
* element then automatically select that element.
*/
var onResult = function(node, query, result, resultCount) {
if (resultCount == 1) {
requestAnimationFrame(function() {
$('.typeahead-list > li:nth-child(2)').addClass('active');
});
}
};
// generate structure needed for typeahead layout and ability to search
var src = {};
var actions = Object.keys(notebook.keyboard_manager.actions._actions);
for (var i = 0; i < actions.length; i++) {
var action_id = actions[i];
var action = notebook.keyboard_manager.actions.get(action_id);
var group = action_id.split(':')[0];
src[group] = src[group] || {
data: [],
display: 'display'
};
var short = notebook.keyboard_manager.command_shortcuts.get_action_shortcut(action_id) ||
notebook.keyboard_manager.edit_shortcuts.get_action_shortcut(action_id);
if (short) {
short = QH.humanize_sequence(short);
}
var display_text;
if (action.cmd) {
display_text = i18n.msg._(action.cmd);
} else {
display_text = humanize_action_id(action_id);
}
var help = null;
if (action.help) {
help = i18n.msg._(action.help);
}
src[group].data.push({
display: display_text,
shortcut: short,
mode_shortcut: get_mode_for_action_id(action_id, notebook),
group: group,
icon: action.icon,
help: help,
key: action_id,
});
}
// now src is the right structure for typeahead
input.typeahead({
emptyTemplate: function(query) {
return $('<div>').text("No results found for ").append(
$('<code>').text(query)
);
},
maxItem: 1e3,
minLength: 0,
hint: true,
group: {
template:"{{group}} command group"
},
searchOnFocus: true,
mustSelectItem: true,
template: '<i class="fa fa-icon {{icon}}"></i>{{display}} <div title={{key}} class="pull-right {{mode_shortcut}}">{{shortcut}}</div>',
order: "asc",
source: src,
callback: {
onSubmit: onSubmit,
onClickAfter: onSubmit,
onResult: onResult
},
debug: false,
});
mod.modal('show');
};
return {'CommandPalette': CommandPalette};
});

@ -1,421 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/keyboard',
'notebook/js/contexthint',
'codemirror/lib/codemirror',
], function($, utils, keyboard, CodeMirror) {
"use strict";
// easier key mapping
var keycodes = keyboard.keycodes;
var prepend_n_prc = function(str, n) {
for( var i =0 ; i< n ; i++){
str = '%'+str ;
}
return str;
};
var _existing_completion = function(item, completion_array){
for( var i=0; i < completion_array.length; i++) {
if (completion_array[i].trim().substr(-item.length) == item) {
return true;
}
}
return false;
};
// what is the common start of all completions
function shared_start(B, drop_prct) {
if (B.length == 1) {
return B[0];
}
var A = [];
var common;
var min_lead_prct = 10;
for (var i = 0; i < B.length; i++) {
var str = B[i].str;
var localmin = 0;
if(drop_prct === true){
while ( str.substr(0, 1) == '%') {
localmin = localmin+1;
str = str.substring(1);
}
}
min_lead_prct = Math.min(min_lead_prct, localmin);
A.push(str);
}
if (A.length > 1) {
var tem1, tem2, s;
A = A.slice(0).sort();
tem1 = A[0];
s = tem1.length;
tem2 = A.pop();
while (s && tem2.indexOf(tem1) == -1) {
tem1 = tem1.substring(0, --s);
}
if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
return {
str:prepend_n_prc('', min_lead_prct),
type: "computed",
from: B[0].from,
to: B[0].to
};
}
return {
str: prepend_n_prc(tem1, min_lead_prct),
type: "computed",
from: B[0].from,
to: B[0].to
};
}
return null;
}
var Completer = function (cell, events) {
this.cell = cell;
this.editor = cell.code_mirror;
var that = this;
events.on('kernel_busy.Kernel', function () {
that.skip_kernel_completion = true;
});
events.on('kernel_idle.Kernel', function () {
that.skip_kernel_completion = false;
});
};
Completer.prototype.startCompletion = function () {
/**
* call for a 'first' completion, that will set the editor and do some
* special behavior like autopicking if only one completion available.
*/
if (this.editor.somethingSelected()|| this.editor.getSelections().length > 1) return;
this.done = false;
// use to get focus back on opera
this.carry_on_completion(true);
};
// easy access for julia to monkeypatch
//
Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;
Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
return Completer.reinvoke_re.test(pre_cursor);
};
/**
*
* pass true as parameter if this is the first invocation of the completer
* this will prevent the completer to dismiss itself if it is not on a
* word boundary like pressing tab after a space, and make it autopick the
* only choice if there is only one which prevent from popping the UI. as
* well as fast-forwarding the typing if all completion have a common
* shared start
**/
Completer.prototype.carry_on_completion = function (first_invocation) {
/**
* Pass true as parameter if you want the completer to autopick when
* only one completion. This function is automatically reinvoked at
* each keystroke with first_invocation = false
*/
var cur = this.editor.getCursor();
var line = this.editor.getLine(cur.line);
var pre_cursor = this.editor.getRange({
line: cur.line,
ch: cur.ch - 1
}, cur);
// we need to check that we are still on a word boundary
// because while typing the completer is still reinvoking itself
// so dismiss if we are on a "bad" character
if (!this.reinvoke(pre_cursor) && !first_invocation) {
this.close();
return;
}
this.autopick = false;
if (first_invocation) {
this.autopick = true;
}
// We want a single cursor position.
if (this.editor.somethingSelected()|| this.editor.getSelections().length > 1) {
return;
}
// one kernel completion came back, finish_completing will be called with the results
// we fork here and directly call finish completing if kernel is busy
var cursor_pos = this.editor.indexFromPos(cur);
var text = this.editor.getValue();
cursor_pos = utils.js_idx_to_char_idx(cursor_pos, text);
if (this.skip_kernel_completion) {
this.finish_completing({ content: {
matches: [],
cursor_start: cursor_pos,
cursor_end: cursor_pos,
}});
} else {
this.cell.kernel.complete(text, cursor_pos,
$.proxy(this.finish_completing, this)
);
}
};
Completer.prototype.finish_completing = function (msg) {
/**
* let's build a function that wrap all that stuff into what is needed
* for the new completer:
*/
var content = msg.content;
var start = content.cursor_start;
var end = content.cursor_end;
var matches = content.matches;
console.log(content);
var cur = this.editor.getCursor();
if (end === null) {
// adapted message spec replies don't have cursor position info,
// interpret end=null as current position,
// and negative start relative to that
end = this.editor.indexFromPos(cur);
if (start === null) {
start = end;
} else if (start < 0) {
start = end + start;
}
} else {
// handle surrogate pairs
var text = this.editor.getValue();
end = utils.char_idx_to_js_idx(end, text);
start = utils.char_idx_to_js_idx(start, text);
}
var results = CodeMirror.contextHint(this.editor);
var filtered_results = [];
//remove results from context completion
//that are already in kernel completion
var i;
for (i=0; i < results.length; i++) {
if (!_existing_completion(results[i].str, matches)) {
filtered_results.push(results[i]);
}
}
// append the introspection result, in order, at at the beginning of
// the table and compute the replacement range from current cursor
// position and matched_text length.
var from = this.editor.posFromIndex(start);
var to = this.editor.posFromIndex(end);
for (i = matches.length - 1; i >= 0; --i) {
filtered_results.unshift({
str: matches[i],
type: "introspection",
from: from,
to: to
});
}
// one the 2 sources results have been merge, deal with it
this.raw_result = filtered_results;
// if empty result return
if (!this.raw_result || !this.raw_result.length) return;
// When there is only one completion, use it directly.
if (this.autopick && this.raw_result.length == 1) {
this.insert(this.raw_result[0]);
return;
}
if (this.raw_result.length == 1) {
// test if first and only completion totally matches
// what is typed, in this case dismiss
var str = this.raw_result[0].str;
var pre_cursor = this.editor.getRange({
line: cur.line,
ch: cur.ch - str.length
}, cur);
if (pre_cursor == str) {
this.close();
return;
}
}
if (!this.visible) {
this.complete = $('<div/>').addClass('completions');
this.complete.attr('id', 'complete');
// Currently webkit doesn't use the size attr correctly. See:
// https://code.google.com/p/chromium/issues/detail?id=4579
this.sel = $('<select/>')
.attr('tabindex', -1)
.attr('multiple', 'true');
this.complete.append(this.sel);
this.visible = true;
$('body').append(this.complete);
//build the container
var that = this;
this.sel.click(function () {
that.pick();
that.editor.focus();
});
this._handle_keydown = function (cm, event) {
that.keydown(event);
};
this.editor.on('keydown', this._handle_keydown);
this._handle_keypress = function (cm, event) {
that.keypress(event);
};
this.editor.on('keypress', this._handle_keypress);
}
this.sel.attr('size', Math.min(10, this.raw_result.length));
// After everything is on the page, compute the position.
// We put it above the code if it is too close to the bottom of the page.
var pos = this.editor.cursorCoords(
this.editor.posFromIndex(start)
);
var left = pos.left-3;
var top;
var cheight = this.complete.height();
var wheight = $(window).height();
if (pos.bottom+cheight+5 > wheight) {
top = pos.top-cheight-4;
} else {
top = pos.bottom+1;
}
this.complete.css('left', left + 'px');
this.complete.css('top', top + 'px');
// Clear and fill the list.
this.sel.text('');
this.build_gui_list(this.raw_result);
return true;
};
Completer.prototype.insert = function (completion) {
this.editor.replaceRange(completion.str, completion.from, completion.to);
};
Completer.prototype.build_gui_list = function (completions) {
var MAXIMUM_GUI_LIST_LENGTH = 1000;
for (var i = 0; i < completions.length && i < MAXIMUM_GUI_LIST_LENGTH; ++i) {
var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
this.sel.append(opt);
}
this.sel.children().first().attr('selected', 'true');
this.sel.scrollTop(0);
};
Completer.prototype.close = function () {
this.done = true;
$('#complete').remove();
this.editor.off('keydown', this._handle_keydown);
this.editor.off('keypress', this._handle_keypress);
this.visible = false;
};
Completer.prototype.pick = function () {
this.insert(this.raw_result[this.sel[0].selectedIndex]);
this.close();
};
Completer.prototype.keydown = function (event) {
var code = event.keyCode;
// Enter
var options;
var index;
if (code == keycodes.enter) {
event.codemirrorIgnore = true;
event._ipkmIgnore = true;
event.preventDefault();
this.pick();
// Escape or backspace
} else if (code == keycodes.esc || code == keycodes.backspace) {
event.codemirrorIgnore = true;
event._ipkmIgnore = true;
event.preventDefault();
this.close();
} else if (code == keycodes.tab) {
//all the fastforwarding operation,
//Check that shared start is not null which can append with prefixed completion
// like %pylab , pylab have no shared start, and ff will result in py<tab><tab>
// to erase py
var sh = shared_start(this.raw_result, true);
if (sh.str !== '') {
this.insert(sh);
}
this.close();
this.carry_on_completion();
} else if (code == keycodes.up || code == keycodes.down) {
// need to do that to be able to move the arrow
// when on the first or last line of a code cell
event.codemirrorIgnore = true;
event._ipkmIgnore = true;
event.preventDefault();
options = this.sel.find('option');
index = this.sel[0].selectedIndex;
if (code == keycodes.up) {
index--;
}
if (code == keycodes.down) {
index++;
}
index = Math.min(Math.max(index, 0), options.length-1);
this.sel[0].selectedIndex = index;
} else if (code == keycodes.pageup || code == keycodes.pagedown) {
event.codemirrorIgnore = true;
event._ipkmIgnore = true;
options = this.sel.find('option');
index = this.sel[0].selectedIndex;
if (code == keycodes.pageup) {
index -= 10; // As 10 is the hard coded size of the drop down menu
} else {
index += 10;
}
index = Math.min(Math.max(index, 0), options.length-1);
this.sel[0].selectedIndex = index;
} else if (code == keycodes.left || code == keycodes.right) {
this.close();
}
};
Completer.prototype.keypress = function (event) {
/**
* FIXME: This is a band-aid.
* on keypress, trigger insertion of a single character.
* This simulates the old behavior of completion as you type,
* before events were disconnected and CodeMirror stopped
* receiving events while the completer is focused.
*/
var that = this;
var code = event.keyCode;
// don't handle keypress if it's not a character (arrows on FF)
// or ENTER/TAB
if (event.charCode === 0 ||
code == keycodes.tab ||
code == keycodes.enter
) return;
this.close();
this.editor.focus();
setTimeout(function () {
that.carry_on_completion();
}, 50);
};
return {'Completer': Completer};
});

@ -1,98 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// highly adapted for codemiror jshint
define(['codemirror/lib/codemirror'], function(CodeMirror) {
"use strict";
var forEach = function(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
};
var arrayContains = function(arr, item) {
if (!Array.prototype.indexOf) {
var i = arr.length;
while (i--) {
if (arr[i] === item) {
return true;
}
}
return false;
}
return arr.indexOf(item) != -1;
};
CodeMirror.contextHint = function (editor) {
// Find the token at the cursor
var cur = editor.getCursor(),
token = editor.getTokenAt(cur),
tprop = token;
// If it's not a 'word-style' token, ignore the token.
// If it is a property, find out what it is a property of.
var list = [];
var clist = getCompletions(token, editor);
for (var i = 0; i < clist.length; i++) {
list.push({
str: clist[i],
type: "context",
from: {
line: cur.line,
ch: token.start
},
to: {
line: cur.line,
ch: token.end
}
});
}
return list;
};
// find all 'words' of current cell
var getAllTokens = function (editor) {
var found = [];
// add to found if not already in it
function maybeAdd(str) {
if (!arrayContains(found, str)) found.push(str);
}
// loop through all token on all lines
var lineCount = editor.lineCount();
// loop on line
for (var l = 0; l < lineCount; l++) {
var line = editor.getLine(l);
//loop on char
for (var c = 1; c < line.length; c++) {
var tk = editor.getTokenAt({
line: l,
ch: c
});
// if token has a class, it has geat chances of beeing
// of interest. Add it to the list of possible completions.
// we could skip token of ClassName 'comment'
// or 'number' and 'operator'
if (tk.className !== null) {
maybeAdd(tk.string);
}
// jump to char after end of current token
c = tk.end;
}
}
return found;
};
var getCompletions = function(token, editor) {
var candidates = getAllTokens(editor);
// filter all token that have a common start (but nox exactly) the length of the current token
var lambda = function (x) {
return (x.indexOf(token.string) === 0 && x != token.string);
};
var filterd = candidates.filter(lambda);
return filterd;
};
return {'contextHint': CodeMirror.contextHint};
});

@ -1,352 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/namespace',
'base/js/dialog',
'base/js/utils',
'base/js/i18n'
], function($, IPython, dialog, utils, i18n) {
"use strict";
var KernelSelector = function(selector, notebook) {
var that = this;
this.selector = selector;
this.notebook = notebook;
this.notebook.set_kernelselector(this);
this.events = notebook.events;
this.current_selection = null;
this.kernelspecs = {};
if (this.selector !== undefined) {
this.element = $(selector);
this.request_kernelspecs();
}
this.bind_events();
// Make the object globally available for user convenience & inspection
IPython.kernelselector = this;
this._finish_load = null;
this._loaded = false;
this.loaded = new Promise(function(resolve) {
that._finish_load = resolve;
});
Object.seal(this);
};
KernelSelector.prototype.request_kernelspecs = function() {
// Preliminary documentation for kernelspecs api is at
// https://github.com/ipython/ipython/wiki/IPEP-25%3A-Registry-of-installed-kernels#rest-api
var url = utils.url_path_join(this.notebook.base_url, 'api/kernelspecs');
utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this));
};
var _sorted_names = function(kernelspecs) {
// sort kernel names
return Object.keys(kernelspecs).sort(function (a, b) {
// sort by display_name
var da = kernelspecs[a].spec.display_name;
var db = kernelspecs[b].spec.display_name;
if (da === db) {
return 0;
} else if (da > db) {
return 1;
} else {
return -1;
}
});
};
KernelSelector.prototype._got_kernelspecs = function(data) {
var that = this;
this.kernelspecs = data.kernelspecs;
var change_kernel_submenu = $("#menu-change-kernel-submenu");
var new_notebook_submenu = $("#menu-new-notebook-submenu");
var keys = _sorted_names(data.kernelspecs);
keys.map(function (key) {
// Create the Kernel > Change kernel submenu
var ks = data.kernelspecs[key];
change_kernel_submenu.append(
$("<li>").attr("id", "kernel-submenu-"+ks.name).append(
$('<a>')
.attr('href', '#')
.click( function () {
that.set_kernel(ks.name);
})
.text(ks.spec.display_name)
)
);
// Create the File > New Notebook submenu
new_notebook_submenu.append(
$("<li>").attr("id", "new-notebook-submenu-"+ks.name).append(
$('<a>')
.attr('href', '#')
.click( function () {
that.new_notebook(ks.name);
})
.text(ks.spec.display_name)
)
);
});
// trigger loaded promise
this._loaded = true;
this._finish_load();
};
KernelSelector.prototype._spec_changed = function (event, ks) {
/** event handler for spec_changed */
var that = this;
// update selection
this.current_selection = ks.name;
// put the current kernel at the top of File > New Notebook
var cur_kernel_entry = $("#new-notebook-submenu-" + ks.name);
var parent = cur_kernel_entry.parent();
// do something only if there is more than one kernel
if (parent.children().length > 1) {
// first, sort back the submenu
parent.append(
parent.children("li[class!='divider']").sort(
function (a,b) {
var da = $("a",a).text();
var db = $("a",b).text();
if (da === db) {
return 0;
} else if (da > db) {
return 1;
} else {
return -1;
}}));
// then, if there is no divider yet, add one
if (!parent.children("li[class='divider']").length) {
parent.prepend($("<li>").attr("class","divider"));
}
// finally, put the current kernel at the top
parent.prepend(cur_kernel_entry);
}
// load logo
var logo_img = this.element.find("img.current_kernel_logo");
$("#kernel_indicator").find('.kernel_indicator_name').text(ks.spec.display_name);
if (ks.resources['logo-64x64']) {
logo_img.attr("src", ks.resources['logo-64x64']);
logo_img.attr("title", ks.spec.display_name);
logo_img.show();
} else {
logo_img.hide();
}
// load kernel css
var css_url = ks.resources['kernel.css'];
if (css_url) {
$('#kernel-css').attr('href', css_url);
} else {
$('#kernel-css').attr('href', '');
}
// load kernel js
if (ks.resources['kernel.js']) {
// Debug added for Notebook 4.2, please remove at some point in the
// future if the following does not append anymore when kernels
// have kernel.js
//
// > Uncaught (in promise) TypeError: require is not a function
//
console.info('Dynamically requiring kernel.js, `requirejs` is ', requirejs);
requirejs([ks.resources['kernel.js']],
function (kernel_mod) {
if (kernel_mod && kernel_mod.onload) {
kernel_mod.onload();
} else {
console.warn("Kernel " + ks.name + " has a kernel.js file that does not contain "+
"any asynchronous module definition. This is undefined behavior "+
"and not recommended.");
}
}, function (err) {
console.warn("Failed to load kernel.js from ", ks.resources['kernel.js'], err);
}
);
this.events.on('spec_changed.Kernel', function (evt, new_ks) {
if (ks.name != new_ks.name) {
console.warn("kernelspec %s had custom kernel.js. Forcing page reload for %s.",
ks.name, new_ks.name);
that.notebook.save_notebook().then(function () {
window.location.reload();
});
}
});
}
};
KernelSelector.prototype.set_kernel = function (selected) {
/** set the kernel by name, ensuring kernelspecs have been loaded, first
kernel can be just a kernel name, or a notebook kernelspec metadata
(name, language, display_name).
*/
var that = this;
if (typeof selected === 'string') {
selected = {
name: selected
};
}
if (this._loaded) {
this._set_kernel(selected);
} else {
return this.loaded.then(function () {
that._set_kernel(selected);
});
}
};
KernelSelector.prototype._set_kernel = function (selected) {
/** Actually set the kernel (kernelspecs have been loaded) */
if (selected.name === this.current_selection) {
// only trigger event if value changed
return;
}
var kernelspecs = this.kernelspecs;
var ks = kernelspecs[selected.name];
if (ks === undefined) {
var available = _sorted_names(kernelspecs);
var matches = [];
if (selected.language && selected.language.length > 0) {
available.map(function (name) {
if (kernelspecs[name].spec.language.toLowerCase() === selected.language.toLowerCase()) {
matches.push(name);
}
});
}
if (matches.length === 1) {
ks = kernelspecs[matches[0]];
console.log("No exact match found for " + selected.name +
", using only kernel that matches language=" + selected.language, ks);
this.events.trigger("spec_match_found.Kernel", {
selected: selected,
found: ks,
});
}
// if still undefined, trigger failure event
if (ks === undefined) {
this.events.trigger("spec_not_found.Kernel", {
selected: selected,
matches: matches,
available: available,
});
return;
}
}
if (this.notebook._session_starting &&
this.notebook.session.kernel.name !== ks.name) {
console.error("Cannot change kernel while waiting for pending session start.");
return;
}
this.current_selection = ks.name;
this.events.trigger('spec_changed.Kernel', ks);
};
KernelSelector.prototype._spec_not_found = function (event, data) {
var that = this;
var select = $("<select>").addClass('form-control');
console.warn("Kernelspec not found:", data);
var names;
if (data.matches.length > 1) {
names = data.matches;
} else {
names = data.available;
}
names.map(function (name) {
var ks = that.kernelspecs[name];
select.append(
$('<option/>').attr('value', ks.name).text(ks.spec.display_name || ks.name)
);
});
var no_kernel_msg = i18n.msg.sprintf(i18n.msg._("Could not find a kernel matching %s. Please select a kernel:"),
(data.selected.display_name || data.selected.name))
var body = $("<form>").addClass("form-inline").append(
$("<span>").text(no_kernel_msg)
).append(select);
// This statement is used simply so that message extraction
// will pick up the strings. The actual setting of the text
// for the button is in dialog.js.
var button_labels = [ i18n.msg._("Continue Without Kernel"), i18n.msg._("Set Kernel"), i18n.msg._("OK") ];
dialog.modal({
title : i18n.msg._('Kernel not found'),
body : body,
buttons : {
'Continue Without Kernel' : {
class : 'btn-danger',
click : function () {
that.events.trigger('no_kernel.Kernel');
}
},
'Set Kernel' : {
class : 'btn-primary',
click : function () {
that.set_kernel(select.val());
}
}
}
});
};
KernelSelector.prototype.new_notebook = function (kernel_name) {
var w = window.open('', IPython._target);
// Create a new notebook in the same path as the current
// notebook's path.
var that = this;
var parent = utils.url_path_split(that.notebook.notebook_path)[0];
that.notebook.contents.new_untitled(parent, {type: "notebook"}).then(
function (data) {
var url = utils.url_path_join(
that.notebook.base_url, 'notebooks',
utils.encode_uri_components(data.path)
);
url += "?kernel_name=" + kernel_name;
w.location = url;
},
function(error) {
w.close();
dialog.modal({
title : i18n.msg._('Creating Notebook Failed'),
body : i18n.msg.sprintf(i18n.msg._("The error was: %s"), error.message),
buttons : {'OK' : {'class' : 'btn-primary'}}
});
}
);
};
KernelSelector.prototype.lock_switch = function() {
// should set a flag and display warning+reload if user want to
// re-change kernel. As UI discussion never finish
// making that a separate PR.
console.warn('switching kernel is not guaranteed to work !');
};
KernelSelector.prototype.bind_events = function() {
var that = this;
this.events.on('spec_changed.Kernel', $.proxy(this._spec_changed, this));
this.events.on('spec_not_found.Kernel', $.proxy(this._spec_not_found, this));
this.events.on('kernel_created.Session', function (event, data) {
that.set_kernel(data.kernel.name);
});
var logo_img = this.element.find("img.current_kernel_logo");
logo_img.on("load", function() {
logo_img.show();
});
logo_img.on("error", function() {
logo_img.hide();
});
};
return {'KernelSelector': KernelSelector};
});

@ -1,277 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
/**
*
*
* @module keyboardmanager
* @namespace keyboardmanager
* @class KeyboardManager
*/
define([
'jquery',
'base/js/utils',
'base/js/keyboard',
], function($, utils, keyboard) {
"use strict";
// Main keyboard manager for the notebook
var keycodes = keyboard.keycodes;
var KeyboardManager = function (options) {
/**
* A class to deal with keyboard event and shortcut
*
* @class KeyboardManager
* @constructor
* @param options {dict} Dictionary of keyword arguments :
* @param options.events {$(Events)} instance
* @param options.pager: {Pager} pager instance
*/
this.mode = 'command';
this.enabled = true;
this.pager = options.pager;
this.quick_help = undefined;
this.notebook = undefined;
this.last_mode = undefined;
this.bind_events();
this.env = {pager:this.pager};
this.actions = options.actions;
this.command_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env, options.config, 'command');
this.command_shortcuts._add_default_shortcuts(this.get_default_common_shortcuts());
this.command_shortcuts._add_default_shortcuts(this.get_default_command_shortcuts());
this.edit_shortcuts = new keyboard.ShortcutManager(undefined, options.events, this.actions, this.env);
this.edit_shortcuts._add_default_shortcuts(this.get_default_common_shortcuts());
this.edit_shortcuts._add_default_shortcuts(this.get_default_edit_shortcuts());
this.config = options.config;
var that = this;
this.config.loaded.then(function(){
var edit_unbind;
try {
edit_unbind = that.config.data.keys.edit.unbind||[];
} catch (e) {
if (e instanceof TypeError) {
edit_unbind = [];
} else {
throw e;
}
}
edit_unbind.forEach(function(u){that.edit_shortcuts.remove_shortcut(u);});
var command_unbind;
try {
command_unbind = that.config.data.keys.command.unbind||[];
} catch (e) {
if (e instanceof TypeError) {
command_unbind = [];
} else {
throw e;
}
}
command_unbind.forEach(function(u){that.command_shortcuts.remove_shortcut(u);});
that.command_shortcuts.add_shortcuts( ((that.config.data.keys||{}).command||{}).bind);
that.edit_shortcuts.add_shortcuts( ((that.config.data.keys||{}).edit ||{}).bind);
}
);
Object.seal(this);
};
/**
* Return a dict of common shortcut
* @method get_default_common_shortcuts
*
* @example Example of returned shortcut
* ```
* 'shortcut-key': 'action-name'
* // a string representing the shortcut as dash separated value.
* // e.g. 'shift' , 'shift-enter', 'cmd-t'
*```
*/
KeyboardManager.prototype.get_default_common_shortcuts = function() {
return {
'shift' : 'jupyter-notebook:ignore',
'shift-enter' : 'jupyter-notebook:run-cell-and-select-next',
'alt-enter' : 'jupyter-notebook:run-cell-and-insert-below',
'ctrl-enter' : 'jupyter-notebook:run-cell',
// cmd on mac, ctrl otherwise
'cmdtrl-enter' : 'jupyter-notebook:run-cell',
'cmdtrl-s' : 'jupyter-notebook:save-notebook'
};
};
KeyboardManager.prototype.get_default_edit_shortcuts = function() {
return {
'cmdtrl-shift-p' : 'jupyter-notebook:show-command-palette',
'cmdtrl-shift-f' : 'jupyter-notebook:show-command-palette',
'esc' : 'jupyter-notebook:enter-command-mode',
'ctrl-m' : 'jupyter-notebook:enter-command-mode',
'up' : 'jupyter-notebook:move-cursor-up',
'down' : 'jupyter-notebook:move-cursor-down',
'ctrl-shift--' : 'jupyter-notebook:split-cell-at-cursor',
};
};
KeyboardManager.prototype.get_default_command_shortcuts = function() {
return {
'cmdtrl-shift-p': 'jupyter-notebook:show-command-palette',
'cmdtrl-shift-f': 'jupyter-notebook:show-command-palette',
'shift-space': 'jupyter-notebook:scroll-notebook-up',
'shift-v' : 'jupyter-notebook:paste-cell-above',
'shift-m' : 'jupyter-notebook:merge-cells',
'shift-o' : 'jupyter-notebook:toggle-cell-output-scrolled',
'enter' : 'jupyter-notebook:enter-edit-mode',
'space' : 'jupyter-notebook:scroll-notebook-down',
'down' : 'jupyter-notebook:select-next-cell',
'i,i' : 'jupyter-notebook:interrupt-kernel',
'0,0' : 'jupyter-notebook:confirm-restart-kernel',
'd,d' : 'jupyter-notebook:delete-cell',
'esc': 'jupyter-notebook:close-pager',
'up' : 'jupyter-notebook:select-previous-cell',
'k' : 'jupyter-notebook:select-previous-cell',
'j' : 'jupyter-notebook:select-next-cell',
'shift-k': 'jupyter-notebook:extend-selection-above',
'shift-j': 'jupyter-notebook:extend-selection-below',
'shift-up': 'jupyter-notebook:extend-selection-above',
'shift-down': 'jupyter-notebook:extend-selection-below',
'cmdtrl-a': 'jupyter-notebook:select-all',
'x' : 'jupyter-notebook:cut-cell',
'c' : 'jupyter-notebook:copy-cell',
'v' : 'jupyter-notebook:paste-cell-below',
'a' : 'jupyter-notebook:insert-cell-above',
'b' : 'jupyter-notebook:insert-cell-below',
'y' : 'jupyter-notebook:change-cell-to-code',
'm' : 'jupyter-notebook:change-cell-to-markdown',
'r' : 'jupyter-notebook:change-cell-to-raw',
'1' : 'jupyter-notebook:change-cell-to-heading-1',
'2' : 'jupyter-notebook:change-cell-to-heading-2',
'3' : 'jupyter-notebook:change-cell-to-heading-3',
'4' : 'jupyter-notebook:change-cell-to-heading-4',
'5' : 'jupyter-notebook:change-cell-to-heading-5',
'6' : 'jupyter-notebook:change-cell-to-heading-6',
'o' : 'jupyter-notebook:toggle-cell-output-collapsed',
's' : 'jupyter-notebook:save-notebook',
'l' : 'jupyter-notebook:toggle-cell-line-numbers',
'shift-l' : 'jupyter-notebook:toggle-all-line-numbers',
'h' : 'jupyter-notebook:show-keyboard-shortcuts',
'z' : 'jupyter-notebook:undo-cell-deletion',
'q' : 'jupyter-notebook:close-pager',
'p' : 'jupyter-notebook:show-command-palette',
};
};
KeyboardManager.prototype.bind_events = function () {
var that = this;
$(document).keydown(function (event) {
if(event._ipkmIgnore===true||(event.originalEvent||{})._ipkmIgnore===true){
return false;
}
return that.handle_keydown(event);
});
};
KeyboardManager.prototype.set_notebook = function (notebook) {
this.notebook = notebook;
this.actions.extend_env({notebook:notebook});
};
KeyboardManager.prototype.set_quickhelp = function (notebook) {
this.actions.extend_env({quick_help:notebook});
};
KeyboardManager.prototype.handle_keydown = function (event) {
/**
* returning false from this will stop event propagation
**/
if (event.which === keycodes.esc) {
// Intercept escape at highest level to avoid closing
// websocket connection with firefox
event.preventDefault();
}
if (!this.enabled) {
if (event.which === keycodes.esc) {
this.notebook.command_mode();
return false;
}
return true;
}
if (this.mode === 'edit') {
return this.edit_shortcuts.call_handler(event);
} else if (this.mode === 'command') {
return this.command_shortcuts.call_handler(event);
}
return true;
};
KeyboardManager.prototype.edit_mode = function () {
this.last_mode = this.mode;
this.mode = 'edit';
};
KeyboardManager.prototype.command_mode = function () {
this.last_mode = this.mode;
this.mode = 'command';
};
KeyboardManager.prototype.enable = function () {
this.enabled = true;
};
KeyboardManager.prototype.disable = function () {
this.enabled = false;
};
KeyboardManager.prototype.register_events = function (e) {
e = $(e);
var that = this;
var handle_focus = function () {
that.disable();
};
var handle_blur = function () {
that.enable();
};
e.on('focusin', handle_focus);
e.on('focusout', handle_blur);
// TODO: Very strange. The focusout event does not seem fire for the
// bootstrap textboxes on FF25&26... This works around that by
// registering focus and blur events recursively on all inputs within
// registered element.
e.find('input').blur(handle_blur);
e.on('DOMNodeInserted', function (event) {
var target = $(event.target);
if (target.is('input')) {
target.blur(handle_blur);
} else {
target.find('input').blur(handle_blur);
}
});
// There are times (raw_input) where we remove the element from the DOM before
// focusout is called. In this case we bind to the remove event of jQueryUI,
// which gets triggered upon removal, iff it is focused at the time.
// is_focused must be used to check for the case where an element within
// the element being removed is focused.
e.on('remove', function () {
if (utils.is_focused(e[0])) {
that.enable();
}
});
};
return {'KeyboardManager': KeyboardManager};
});

@ -1,249 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// adapted from Mozilla Developer Network example at
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
// shim `bind` for testing under casper.js
var bind = function bind(obj) {
var slice = [].slice;
var args = slice.call(arguments, 1),
self = this,
nop = function() {
},
bound = function() {
return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
};
nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined
bound.prototype = new nop();
return bound;
};
Function.prototype.bind = Function.prototype.bind || bind ;
requirejs.config({
map: {
"*": {
"typeahead": "jquery-typeahead"
}
}
})
requirejs([
'jquery',
'contents',
'base/js/namespace',
'notebook/js/notebook',
'services/config',
'base/js/utils',
'base/js/page',
'base/js/events',
'base/js/promises',
'auth/js/loginwidget',
'notebook/js/maintoolbar',
'notebook/js/pager',
'notebook/js/promises',
'notebook/js/quickhelp',
'notebook/js/menubar',
'notebook/js/notificationarea',
'notebook/js/savewidget',
'notebook/js/actions',
'notebook/js/keyboardmanager',
'notebook/js/kernelselector',
'codemirror/lib/codemirror',
'notebook/js/about',
'notebook/js/searchandreplace',
'notebook/js/clipboard',
'bidi/bidi',
'notebook/js/celltoolbarpresets/tags'
], function(
$,
contents_service,
IPython,
notebook,
configmod,
utils,
page,
events,
promises,
loginwidget,
maintoolbar,
pager,
nb_promises,
quickhelp,
menubar,
notificationarea,
savewidget,
actions,
keyboardmanager,
kernelselector,
CodeMirror,
about,
searchandreplace,
clipboard,
bidi
) {
"use strict";
// Pull typeahead from the global jquery object
var typeahead = $.typeahead;
try{
requirejs(['custom/custom'], function() {});
bidi.loadLocale();
} catch(err) {
console.log("Error processing custom.js. Logging and continuing");
console.warn(err);
}
// compat with old IPython, remove for IPython > 3.0
window.CodeMirror = CodeMirror;
// Setup all of the config related things
var common_options = {
ws_url : utils.get_body_data("wsUrl"),
base_url : utils.get_body_data("baseUrl"),
notebook_path : utils.get_body_data("notebookPath"),
notebook_name : utils.get_body_data('notebookName')
};
var config_section = new configmod.ConfigSection('notebook', common_options);
config_section.load();
var common_config = new configmod.ConfigSection('common', common_options);
common_config.load();
// Instantiate the main objects
var page = new page.Page('div#header', 'div#site');
var pager = new pager.Pager('div#pager', {
events: events});
var acts = new actions.init();
var keyboard_manager = new keyboardmanager.KeyboardManager({
pager: pager,
events: events,
actions: acts,
config: config_section,
});
var save_widget = new savewidget.SaveWidget('span#save_widget', {
events: events,
keyboard_manager: keyboard_manager});
acts.extend_env({save_widget:save_widget});
var contents = new contents_service.Contents({
base_url: common_options.base_url,
common_config: common_config
});
var notebook = new notebook.Notebook('div#notebook', $.extend({
events: events,
keyboard_manager: keyboard_manager,
save_widget: save_widget,
contents: contents,
config: config_section},
common_options));
var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
notebook: notebook,
events: events,
actions: acts});
var quick_help = new quickhelp.QuickHelp({
keyboard_manager: keyboard_manager,
events: events,
notebook: notebook});
keyboard_manager.set_notebook(notebook);
keyboard_manager.set_quickhelp(quick_help);
var menubar = new menubar.MenuBar('#menubar', $.extend({
notebook: notebook,
contents: contents,
events: events,
save_widget: save_widget,
quick_help: quick_help,
actions: acts,
config: config_section},
common_options));
var notification_area = new notificationarea.NotebookNotificationArea(
'#notification_area', {
events: events,
save_widget: save_widget,
notebook: notebook,
keyboard_manager: keyboard_manager});
notification_area.init_notification_widgets();
var kernel_selector = new kernelselector.KernelSelector(
'#kernel_logo_widget', notebook);
searchandreplace.load(keyboard_manager);
$('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
'<span id="test2" style="font-weight: bold;">x</span>'+
'<span id="test3" style="font-style: italic;">x</span></pre></div>');
var nh = $('#test1').innerHeight();
var bh = $('#test2').innerHeight();
var ih = $('#test3').innerHeight();
if(nh != bh || nh != ih) {
$('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
}
$('#fonttest').remove();
page.show();
events.one('notebook_loaded.Notebook', function () {
var hash = document.location.hash;
if (hash) {
document.location.hash = '';
document.location.hash = hash;
}
notebook.set_autosave_interval(notebook.minimum_autosave_interval);
});
IPython.page = page;
IPython.notebook = notebook;
IPython.contents = contents;
IPython.pager = pager;
IPython.quick_help = quick_help;
IPython.login_widget = login_widget;
IPython.menubar = menubar;
IPython.toolbar = toolbar;
IPython.notification_area = notification_area;
IPython.keyboard_manager = keyboard_manager;
IPython.save_widget = save_widget;
IPython.tooltip = notebook.tooltip;
try {
events.trigger('app_initialized.NotebookApp');
} catch (e) {
console.error("Error in app_initialized callback", e);
}
Object.defineProperty( IPython, 'actions', {
get: function() {
console.warn('accessing "actions" on the global IPython/Jupyter is not recommended. Pass it to your objects constructors at creation time');
return acts;
},
enumerable: true,
configurable: false
});
clipboard.setup_clipboard_events();
// Now actually load nbextensionsload_extensions_from_config
Promise.all([
utils.load_extensions_from_config(config_section),
utils.load_extensions_from_config(common_config),
])
.catch(function(error) {
console.error('Could not load nbextensions from user config files', error);
})
// BEGIN HARDCODED WIDGETS HACK
.then(function() {
if (!utils.is_loaded('jupyter-js-widgets/extension')) {
// Fallback to the ipywidgets extension
utils.load_extension('widgets/notebook/js/extension').catch(function () {
console.warn('Widgets are not available. Please install widgetsnbextension or ipywidgets 4.0');
});
}
})
.catch(function(error) {
console.error('Could not load ipywidgets', error);
});
// END HARDCODED WIDGETS HACK
notebook.load_notebook(common_options.notebook_path);
});

@ -1,135 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'require',
'./toolbar',
'./celltoolbar',
'base/js/i18n'
], function($, requirejs, toolbar, celltoolbar, i18n) {
"use strict";
var MainToolBar = function (selector, options) {
/**
* Constructor
*
* Parameters:
* selector: string
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* notebook: Notebook instance
**/
toolbar.ToolBar.apply(this, [selector, options] );
this.events = options.events;
this.notebook = options.notebook;
this._make();
Object.seal(this);
};
MainToolBar.prototype = Object.create(toolbar.ToolBar.prototype);
MainToolBar.prototype._make = function () {
var grps = [
[
['jupyter-notebook:save-notebook'],
'save-notbook'
],
[
['jupyter-notebook:insert-cell-below'],
'insert_above_below'],
[
['jupyter-notebook:cut-cell',
'jupyter-notebook:copy-cell',
'jupyter-notebook:paste-cell-below'
] ,
'cut_copy_paste'],
[
['jupyter-notebook:move-cell-up',
'jupyter-notebook:move-cell-down'
],
'move_up_down'],
[ [new toolbar.Button('jupyter-notebook:run-cell-and-select-next',
{label: i18n.msg._('Run')}),
'jupyter-notebook:interrupt-kernel',
'jupyter-notebook:confirm-restart-kernel',
'jupyter-notebook:confirm-restart-kernel-and-run-all-cells'
],
'run_int'],
['<add_celltype_list>'],
[
['jupyter-notebook:show-command-palette'],
'cmd_palette']
];
this.construct(grps);
};
MainToolBar.prototype._pseudo_actions = {};
// add a cell type drop down to the maintoolbar.
// triggered when the pseudo action `<add_celltype_list>` is
// encountered when building a toolbar.
MainToolBar.prototype._pseudo_actions.add_celltype_list = function () {
var that = this;
var multiselect = $('<option/>').attr('value','multiselect').attr('disabled','').text('-');
var sel = $('<select/>')
.attr('id','cell_type')
.attr('aria-label', i18n.msg._('combobox, select cell type'))
.attr('role', 'combobox')
.addClass('form-control select-xs')
.append($('<option/>').attr('value','code').text(i18n.msg._('Code')))
.append($('<option/>').attr('value','markdown').text(i18n.msg._('Markdown')))
.append($('<option/>').attr('value','raw').text(i18n.msg._('Raw NBConvert')))
.append($('<option/>').attr('value','heading').text(i18n.msg._('Heading')))
.append(multiselect);
this.notebook.keyboard_manager.register_events(sel);
this.events.on('selected_cell_type_changed.Notebook', function (event, data) {
if (data.editable === false) {
sel.attr('disabled', true);
} else {
sel.removeAttr('disabled');
}
if (that.notebook.get_selected_cells_indices().length > 1) {
multiselect.show();
sel.val('multiselect');
} else {
multiselect.hide();
if (data.cell_type === 'heading') {
sel.val('Markdown');
} else {
sel.val(data.cell_type);
}
}
});
sel.change(function () {
var cell_type = $(this).val();
switch (cell_type) {
case 'code':
that.notebook.cells_to_code();
break;
case 'markdown':
that.notebook.cells_to_markdown();
break;
case 'raw':
that.notebook.cells_to_raw();
break;
case 'heading':
that.notebook._warn_heading();
that.notebook.to_heading();
sel.val('markdown');
break;
case 'multiselect':
break;
default:
console.log(i18n.msg._("unrecognized cell type:"), cell_type);
}
that.notebook.focus_cell();
});
return sel;
};
return {'MainToolBar': MainToolBar};
});

@ -1,8 +0,0 @@
define([
'base/js/mathjaxutils'
], function(mathjaxutils) {
"use strict"
return mathjaxutils;
});

@ -1,469 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/namespace',
'base/js/dialog',
'base/js/utils',
'base/js/i18n',
'notebook/js/quickhelp',
'./celltoolbar',
'./tour',
'moment',
], function($, IPython, dialog, utils, i18n, quickhelp, celltoolbar, tour, moment) {
"use strict";
var MenuBar = function (selector, options) {
/**
* Constructor
*
* A MenuBar Class to generate the menubar of Jupyter notebook
*
* Parameters:
* selector: string
* options: dictionary
* Dictionary of keyword arguments.
* notebook: Notebook instance
* render keyboard shortcuts from KeyboardManager
* contents: ContentManager instance
* events: $(Events) instance
* save_widget: SaveWidget instance
* quick_help: QuickHelp instance
* base_url : string
* notebook_path : string
* notebook_name : string
* config: ConfigSection instance
*/
options = options || {};
this.base_url = options.base_url || utils.get_body_data("baseUrl");
this.selector = selector;
this.notebook = options.notebook;
this.keyboard_manager = this.notebook.keyboard_manager;
this.actions = this.keyboard_manager.actions;
this.contents = options.contents;
this.events = options.events;
this.save_widget = options.save_widget;
this.quick_help = options.quick_help;
this.actions = options.actions;
this.config = options.config;
try {
this.tour = new tour.Tour(this.notebook, this.events);
} catch (e) {
this.tour = undefined;
console.log("Failed to instantiate Notebook Tour", e);
}
if (this.selector !== undefined) {
this.element = $(selector);
this.style();
this.add_bundler_items();
this.bind_events();
}
};
// TODO: This has definitively nothing to do with style ...
MenuBar.prototype.style = function () {
var that = this;
this.element.find("li").click(function (event, ui) {
// The selected cell loses focus when the menu is entered, so we
// re-select it upon selection.
var i = that.notebook.get_selected_index();
that.notebook.select(i, false);
}
);
};
MenuBar.prototype.add_bundler_items = function() {
var that = this;
this.config.loaded.then(function() {
var bundlers = that.config.data.bundlerextensions;
if(bundlers) {
// Stable sort the keys to ensure menu items don't hop around
var ids = Object.keys(bundlers).sort()
ids.forEach(function(bundler_id) {
var bundler = bundlers[bundler_id];
var group = that.element.find('#'+bundler.group+'_menu')
// Validate menu item metadata
if(!group.length) {
console.warn('unknown group', bundler.group, 'for bundler ID', bundler_id, '; skipping');
return;
} else if(!bundler.label) {
console.warn('no label for bundler ID', bundler_id, '; skipping');
return;
}
// Insert menu item into correct group, click handler
group.parent().removeClass('hidden');
var $li = $('<li>')
.appendTo(group);
$('<a>')
.attr('href', '#')
.text(bundler.label)
.appendTo($li)
.on('click', that._bundle.bind(that, bundler_id))
.appendTo($li);
});
}
});
};
MenuBar.prototype._new_window = function(url) {
var w = window.open('', IPython._target);
if (this.notebook.dirty && this.notebook.writable) {
this.notebook.save_notebook().then(function() {
w.location = url;
});
} else {
w.location = url;
}
};
MenuBar.prototype._bundle = function(bundler_id) {
// Read notebook path and base url here in case they change
var notebook_path = utils.encode_uri_components(this.notebook.notebook_path);
var url = utils.url_path_join(
this.base_url,
'bundle',
notebook_path
) + '?bundler=' + utils.encode_uri_components(bundler_id);
this._new_window(url);
};
MenuBar.prototype._nbconvert = function (format, download) {
download = download || false;
var notebook_path = utils.encode_uri_components(this.notebook.notebook_path);
var url = utils.url_path_join(
this.base_url,
'nbconvert',
format,
notebook_path
) + "?download=" + download.toString();
this._new_window(url);
};
MenuBar.prototype._size_header = function() {
/**
* Update header spacer size.
*/
console.warn('`_size_header` is deprecated and will be removed in future versions.'+
' Please trigger the `resize-header.Page` manually if you rely on it.');
this.events.trigger('resize-header.Page');
};
(function($){
$(document).ready(function(){
$('ul.dropdown-menu [data-toggle=dropdown]').on('click', function(event) {
event.preventDefault();
event.stopPropagation();
$(this).parent().siblings().removeClass('open');
$(this).parent().toggleClass('open');
});
});
})(jQuery);
MenuBar.prototype.bind_events = function () {
/**
* File
*/
var that = this;
this.element.find('#open_notebook').click(function () {
var parent = utils.url_path_split(that.notebook.notebook_path)[0];
window.open(
utils.url_path_join(
that.base_url, 'tree',
utils.encode_uri_components(parent)
), IPython._target);
});
this.element.find('#copy_notebook').click(function () {
that.notebook.copy_notebook();
return false;
});
this.element.find('#save_notebook_as').click(function() {
that.notebook.save_notebook_as();
return false;
});
this.element.find('#print_preview').click(function () {
that._nbconvert('html', false);
});
this.element.find('#download_menu li').click(function (ev) {
that._nbconvert(ev.target.parentElement.getAttribute('id').substring(9), true);
});
this.events.on('trust_changed.Notebook', function (event, trusted) {
if (trusted) {
that.element.find('#trust_notebook')
.addClass("disabled").off('click')
.find("a").text(i18n.msg._("Trusted Notebook"));
} else {
that.element.find('#trust_notebook')
.removeClass("disabled").on('click', function () {
that.notebook.trust_notebook();
})
.find("a").text(i18n.msg._("Trust Notebook"));
}
});
// View
this._add_celltoolbar_list();
// Edit
this.element.find('#edit_nb_metadata').click(function () {
that.notebook.edit_metadata({
notebook: that.notebook,
keyboard_manager: that.notebook.keyboard_manager});
});
var id_actions_dict = {
'#trust_notebook' : 'trust-notebook',
'#rename_notebook' : 'rename-notebook',
'#find_and_replace' : 'find-and-replace',
'#save_checkpoint': 'save-notebook',
'#shutdown_kernel': 'confirm-shutdown-kernel',
'#restart_kernel': 'confirm-restart-kernel',
'#restart_clear_output': 'confirm-restart-kernel-and-clear-output',
'#restart_run_all': 'confirm-restart-kernel-and-run-all-cells',
'#close_and_halt': 'close-and-halt',
'#int_kernel': 'interrupt-kernel',
'#cut_cell': 'cut-cell',
'#copy_cell': 'copy-cell',
'#paste_cell_above': 'paste-cell-above',
'#paste_cell_below': 'paste-cell-below',
'#paste_cell_replace': 'paste-cell-replace',
'#delete_cell': 'delete-cell',
'#undelete_cell': 'undo-cell-deletion',
'#split_cell': 'split-cell-at-cursor',
'#merge_cell_above': 'merge-cell-with-previous-cell',
'#merge_cell_below': 'merge-cell-with-next-cell',
'#move_cell_up': 'move-cell-up',
'#move_cell_down': 'move-cell-down',
'#toggle_header': 'toggle-header',
'#toggle_toolbar': 'toggle-toolbar',
'#toggle_line_numbers': 'toggle-all-line-numbers',
'#insert_cell_above': 'insert-cell-above',
'#insert_cell_below': 'insert-cell-below',
'#run_cell': 'run-cell',
'#run_cell_select_below': 'run-cell-and-select-next',
'#run_cell_insert_below': 'run-cell-and-insert-below',
'#run_all_cells': 'run-all-cells',
'#run_all_cells_above': 'run-all-cells-above',
'#run_all_cells_below': 'run-all-cells-below',
'#to_code': 'change-cell-to-code',
'#to_markdown': 'change-cell-to-markdown',
'#to_raw': 'change-cell-to-raw',
'#toggle_current_output': 'toggle-cell-output-collapsed',
'#toggle_current_output_scroll': 'toggle-cell-output-scrolled',
'#clear_current_output': 'clear-cell-output',
'#toggle_all_output': 'toggle-all-cells-output-collapsed',
'#toggle_all_output_scroll': 'toggle-all-cells-output-scrolled',
'#clear_all_output': 'clear-all-cells-output',
'#cut_cell_attachments': 'cut-cell-attachments',
'#copy_cell_attachments': 'copy-cell-attachments',
'#paste_cell_attachments': 'paste-cell-attachments',
'#insert_image': 'insert-image',
'#keyboard_shortcuts' : 'show-keyboard-shortcuts',
'#edit_keyboard_shortcuts' : 'edit-command-mode-keyboard-shortcuts',
};
for(var idx in id_actions_dict){
if (!id_actions_dict.hasOwnProperty(idx)){
continue;
}
var id_act = 'jupyter-notebook:'+id_actions_dict[idx];
if(!that.actions.exists(id_act)){
console.warn('actions', id_act, 'does not exist, still binding it in case it will be defined later...');
}
// Immediately-Invoked Function Expression cause JS.
(function(that, id_act, idx){
var el = that.element.find(idx);
el.click(function(event){
that.actions.call(id_act, event);
});
var keybinding = that.keyboard_manager.command_shortcuts.get_action_shortcut(id_act) || that.keyboard_manager.edit_shortcuts.get_action_shortcut(id_act);
if (keybinding) {
var shortcut = quickhelp.humanize_sequence(keybinding);
var link_element = el.children('a');
var text = link_element.text();
link_element.text('');
link_element.addClass('menu-shortcut-container');
link_element.append('<span class="action">' + text + '</span>');
link_element.append('<span class="kb">' + shortcut + '</span>');
}
})(that, id_act, idx);
}
// Kernel
this.element.find('#reconnect_kernel').click(function () {
that.notebook.kernel.reconnect();
});
// Help
if (this.tour) {
this.element.find('#notebook_tour').click(function () {
that.tour.start();
});
} else {
this.element.find('#notebook_tour').addClass("disabled");
}
this.update_restore_checkpoint(null);
this.events.on('checkpoints_listed.Notebook', function (event, data) {
that.update_restore_checkpoint(that.notebook.checkpoints);
});
this.events.on('checkpoint_created.Notebook', function (event, data) {
that.update_restore_checkpoint(that.notebook.checkpoints);
});
this.events.on('notebook_loaded.Notebook', function() {
var langinfo = that.notebook.metadata.language_info || {};
that.update_nbconvert_script(langinfo);
});
this.events.on('kernel_ready.Kernel', function(event, data) {
var langinfo = data.kernel.info_reply.language_info || {};
that.update_nbconvert_script(langinfo);
that.add_kernel_help_links(data.kernel.info_reply.help_links || []);
});
};
MenuBar.prototype._add_celltoolbar_list = function () {
var that = this;
var submenu = $("#menu-cell-toolbar-submenu");
function preset_added(event, data) {
var name = data.name;
submenu.append(
$("<li/>")
.attr('data-name', encodeURIComponent(name))
.append(
$("<a/>")
.attr('href', '#')
.text(name)
.click(function () {
if (name ==='None') {
celltoolbar.CellToolbar.global_hide();
delete that.notebook.metadata.celltoolbar;
} else {
celltoolbar.CellToolbar.global_show();
celltoolbar.CellToolbar.activate_preset(name, that.events);
that.notebook.metadata.celltoolbar = name;
}
that.notebook.focus_cell();
})
)
);
}
// Setup the existing presets
var presets = celltoolbar.CellToolbar.list_presets();
preset_added(null, {name: i18n.msg._("None")});
presets.map(function (name) {
preset_added(null, {name: name});
});
// Setup future preset registrations
this.events.on('preset_added.CellToolbar', preset_added);
// Handle unregistered presets
this.events.on('unregistered_preset.CellToolbar', function (event, data) {
submenu.find("li[data-name='" + encodeURIComponent(data.name) + "']").remove();
});
};
MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
var ul = this.element.find("#restore_checkpoint").find("ul");
ul.empty();
if (!checkpoints || checkpoints.length === 0) {
ul.append(
$("<li/>")
.addClass("disabled")
.append(
$("<a/>")
.text(i18n.msg._("No checkpoints"))
)
);
return;
}
var that = this;
checkpoints.map(function (checkpoint) {
var d = new Date(checkpoint.last_modified);
ul.append(
$("<li/>").append(
$("<a/>")
.attr("href", "#")
.text(moment(d).format("LLLL"))
.click(function () {
that.notebook.restore_checkpoint_dialog(checkpoint);
})
)
);
});
};
MenuBar.prototype.update_nbconvert_script = function(langinfo) {
/**
* Set the 'Download as foo' menu option for the relevant language.
*/
var el = this.element.find('#download_script');
// Set menu entry text to e.g. "Python (.py)"
var langname = (langinfo.name || 'Script');
langname = langname.charAt(0).toUpperCase()+langname.substr(1); // Capitalise
el.find('a').text(langname + ' ('+(langinfo.file_extension || 'txt')+')');
};
MenuBar.prototype.add_kernel_help_links = function(help_links) {
/** add links from kernel_info to the help menu */
var divider = $("#kernel-help-links");
if (divider.length === 0) {
// insert kernel help section above about link
var about = $("#notebook_about").parent();
divider = $("<li>")
.attr('id', "kernel-help-links")
.addClass('divider');
about.prev().before(divider);
}
// remove previous entries
while (!divider.next().hasClass('divider')) {
divider.next().remove();
}
if (help_links.length === 0) {
// no help links, remove the divider
divider.remove();
return;
}
var cursor = divider;
help_links.map(function (link) {
cursor.after($("<li>")
.append($("<a>")
.attr('target', '_blank')
.attr('title', i18n.msg._('Opens in a new window'))
.attr('href', requirejs.toUrl(link.url))
.append($("<i>")
.addClass("fa fa-external-link menu-icon pull-right")
)
.append($("<span>")
.text(link.text)
)
)
);
cursor = cursor.next();
});
};
return {'MenuBar': MenuBar};
});

File diff suppressed because it is too large Load Diff

@ -1,414 +0,0 @@
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'base/js/dialog',
'base/js/notificationarea',
'moment'
], function($, utils, i18n, dialog, notificationarea, moment) {
"use strict";
var NotificationArea = notificationarea.NotificationArea;
var NotebookNotificationArea = function(selector, options) {
NotificationArea.apply(this, [selector, options]);
this.save_widget = options.save_widget;
this.notebook = options.notebook;
this.keyboard_manager = options.keyboard_manager;
};
NotebookNotificationArea.prototype = Object.create(NotificationArea.prototype);
/**
* Initialize the default set of notification widgets.
*
* @method init_notification_widgets
*/
NotebookNotificationArea.prototype.init_notification_widgets = function () {
this.init_kernel_notification_widget();
this.init_notebook_notification_widget();
this.init_trusted_notebook_notification_widget();
};
/**
* Initialize the notification widget for kernel status messages.
*
* @method init_kernel_notification_widget
*/
NotebookNotificationArea.prototype.init_kernel_notification_widget = function () {
var that = this;
var knw = this.widget('kernel');
var $kernel_ind_icon = $("#kernel_indicator_icon");
var $modal_ind_icon = $("#modal_indicator");
var $readonly_ind_icon = $('#readonly-indicator');
var $body = $('body');
var busy_favicon_timer = -1;
var set_busy_favicon = function(on) {
if (on) {
// Only show the busy icon if execution lasts > 1s
// This is to avoid rapidly switching icons and making lots of
// HTTP requests.
clearTimeout(busy_favicon_timer);
busy_favicon_timer = setTimeout(function() {
utils.change_favicon('/static/base/images/favicon-busy-1.ico');
}, 1000);
} else {
clearTimeout(busy_favicon_timer);
utils.change_favicon('/static/base/images/favicon-notebook.ico');
}
};
// Listen for the notebook loaded event. Set readonly indicator.
this.events.on('notebook_loaded.Notebook', function() {
if (that.notebook.writable) {
$readonly_ind_icon.hide();
} else {
$readonly_ind_icon.show();
}
});
// Command/Edit mode
this.events.on('edit_mode.Notebook', function () {
that.save_widget.update_document_title();
$body.addClass('edit_mode');
$body.removeClass('command_mode');
$modal_ind_icon.attr('title',i18n.msg._('Edit Mode'));
});
this.events.on('command_mode.Notebook', function () {
that.save_widget.update_document_title();
$body.removeClass('edit_mode');
$body.addClass('command_mode');
$modal_ind_icon.attr('title',i18n.msg._('Command Mode'));
});
// Implicitly start off in Command mode, switching to Edit mode will trigger event
$modal_ind_icon.addClass('modal_indicator').attr('title',i18n.msg._('Command Mode'));
$body.addClass('command_mode');
// Kernel events
// this can be either kernel_created.Kernel or kernel_created.Session
this.events.on('kernel_created.Kernel kernel_created.Session', function () {
knw.info(i18n.msg._("Kernel Created"), 500);
});
this.events.on('kernel_reconnecting.Kernel', function () {
knw.warning(i18n.msg._("Connecting to kernel"));
});
this.events.on('kernel_connection_dead.Kernel', function (evt, info) {
knw.danger(i18n.msg._("Not Connected"), undefined, function () {
// schedule reconnect a short time in the future, don't reconnect immediately
setTimeout($.proxy(info.kernel.reconnect, info.kernel), 500);
}, {title: i18n.msg._('click to reconnect')});
});
this.events.on('kernel_connected.Kernel', function () {
knw.info("Connected", 500);
// trigger busy in the status to clear broken-link state immediately
// a kernel_ready event will come when the kernel becomes responsive.
$kernel_ind_icon
.attr('class', 'kernel_busy_icon')
.attr('title', i18n.msg._('Kernel Connected'));
});
this.events.on('kernel_restarting.Kernel', function () {
that.save_widget.update_document_title();
knw.set_message(i18n.msg._("Restarting kernel"), 2000);
});
this.events.on('kernel_autorestarting.Kernel', function (evt, info) {
// Only show the dialog on the first restart attempt. This
// number gets tracked by the `Kernel` object and passed
// along here, because we don't want to show the user 5
// dialogs saying the same thing (which is the number of
// times it tries restarting).
if (info.attempt === 1) {
dialog.kernel_modal({
notebook: that.notebook,
keyboard_manager: that.keyboard_manager,
title: i18n.msg._("Kernel Restarting"),
body: i18n.msg._("The kernel appears to have died. It will restart automatically."),
buttons: {
OK : {
class : "btn-primary"
}
}
});
}
that.save_widget.update_document_title();
knw.danger(i18n.msg._("Dead kernel"));
$kernel_ind_icon.attr('class','kernel_dead_icon').attr('title',i18n.msg._('Kernel Dead'));
});
this.events.on('kernel_interrupting.Kernel', function () {
knw.set_message(i18n.msg._("Interrupting kernel"), 2000);
});
this.events.on('kernel_disconnected.Kernel', function () {
$kernel_ind_icon
.attr('class', 'kernel_disconnected_icon')
.attr('title', i18n.msg._('No Connection to Kernel'));
});
this.events.on('kernel_connection_failed.Kernel', function (evt, info) {
// only show the dialog if this is the first failed
// connect attempt, because the kernel will continue
// trying to reconnect and we don't want to spam the user
// with messages
if (info.attempt === 1) {
var msg = i18n.msg._("A connection to the notebook server could not be established." +
" The notebook will continue trying to reconnect. Check your" +
" network connection or notebook server configuration.");
var the_dialog = dialog.kernel_modal({
title: i18n.msg._("Connection failed"),
body: msg,
keyboard_manager: that.keyboard_manager,
notebook: that.notebook,
buttons : {
"OK": {}
}
});
// hide the dialog on reconnect if it's still visible
var dismiss = function () {
the_dialog.modal('hide');
}
that.events.on("kernel_connected.Kernel", dismiss);
the_dialog.on("hidden.bs.modal", function () {
// clear handler on dismiss
that.events.off("kernel_connected.Kernel", dismiss);
});
}
});
this.events.on('kernel_killed.Kernel kernel_killed.Session', function () {
that.save_widget.update_document_title();
knw.warning(i18n.msg._("No kernel"));
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title',i18n.msg._('Kernel is not running'));
});
this.events.on('kernel_dead.Kernel', function () {
// This statement is used simply so that message extraction
// will pick up the strings. The actual setting of the text
// for the button is in dialog.js.
var button_labels = [ i18n.msg._("Don't Restart"), i18n.msg._("Try Restarting Now"), i18n.msg._("OK")];
var showMsg = function () {
var msg = i18n.msg._('The kernel has died, and the automatic restart has failed.' +
' It is possible the kernel cannot be restarted. ' +
'If you are not able to restart the kernel, you will still be able to save' +
' the notebook, but running code will no longer work until the notebook' +
' is reopened.');
dialog.kernel_modal({
title: i18n.msg._("Dead kernel"),
body : msg,
keyboard_manager: that.keyboard_manager,
notebook: that.notebook,
default_button: "Don't Restart",
buttons : {
"Don't Restart": {},
"Try Restarting Now": {
class: "btn-danger",
click: function () {
that.notebook.start_session();
}
}
}
});
return false;
};
that.save_widget.update_document_title();
knw.danger(i18n.msg._("Dead kernel"), undefined, showMsg);
$kernel_ind_icon.attr('class','kernel_dead_icon').attr('title',i18n.msg._('Kernel Dead'));
showMsg();
});
this.events.on("no_kernel.Kernel", function (evt, data) {
$("#kernel_indicator").find('.kernel_indicator_name').text(i18n.msg._("No Kernel"));
});
this.events.on('kernel_dead.Session', function (evt, info) {
var full = info.xhr.responseJSON.message;
var short = info.xhr.responseJSON.short_message || 'Kernel error';
var traceback = info.xhr.responseJSON.traceback;
var showMsg = function () {
var msg = $('<div/>').append($('<p/>').text(full));
var cm, cm_elem, cm_open;
if (traceback) {
cm_elem = $('<div/>')
.css('margin-top', '1em')
.css('padding', '1em')
.addClass('output_scroll');
msg.append(cm_elem);
cm = new CodeMirror(cm_elem.get(0), {
mode: "python",
readOnly : true
});
cm.setValue(traceback);
cm_open = $.proxy(cm.refresh, cm);
}
dialog.kernel_modal({
title: i18n.msg._("Failed to start the kernel"),
body : msg,
keyboard_manager: that.keyboard_manager,
notebook: that.notebook,
open: cm_open,
buttons : {
"OK": { class: 'btn-primary' }
}
});
return false;
};
that.save_widget.update_document_title();
$kernel_ind_icon.attr('class','kernel_dead_icon').attr('title',i18n.msg._('Kernel Dead'));
knw.danger(short, undefined, showMsg);
});
this.events.on('kernel_starting.Kernel kernel_created.Session', function () {
// window.document.title='(Starting) '+window.document.title;
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title',i18n.msg._('Kernel Busy'));
knw.set_message(i18n.msg._("Kernel starting, please wait..."));
set_busy_favicon(true);
});
this.events.on('kernel_ready.Kernel', function () {
// that.save_widget.update_document_title();
$kernel_ind_icon.attr('class','kernel_idle_icon').attr('title',i18n.msg._('Kernel Idle'));
knw.info(i18n.msg._("Kernel ready"), 500);
set_busy_favicon(false);
});
this.events.on('kernel_idle.Kernel', function () {
// that.save_widget.update_document_title();
$kernel_ind_icon.attr('class','kernel_idle_icon').attr('title',i18n.msg._('Kernel Idle'));
set_busy_favicon(false);
});
this.events.on('kernel_busy.Kernel', function () {
// window.document.title='(Busy) '+window.document.title;
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title',i18n.msg._('Kernel Busy'));
set_busy_favicon(true);
});
this.events.on('spec_match_found.Kernel', function (evt, data) {
that.widget('kernelspec').info(i18n.msg._("Using kernel: ") + data.found.spec.display_name, 3000, undefined, {
title: i18n.msg.sprintf(i18n.msg._("Only candidate for language: %1$s was %2$s."),
data.selected.language, data.found.spec.display_name)
});
});
// Start the kernel indicator in the busy state, and send a kernel_info request.
// When the kernel_info reply arrives, the kernel is idle.
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title',i18n.msg._('Kernel Busy'));
};
/**
* Initialize the notification widget for notebook status messages.
*
* @method init_notebook_notification_widget
*/
NotebookNotificationArea.prototype.init_notebook_notification_widget = function () {
var nnw = this.widget('notebook');
// Notebook events
this.events.on('notebook_loading.Notebook', function () {
nnw.set_message(i18n.msg._("Loading notebook"),500);
});
this.events.on('notebook_loaded.Notebook', function () {
nnw.set_message(i18n.msg._("Notebook loaded"),500);
});
this.events.on('notebook_saving.Notebook', function () {
nnw.set_message(i18n.msg._("Saving notebook"),500);
});
this.events.on('notebook_saved.Notebook', function () {
nnw.set_message(i18n.msg._("Notebook saved"),2000);
});
this.events.on('notebook_save_failed.Notebook', function (evt, error) {
nnw.warning(error.message || i18n.msg._("Notebook save failed"));
});
this.events.on('notebook_copy_failed.Notebook', function (evt, error) {
nnw.warning(error.message || i18n.msg._("Notebook copy failed"));
});
// Checkpoint events
this.events.on('checkpoint_created.Notebook', function (evt, data) {
var msg = i18n.msg._("Checkpoint created");
if (data.last_modified) {
var d = new Date(data.last_modified);
msg = msg + ": " + moment(d).format("HH:mm:ss");
}
nnw.set_message(msg, 2000);
});
this.events.on('checkpoint_failed.Notebook', function () {
nnw.warning(i18n.msg._("Checkpoint failed"));
});
this.events.on('checkpoint_deleted.Notebook', function () {
nnw.set_message(i18n.msg._("Checkpoint deleted"), 500);
});
this.events.on('checkpoint_delete_failed.Notebook', function () {
nnw.warning(i18n.msg._("Checkpoint delete failed"));
});
this.events.on('checkpoint_restoring.Notebook', function () {
nnw.set_message(i18n.msg._("Restoring to checkpoint..."), 500);
});
this.events.on('checkpoint_restore_failed.Notebook', function () {
nnw.warning(i18n.msg._("Checkpoint restore failed"));
});
// Autosave events
this.events.on('autosave_disabled.Notebook', function () {
nnw.set_message(i18n.msg._("Autosave disabled"), 2000);
});
this.events.on('autosave_enabled.Notebook', function (evt, interval) {
nnw.set_message(i18n.msg.sprintf(i18n.msg._("Saving every %d sec."), interval / 1000) , 1000);
});
};
/**
* Initialize the notification widget for trusted notebook messages.
*
* @method init_trusted_notebook_notification_widget
*/
NotebookNotificationArea.prototype.init_trusted_notebook_notification_widget = function () {
var that = this;
var tnw = this.widget('trusted');
// Notebook trust events
this.events.on('trust_changed.Notebook', function (event, trusted) {
if (trusted) {
tnw.set_message(i18n.msg._("Trusted"), undefined, function() {
return false;
}, {'title':'Javascript enabled for notebook display'});
// don't allow 'Trusted' button to be clicked
$(tnw.selector).attr('disabled', true);
$(tnw.selector).css('cursor', 'help');
} else {
tnw.set_message(i18n.msg._("Not Trusted"), undefined, function() {
that.notebook.trust_notebook("#notification_trusted");
return false;
}, {'title':'Javascript disabled for notebook display'});
$(tnw.selector).attr('role', 'button');
}
});
};
return {'NotebookNotificationArea': NotebookNotificationArea};
});

File diff suppressed because it is too large Load Diff

@ -1,187 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'jquery-ui'
], function($, utils, i18n) {
"use strict";
var Pager = function (pager_selector, options) {
/**
* Constructor
*
* Parameters:
* pager_selector: string
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
*/
this.events = options.events;
this.pager_element = $(pager_selector);
this.pager_button_area = $('#pager-button-area');
this._default_end_space = 100;
this.pager_element.resizable({handles: 'n', resize: $.proxy(this._resize, this)});
this.expanded = false;
this.create_button_area();
this.bind_events();
};
Pager.prototype.create_button_area = function(){
var that = this;
this.pager_button_area.append(
$('<a>').attr('role', "button")
.attr('title',i18n.msg._("Open the pager in an external window"))
.addClass('ui-button')
.click(function(){that.detach();})
.append(
$('<span>').addClass("ui-icon ui-icon-extlink")
)
);
this.pager_button_area.append(
$('<a>').attr('role', "button")
.attr('title',i18n.msg._("Close the pager"))
.addClass('ui-button')
.click(function(){that.collapse();})
.append(
$('<span>').addClass("ui-icon ui-icon-close")
)
);
};
Pager.prototype.bind_events = function () {
var that = this;
this.pager_element.bind('collapse_pager', function (event, extrap) {
// Animate hiding of the pager.
var time = (extrap && extrap.duration) ? extrap.duration : 'fast';
that.pager_element.animate({
height: 'toggle'
}, {
duration: time,
done: function() {
$('.end_space').css('height', that._default_end_space);
}
});
});
this.pager_element.bind('expand_pager', function (event, extrap) {
// Clear the pager's height attr if it's set. This allows the
// pager to size itself according to its contents.
that.pager_element.height('initial');
// Animate the showing of the pager
var time = (extrap && extrap.duration) ? extrap.duration : 'fast';
that.pager_element.show(time, function() {
// Explicitly set pager height once the pager has shown itself.
// This allows the pager-contents div to use percentage sizing.
that.pager_element.height(that.pager_element.height());
that._resize();
// HACK: Less horrible, but still horrible hack to force the
// pager to show it's scrollbars on FireFox. ipython/ipython/#8853
that.pager_element.css('position', 'relative');
window.requestAnimationFrame(function() { /* Wait one frame */
that.pager_element.css('position', '');
});
});
});
this.events.on('open_with_text.Pager', function (event, payload) {
// FIXME: support other mime types with generic mimebundle display
// mechanism
if (payload.data['text/html'] && payload.data['text/html'] !== "") {
that.clear();
that.expand();
that.append(payload.data['text/html']);
} else if (payload.data['text/plain'] && payload.data['text/plain'] !== "") {
that.clear();
that.expand();
that.append_text(payload.data['text/plain']);
}
});
};
Pager.prototype.collapse = function (extrap) {
if (this.expanded === true) {
this.expanded = false;
this.pager_element.trigger('collapse_pager', extrap);
}
};
Pager.prototype.expand = function (extrap) {
if (this.expanded !== true) {
this.expanded = true;
this.pager_element.trigger('expand_pager', extrap);
}
};
Pager.prototype.toggle = function () {
if (this.expanded === true) {
this.collapse();
} else {
this.expand();
}
};
Pager.prototype.clear = function (text) {
this.pager_element.find(".container").empty();
};
Pager.prototype.detach = function(){
var w = window.open("","_blank");
$(w.document.head)
.append(
$('<link>')
.attr('rel',"stylesheet")
.attr('href', utils.url_path_join(utils.get_body_data('baseUrl'), "static/style/style.min.css"))
.attr('type',"text/css")
)
.append(
$('<title>').text(i18n.msg._("Jupyter Pager"))
);
var pager_body = $(w.document.body);
pager_body.css('overflow','scroll');
pager_body.append(this.pager_element.clone().children());
w.document.close();
this.collapse();
};
Pager.prototype.append_text = function (text) {
/**
* The only user content injected with this HTML call is escaped by
* the fixConsole() method.
*/
this.pager_element.find(".container").append($('<pre/>').html(utils.fixConsole(utils.fixOverwrittenChars(text))));
};
Pager.prototype.append = function (htm) {
/**
* The only user content injected with this HTML call is escaped by
* the fixConsole() method.
*/
this.pager_element.find(".container").append(htm);
};
Pager.prototype._resize = function() {
/**
* Update document based on pager size.
*/
// Make sure the padding at the end of the notebook is large
// enough that the user can scroll to the bottom of the
// notebook.
$('.end_space').css('height', Math.max(this.pager_element.height(), this._default_end_space));
};
return {'Pager': Pager};
});

@ -1,22 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// Define promises for notebook events.
define(['base/js/events', 'base/js/promises'], function(events, promises) {
"use strict";
// Promise to be resolved when the notebook is *initially* loaded.
// The event may fire again if the notebook is reloaded later, but this
// promise only tracks the initial load.
promises.notebook_loaded = new Promise(function(resolve, reject) {
events.one('notebook_loaded.Notebook', function() {
resolve();
});
events.one('notebook_load_failed.Notebook', function() {
reject();
});
});
return promises;
});

@ -1,349 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'base/js/dialog',
'underscore'
], function($, utils, i18n, dialog, _) {
"use strict";
var platform = utils.platform;
var QuickHelp = function (options) {
/**
* Constructor
*
* Parameters:
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* keyboard_manager: KeyboardManager instance
* notebook: Notebook instance
*/
this.keyboard_manager = options.keyboard_manager;
this.notebook = options.notebook;
this.keyboard_manager.quick_help = this;
this.events = options.events;
};
var cmd_ctrl = 'Ctrl-';
var platform_specific;
if (platform === 'MacOS') {
// Mac OS X specific
cmd_ctrl = 'Cmd-';
platform_specific = [
{ shortcut: "Cmd-Up", help:i18n.msg._("go to cell start") },
{ shortcut: "Cmd-Down", help:i18n.msg._("go to cell end") },
{ shortcut: "Alt-Left", help:i18n.msg._("go one word left") },
{ shortcut: "Alt-Right", help:i18n.msg._("go one word right") },
{ shortcut: "Alt-Backspace", help:i18n.msg._("delete word before") },
{ shortcut: "Alt-Delete", help:i18n.msg._("delete word after") },
{ shortcut: "Cmd-Shift-z", help:i18n.msg._("redo") },
{ shortcut: "Cmd-Shift-u", help:i18n.msg._("redo selection") },
{ shortcut: "Ctrl-k", help:i18n.msg._("emacs-style line kill") },
{ shortcut: "Cmd-Backspace", help:i18n.msg._("delete line left of cursor") },
{ shortcut: "Cmd-Delete", help:i18n.msg._("delete line right of cursor") }
];
} else {
// PC specific
platform_specific = [
{ shortcut: "Ctrl-Home", help:i18n.msg._("go to cell start") },
{ shortcut: "Ctrl-Up", help:i18n.msg._("go to cell start") },
{ shortcut: "Ctrl-End", help:i18n.msg._("go to cell end") },
{ shortcut: "Ctrl-Down", help:i18n.msg._("go to cell end") },
{ shortcut: "Ctrl-Left", help:i18n.msg._("go one word left") },
{ shortcut: "Ctrl-Right", help:i18n.msg._("go one word right") },
{ shortcut: "Ctrl-Backspace", help:i18n.msg._("delete word before")},
{ shortcut: "Ctrl-Delete", help:i18n.msg._("delete word after")},
{ shortcut: "Ctrl-y", help:i18n.msg._("redo")},
{ shortcut: "Alt-u", help:i18n.msg._("redo selection") }
];
}
var cm_shortcuts = [
{ shortcut:"Tab", help:i18n.msg._("code completion or indent") },
{ shortcut:"Shift-Tab", help:i18n.msg._("tooltip") },
{ shortcut: cmd_ctrl + "]", help:i18n.msg._("indent") },
{ shortcut: cmd_ctrl + "[", help:i18n.msg._("dedent") },
{ shortcut: cmd_ctrl + "a", help:i18n.msg._("select all") },
{ shortcut: cmd_ctrl + "z", help:i18n.msg._("undo") },
{ shortcut: cmd_ctrl + "/", help:i18n.msg._("comment") },
{ shortcut: cmd_ctrl + "d", help:i18n.msg._("delete whole line") },
{ shortcut: cmd_ctrl + "u", help:i18n.msg._("undo selection") },
{ shortcut: "Insert", help:i18n.msg._("toggle overwrite flag") }
].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 https://apple.stackexchange.com/questions/55727/
// for htmlentities and/or unicode value
'cmd':'⌘',
'shift':'⇧',
'alt':'⌥',
'up':'↑',
'down':'↓',
'left':'←',
'right':'→',
'eject':'⏏',
'tab':'⇥',
'backtab':'⇤',
'capslock':'⇪',
'esc':'esc',
'ctrl':'⌃',
'enter':'↩',
'pageup':'⇞',
'pagedown':'⇟',
'home':'↖',
'end':'↘',
'altenter':'⌤',
'space':'␣',
'delete':'⌦',
'backspace':'⌫',
'apple':'',
};
var default_humanize_map = {
'shift':i18n.msg._('Shift'),
'alt':i18n.msg._('Alt'),
'up':i18n.msg._('Up'),
'down':i18n.msg._('Down'),
'left':i18n.msg._('Left'),
'right':i18n.msg._('Right'),
'tab':i18n.msg._('Tab'),
'capslock':i18n.msg._('Caps Lock'),
'esc':i18n.msg._('Esc'),
'ctrl':i18n.msg._('Ctrl'),
'enter':i18n.msg._('Enter'),
'pageup':i18n.msg._('Page Up'),
'pagedown':i18n.msg._('Page Down'),
'home':i18n.msg._('Home'),
'end':i18n.msg._('End'),
'space':i18n.msg._('Space'),
'backspace':i18n.msg._('Backspace'),
'-':i18n.msg._('Minus')
};
var humanize_map;
if (platform === 'MacOS'){
humanize_map = mac_humanize_map;
} else {
humanize_map = default_humanize_map;
}
var special_case = { pageup: i18n.msg._("PageUp"), pagedown: i18n.msg._("Page Down") };
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 separated 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_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 = '';
}
return _.map(shortcut.split('-'), humanize_key ).join(joinchar);
}
function humanize_shortcut(shortcut){
return '<kbd>'+_humanize_shortcut(shortcut)+'</kbd>';
}
QuickHelp.prototype.show_keyboard_shortcuts = function () {
/**
* toggles display of keyboard shortcut dialog
*/
var that = this;
if ( this.force_rebuild ) {
this.shortcut_dialog.remove();
delete(this.shortcut_dialog);
this.force_rebuild = false;
}
if ( this.shortcut_dialog ){
// if dialog is already shown, close it
$(this.shortcut_dialog).modal("toggle");
return;
}
var element = $('<div/>');
// The documentation
var doc = $('<div/>').addClass('alert alert-info');
doc.append(i18n.msg._('The Jupyter Notebook has two different keyboard input modes.'))
.append(' ')
.append(i18n.msg._('<b>Edit mode</b> allows you to type code or text into a cell and is indicated by a green cell border.'))
.append(' ')
.append(i18n.msg._('<b>Command mode</b> binds the keyboard to notebook level commands and is indicated by a grey cell border with a blue left margin.')
);
element.append(doc);
if (platform === 'MacOS') {
doc = $('<div/>').addClass('alert alert-info');
var key_div = this.build_key_names();
doc.append(key_div);
element.append(doc);
}
// Command mode
var cmd_div = this.build_command_help();
element.append(cmd_div);
// Edit mode
var edit_div = this.build_edit_help(cm_shortcuts);
element.append(edit_div);
// This statement is used simply so that message extraction
// will pick up the strings. The actual setting of the text
// for the button is in dialog.js.
var button_labels = [ i18n.msg._("Close") ];
this.shortcut_dialog = dialog.modal({
title : i18n.msg._("Keyboard shortcuts"),
body : element,
destroy : false,
buttons : {
Close : {}
},
notebook: this.notebook,
keyboard_manager: this.keyboard_manager,
});
this.shortcut_dialog.addClass("modal_stretch");
this.events.on('rebuild.QuickHelp', function() { that.force_rebuild = true;});
};
QuickHelp.prototype.build_key_names = function () {
var key_names_mac = [{ shortcut:"⌘", help:i18n.msg._("Command") },
{ shortcut:"⌃", help:i18n.msg._("Control") },
{ shortcut:"⌥", help:i18n.msg._("Option") },
{ shortcut:"⇧", help:i18n.msg._("Shift") },
{ shortcut:"↩", help:i18n.msg._("Return") },
{ shortcut:"␣", help:i18n.msg._("Space") },
{ shortcut:"⇥", help:i18n.msg._("Tab") }];
var i, half, n;
var div = $('<div/>').append('Mac OS X modifier keys:');
var sub_div = $('<div/>').addClass('container-fluid');
var col1 = $('<div/>').addClass('col-md-6');
var col2 = $('<div/>').addClass('col-md-6');
n = key_names_mac.length;
half = ~~(n/2);
for (i=0; i<half; i++) { col1.append(
build_one(key_names_mac[i])
); }
for (i=half; i<n; i++) { col2.append(
build_one(key_names_mac[i])
); }
sub_div.append(col1).append(col2);
div.append(sub_div);
return div;
};
QuickHelp.prototype.build_command_help = function () {
var that = this;
var command_shortcuts = this.keyboard_manager.command_shortcuts.help();
var cmdkey = '<kbd>'+i18n.msg._('Esc')+'</kbd>';
var div = build_div('<h4>'+i18n.msg.sprintf(i18n.msg._('Command Mode (press %s to enable)'),cmdkey)+'</h4>', command_shortcuts);
var edit_button = $('<button/>')
.text(i18n.msg._("Edit Shortcuts"))
.addClass('btn btn-xs btn-default pull-right')
.attr('href', '#')
.attr('title', i18n.msg._('edit command-mode keyboard shortcuts'))
.click(function () {
// close this dialog
$(that.shortcut_dialog).modal("toggle");
// and open the next one
$(that.shortcut_dialog).on('hidden.bs.modal', function (e) {
that.keyboard_manager.actions.call(
'jupyter-notebook:edit-command-mode-keyboard-shortcuts'
);
});
});
div.find('h4').append(edit_button);
return div;
};
QuickHelp.prototype.build_edit_help = function (cm_shortcuts) {
var edit_shortcuts = this.keyboard_manager.edit_shortcuts.help();
var enterkey = '<kbd>'+i18n.msg._('Enter')+'</kbd>';
edit_shortcuts = $.merge($.merge([], cm_shortcuts), edit_shortcuts);
return build_div('<h4>'+i18n.msg.sprintf(i18n.msg._('Edit Mode (press %s to enable)'),enterkey)+'</h4>', edit_shortcuts);
};
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;
};
return {'QuickHelp': QuickHelp,
humanize_shortcut: humanize_shortcut,
humanize_sequence: humanize_sequence,
_humanize_sequence: _humanize_sequence,
};
});

@ -1,228 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'base/js/dialog',
'base/js/keyboard',
'moment',
'bidi/bidi',
], function($, utils, i18n, dialog, keyboard, moment, bidi) {
"use strict";
var SaveWidget = function (selector, options) {
/**
* TODO: Remove circular ref.
*/
this.notebook = undefined;
this.selector = selector;
this.events = options.events;
this._checkpoint_date = undefined;
this.keyboard_manager = options.keyboard_manager;
if (this.selector !== undefined) {
this.element = $(selector);
this.bind_events();
}
};
SaveWidget.prototype.bind_events = function () {
var that = this;
this.element.find('span.filename').click(function () {
that.rename_notebook({notebook: that.notebook});
});
this.events.on('notebook_loaded.Notebook', function () {
that.update_notebook_name();
that.update_document_title();
});
this.events.on('notebook_saved.Notebook', function () {
that.update_notebook_name();
that.update_document_title();
});
this.events.on('notebook_renamed.Notebook', function () {
that.update_notebook_name();
that.update_document_title();
that.update_address_bar();
});
this.events.on('notebook_save_failed.Notebook', function () {
that.set_save_status(i18n.msg._('Autosave Failed!'));
});
this.events.on('notebook_read_only.Notebook', function () {
that.set_save_status('(read only)');
// disable future set_save_status
that.set_save_status = function () {};
});
this.events.on('checkpoints_listed.Notebook', function (event, data) {
that._set_last_checkpoint(data[0]);
});
this.events.on('checkpoint_created.Notebook', function (event, data) {
that._set_last_checkpoint(data);
});
this.events.on('set_dirty.Notebook', function (event, data) {
that.set_autosaved(data.value);
});
};
// This statement is used simply so that message extraction
// will pick up the strings. The actual setting of the text
// for the button is in dialog.js.
var button_labels = [ i18n.msg._("Cancel"), i18n.msg._("Rename"), i18n.msg._("OK")];
SaveWidget.prototype.rename_notebook = function (options) {
options = options || {};
var that = this;
var dialog_body = $('<div/>').append(
$("<p/>").addClass("rename-message")
.text(i18n.msg._('Enter a new notebook name:'))
).append(
$("<br/>")
).append(
$('<input/>').attr('type','text').attr('size','25').addClass('form-control')
.val(options.notebook.get_notebook_name())
);
var d = dialog.modal({
title: i18n.msg._("Rename Notebook"),
body: dialog_body,
notebook: options.notebook,
keyboard_manager: this.keyboard_manager,
default_button: "Cancel",
buttons : {
"Cancel": {},
"Rename": {
class: "btn-primary",
click: function () {
var new_name = d.find('input').val();
if (!options.notebook.test_notebook_name(new_name)) {
d.find('.rename-message').text(i18n.msg._(
"Invalid notebook name. Notebook names must have 1 or more characters and can contain any characters except :/\\. Please enter a new notebook name:")
);
return false;
} else {
d.find('.rename-message').text(i18n.msg._("Renaming..."));
d.find('input[type="text"]').prop('disabled', true);
that.notebook.rename(new_name).then(
function () {
d.modal('hide');
}, function (error) {
d.find('.rename-message').text(error.message || i18n.msg._('Unknown error'));
d.find('input[type="text"]').prop('disabled', false).focus().select();
}
);
return false;
}
}
}
},
open : function () {
/**
* Upon ENTER, click the OK button.
*/
d.find('input[type="text"]').keydown(function (event) {
if (event.which === keyboard.keycodes.enter) {
d.find('.btn-primary').first().click();
return false;
}
});
d.find('input[type="text"]').focus().select();
}
});
};
SaveWidget.prototype.update_notebook_name = function () {
var nbname = this.notebook.get_notebook_name();
nbname = bidi.applyBidi(nbname);
this.element.find('span.filename').text(nbname);
};
SaveWidget.prototype.update_document_title = function () {
var nbname = this.notebook.get_notebook_name();
document.title = nbname + ' - Jupyter Notebook';
};
SaveWidget.prototype.update_address_bar = function(){
var base_url = this.notebook.base_url;
var path = this.notebook.notebook_path;
var state = {path : path};
window.history.replaceState(state, "", utils.url_path_join(
base_url,
"notebooks",
utils.encode_uri_components(path))
);
};
SaveWidget.prototype.set_save_status = function (msg) {
this.element.find('span.autosave_status').text(msg);
};
SaveWidget.prototype._set_last_checkpoint = function (checkpoint) {
if (checkpoint) {
this._checkpoint_date = new Date(checkpoint.last_modified);
} else {
this._checkpoint_date = null;
}
this._render_checkpoint();
};
SaveWidget.prototype._render_checkpoint = function () {
/** actually set the text in the element, from our _checkpoint value
called directly, and periodically in timeouts.
*/
this._schedule_render_checkpoint();
var el = this.element.find('span.checkpoint_status');
if (!this._checkpoint_date) {
el.text('').attr('title', i18n.msg._('no checkpoint'));
return;
}
var chkd = moment(this._checkpoint_date);
var long_date = chkd.format('llll');
var human_date;
var tdelta = Math.ceil(new Date() - this._checkpoint_date);
if (tdelta < utils.time.milliseconds.d){
// less than 24 hours old, use relative date
human_date = chkd.fromNow();
} else {
// otherwise show calendar
// <Today | yesterday|...> at hh,mm,ss
human_date = chkd.calendar();
}
el.text(i18n.msg.sprintf(i18n.msg._('Last Checkpoint: %s'),human_date)).attr('title', long_date);
};
SaveWidget.prototype._schedule_render_checkpoint = function () {
/** schedule the next update to relative date
periodically updated, so short values like 'a few seconds ago' don't get stale.
*/
if (!this._checkpoint_date) {
return;
}
if ((this._checkpoint_timeout)) {
clearTimeout(this._checkpoint_timeout);
}
var dt = Math.ceil(new Date() - this._checkpoint_date);
this._checkpoint_timeout = setTimeout(
$.proxy(this._render_checkpoint, this),
utils.time.timeout_from_dt(dt)
);
};
SaveWidget.prototype.set_autosaved = function (dirty) {
if (dirty) {
this.set_save_status(i18n.msg._("(unsaved changes)"));
} else {
this.set_save_status(i18n.msg._("(autosaved)"));
}
};
return {'SaveWidget': SaveWidget};
});

@ -1,232 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['jquery'], function($) {
"use strict";
var ScrollManager = function(notebook, options) {
/**
* Public constructor.
*/
this.notebook = notebook;
this.element = $('#site');
options = options || {};
this.animation_speed = options.animation_speed || 250; //ms
};
ScrollManager.prototype.onScroll = function (func, rate) {
/**
* Register a function to be called when the page is scrolled, throttled
* at a particular rate limit.
*/
rate = rate || 100; // default rate limit
this.element.scroll(function () {
clearTimeout(func._timeout);
func._timeout = setTimeout(func, rate);
});
};
ScrollManager.prototype.scroll = function (delta) {
/**
* Scroll the document.
*
* Parameters
* ----------
* delta: integer
* direction to scroll the document. Positive is downwards.
* Unit is one page length.
*/
this.scroll_some(delta);
return false;
};
ScrollManager.prototype.scroll_to = function(selector) {
/**
* Scroll to an element in the notebook.
*/
this.element.animate({'scrollTop': $(selector).offset().top + this.element.scrollTop() - this.element.offset().top}, this.animation_speed);
};
ScrollManager.prototype.scroll_some = function(pages) {
/**
* Scroll up or down a given number of pages.
*
* Parameters
* ----------
* pages: integer
* number of pages to scroll the document, may be positive or negative.
*/
this.element.animate({'scrollTop': this.element.scrollTop() + pages * this.element.height()}, this.animation_speed);
};
ScrollManager.prototype.get_first_visible_cell = function() {
/**
* Gets the index of the first visible cell in the document.
*
* First, attempt to be smart by guessing the index of the cell we are
* scrolled to. Then, walk from there up or down until the right cell
* is found. To guess the index, get the top of the last cell, and
* divide that by the number of cells to get an average cell height.
* Then divide the scroll height by the average cell height.
*/
var cell_count = this.notebook.ncells();
var first_cell_top = this.notebook.get_cell(0).element.offset().top;
var last_cell_top = this.notebook.get_cell(cell_count-1).element.offset().top;
var avg_cell_height = (last_cell_top - first_cell_top) / cell_count;
var i = Math.ceil(this.element.scrollTop() / avg_cell_height);
i = Math.min(Math.max(i , 0), cell_count - 1);
while (this.notebook.get_cell(i).element.offset().top - first_cell_top < this.element.scrollTop() && i < cell_count - 1) {
i += 1;
}
while (this.notebook.get_cell(i).element.offset().top - first_cell_top > this.element.scrollTop() - 50 && i >= 0) {
i -= 1;
}
return Math.min(i + 1, cell_count - 1);
};
ScrollManager.prototype.is_cell_visible = function (cell) {
var cell_rect = cell.element[0].getBoundingClientRect();
var scroll_rect = this.element[0].getBoundingClientRect();
return ((cell_rect.top <= scroll_rect.bottom) && (cell_rect.bottom >= scroll_rect.top));
};
var TargetScrollManager = function(notebook, options) {
/**
* Public constructor.
*/
ScrollManager.apply(this, [notebook, options]);
};
TargetScrollManager.prototype = Object.create(ScrollManager.prototype);
TargetScrollManager.prototype.is_target = function (index) {
/**
* Check if a cell should be a scroll stop.
*
* Returns `true` if the cell is a cell that the scroll manager
* should scroll to. Otherwise, false is returned.
*
* Parameters
* ----------
* index: integer
* index of the cell to test.
*/
return false;
};
TargetScrollManager.prototype.scroll = function (delta) {
/**
* Scroll the document.
*
* Parameters
* ----------
* delta: integer
* direction to scroll the document. Positive is downwards.
* Units are targets.
*
* Try to scroll to the next slide.
*/
var cell_count = this.notebook.ncells();
var selected_index = this.get_first_visible_cell() + delta;
while (0 <= selected_index && selected_index < cell_count && !this.is_target(selected_index)) {
selected_index += delta;
}
if (selected_index < 0 || cell_count <= selected_index) {
return ScrollManager.prototype.scroll.apply(this, [delta]);
} else {
this.scroll_to(this.notebook.get_cell(selected_index).element);
// Cancel browser keyboard scroll.
return false;
}
};
var SlideScrollManager = function(notebook, options) {
/**
* Public constructor.
*/
TargetScrollManager.apply(this, [notebook, options]);
};
SlideScrollManager.prototype = Object.create(TargetScrollManager.prototype);
SlideScrollManager.prototype.is_target = function (index) {
var cell = this.notebook.get_cell(index);
return cell.metadata && cell.metadata.slideshow &&
cell.metadata.slideshow.slide_type &&
(cell.metadata.slideshow.slide_type === "slide" ||
cell.metadata.slideshow.slide_type === "subslide");
};
var HeadingScrollManager = function(notebook, options) {
/**
* Public constructor.
*/
ScrollManager.apply(this, [notebook, options]);
options = options || {};
this._level = options.heading_level || 1;
};
HeadingScrollManager.prototype = Object.create(ScrollManager.prototype);
HeadingScrollManager.prototype.scroll = function (delta) {
/**
* Scroll the document.
*
* Parameters
* ----------
* delta: integer
* direction to scroll the document. Positive is downwards.
* Units are headers.
*
* Get all of the header elements that match the heading level or are of
* greater magnitude (a smaller header number).
*/
var headers = $();
var i;
for (i = 1; i <= this._level; i++) {
headers = headers.add('#notebook-container h' + i);
}
// Find the header the user is on or below.
var first_cell_top = this.notebook.get_cell(0).element.offset().top;
var current_scroll = this.element.scrollTop();
var header_scroll = 0;
i = -1;
while (current_scroll >= header_scroll && i < headers.length) {
if (++i < headers.length) {
header_scroll = $(headers[i]).offset().top - first_cell_top;
}
}
i--;
// Check if the user is below the header.
if (i < 0 || current_scroll > $(headers[i]).offset().top - first_cell_top + 30) {
// Below the header, count the header as a target.
if (delta < 0) {
delta += 1;
}
}
i += delta;
// Scroll!
if (0 <= i && i < headers.length) {
this.scroll_to(headers[i]);
return false;
} else {
// Default to the base's scroll behavior when target header doesn't
// exist.
return ScrollManager.prototype.scroll.apply(this, [delta]);
}
};
// Return namespace for require.js loads
return {
'ScrollManager': ScrollManager,
'SlideScrollManager': SlideScrollManager,
'HeadingScrollManager': HeadingScrollManager,
'TargetScrollManager': TargetScrollManager
};
});

@ -1,401 +0,0 @@
define([
'jquery',
'base/js/dialog',
'base/js/i18n'
], function($, dialog, i18n){
"use strict";
/**
* escape a Regular expression to act as a pure search string.
* though it will still have the case sensitivity options and all
* the benefits
**/
function escapeRegExp(string){
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
/**
* Compute the model of the preview for the search and replace.
* It might not be perfectly accurate if matches overlap...
* Parameter:
* sre: the string that will become the Search Regular Expression
* arr: a list of string on which the match will be applied.
* isCaseSensitive: should the match be CaseSensitive
* RegExOrNot: a `RegExOrNot` object.
* replace: the replacement string for the matching `sre`
* Return: a tuple of 2 value:
* 1) array of [before match, match, replacement, after match]
* where before and after match are cut to a reasonable length after the match.
* 2) Boolean, whether the matching has been aborted because one of the element of
* arr have too many matches.
**/
var compute_preview_model = function(sre, arr, isCaseSensitive, RegExpOrNot, replace){
var html = [];
// and create an array of
// before_match, match , replacement, after_match
var aborted = false;
var replacer_reg = new RegExpOrNot(sre);
for(var r=0; r < arr.length; r++){
var current_line = arr[r];
var match_abort = getMatches(sre, current_line, isCaseSensitive, RegExpOrNot);
aborted = aborted || match_abort[1];
var matches = match_abort[0];
for(var mindex=0; mindex < matches.length ; mindex++){
var start = matches[mindex][0];
var stop = matches[mindex][1];
var initial = current_line.slice(start, stop);
var replaced = initial.replace(replacer_reg, replace);
// that might be better as a dictionary
html.push([cutBefore(current_line.slice(0, start)),
initial,
replaced,
cutAfter(current_line.slice(stop), 30-(stop-start))]);
}
}
return [html, aborted];
};
/**
* Build the preview model where things matched and their replacement values
* are wrapped in tags with correct CSS classes.
* Parameter:
* body: jQuery element into which the preview will be build
* aborted : have the model been aborted (Boolean) use to tell the user
* that the preview might not show all the replacements
* html: array of model created by compute_preview_model
* replace: Boolean: whether we are actually replacing with something or just matching.
**/
var build_preview = function(body, aborted, html, replace){
body.empty();
if(aborted){
var warnmsg = i18n.msg.sprintf(i18n.msg._("Warning: too many matches (%d). Some changes might not be shown or applied."),html.length);
body.append($('<p/>').addClass('bg-warning').text(warnmsg));
} else {
var matchmsg = i18n.msg.sprintf(i18n.msg.ngettext("%d match","%d matches",html.length),html.length);
body.append($('<p/>').text(matchmsg));
}
for(var rindex=0; rindex<html.length; rindex++){
var pre = $('<pre/>')
.append(html[rindex][0])
.append($('<span/>').addClass('match').text(html[rindex][1]));
if(replace){
pre.append($('<span/>').addClass('insert').text(html[rindex][2]));
pre.addClass('replace');
}
pre.append(html[rindex][3]);
body.append(pre);
}
};
/**
* Given a string, return only the beginning, with potentially an ellipsis
* at the end.
**/
var cutAfter = function(string, n){
n=n||10;
while(n<10){
n+=15;
}
if(string.length > n+3){
return string.slice(0, n)+'...';
}
return string;
};
/**
* Given a string, return only the end, with potentially an ellipsis
* at the beginning.
**/
var cutBefore = function(string){
if(string.length > 33){
return '...'+string.slice(-30);
}
return string;
};
/**
* Find all occurrences of `re` in `string`, match in a `caseSensitive`
* manner or not, and determine whether `re` is a RegExp or not depending of
* the type of object passed as `r`.
*
* Return a tuple
* 1) list of matches [start, stop] indexes in the string.
* 2) abort Boolean, if more that 100 matches and the matches were aborted.
**/
var getMatches = function(re, string, caseSensitive, r){
var extra = caseSensitive ? '':'i';
extra = '';
try {
re = r(re, 'g'+extra);// have to global or infinite loop
} catch (e){
return [[], false];
}
var res = [];
var match;
var escape_hatch = 0;
var abort = false;
while((match = re.exec(string)) !== null) {
res.push([match.index, match.index+match[0].length]);
escape_hatch++;
if(escape_hatch > 100){
console.warn(i18n.msg._("More than 100 matches, aborting"));
abort = true;
break;
}
}
return [res, abort];
};
// main function
/**
* Search N' Replace action handler.
**/
var snr = function(env, event) {
var isRegExpButton = $('<button/>')
.attr('type', 'button')
.attr('id', 'isreg')
.addClass("btn btn-default btn-sm")
.attr('data-toggle','button')
.css('font-weight', 'bold')
.attr('title', i18n.msg._('Use regex (JavaScript regex syntax)'))
.text('.*');
var allCellsButton = $('<button/>')
.append($('<i/>').addClass('fa fa-arrows-v'))
.attr('id', 'findreplace_allcells_btn')
.attr('type', 'button')
.addClass("btn btn-default btn-sm")
.attr('data-toggle','button')
.attr('title', i18n.msg._('Replace in all cells'));
var isCaseSensitiveButton = $('<button/>')
.attr('type', 'button')
.addClass("btn btn-default btn-sm")
.attr('data-toggle','button')
.attr('tabindex', '0')
.attr('title', i18n.msg._('Match case'))
.css('font-weight', 'bold')
.text('Aa');
var search = $("<input/>")
.addClass('form-control input-sm')
.attr('id', 'findreplace_find_inp')
.attr('placeholder',i18n.msg._('Find'));
var findFormGroup = $('<div/>').addClass('form-group');
findFormGroup.append(
$('<div/>').addClass('input-group')
.append(
$('<div/>').addClass('input-group-btn')
.append(isCaseSensitiveButton)
.append(isRegExpButton)
.append(allCellsButton)
)
.append(search)
)
var replace = $("<input/>")
.attr('id', 'findreplace_replace_inp')
.addClass('form-control input-sm')
.attr('placeholder',i18n.msg._('Replace'));
var replaceFormGroup = $('<div/>').addClass('form-group');
replaceFormGroup.append(replace);
var body = $('<div/>').attr('id', 'replace-preview');
var form = $('<form/>').attr('id', 'find-and-replace')
form.append(findFormGroup);
form.append(replaceFormGroup);
form.append(body);
// return whether the search is case sensitive
var isCaseSensitive = function(){
var value = isCaseSensitiveButton.attr('aria-pressed') == 'true';
return value;
};
// return whether the search is RegExp based, or
// plain string matching.
var isReg = function(){
var value = isRegExpButton.attr('aria-pressed') == 'true';
return value;
};
var allCells = function(){
return (allCellsButton.attr('aria-pressed') == 'true');
};
// return a Pseudo RegExp object that acts
// either as a plain RegExp Object, or as a pure string matching.
// automatically set the flags for case sensitivity from the UI
var RegExpOrNot = function(str, flags){
if (!isCaseSensitive()){
flags = (flags || '')+'i';
}
if (isRegExpButton.attr('aria-pressed') === 'true'){
return new RegExp(str, flags);
} else {
return new RegExp(escapeRegExp(str), flags);
}
};
var onError = function(body){
body.empty();
body.append($('<p/>').text(i18n.msg._('No matches, invalid or empty regular expression')));
};
var get_cells = function(env){
if(allCells()){
return env.notebook.get_cells();
} else {
return env.notebook.get_selected_cells();
}
};
var get_all_text = function(cells) {
var arr = [];
for (var c = 0; c < cells.length; c++) {
arr = arr.concat(cells[c].code_mirror.getValue().split('\n'));
}
return arr;
};
/**
* callback triggered anytime a change is made to the
* request, case sensitivity, isregex, search or replace
* modification.
**/
var onChange = function(){
var sre = search.val();
// abort on invalid RE
if (!sre) {
return onError(body);
}
try {
new RegExpOrNot(sre);
} catch (e) {
return onError(body);
}
// might want to warn if replace is empty
var replaceValue = replace.val();
var lines = get_all_text(get_cells(env));
var _hb = compute_preview_model(sre, lines, isCaseSensitive(), RegExpOrNot, replaceValue);
var html = _hb[0];
var aborted = _hb[1];
build_preview(body, aborted, html, replaceValue);
// done on type return false not to submit form
return false;
};
var onsubmit = function(event) {
var sre = search.val();
var replaceValue = replace.val();
if (!sre) {
return false;
}
// should abort on invalid RegExp.
// need to be multi line if we want to directly replace in codemirror.
// or need to split/replace/join
var reg = RegExpOrNot(sre, 'gm');
var cells = get_cells(env);
for (var c = 0; c < cells.length; c++) {
var cell = cells[c];
if (!cell.is_editable()) {
continue;
}
var oldvalue = cell.code_mirror.getValue();
var newvalue = oldvalue.replace(reg , replaceValue);
cell.code_mirror.setValue(newvalue);
if (cell.cell_type === 'markdown') {
cell.rendered = false;
cell.render();
}
}
};
// wire-up the UI
isRegExpButton.click(function(){
search.focus();
setTimeout(function(){onChange();}, 100);
});
isCaseSensitiveButton.click(function(){
search.focus();
setTimeout(function(){onChange();}, 100);
});
allCellsButton.click(function(){
replace.focus();
setTimeout(function(){onChange();}, 100);
});
search.keypress(function (e) {
if (e.which == 13) {//enter
replace.focus();
}
});
search.on('input', onChange);
replace.on('input', onChange);
// This statement is used simply so that message extraction
// will pick up the strings. The actual setting of the text
// for the button is in dialog.js.
var button_labels = [ i18n.msg._("Replace All")];
var mod = dialog.modal({
show: false,
title: i18n.msg._("Find and Replace"),
body:form,
keyboard_manager: env.notebook.keyboard_manager,
buttons:{
'Replace All':{ class: "btn-primary",
click: function(event){onsubmit(event); return true;},
id: "findreplace_replaceall_btn",
}
},
open: function(){
search.focus();
}
});
replace.keypress(function (e) {
if (e.which == 13) {//enter
onsubmit();
mod.modal('hide');
}
});
mod.modal('show');
};
var load = function(keyboard_manager){
var action_all = {
cmd: i18n.msg._('find and replace'),
help: i18n.msg._('find and replace'),
handler: function(env, event){
snr(env, event);
}
};
var act_all = keyboard_manager.actions.register(action_all, 'find-and-replace', 'jupyter-notebook');
keyboard_manager.command_shortcuts.add_shortcuts({
'f': 'jupyter-notebook:find-and-replace'
});
};
return {load:load};
});

@ -1,232 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"jquery",
"notebook/js/quickhelp",
"base/js/dialog",
"components/marked/lib/marked"
], function (
$,
QH,
dialog,
marked
) {
/**
* Humanize the action name to be consumed by user.
* internally the actions name are of the form
* <namespace>:<description-with-dashes>
* we drop <namespace> and replace dashes for space.
*/
var humanize_action_id = function(str) {
return str.split(':')[1].replace(/-/g, ' ').replace(/_/g, '-');
};
/**
* given an action id return 'command-shortcut', 'edit-shortcut' or 'no-shortcut'
* for the action. This allows us to tag UI in order to visually distinguish
* Wether an action have a keybinding or not.
**/
var KeyBinding = createReactClass({
displayName: 'KeyBindings',
getInitialState: function() {
return {shrt:''};
},
handleShrtChange: function (element){
this.setState({shrt:element.target.value});
},
render: function(){
var that = this;
var available = this.props.available(this.state.shrt);
var empty = (this.state.shrt === '');
var binding_setter = function(){
if (available) {
that.props.onAddBindings(that.state.shrt, that.props.ckey);
}
that.state.shrt='';
event.preventDefault();
return false;
};
return React.createElement('form', {className:'jupyter-keybindings',
onSubmit: binding_setter
},
React.createElement('i', {className: "pull-right fa fa-plus", alt: 'add-keyboard-shortcut',
onClick: binding_setter
}),
React.createElement('input', {
type:'text',
placeholder:'add shortcut',
className:'pull-right'+((available||empty)?'':' alert alert-danger'),
value:that.state.shrt,
onChange:that.handleShrtChange
}),
that.props.shortcuts ? that.props.shortcuts.map(function (item, index) {
return React.createElement('span', {className: 'pull-right'},
React.createElement('kbd', {}, [
item.h,
React.createElement('i', {className: "fa fa-times", alt: 'remove '+item.h,
onClick:function () {
that.props.unbind(item.raw);
}
})
])
);
}): null,
React.createElement('div', {title: '(' + that.props.ckey + ')' ,
className:'jupyter-keybindings-text'}, that.props.display )
);
}
});
var KeyBindingList = createReactClass({
displayName: 'KeyBindingList',
getInitialState: function(){
return {data:[]};
},
componentDidMount: function(){
this.setState({data:this.props.callback()});
},
render: function() {
var that = this;
var children = this.state.data.map(function (binding) {
return React.createElement(KeyBinding, Object.assign({}, binding, {
onAddBindings: function (shortcut, action) {
that.props.bind(shortcut, action);
that.setState({data:that.props.callback()});
},
available: that.props.available,
unbind: function (shortcut) {
that.props.unbind(shortcut);
that.setState({data:that.props.callback()});
}
}));
});
children.unshift(React.createElement('div', {className:'well', key:'disclamer', id:'short-key-binding-intro', dangerouslySetInnerHTML:
{__html:
marked(
"Here you can modify the keyboard shortcuts available in "+
"command mode. Your changes will be stored for later sessions. "+
"See more [**details of defining keyboard shortcuts**](#long-key-binding-intro) below."
)}
}));
children.push(React.createElement('div', {className:'well', key:'disclamer', id:'long-key-binding-intro', dangerouslySetInnerHTML:
{__html:
marked(
"This dialog allows you to modify the keyboard shortcuts available in command mode. "+
"Any changes will be persisted between sessions and across environments. "+
"You can define two kinds of shortcuts: **key combinations** and **key sequences**.\n"+
"\n"+
" - **Key Combinations**:\n"+
" - Use hyphens `-` to represent keys that should be pressed at the same time.\n"+
" - This is designed for use with *modifier* keys: `Cmd`, `Ctrl`, `Alt` ,`Meta`, "+
"`Cmdtrl`, and `Shift`.\n"+
" - `Cmdtrl` acts like `Cmd` on OS X/MacOS and `Ctrl` on Windows/Linux.\n"+
" - At most, one non-modifier key can exist in a key combination.\n"+
" - Multiple modifier keys can exist in a key combination.\n"+
" - Modifier keys need to precede the non-modifier key in a combination.\n"+
" - *Valid Examples*: `Shift-a`, `Ctrl-;`, or `Ctrl-Shift-a`. \n"+
" - *Invalid Example*s: `a-b` and `a-Ctrl-Shift`. \n"+
" - **Key Sequences**:\n"+
" - Use commas `,` to represent keys that should be pressed in sequence.\n"+
" - The order in which keys must be pressed exactly matches the left-to-right order of "+
"the characters in the sequence, with no interruptions.\n"+
" - E.g., `h,a,l,t` would be triggered by typing <kbd>h</kbd> <kbd>a</kbd> "+
"<kbd>l</kbd> <kbd>t</kbd> but not <kbd>h</kbd> <kbd>a</kbd> <kbd>a</kbd> <kbd>l</kbd> "+
"<kbd>t</kbd> or <kbd>a</kbd> <kbd>h</kbd> <kbd>l</kbd> <kbd>t</kbd>.\n"+
" - Sequences can include the same key multiple times (e.g., `d,d`).\n"+
" - You cannot include any pairs of sequences where one is a 'prefix' the other.\n"+
" - E.g., `d,d,d` cannot be used a the same time as `d,d`.\n"+
" - Key combinations are unique elements that can be used in a sequence.\n"+
" - E.g., `Ctrl-d,d` and `d,d` can exist at the same time and are both valid key sequences.\n"+
"\n"+
"**Additional notes**:\n"+
"\n"+
"The case in which elements are written does not change the binding's meaning. "+
"E.g., `Ctrl-D` and `cTrl-d` are the same key binding. "+
"Thus, `Shift` needs to be explicitly included if it is part of the key binding. "+
"So, for example, if you set a command to be activated by `Shift-D,D`, the second `d` "+
"cannot be pressed at the same time as the `Shift` modifier key.\n"+
"\n"+
"Valid modifiers are specified by writing out their names explicitly: "+
"e.g., `Shift`, `Cmd`, `Ctrl`, `Alt` ,`Meta`, `Cmdtrl`. You cannot use the symbol equivalents "+
"(e.g., `⇧`, `⌘`, `⌃`, `⌥`); refer to developer docs for the corresponding keys "+
"(the mapping of which depends on the platform you are using)."+
"You can hover on the name/description of a command to see its exact internal name and "+
"differentiate from actions defined in various plugins. \n"+
"\n"+
"Changing the keybindings of edit mode is not currently available."
)}
}));
return React.createElement('div',{}, children);
}
});
var get_shortcuts_data = function(notebook) {
var actions = Object.keys(notebook.keyboard_manager.actions._actions);
var src = [];
for (var i = 0; i < actions.length; i++) {
var action_id = actions[i];
var action = notebook.keyboard_manager.actions.get(action_id);
var shortcuts = notebook.keyboard_manager.command_shortcuts.get_action_shortcuts(action_id);
var hshortcuts = [];
if (shortcuts.length > 0) {
hshortcuts = shortcuts.map(function (raw) {
return {h:QH._humanize_sequence(raw),raw:raw};}
);
}
src.push({
display: humanize_action_id(action_id),
shortcuts: hshortcuts,
key:action_id, // react specific thing
ckey: action_id
});
}
return src;
};
var ShortcutEditor = function(notebook) {
if(!notebook){
throw new Error("CommandPalette takes a notebook non-null mandatory argument");
}
var body = $('<div>');
var mod = dialog.modal({
notebook: notebook,
keyboard_manager: notebook.keyboard_manager,
title : "Edit Command mode Shortcuts",
body : body,
buttons : {
OK : {}
}
});
var src = get_shortcuts_data(notebook);
mod.addClass("modal_stretch");
mod.modal('show');
ReactDOM.render(
React.createElement(KeyBindingList, {
callback: function () { return get_shortcuts_data(notebook);},
bind: function (shortcut, command) {
return notebook.keyboard_manager.command_shortcuts._persist_shortcut(shortcut, command);
},
unbind: function (shortcut) {
return notebook.keyboard_manager.command_shortcuts._persist_remove_shortcut(shortcut);
},
available: function (shrt) { return notebook.keyboard_manager.command_shortcuts.is_available_shortcut(shrt);}
}),
body.get(0)
);
};
return {ShortcutEditor: ShortcutEditor};
});

@ -1,654 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/i18n',
'notebook/js/cell',
'base/js/markdown',
'services/config',
'notebook/js/celltoolbar',
'codemirror/lib/codemirror',
'codemirror/mode/gfm/gfm',
'notebook/js/codemirror-ipythongfm',
'bidi/bidi'
], function(
$,
utils,
i18n,
cell,
markdown,
configmod,
celltoolbar,
CodeMirror,
gfm,
ipgfm,
bidi
) {
"use strict";
function encodeURIandParens(uri){return encodeURI(uri).replace('(','%28').replace(')','%29')}
/**
* Given a file name and a list of existing file names, returns a new file name
* that is not in the existing list. If the file name already exists, a new one with
* an incremented index is returned instead.
*
* Example:
* addIndexToFileName('attachment.png',
* ['attachment.png', 'attachment-3.png']) returns 'attachment-4.png'
*
* @param {string} fileName - original file name
* @param {string} fileNames - other file names
* @return {string} the original file name or one with a postfix
* index (before the extension, if one exists)
*/
function addIndexToFileName(fileName, fileNames) {
if (fileNames === undefined) {
return fileName;
}
var lastDot = fileName.lastIndexOf('.');
var pre = fileName.substr(0, lastDot);
var optionalExt = fileName.substr(lastDot);
var indexMatch = '-(\\d+)';
// Make the index match optional so we can match both 'fileName.png' and 'fileName-2.png'
// The ?: makes it a non-capturing group.
var optionalIndexMatch = '(?:' + indexMatch + ')?';
var regex = new RegExp(pre + optionalIndexMatch + optionalExt);
var highestIndex = 0;
for (var existingFileName in fileNames) {
var match = existingFileName.match(regex);
var index = match[1];
if (index === undefined) {
index = 1;
}
else {
index = parseInt(index);
}
if (index > highestIndex) {
highestIndex = index;
}
}
if (highestIndex > 0) {
return pre + "-" + (highestIndex + 1) + optionalExt;
}
else {
return fileName;
}
};
var Cell = cell.Cell;
var TextCell = function (options) {
/**
* Constructor
*
* Construct a new TextCell, codemirror mode is by default 'htmlmixed',
* and cell type is 'text' cell start as not redered.
*
* Parameters:
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* config: dictionary
* keyboard_manager: KeyboardManager instance
* notebook: Notebook instance
*/
options = options || {};
// in all TextCell/Cell subclasses
// do not assign most of members here, just pass it down
// in the options dict potentially overwriting what you wish.
// they will be assigned in the base class.
this.notebook = options.notebook;
this.events = options.events;
this.config = options.config;
// we cannot put this as a class key as it has handle to "this".
Cell.apply(this, [{
config: options.config,
keyboard_manager: options.keyboard_manager,
events: this.events}]);
this.cell_type = this.cell_type || 'text';
this.rendered = false;
};
TextCell.prototype = Object.create(Cell.prototype);
TextCell.options_default = {
cm_config : {
mode: 'htmlmixed',
lineWrapping : true,
}
};
/**
* Create the DOM element of the TextCell
* @method create_element
* @private
*/
TextCell.prototype.create_element = function () {
Cell.prototype.create_element.apply(this, arguments);
var that = this;
var cell = $("<div>").addClass('cell text_cell');
cell.attr('tabindex','2');
var prompt = $('<div/>').addClass('prompt input_prompt');
cell.append(prompt);
var inner_cell = $('<div/>').addClass('inner_cell');
this.celltoolbar = new celltoolbar.CellToolbar({
cell: this,
notebook: this.notebook});
inner_cell.append(this.celltoolbar.element);
var input_area = $('<div/>').addClass('input_area').attr("aria-label", i18n.msg._("Edit Markup Text here"));
this.code_mirror = new CodeMirror(input_area.get(0), this._options.cm_config);
// In case of bugs that put the keyboard manager into an inconsistent state,
// ensure KM is enabled when CodeMirror is focused:
this.code_mirror.on('focus', function () {
if (that.keyboard_manager) {
that.keyboard_manager.enable();
}
that.code_mirror.setOption('readOnly', !that.is_editable());
});
this.code_mirror.on('keydown', $.proxy(this.handle_keyevent,this))
// The tabindex=-1 makes this div focusable.
var render_area = $('<div/>').addClass('text_cell_render rendered_html')
.attr('tabindex','-1');
inner_cell.append(input_area).append(render_area);
cell.append(inner_cell);
this.element = cell;
this.inner_cell = inner_cell;
};
// Cell level actions
TextCell.prototype.add_attachment = function (key, mime_type, b64_data) {
/**
* Add a new attachment to this cell
*/
this.attachments[key] = {};
this.attachments[key][mime_type] = b64_data;
};
TextCell.prototype.select = function () {
var cont = Cell.prototype.select.apply(this, arguments);
if (cont) {
if (this.mode === 'edit') {
this.code_mirror.refresh();
}
}
return cont;
};
TextCell.prototype.unrender = function () {
var cont = Cell.prototype.unrender.apply(this);
if (cont) {
var text_cell = this.element;
if (this.get_text() === this.placeholder) {
this.set_text('');
}
this.refresh();
}
return cont;
};
TextCell.prototype.execute = function () {
this.render();
};
/**
* setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
* @method get_text
* @return {string} CodeMirror current text value
*/
TextCell.prototype.get_text = function() {
return this.code_mirror.getValue();
};
/**
* @param {string} text - Codemiror text value
* @see TextCell#get_text
* @method set_text
* */
TextCell.prototype.set_text = function(text) {
this.code_mirror.setValue(text);
this.unrender();
this.code_mirror.refresh();
};
/**
* setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
* @method get_rendered
* */
TextCell.prototype.get_rendered = function() {
return this.element.find('div.text_cell_render').html();
};
/**
* @method set_rendered
*/
TextCell.prototype.set_rendered = function(text) {
this.element.find('div.text_cell_render').html(text);
};
/**
* Create Text cell from JSON
* @param {json} data - JSON serialized text-cell
* @method fromJSON
*/
TextCell.prototype.fromJSON = function (data) {
Cell.prototype.fromJSON.apply(this, arguments);
if (data.cell_type === this.cell_type) {
if (data.attachments !== undefined) {
this.attachments = data.attachments;
}
if (data.source !== undefined) {
this.set_text(data.source);
// make this value the starting point, so that we can only undo
// to this state, instead of a blank cell
this.code_mirror.clearHistory();
// TODO: This HTML needs to be treated as potentially dangerous
// user input and should be handled before set_rendered.
this.set_rendered(data.rendered || '');
this.rendered = false;
this.render();
}
}
};
/** Generate JSON from cell
* @param {bool} gc_attachments - If true, will remove unused attachments
* from the returned JSON
* @return {object} cell data serialised to json
*/
TextCell.prototype.toJSON = function (gc_attachments) {
if (gc_attachments === undefined) {
gc_attachments = false;
}
var data = Cell.prototype.toJSON.apply(this);
data.source = this.get_text();
if (data.source == this.placeholder) {
data.source = "";
}
// We deepcopy the attachments so copied cells don't share the same
// objects
if (Object.keys(this.attachments).length > 0) {
if (gc_attachments) {
// Garbage collect unused attachments : The general idea is to
// render the text, and find used attachments like when we
// substitute them in render()
var that = this;
data.attachments = {};
// To find attachments, rendering to HTML is easier than
// searching in the markdown source for the multiple ways you
// can reference an image in markdown (using []() or a
// HTML <img>)
var text = this.get_text();
markdown.render(text, {
sanitize: true,
}, function (err, html) {
html.find('img[src^="attachment:"]').each(function (i, h) {
h = $(h);
var key = h.attr('src').replace(/^attachment:/, '');
if (that.attachments.hasOwnProperty(key)) {
data.attachments[key] = JSON.parse(JSON.stringify(
that.attachments[key]));
}
// This is to avoid having the browser do a GET request
// on the invalid attachment: URL
h.attr('src', '');
});
});
if (data.attachments.length === 0) {
// omit attachments dict if no attachments
delete data.attachments;
}
} else {
data.attachments = JSON.parse(JSON.stringify(this.attachments));
}
}
return data;
};
var MarkdownCell = function (options) {
/**
* Constructor
*
* Parameters:
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* config: ConfigSection instance
* keyboard_manager: KeyboardManager instance
* notebook: Notebook instance
*/
options = options || {};
var config_default = utils.mergeopt(TextCell, MarkdownCell.options_default);
this.class_config = new configmod.ConfigWithDefaults(options.config,
config_default, 'MarkdownCell');
TextCell.apply(this, [$.extend({}, options, {config: options.config})]);
this.cell_type = 'markdown';
// Used to keep track of drag events
this.drag_counter = 0;
};
MarkdownCell.options_default = {
cm_config: {
mode: 'ipythongfm',
},
placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
};
MarkdownCell.prototype = Object.create(TextCell.prototype);
MarkdownCell.prototype.set_heading_level = function (level) {
/**
* make a markdown cell a heading
*/
level = level || 1;
var source = this.get_text();
source = source.replace(/^(#*)\s?/,
new Array(level + 1).join('#') + ' ');
this.set_text(source);
this.refresh();
if (this.rendered) {
this.render();
}
};
MarkdownCell.prototype.select = function () {
var cont = TextCell.prototype.select.apply(this, arguments);
if (cont) {
this.notebook.set_insert_image_enabled(!this.rendered);
}
};
MarkdownCell.prototype.unrender = function () {
var cont = TextCell.prototype.unrender.apply(this);
this.notebook.set_insert_image_enabled(true);
};
MarkdownCell.prototype.insert_inline_image_from_blob = function(blob) {
/**
* Insert markup for an inline image at the current cursor position.
* This works as follow :
* - We insert the base64-encoded blob data into the cell attachments
* dictionary, keyed by the filename.
* - We insert an img tag with a 'attachment:key' src that refers to
* the attachments entry.
*
* Parameters:
* file: Blob
* The JS Blob object (e.g. from the DataTransferItem)
*/
var that = this;
var pos = this.code_mirror.getCursor();
var reader = new FileReader();
// We can get either a named file (drag'n'drop) or a blob (copy/paste)
// We generate names for blobs
var key;
if (blob.name !== undefined) {
key = encodeURIandParens(blob.name);
// Add an index to the filename if we already have one with the same name
key = addIndexToFileName(key, that.attachments);
} else {
key = '_auto_' + Object.keys(that.attachments).length;
}
reader.onloadend = function() {
var d = utils.parse_b64_data_uri(reader.result);
var blobData = d[1]
if (blob.type != d[0]) {
// TODO(julienr): Not sure what we should do in this case
console.log('File type (' + blob.type + ') != data-uri ' +
'type (' + d[0] + ')');
}
// If we have the same attachment already under another key, we change the key to that.
// This ensures we don't create two attachments if pasting the same image twice.
for (var savedKey in that.attachments) {
var attachment = that.attachments[savedKey];
if (attachment === undefined) continue;
var savedBlob = attachment[blob.type];
if (savedBlob === blobData) {
key = savedKey;
}
}
that.add_attachment(key, blob.type, blobData);
var img_md = '![' + key + '](attachment:' + key + ')';
that.code_mirror.replaceRange(img_md, pos);
}
reader.readAsDataURL(blob);
};
/**
* @method render
*/
MarkdownCell.prototype.render = function () {
// We clear the dropzone here just in case the dragenter/leave
// logic of bind_events wasn't 100% successful.
this.drag_counter = 0;
this.inner_cell.removeClass('dropzone');
var cont = TextCell.prototype.render.apply(this);
if (cont) {
var that = this;
var text = this.get_text();
var math = null;
if (text === "") { text = this.placeholder; }
markdown.render(text, {
with_math: true,
clean_tables: true,
sanitize: true,
}, function (err, 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('¶')
.on('click',function(){
setTimeout(function(){that.unrender(); that.render()}, 100)
})
);
});
// links in markdown cells should open in new tabs
html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
// replace attachment:<key> by the corresponding entry
// in the cell's attachments
html.find('img[src^="attachment:"]').each(function (i, h) {
h = $(h);
var key = h.attr('src').replace(/^attachment:/, '');
if (that.attachments.hasOwnProperty(key)) {
var att = that.attachments[key];
var mime = Object.keys(att)[0];
h.attr('src', 'data:' + mime + ';base64,' + att[mime]);
} else {
h.attr('src', '');
}
});
that.set_rendered(html);
that.typeset();
that.events.trigger("rendered.MarkdownCell", {cell: that});
});
}
return cont;
};
/** @method bind_events **/
MarkdownCell.prototype.bind_events = function () {
TextCell.prototype.bind_events.apply(this);
var that = this;
this.element.dblclick(function () {
var cont = that.unrender();
if (cont) {
that.focus_editor();
}
});
var attachment_regex = /^image\/.*$/;
// Event handlers to allow users to insert image using either
// drag'n'drop or copy/paste
var div = that.code_mirror.getWrapperElement();
$(div).on('paste', function(evt) {
var data = evt.originalEvent.clipboardData;
var items = data.items;
if (items !== undefined) {
for (var i = 0; i < items.length; ++i) {
var item = items[i];
if (item.kind == 'file' && attachment_regex.test(item.type)) {
// TODO(julienr): This does not stop code_mirror from pasting
// the filename.
evt.stopPropagation();
evt.preventDefault();
that.insert_inline_image_from_blob(item.getAsFile());
}
}
}
});
// Allow drag event if the dragged file can be used as an attachment
// If we use this.code_mirror.on to register a "dragover" handler, we
// get an empty dataTransfer
this.code_mirror.on("dragover", function(cm, evt) {
if (utils.dnd_contain_file(evt)) {
evt.preventDefault();
}
});
// We want to display a visual indicator that the drop is possible.
// The dragleave event is fired when we hover a child element (which
// is often immediately after we got the dragenter), so we keep track
// of the number of dragenter/dragleave we got, as discussed here :
// https://stackoverflow.com/q/7110353/116067
// This doesn't seem to be 100% reliable, so we clear the dropzone
// class when the cell is rendered as well
this.code_mirror.on("dragenter", function(cm, evt) {
if (utils.dnd_contain_file(evt)) {
that.drag_counter++;
that.inner_cell.addClass('dropzone');
}
evt.preventDefault();
evt.stopPropagation();
});
this.code_mirror.on("dragleave", function(cm, evt) {
that.drag_counter--;
if (that.drag_counter <= 0) {
that.inner_cell.removeClass('dropzone');
}
evt.preventDefault();
evt.stopPropagation();
});
this.code_mirror.on("drop", function(cm, evt) {
that.drag_counter = 0;
that.inner_cell.removeClass('dropzone');
var files = evt.dataTransfer.files;
for (var i = 0; i < files.length; ++i) {
var file = files[i];
if (attachment_regex.test(file.type)) {
// Prevent the default code_mirror 'drop' event handler
// (which inserts the file content) if this is a
// recognized media file
evt.stopPropagation();
evt.preventDefault();
that.insert_inline_image_from_blob(file);
}
}
});
};
var RawCell = function (options) {
/**
* Constructor
*
* Parameters:
* options: dictionary
* Dictionary of keyword arguments.
* events: $(Events) instance
* config: ConfigSection instance
* keyboard_manager: KeyboardManager instance
* notebook: Notebook instance
*/
options = options || {};
var config_default = utils.mergeopt(TextCell, RawCell.options_default);
this.class_config = new configmod.ConfigWithDefaults(options.config,
config_default, 'RawCell');
TextCell.apply(this, [$.extend({}, options, {config: options.config})]);
this.cell_type = 'raw';
};
RawCell.options_default = {
highlight_modes : {
'diff' :{'reg':[/^diff/]}
},
placeholder : i18n.msg._("Write raw LaTeX or other formats here, for use with nbconvert. " +
"It will not be rendered in the notebook. " +
"When passing through nbconvert, a Raw Cell's content is added to the output unmodified."),
};
RawCell.prototype = Object.create(TextCell.prototype);
/** @method bind_events **/
RawCell.prototype.bind_events = function () {
TextCell.prototype.bind_events.apply(this);
var that = this;
this.element.focusout(function() {
that.auto_highlight();
that.render();
});
this.code_mirror.on('focus', function() { that.unrender(); });
};
/** @method render **/
RawCell.prototype.render = function () {
var cont = TextCell.prototype.render.apply(this);
if (cont){
var text = this.get_text();
if (text === "") { text = this.placeholder; }
this.set_text(text);
this.element.removeClass('rendered');
this.auto_highlight();
}
return cont;
};
var textcell = {
TextCell: TextCell,
MarkdownCell: MarkdownCell,
RawCell: RawCell
};
return textcell;
});

@ -1,153 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['jquery','base/js/i18n'], function($, i18n) {
"use strict";
/**
* A generic toolbar on which one can add button
* @class ToolBar
* @constructor
* @param {Dom_object} selector
*/
var ToolBar = function (selector, options) {
this.selector = selector;
this.actions = (options||{}).actions;
if (this.selector !== undefined) {
this.element = $(selector);
this.style();
}
};
ToolBar.prototype._pseudo_actions={};
ToolBar.prototype.construct = function (config) {
for(var k=0; k<config.length; k++) {
this.add_buttons_group(config[k][0],config[k][1]);
}
};
/**
* Add a group of button into the current toolbar.
*
* Use a [dict of [list of action name]] to trigger
* on click to the button
*
* @example
*
* ... todo, maybe use a list of list to keep ordering.
*
* [
* [
* [
* action_name_1,
* action_name_2,
* action_name_3,
* ],
* optional_group_name
* ],
* ...
* ]
*
* @param list {List}
* List of button of the group, with the following parameter for each :
* @param list.label {string} text to show on button hover
* @param list.icon {string} icon to choose from [Font Awesome](http://fortawesome.github.io/Font-Awesome)
* @param list.callback {function} function to be called on button click
* @param [list.id] {String} id to give to the button
* @param [group_id] {String} optional id to give to the group
*
*
* for private usage, the key can also be strings starting with '<' and ending with '>' to inject custom element that cannot
* be bound to an action.
*
*/
// TODO JUPYTER:
// get rid of legacy code that handle things that are not actions.
ToolBar.prototype.add_buttons_group = function (list, group_id) {
// handle custom call of pseudoaction binding.
if(typeof(list) === 'string' && list.slice(0,1) === '<' && list.slice(-1) === '>'){
var _pseudo_action;
try{
_pseudo_action = list.slice(1,-1);
this.element.append(this._pseudo_actions[_pseudo_action].call(this));
} catch (e) {
console.warn('ouch, calling ', _pseudo_action, 'does not seem to work...:', e);
}
return ;
}
var that = this;
var btn_group = $('<div/>').addClass("btn-group");
if( group_id !== undefined ) {
btn_group.attr('id',group_id);
}
list.forEach(function(el) {
var action_name;
var action;
if(typeof(el) === 'string'){
action = that.actions.get(el);
action_name = el;
} else if (el.action) {
action = that.actions.get(el.action);
action_name = el.action
}
var title = el.label;
if(action && action.help) {
title = i18n.msg._(action.help) || el.label;
}
var button = $('<button/>')
.addClass('btn btn-default')
.attr("aria-label", el.label)
.attr("title", title)
.append(
$("<i/>").addClass(el.icon||(action||{icon:'fa-exclamation-triangle'}).icon).addClass('fa')
);
if (el.label) {
var label = $('<span/>').text(i18n.msg._(el.label)).addClass('toolbar-btn-label');
button.append(label);
}
var id = el.id;
if( id !== undefined ){
button.attr('id',id);
}
button.attr('data-jupyter-action', action_name);
var fun = el.callback|| function(){
that.actions.call(action_name);
};
button.click(fun);
btn_group.append(button);
});
$(this.selector).append(btn_group);
return btn_group;
};
ToolBar.prototype.style = function () {
this.element.addClass('toolbar');
};
/**
* Show and hide toolbar
* @method toggle
*/
ToolBar.prototype.toggle = function () {
this.element.toggle();
};
/**
* A simple class to hold information defining one toolbar button.
* @class ToolBar
* @constructor
* @param action {String} name of a Jupyter action taken when pressed
* @param options.label {String} short label to display on the button
*/
var Button = function(action, options) {
this.action = action;
this.label = (options||{}).label;
};
return {
'ToolBar': ToolBar,
'Button': Button
};
});

@ -1,324 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/utils',
'base/js/i18n'
], function($, utils, i18n) {
"use strict";
// tooltip constructor
var Tooltip = function (events) {
var that = this;
this.events = events;
this.time_before_tooltip = 1200;
// handle to html
this.tooltip = $('#tooltip');
this._hidden = true;
// variable for consecutive call
this._old_cell = null;
this._old_request = null;
this._consecutive_counter = 0;
// 'sticky ?'
this._sticky = false;
// display tooltip if the docstring is empty?
this._hide_if_no_docstring = false;
// contain the button in the upper right corner
this.buttons = $('<div/>').addClass('tooltipbuttons');
// will contain the docstring
this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
// build the buttons menu on the upper right
// expand the tooltip to see more
var expandlink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button')
.attr('id', 'expanbutton').attr('title', i18n.msg._('Grow the tooltip vertically (press shift-tab twice)')).click(function () {
that.expand();
event.preventDefault();
}).append(
$('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
// open in pager
var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', i18n.msg._('show the current docstring in pager (press shift-tab 4 times)'));
var morespan = $('<span/>').text(i18n.msg._('Open in Pager')).addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
morelink.append(morespan);
morelink.click(function () {
that.showInPager(that._old_cell);
event.preventDefault();
});
// close the tooltip
var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
var closespan = $('<span/>').text(i18n.msg._('Close')).addClass('ui-icon').addClass('ui-icon-close');
closelink.append(closespan);
closelink.click(function () {
that.remove_and_cancel_tooltip(true);
event.preventDefault();
});
this._clocklink = $('<a/>').attr('href', "#");
this._clocklink.attr('role', "button");
this._clocklink.addClass('ui-button');
this._clocklink.attr('title', i18n.msg._('Tooltip will linger for 10 seconds while you type'));
var clockspan = $('<span/>').text(i18n.msg._('Close'));
clockspan.addClass('ui-icon');
clockspan.addClass('ui-icon-clock');
this._clocklink.append(clockspan);
this._clocklink.click(function () {
that.cancel_stick();
event.preventDefault();
});
//construct the tooltip
// add in the reverse order you want them to appear
this.buttons.append(closelink);
this.buttons.append(expandlink);
this.buttons.append(morelink);
this.buttons.append(this._clocklink);
this._clocklink.hide();
// we need a phony element to make the small arrow
// of the tooltip in css
// we will move the arrow later
this.arrow = $('<div/>').addClass('pretooltiparrow');
this.tooltip.append(this.buttons);
this.tooltip.append(this.arrow);
this.tooltip.append(this.text);
// function that will be called if you press tab 1, 2, 3... times in a row
this.tabs_functions = [function (cell, text, cursor) {
that._request_tooltip(cell, text, cursor);
}, function () {
that.expand();
}, function () {
that.stick();
}, function (cell) {
that.cancel_stick();
that.showInPager(cell);
}];
// call after all the tabs function above have bee call to clean their effects
// if necessary
this.reset_tabs_function = function (cell, text) {
this._old_cell = (cell) ? cell : null;
this._old_request = (text) ? text : null;
this._consecutive_counter = 0;
};
};
Tooltip.prototype.is_visible = function () {
return !this._hidden;
};
Tooltip.prototype.showInPager = function (cell) {
/**
* reexecute last call in pager by appending ? to show back in pager
*/
this.events.trigger('open_with_text.Pager', this._reply.content);
this.remove_and_cancel_tooltip();
};
// grow the tooltip vertically
Tooltip.prototype.expand = function () {
this.text.removeClass('smalltooltip');
this.text.addClass('bigtooltip');
$('#expanbutton').hide('slow');
};
// deal with all the logic of hiding the tooltip
// and reset its status
Tooltip.prototype._hide = function () {
this._hidden = true;
this.tooltip.fadeOut('fast');
$('#expanbutton').show('slow');
this.text.removeClass('bigtooltip');
this.text.addClass('smalltooltip');
// keep scroll top to be sure to always see the first line
this.text.scrollTop(0);
this.code_mirror = null;
};
// return true on successfully removing a visible tooltip; otherwise return
// false.
Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
/**
* note that we don't handle closing directly inside the calltip
* as in the completer, because it is not focusable, so won't
* get the event.
*/
this.cancel_pending();
if (!this._hidden) {
if (force || !this._sticky) {
this.cancel_stick();
this._hide();
}
this.reset_tabs_function();
return true;
} else {
return false;
}
};
// cancel autocall done after '(' for example.
Tooltip.prototype.cancel_pending = function () {
if (this._tooltip_timeout !== null) {
clearTimeout(this._tooltip_timeout);
this._tooltip_timeout = null;
}
};
// will trigger tooltip after timeout
Tooltip.prototype.pending = function (cell, hide_if_no_docstring) {
var that = this;
this._tooltip_timeout = setTimeout(function () {
that.request(cell, hide_if_no_docstring);
}, that.time_before_tooltip);
};
// easy access for julia monkey patching.
Tooltip.last_token_re = /[a-z_][0-9a-z._]*$/gi;
Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) {
var callbacks = $.proxy(this._show, this);
var msg_id = cell.kernel.inspect(text, cursor_pos, callbacks);
};
// make an immediate completion request
Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
/**
* request(codecell)
* Deal with extracting the text from the cell and counting
* call in a row
*/
this.cancel_pending();
var editor = cell.code_mirror;
var cursor = editor.getCursor();
var text = cell.get_text();
var cursor_pos = utils.js_idx_to_char_idx(editor.indexFromPos(cursor), text);
this._hide_if_no_docstring = hide_if_no_docstring;
if(editor.somethingSelected()){
// get only the most recent selection.
text = editor.getSelection();
}
// need a permanent handle to code_mirror for future auto recall
this.code_mirror = editor;
// now we treat the different number of keypress
// first if same cell, same text, increment counter by 1
if (this._old_cell == cell && this._old_request == text && this._hidden === false) {
this._consecutive_counter++;
} else {
// else reset
this.cancel_stick();
this.reset_tabs_function (cell, text);
}
this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos);
// then if we are at the end of list function, reset
if (this._consecutive_counter == this.tabs_functions.length) {
this.reset_tabs_function (cell, text, cursor);
}
return;
};
// cancel the option of having the tooltip to stick
Tooltip.prototype.cancel_stick = function () {
clearTimeout(this._stick_timeout);
this._stick_timeout = null;
this._clocklink.hide('slow');
this._sticky = false;
};
// put the tooltip in a sticky state for 10 seconds
// it won't be removed by remove_and_cancel() unless you called with
// the first parameter set to true.
// remove_and_cancel_tooltip(true)
Tooltip.prototype.stick = function (time) {
time = (time !== undefined) ? time : 10;
var that = this;
this._sticky = true;
this._clocklink.show('slow');
this._stick_timeout = setTimeout(function () {
that._sticky = false;
that._clocklink.hide('slow');
}, time * 1000);
};
// should be called with the kernel reply to actually show the tooltip
Tooltip.prototype._show = function (reply) {
/**
* move the bubble if it is not hidden
* otherwise fade it
*/
this._reply = reply;
var content = reply.content;
if (!content.found) {
// object not found, nothing to show
return;
}
this.name = content.name;
// do some math to have the tooltip arrow on more or less on left or right
// position of the editor
var cm_pos = $(this.code_mirror.getWrapperElement()).position();
var cell_pos = $(this.code_mirror.getWrapperElement().offsetParent).position();
// anchor and head positions are local within CodeMirror element
var anchor = this.code_mirror.cursorCoords(false, 'local');
var head = this.code_mirror.cursorCoords(true, 'local');
// locate the target at the center of anchor, head
var center_left = (head.left + anchor.left) / 2;
// locate the left edge of the tooltip, at most 450 px left of the arrow
var edge_left = Math.max(center_left - 450, 0);
// locate the arrow at the cursor. A 24 px offset seems necessary.
var arrow_left = center_left - edge_left - 24;
// locate left, top within container element
var left = (cell_pos.left + cm_pos.left + edge_left) + 'px';
var top = (cell_pos.top + cm_pos.top + head.bottom + 10) + 'px';
if (this._hidden === false) {
this.tooltip.animate({
left: left,
top: top
});
} else {
this.tooltip.css({
left: left
});
this.tooltip.css({
top: top
});
}
this.arrow.animate({
'left': arrow_left + 'px'
});
this._hidden = false;
this.tooltip.fadeIn('fast');
this.text.children().remove();
// This should support rich data types, but only text/plain for now
// Any HTML within the docstring is escaped by the fixConsole() method.
var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain']));
this.text.append(pre);
// keep scroll top to be sure to always see the first line
this.text.scrollTop(0);
};
return {'Tooltip': Tooltip};
});

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save