##// END OF EJS Templates
@carreau review changes
Jonathan Frederic -
Show More
@@ -1,10 +1,12 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 var ipython = ipython || {};
4 var ipython = ipython || {};
5 require(['base/js/page'], function(page) {
5 require(['base/js/page'], function(page) {
6 ipython.page = new page.Page();
6 var page_instance = new page.Page();
7 $('button#login_submit').addClass("btn btn-default");
7 $('button#login_submit').addClass("btn btn-default");
8 ipython.page.show();
8 page_instance.show();
9 $('input#password_input').focus();
9 $('input#password_input').focus();
10
11 ipython.page = page_instance;
10 });
12 });
@@ -1,9 +1,11 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 var ipython = ipython || {};
4 var ipython = ipython || {};
5 require(['base/js/page'], function(Page) {
5 require(['base/js/page'], function(page) {
6 ipython.page = new page.Page();
6 var page_instance = new page.Page();
7 $('#ipython-main-app').addClass('border-box-sizing');
7 $('#ipython-main-app').addClass('border-box-sizing');
8 ipython.page.show();
8 page_instance.show();
9
10 ipython.page = page_instance;
9 });
11 });
@@ -1,158 +1,158 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 ], function(IPython, $) {
7 ], function(IPython, $) {
8 "use strict";
8 "use strict";
9
9
10 var modal = function (options, keyboard_manager, notebook) {
10 var modal = function (options, keyboard_manager, notebook) {
11 var modal = $("<div/>")
11 var modal = $("<div/>")
12 .addClass("modal")
12 .addClass("modal")
13 .addClass("fade")
13 .addClass("fade")
14 .attr("role", "dialog");
14 .attr("role", "dialog");
15 var dialog = $("<div/>")
15 var dialog = $("<div/>")
16 .addClass("modal-dialog")
16 .addClass("modal-dialog")
17 .appendTo(modal);
17 .appendTo(modal);
18 var dialog_content = $("<div/>")
18 var dialog_content = $("<div/>")
19 .addClass("modal-content")
19 .addClass("modal-content")
20 .appendTo(dialog);
20 .appendTo(dialog);
21 dialog_content.append(
21 dialog_content.append(
22 $("<div/>")
22 $("<div/>")
23 .addClass("modal-header")
23 .addClass("modal-header")
24 .append($("<button>")
24 .append($("<button>")
25 .attr("type", "button")
25 .attr("type", "button")
26 .addClass("close")
26 .addClass("close")
27 .attr("data-dismiss", "modal")
27 .attr("data-dismiss", "modal")
28 .attr("aria-hidden", "true")
28 .attr("aria-hidden", "true")
29 .html("&times;")
29 .html("&times;")
30 ).append(
30 ).append(
31 $("<h4/>")
31 $("<h4/>")
32 .addClass('modal-title')
32 .addClass('modal-title')
33 .text(options.title || "")
33 .text(options.title || "")
34 )
34 )
35 ).append(
35 ).append(
36 $("<div/>").addClass("modal-body").append(
36 $("<div/>").addClass("modal-body").append(
37 options.body || $("<p/>")
37 options.body || $("<p/>")
38 )
38 )
39 );
39 );
40
40
41 var footer = $("<div/>").addClass("modal-footer");
41 var footer = $("<div/>").addClass("modal-footer");
42
42
43 for (var label in options.buttons) {
43 for (var label in options.buttons) {
44 var btn_opts = options.buttons[label];
44 var btn_opts = options.buttons[label];
45 var button = $("<button/>")
45 var button = $("<button/>")
46 .addClass("btn btn-default btn-sm")
46 .addClass("btn btn-default btn-sm")
47 .attr("data-dismiss", "modal")
47 .attr("data-dismiss", "modal")
48 .text(label);
48 .text(label);
49 if (btn_opts.click) {
49 if (btn_opts.click) {
50 button.click($.proxy(btn_opts.click, dialog_content));
50 button.click($.proxy(btn_opts.click, dialog_content));
51 }
51 }
52 if (btn_opts.class) {
52 if (btn_opts.class) {
53 button.addClass(btn_opts.class);
53 button.addClass(btn_opts.class);
54 }
54 }
55 footer.append(button);
55 footer.append(button);
56 }
56 }
57 dialog_content.append(footer);
57 dialog_content.append(footer);
58 // hook up on-open event
58 // hook up on-open event
59 modal.on("shown.bs.modal", function() {
59 modal.on("shown.bs.modal", function() {
60 setTimeout(function() {
60 setTimeout(function() {
61 footer.find("button").last().focus();
61 footer.find("button").last().focus();
62 if (options.open) {
62 if (options.open) {
63 $.proxy(options.open, modal)();
63 $.proxy(options.open, modal)();
64 }
64 }
65 }, 0);
65 }, 0);
66 });
66 });
67
67
68 // destroy modal on hide, unless explicitly asked not to
68 // destroy modal on hide, unless explicitly asked not to
69 if (options.destroy === undefined || options.destroy) {
69 if (options.destroy === undefined || options.destroy) {
70 modal.on("hidden.bs.modal", function () {
70 modal.on("hidden.bs.modal", function () {
71 modal.remove();
71 modal.remove();
72 });
72 });
73 }
73 }
74 modal.on("hidden.bs.modal", function () {
74 modal.on("hidden.bs.modal", function () {
75 if (notebook) {
75 if (notebook) {
76 var cell = notebook.get_selected_cell();
76 var cell = notebook.get_selected_cell();
77 if (cell) cell.select();
77 if (cell) cell.select();
78 keyboard_manager.enable();
78 keyboard_manager.enable();
79 keyboard_manager.command_mode();
79 keyboard_manager.command_mode();
80 }
80 }
81 });
81 });
82
82
83 if (keyboard_manager) {
83 if (keyboard_manager) {
84 keyboard_manager.disable();
84 keyboard_manager.disable();
85 }
85 }
86
86
87 return modal.modal(options);
87 return modal.modal(options);
88 };
88 };
89
89
90 var edit_metadata = function (md, callback, name, keyboard_manager, notebook) {
90 var edit_metadata = function (md, callback, name, keyboard_manager, notebook) {
91 name = name || "Cell";
91 name = name || "Cell";
92 var error_div = $('<div/>').css('color', 'red');
92 var error_div = $('<div/>').css('color', 'red');
93 var message =
93 var message =
94 "Manually edit the JSON below to manipulate the metadata for this " + name + "." +
94 "Manually edit the JSON below to manipulate the metadata for this " + name + "." +
95 " We recommend putting custom metadata attributes in an appropriately named sub-structure," +
95 " We recommend putting custom metadata attributes in an appropriately named sub-structure," +
96 " so they don't conflict with those of others.";
96 " so they don't conflict with those of others.";
97
97
98 var textarea = $('<textarea/>')
98 var textarea = $('<textarea/>')
99 .attr('rows', '13')
99 .attr('rows', '13')
100 .attr('cols', '80')
100 .attr('cols', '80')
101 .attr('name', 'metadata')
101 .attr('name', 'metadata')
102 .text(JSON.stringify(md || {}, null, 2));
102 .text(JSON.stringify(md || {}, null, 2));
103
103
104 var dialogform = $('<div/>').attr('title', 'Edit the metadata')
104 var dialogform = $('<div/>').attr('title', 'Edit the metadata')
105 .append(
105 .append(
106 $('<form/>').append(
106 $('<form/>').append(
107 $('<fieldset/>').append(
107 $('<fieldset/>').append(
108 $('<label/>')
108 $('<label/>')
109 .attr('for','metadata')
109 .attr('for','metadata')
110 .text(message)
110 .text(message)
111 )
111 )
112 .append(error_div)
112 .append(error_div)
113 .append($('<br/>'))
113 .append($('<br/>'))
114 .append(textarea)
114 .append(textarea)
115 )
115 )
116 );
116 );
117 var editor = CodeMirror.fromTextArea(textarea[0], {
117 var editor = CodeMirror.fromTextArea(textarea[0], {
118 lineNumbers: true,
118 lineNumbers: true,
119 matchBrackets: true,
119 matchBrackets: true,
120 indentUnit: 2,
120 indentUnit: 2,
121 autoIndent: true,
121 autoIndent: true,
122 mode: 'application/json',
122 mode: 'application/json',
123 });
123 });
124 var modal = modal({
124 var modal = modal({
125 title: "Edit " + name + " Metadata",
125 title: "Edit " + name + " Metadata",
126 body: dialogform,
126 body: dialogform,
127 buttons: {
127 buttons: {
128 OK: { class : "btn-primary",
128 OK: { class : "btn-primary",
129 click: function() {
129 click: function() {
130 // validate json and set it
130 // validate json and set it
131 var new_md;
131 var new_md;
132 try {
132 try {
133 new_md = JSON.parse(editor.getValue());
133 new_md = JSON.parse(editor.getValue());
134 } catch(e) {
134 } catch(e) {
135 console.log(e);
135 console.log(e);
136 error_div.text('WARNING: Could not save invalid JSON.');
136 error_div.text('WARNING: Could not save invalid JSON.');
137 return false;
137 return false;
138 }
138 }
139 callback(new_md);
139 callback(new_md);
140 }
140 }
141 },
141 },
142 Cancel: {}
142 Cancel: {}
143 }
143 }
144 }, keyboard_manager, notebook);
144 }, keyboard_manager, notebook);
145
145
146 modal.on('shown.bs.modal', function(){ editor.refresh(); });
146 modal.on('shown.bs.modal', function(){ editor.refresh(); });
147 };
147 };
148
148
149 var Dialog = {
149 var dialog = {
150 modal : modal,
150 modal : modal,
151 edit_metadata : edit_metadata,
151 edit_metadata : edit_metadata,
152 };
152 };
153
153
154 // Backwards compatability.
154 // Backwards compatability.
155 IPython.Dialog = Dialog;
155 IPython.Dialog = dialog;
156
156
157 return Dialog;
157 return dialog;
158 });
158 });
@@ -1,8 +1,10 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 var ipython = ipython || {};
4 var ipython = ipython || {};
5 require(['base/js/page'], function(Page) {
5 require(['base/js/page'], function(page) {
6 ipython.page = new page.Page();
6 var page_instance = new page.Page();
7 ipython.page.show();
7 page_instance.show();
8
9 ipython.page = page_instance;
8 });
10 });
@@ -1,557 +1,557 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 ], function(IPython, $){
7 ], function(IPython, $){
8 "use strict";
8 "use strict";
9
9
10 IPython.load_extensions = function () {
10 IPython.load_extensions = function () {
11 // load one or more IPython notebook extensions with requirejs
11 // load one or more IPython notebook extensions with requirejs
12
12
13 var extensions = [];
13 var extensions = [];
14 var extension_names = arguments;
14 var extension_names = arguments;
15 for (var i = 0; i < extension_names.length; i++) {
15 for (var i = 0; i < extension_names.length; i++) {
16 extensions.push("nbextensions/" + arguments[i]);
16 extensions.push("nbextensions/" + arguments[i]);
17 }
17 }
18
18
19 require(extensions,
19 require(extensions,
20 function () {
20 function () {
21 for (var i = 0; i < arguments.length; i++) {
21 for (var i = 0; i < arguments.length; i++) {
22 var ext = arguments[i];
22 var ext = arguments[i];
23 var ext_name = extension_names[i];
23 var ext_name = extension_names[i];
24 // success callback
24 // success callback
25 console.log("Loaded extension: " + ext_name);
25 console.log("Loaded extension: " + ext_name);
26 if (ext && ext.load_ipython_extension !== undefined) {
26 if (ext && ext.load_ipython_extension !== undefined) {
27 ext.load_ipython_extension();
27 ext.load_ipython_extension();
28 }
28 }
29 }
29 }
30 },
30 },
31 function (err) {
31 function (err) {
32 // failure callback
32 // failure callback
33 console.log("Failed to load extension(s):", err.requireModules, err);
33 console.log("Failed to load extension(s):", err.requireModules, err);
34 }
34 }
35 );
35 );
36 };
36 };
37
37
38 //============================================================================
38 //============================================================================
39 // Cross-browser RegEx Split
39 // Cross-browser RegEx Split
40 //============================================================================
40 //============================================================================
41
41
42 // This code has been MODIFIED from the code licensed below to not replace the
42 // This code has been MODIFIED from the code licensed below to not replace the
43 // default browser split. The license is reproduced here.
43 // default browser split. The license is reproduced here.
44
44
45 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
45 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
46 /*!
46 /*!
47 * Cross-Browser Split 1.1.1
47 * Cross-Browser Split 1.1.1
48 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
48 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
49 * Available under the MIT License
49 * Available under the MIT License
50 * ECMAScript compliant, uniform cross-browser split method
50 * ECMAScript compliant, uniform cross-browser split method
51 */
51 */
52
52
53 /**
53 /**
54 * Splits a string into an array of strings using a regex or string
54 * Splits a string into an array of strings using a regex or string
55 * separator. Matches of the separator are not included in the result array.
55 * separator. Matches of the separator are not included in the result array.
56 * However, if `separator` is a regex that contains capturing groups,
56 * However, if `separator` is a regex that contains capturing groups,
57 * backreferences are spliced into the result each time `separator` is
57 * backreferences are spliced into the result each time `separator` is
58 * matched. Fixes browser bugs compared to the native
58 * matched. Fixes browser bugs compared to the native
59 * `String.prototype.split` and can be used reliably cross-browser.
59 * `String.prototype.split` and can be used reliably cross-browser.
60 * @param {String} str String to split.
60 * @param {String} str String to split.
61 * @param {RegExp|String} separator Regex or string to use for separating
61 * @param {RegExp|String} separator Regex or string to use for separating
62 * the string.
62 * the string.
63 * @param {Number} [limit] Maximum number of items to include in the result
63 * @param {Number} [limit] Maximum number of items to include in the result
64 * array.
64 * array.
65 * @returns {Array} Array of substrings.
65 * @returns {Array} Array of substrings.
66 * @example
66 * @example
67 *
67 *
68 * // Basic use
68 * // Basic use
69 * regex_split('a b c d', ' ');
69 * regex_split('a b c d', ' ');
70 * // -> ['a', 'b', 'c', 'd']
70 * // -> ['a', 'b', 'c', 'd']
71 *
71 *
72 * // With limit
72 * // With limit
73 * regex_split('a b c d', ' ', 2);
73 * regex_split('a b c d', ' ', 2);
74 * // -> ['a', 'b']
74 * // -> ['a', 'b']
75 *
75 *
76 * // Backreferences in result array
76 * // Backreferences in result array
77 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
77 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
78 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
78 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
79 */
79 */
80 var regex_split = function (str, separator, limit) {
80 var regex_split = function (str, separator, limit) {
81 // If `separator` is not a regex, use `split`
81 // If `separator` is not a regex, use `split`
82 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
82 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
83 return split.call(str, separator, limit);
83 return split.call(str, separator, limit);
84 }
84 }
85 var output = [],
85 var output = [],
86 flags = (separator.ignoreCase ? "i" : "") +
86 flags = (separator.ignoreCase ? "i" : "") +
87 (separator.multiline ? "m" : "") +
87 (separator.multiline ? "m" : "") +
88 (separator.extended ? "x" : "") + // Proposed for ES6
88 (separator.extended ? "x" : "") + // Proposed for ES6
89 (separator.sticky ? "y" : ""), // Firefox 3+
89 (separator.sticky ? "y" : ""), // Firefox 3+
90 lastLastIndex = 0,
90 lastLastIndex = 0,
91 // Make `global` and avoid `lastIndex` issues by working with a copy
91 // Make `global` and avoid `lastIndex` issues by working with a copy
92 separator = new RegExp(separator.source, flags + "g"),
92 separator = new RegExp(separator.source, flags + "g"),
93 separator2, match, lastIndex, lastLength;
93 separator2, match, lastIndex, lastLength;
94 str += ""; // Type-convert
94 str += ""; // Type-convert
95
95
96 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
96 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
97 if (!compliantExecNpcg) {
97 if (!compliantExecNpcg) {
98 // Doesn't need flags gy, but they don't hurt
98 // Doesn't need flags gy, but they don't hurt
99 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
99 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
100 }
100 }
101 /* Values for `limit`, per the spec:
101 /* Values for `limit`, per the spec:
102 * If undefined: 4294967295 // Math.pow(2, 32) - 1
102 * If undefined: 4294967295 // Math.pow(2, 32) - 1
103 * If 0, Infinity, or NaN: 0
103 * If 0, Infinity, or NaN: 0
104 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
104 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
105 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
105 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
106 * If other: Type-convert, then use the above rules
106 * If other: Type-convert, then use the above rules
107 */
107 */
108 limit = typeof(limit) === "undefined" ?
108 limit = typeof(limit) === "undefined" ?
109 -1 >>> 0 : // Math.pow(2, 32) - 1
109 -1 >>> 0 : // Math.pow(2, 32) - 1
110 limit >>> 0; // ToUint32(limit)
110 limit >>> 0; // ToUint32(limit)
111 while (match = separator.exec(str)) {
111 while (match = separator.exec(str)) {
112 // `separator.lastIndex` is not reliable cross-browser
112 // `separator.lastIndex` is not reliable cross-browser
113 lastIndex = match.index + match[0].length;
113 lastIndex = match.index + match[0].length;
114 if (lastIndex > lastLastIndex) {
114 if (lastIndex > lastLastIndex) {
115 output.push(str.slice(lastLastIndex, match.index));
115 output.push(str.slice(lastLastIndex, match.index));
116 // Fix browsers whose `exec` methods don't consistently return `undefined` for
116 // Fix browsers whose `exec` methods don't consistently return `undefined` for
117 // nonparticipating capturing groups
117 // nonparticipating capturing groups
118 if (!compliantExecNpcg && match.length > 1) {
118 if (!compliantExecNpcg && match.length > 1) {
119 match[0].replace(separator2, function () {
119 match[0].replace(separator2, function () {
120 for (var i = 1; i < arguments.length - 2; i++) {
120 for (var i = 1; i < arguments.length - 2; i++) {
121 if (typeof(arguments[i]) === "undefined") {
121 if (typeof(arguments[i]) === "undefined") {
122 match[i] = undefined;
122 match[i] = undefined;
123 }
123 }
124 }
124 }
125 });
125 });
126 }
126 }
127 if (match.length > 1 && match.index < str.length) {
127 if (match.length > 1 && match.index < str.length) {
128 Array.prototype.push.apply(output, match.slice(1));
128 Array.prototype.push.apply(output, match.slice(1));
129 }
129 }
130 lastLength = match[0].length;
130 lastLength = match[0].length;
131 lastLastIndex = lastIndex;
131 lastLastIndex = lastIndex;
132 if (output.length >= limit) {
132 if (output.length >= limit) {
133 break;
133 break;
134 }
134 }
135 }
135 }
136 if (separator.lastIndex === match.index) {
136 if (separator.lastIndex === match.index) {
137 separator.lastIndex++; // Avoid an infinite loop
137 separator.lastIndex++; // Avoid an infinite loop
138 }
138 }
139 }
139 }
140 if (lastLastIndex === str.length) {
140 if (lastLastIndex === str.length) {
141 if (lastLength || !separator.test("")) {
141 if (lastLength || !separator.test("")) {
142 output.push("");
142 output.push("");
143 }
143 }
144 } else {
144 } else {
145 output.push(str.slice(lastLastIndex));
145 output.push(str.slice(lastLastIndex));
146 }
146 }
147 return output.length > limit ? output.slice(0, limit) : output;
147 return output.length > limit ? output.slice(0, limit) : output;
148 };
148 };
149
149
150 //============================================================================
150 //============================================================================
151 // End contributed Cross-browser RegEx Split
151 // End contributed Cross-browser RegEx Split
152 //============================================================================
152 //============================================================================
153
153
154
154
155 var uuid = function () {
155 var uuid = function () {
156 // http://www.ietf.org/rfc/rfc4122.txt
156 // http://www.ietf.org/rfc/rfc4122.txt
157 var s = [];
157 var s = [];
158 var hexDigits = "0123456789ABCDEF";
158 var hexDigits = "0123456789ABCDEF";
159 for (var i = 0; i < 32; i++) {
159 for (var i = 0; i < 32; i++) {
160 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
160 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
161 }
161 }
162 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
162 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
163 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
163 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
164
164
165 var uuid = s.join("");
165 var uuid = s.join("");
166 return uuid;
166 return uuid;
167 };
167 };
168
168
169
169
170 //Fix raw text to parse correctly in crazy XML
170 //Fix raw text to parse correctly in crazy XML
171 function xmlencode(string) {
171 function xmlencode(string) {
172 return string.replace(/\&/g,'&'+'amp;')
172 return string.replace(/\&/g,'&'+'amp;')
173 .replace(/</g,'&'+'lt;')
173 .replace(/</g,'&'+'lt;')
174 .replace(/>/g,'&'+'gt;')
174 .replace(/>/g,'&'+'gt;')
175 .replace(/\'/g,'&'+'apos;')
175 .replace(/\'/g,'&'+'apos;')
176 .replace(/\"/g,'&'+'quot;')
176 .replace(/\"/g,'&'+'quot;')
177 .replace(/`/g,'&'+'#96;');
177 .replace(/`/g,'&'+'#96;');
178 }
178 }
179
179
180
180
181 //Map from terminal commands to CSS classes
181 //Map from terminal commands to CSS classes
182 var ansi_colormap = {
182 var ansi_colormap = {
183 "01":"ansibold",
183 "01":"ansibold",
184
184
185 "30":"ansiblack",
185 "30":"ansiblack",
186 "31":"ansired",
186 "31":"ansired",
187 "32":"ansigreen",
187 "32":"ansigreen",
188 "33":"ansiyellow",
188 "33":"ansiyellow",
189 "34":"ansiblue",
189 "34":"ansiblue",
190 "35":"ansipurple",
190 "35":"ansipurple",
191 "36":"ansicyan",
191 "36":"ansicyan",
192 "37":"ansigray",
192 "37":"ansigray",
193
193
194 "40":"ansibgblack",
194 "40":"ansibgblack",
195 "41":"ansibgred",
195 "41":"ansibgred",
196 "42":"ansibggreen",
196 "42":"ansibggreen",
197 "43":"ansibgyellow",
197 "43":"ansibgyellow",
198 "44":"ansibgblue",
198 "44":"ansibgblue",
199 "45":"ansibgpurple",
199 "45":"ansibgpurple",
200 "46":"ansibgcyan",
200 "46":"ansibgcyan",
201 "47":"ansibggray"
201 "47":"ansibggray"
202 };
202 };
203
203
204 function _process_numbers(attrs, numbers) {
204 function _process_numbers(attrs, numbers) {
205 // process ansi escapes
205 // process ansi escapes
206 var n = numbers.shift();
206 var n = numbers.shift();
207 if (ansi_colormap[n]) {
207 if (ansi_colormap[n]) {
208 if ( ! attrs["class"] ) {
208 if ( ! attrs["class"] ) {
209 attrs["class"] = ansi_colormap[n];
209 attrs["class"] = ansi_colormap[n];
210 } else {
210 } else {
211 attrs["class"] += " " + ansi_colormap[n];
211 attrs["class"] += " " + ansi_colormap[n];
212 }
212 }
213 } else if (n == "38" || n == "48") {
213 } else if (n == "38" || n == "48") {
214 // VT100 256 color or 24 bit RGB
214 // VT100 256 color or 24 bit RGB
215 if (numbers.length < 2) {
215 if (numbers.length < 2) {
216 console.log("Not enough fields for VT100 color", numbers);
216 console.log("Not enough fields for VT100 color", numbers);
217 return;
217 return;
218 }
218 }
219
219
220 var index_or_rgb = numbers.shift();
220 var index_or_rgb = numbers.shift();
221 var r,g,b;
221 var r,g,b;
222 if (index_or_rgb == "5") {
222 if (index_or_rgb == "5") {
223 // 256 color
223 // 256 color
224 var idx = parseInt(numbers.shift());
224 var idx = parseInt(numbers.shift());
225 if (idx < 16) {
225 if (idx < 16) {
226 // indexed ANSI
226 // indexed ANSI
227 // ignore bright / non-bright distinction
227 // ignore bright / non-bright distinction
228 idx = idx % 8;
228 idx = idx % 8;
229 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
229 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
230 if ( ! attrs["class"] ) {
230 if ( ! attrs["class"] ) {
231 attrs["class"] = ansiclass;
231 attrs["class"] = ansiclass;
232 } else {
232 } else {
233 attrs["class"] += " " + ansiclass;
233 attrs["class"] += " " + ansiclass;
234 }
234 }
235 return;
235 return;
236 } else if (idx < 232) {
236 } else if (idx < 232) {
237 // 216 color 6x6x6 RGB
237 // 216 color 6x6x6 RGB
238 idx = idx - 16;
238 idx = idx - 16;
239 b = idx % 6;
239 b = idx % 6;
240 g = Math.floor(idx / 6) % 6;
240 g = Math.floor(idx / 6) % 6;
241 r = Math.floor(idx / 36) % 6;
241 r = Math.floor(idx / 36) % 6;
242 // convert to rgb
242 // convert to rgb
243 r = (r * 51);
243 r = (r * 51);
244 g = (g * 51);
244 g = (g * 51);
245 b = (b * 51);
245 b = (b * 51);
246 } else {
246 } else {
247 // grayscale
247 // grayscale
248 idx = idx - 231;
248 idx = idx - 231;
249 // it's 1-24 and should *not* include black or white,
249 // it's 1-24 and should *not* include black or white,
250 // so a 26 point scale
250 // so a 26 point scale
251 r = g = b = Math.floor(idx * 256 / 26);
251 r = g = b = Math.floor(idx * 256 / 26);
252 }
252 }
253 } else if (index_or_rgb == "2") {
253 } else if (index_or_rgb == "2") {
254 // Simple 24 bit RGB
254 // Simple 24 bit RGB
255 if (numbers.length > 3) {
255 if (numbers.length > 3) {
256 console.log("Not enough fields for RGB", numbers);
256 console.log("Not enough fields for RGB", numbers);
257 return;
257 return;
258 }
258 }
259 r = numbers.shift();
259 r = numbers.shift();
260 g = numbers.shift();
260 g = numbers.shift();
261 b = numbers.shift();
261 b = numbers.shift();
262 } else {
262 } else {
263 console.log("unrecognized control", numbers);
263 console.log("unrecognized control", numbers);
264 return;
264 return;
265 }
265 }
266 if (r !== undefined) {
266 if (r !== undefined) {
267 // apply the rgb color
267 // apply the rgb color
268 var line;
268 var line;
269 if (n == "38") {
269 if (n == "38") {
270 line = "color: ";
270 line = "color: ";
271 } else {
271 } else {
272 line = "background-color: ";
272 line = "background-color: ";
273 }
273 }
274 line = line + "rgb(" + r + "," + g + "," + b + ");"
274 line = line + "rgb(" + r + "," + g + "," + b + ");"
275 if ( !attrs["style"] ) {
275 if ( !attrs["style"] ) {
276 attrs["style"] = line;
276 attrs["style"] = line;
277 } else {
277 } else {
278 attrs["style"] += " " + line;
278 attrs["style"] += " " + line;
279 }
279 }
280 }
280 }
281 }
281 }
282 }
282 }
283
283
284 function ansispan(str) {
284 function ansispan(str) {
285 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
285 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
286 // regular ansi escapes (using the table above)
286 // regular ansi escapes (using the table above)
287 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
287 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
288 if (!pattern) {
288 if (!pattern) {
289 // [(01|22|39|)m close spans
289 // [(01|22|39|)m close spans
290 return "</span>";
290 return "</span>";
291 }
291 }
292 // consume sequence of color escapes
292 // consume sequence of color escapes
293 var numbers = pattern.match(/\d+/g);
293 var numbers = pattern.match(/\d+/g);
294 var attrs = {};
294 var attrs = {};
295 while (numbers.length > 0) {
295 while (numbers.length > 0) {
296 _process_numbers(attrs, numbers);
296 _process_numbers(attrs, numbers);
297 }
297 }
298
298
299 var span = "<span ";
299 var span = "<span ";
300 for (var attr in attrs) {
300 for (var attr in attrs) {
301 var value = attrs[attr];
301 var value = attrs[attr];
302 span = span + " " + attr + '="' + attrs[attr] + '"';
302 span = span + " " + attr + '="' + attrs[attr] + '"';
303 }
303 }
304 return span + ">";
304 return span + ">";
305 });
305 });
306 };
306 };
307
307
308 // Transform ANSI color escape codes into HTML <span> tags with css
308 // Transform ANSI color escape codes into HTML <span> tags with css
309 // classes listed in the above ansi_colormap object. The actual color used
309 // classes listed in the above ansi_colormap object. The actual color used
310 // are set in the css file.
310 // are set in the css file.
311 function fixConsole(txt) {
311 function fixConsole(txt) {
312 txt = xmlencode(txt);
312 txt = xmlencode(txt);
313 var re = /\033\[([\dA-Fa-f;]*?)m/;
313 var re = /\033\[([\dA-Fa-f;]*?)m/;
314 var opened = false;
314 var opened = false;
315 var cmds = [];
315 var cmds = [];
316 var opener = "";
316 var opener = "";
317 var closer = "";
317 var closer = "";
318
318
319 // Strip all ANSI codes that are not color related. Matches
319 // Strip all ANSI codes that are not color related. Matches
320 // all ANSI codes that do not end with "m".
320 // all ANSI codes that do not end with "m".
321 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
321 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
322 txt = txt.replace(ignored_re, "");
322 txt = txt.replace(ignored_re, "");
323
323
324 // color ansi codes
324 // color ansi codes
325 txt = ansispan(txt);
325 txt = ansispan(txt);
326 return txt;
326 return txt;
327 }
327 }
328
328
329 // Remove chunks that should be overridden by the effect of
329 // Remove chunks that should be overridden by the effect of
330 // carriage return characters
330 // carriage return characters
331 function fixCarriageReturn(txt) {
331 function fixCarriageReturn(txt) {
332 var tmp = txt;
332 var tmp = txt;
333 do {
333 do {
334 txt = tmp;
334 txt = tmp;
335 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
335 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
336 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
336 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
337 } while (tmp.length < txt.length);
337 } while (tmp.length < txt.length);
338 return txt;
338 return txt;
339 }
339 }
340
340
341 // Locate any URLs and convert them to a anchor tag
341 // Locate any URLs and convert them to a anchor tag
342 function autoLinkUrls(txt) {
342 function autoLinkUrls(txt) {
343 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
343 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
344 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
344 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
345 }
345 }
346
346
347 var points_to_pixels = function (points) {
347 var points_to_pixels = function (points) {
348 // A reasonably good way of converting between points and pixels.
348 // A reasonably good way of converting between points and pixels.
349 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
349 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
350 $(body).append(test);
350 $(body).append(test);
351 var pixel_per_point = test.width()/10000;
351 var pixel_per_point = test.width()/10000;
352 test.remove();
352 test.remove();
353 return Math.floor(points*pixel_per_point);
353 return Math.floor(points*pixel_per_point);
354 };
354 };
355
355
356 var always_new = function (constructor) {
356 var always_new = function (constructor) {
357 // wrapper around contructor to avoid requiring `var a = new constructor()`
357 // wrapper around contructor to avoid requiring `var a = new constructor()`
358 // useful for passing constructors as callbacks,
358 // useful for passing constructors as callbacks,
359 // not for programmer laziness.
359 // not for programmer laziness.
360 // from http://programmers.stackexchange.com/questions/118798
360 // from http://programmers.stackexchange.com/questions/118798
361 return function () {
361 return function () {
362 var obj = Object.create(constructor.prototype);
362 var obj = Object.create(constructor.prototype);
363 constructor.apply(obj, arguments);
363 constructor.apply(obj, arguments);
364 return obj;
364 return obj;
365 };
365 };
366 };
366 };
367
367
368 var url_path_join = function () {
368 var url_path_join = function () {
369 // join a sequence of url components with '/'
369 // join a sequence of url components with '/'
370 var url = '';
370 var url = '';
371 for (var i = 0; i < arguments.length; i++) {
371 for (var i = 0; i < arguments.length; i++) {
372 if (arguments[i] === '') {
372 if (arguments[i] === '') {
373 continue;
373 continue;
374 }
374 }
375 if (url.length > 0 && url[url.length-1] != '/') {
375 if (url.length > 0 && url[url.length-1] != '/') {
376 url = url + '/' + arguments[i];
376 url = url + '/' + arguments[i];
377 } else {
377 } else {
378 url = url + arguments[i];
378 url = url + arguments[i];
379 }
379 }
380 }
380 }
381 url = url.replace(/\/\/+/, '/');
381 url = url.replace(/\/\/+/, '/');
382 return url;
382 return url;
383 };
383 };
384
384
385 var parse_url = function (url) {
385 var parse_url = function (url) {
386 // an `a` element with an href allows attr-access to the parsed segments of a URL
386 // an `a` element with an href allows attr-access to the parsed segments of a URL
387 // a = parse_url("http://localhost:8888/path/name#hash")
387 // a = parse_url("http://localhost:8888/path/name#hash")
388 // a.protocol = "http:"
388 // a.protocol = "http:"
389 // a.host = "localhost:8888"
389 // a.host = "localhost:8888"
390 // a.hostname = "localhost"
390 // a.hostname = "localhost"
391 // a.port = 8888
391 // a.port = 8888
392 // a.pathname = "/path/name"
392 // a.pathname = "/path/name"
393 // a.hash = "#hash"
393 // a.hash = "#hash"
394 var a = document.createElement("a");
394 var a = document.createElement("a");
395 a.href = url;
395 a.href = url;
396 return a;
396 return a;
397 };
397 };
398
398
399 var encode_uri_components = function (uri) {
399 var encode_uri_components = function (uri) {
400 // encode just the components of a multi-segment uri,
400 // encode just the components of a multi-segment uri,
401 // leaving '/' separators
401 // leaving '/' separators
402 return uri.split('/').map(encodeURIComponent).join('/');
402 return uri.split('/').map(encodeURIComponent).join('/');
403 };
403 };
404
404
405 var url_join_encode = function () {
405 var url_join_encode = function () {
406 // join a sequence of url components with '/',
406 // join a sequence of url components with '/',
407 // encoding each component with encodeURIComponent
407 // encoding each component with encodeURIComponent
408 return encode_uri_components(url_path_join.apply(null, arguments));
408 return encode_uri_components(url_path_join.apply(null, arguments));
409 };
409 };
410
410
411
411
412 var splitext = function (filename) {
412 var splitext = function (filename) {
413 // mimic Python os.path.splitext
413 // mimic Python os.path.splitext
414 // Returns ['base', '.ext']
414 // Returns ['base', '.ext']
415 var idx = filename.lastIndexOf('.');
415 var idx = filename.lastIndexOf('.');
416 if (idx > 0) {
416 if (idx > 0) {
417 return [filename.slice(0, idx), filename.slice(idx)];
417 return [filename.slice(0, idx), filename.slice(idx)];
418 } else {
418 } else {
419 return [filename, ''];
419 return [filename, ''];
420 }
420 }
421 };
421 };
422
422
423
423
424 var escape_html = function (text) {
424 var escape_html = function (text) {
425 // escape text to HTML
425 // escape text to HTML
426 return $("<div/>").text(text).html();
426 return $("<div/>").text(text).html();
427 };
427 };
428
428
429
429
430 var get_body_data = function(key) {
430 var get_body_data = function(key) {
431 // get a url-encoded item from body.data and decode it
431 // get a url-encoded item from body.data and decode it
432 // we should never have any encoded URLs anywhere else in code
432 // we should never have any encoded URLs anywhere else in code
433 // until we are building an actual request
433 // until we are building an actual request
434 return decodeURIComponent($('body').data(key));
434 return decodeURIComponent($('body').data(key));
435 };
435 };
436
436
437 var to_absolute_cursor_pos = function (cm, cursor) {
437 var to_absolute_cursor_pos = function (cm, cursor) {
438 // get the absolute cursor position from CodeMirror's col, ch
438 // get the absolute cursor position from CodeMirror's col, ch
439 if (!cursor) {
439 if (!cursor) {
440 cursor = cm.getCursor();
440 cursor = cm.getCursor();
441 }
441 }
442 var cursor_pos = cursor.ch;
442 var cursor_pos = cursor.ch;
443 for (var i = 0; i < cursor.line; i++) {
443 for (var i = 0; i < cursor.line; i++) {
444 cursor_pos += cm.getLine(i).length + 1;
444 cursor_pos += cm.getLine(i).length + 1;
445 }
445 }
446 return cursor_pos;
446 return cursor_pos;
447 };
447 };
448
448
449 var from_absolute_cursor_pos = function (cm, cursor_pos) {
449 var from_absolute_cursor_pos = function (cm, cursor_pos) {
450 // turn absolute cursor postion into CodeMirror col, ch cursor
450 // turn absolute cursor postion into CodeMirror col, ch cursor
451 var i, line;
451 var i, line;
452 var offset = 0;
452 var offset = 0;
453 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
453 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
454 if (offset + line.length < cursor_pos) {
454 if (offset + line.length < cursor_pos) {
455 offset += line.length + 1;
455 offset += line.length + 1;
456 } else {
456 } else {
457 return {
457 return {
458 line : i,
458 line : i,
459 ch : cursor_pos - offset,
459 ch : cursor_pos - offset,
460 };
460 };
461 }
461 }
462 }
462 }
463 // reached end, return endpoint
463 // reached end, return endpoint
464 return {
464 return {
465 ch : line.length - 1,
465 ch : line.length - 1,
466 line : i - 1,
466 line : i - 1,
467 };
467 };
468 };
468 };
469
469
470 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
470 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
471 var browser = (function() {
471 var browser = (function() {
472 if (typeof navigator === 'undefined') {
472 if (typeof navigator === 'undefined') {
473 // navigator undefined in node
473 // navigator undefined in node
474 return 'None';
474 return 'None';
475 }
475 }
476 var N= navigator.appName, ua= navigator.userAgent, tem;
476 var N= navigator.appName, ua= navigator.userAgent, tem;
477 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
477 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
478 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
478 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
479 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
479 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
480 return M;
480 return M;
481 })();
481 })();
482
482
483 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
483 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
484 var platform = (function () {
484 var platform = (function () {
485 if (typeof navigator === 'undefined') {
485 if (typeof navigator === 'undefined') {
486 // navigator undefined in node
486 // navigator undefined in node
487 return 'None';
487 return 'None';
488 }
488 }
489 var OSName="None";
489 var OSName="None";
490 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
490 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
491 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
491 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
492 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
492 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
493 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
493 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
494 return OSName;
494 return OSName;
495 })();
495 })();
496
496
497 var is_or_has = function (a, b) {
497 var is_or_has = function (a, b) {
498 // Is b a child of a or a itself?
498 // Is b a child of a or a itself?
499 return a.has(b).length !==0 || a.is(b);
499 return a.has(b).length !==0 || a.is(b);
500 };
500 };
501
501
502 var is_focused = function (e) {
502 var is_focused = function (e) {
503 // Is element e, or one of its children focused?
503 // Is element e, or one of its children focused?
504 e = $(e);
504 e = $(e);
505 var target = $(document.activeElement);
505 var target = $(document.activeElement);
506 if (target.length > 0) {
506 if (target.length > 0) {
507 if (is_or_has(e, target)) {
507 if (is_or_has(e, target)) {
508 return true;
508 return true;
509 } else {
509 } else {
510 return false;
510 return false;
511 }
511 }
512 } else {
512 } else {
513 return false;
513 return false;
514 }
514 }
515 };
515 };
516
516
517 var log_ajax_error = function (jqXHR, status, error) {
517 var log_ajax_error = function (jqXHR, status, error) {
518 // log ajax failures with informative messages
518 // log ajax failures with informative messages
519 var msg = "API request failed (" + jqXHR.status + "): ";
519 var msg = "API request failed (" + jqXHR.status + "): ";
520 console.log(jqXHR);
520 console.log(jqXHR);
521 if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
521 if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
522 msg += jqXHR.responseJSON.message;
522 msg += jqXHR.responseJSON.message;
523 } else {
523 } else {
524 msg += jqXHR.statusText;
524 msg += jqXHR.statusText;
525 }
525 }
526 console.log(msg);
526 console.log(msg);
527 };
527 };
528
528
529 var Utils = {
529 var utils = {
530 regex_split : regex_split,
530 regex_split : regex_split,
531 uuid : uuid,
531 uuid : uuid,
532 fixConsole : fixConsole,
532 fixConsole : fixConsole,
533 fixCarriageReturn : fixCarriageReturn,
533 fixCarriageReturn : fixCarriageReturn,
534 autoLinkUrls : autoLinkUrls,
534 autoLinkUrls : autoLinkUrls,
535 points_to_pixels : points_to_pixels,
535 points_to_pixels : points_to_pixels,
536 get_body_data : get_body_data,
536 get_body_data : get_body_data,
537 parse_url : parse_url,
537 parse_url : parse_url,
538 url_path_join : url_path_join,
538 url_path_join : url_path_join,
539 url_join_encode : url_join_encode,
539 url_join_encode : url_join_encode,
540 encode_uri_components : encode_uri_components,
540 encode_uri_components : encode_uri_components,
541 splitext : splitext,
541 splitext : splitext,
542 escape_html : escape_html,
542 escape_html : escape_html,
543 always_new : always_new,
543 always_new : always_new,
544 to_absolute_cursor_pos : to_absolute_cursor_pos,
544 to_absolute_cursor_pos : to_absolute_cursor_pos,
545 from_absolute_cursor_pos : from_absolute_cursor_pos,
545 from_absolute_cursor_pos : from_absolute_cursor_pos,
546 browser : browser,
546 browser : browser,
547 platform: platform,
547 platform: platform,
548 is_or_has : is_or_has,
548 is_or_has : is_or_has,
549 is_focused : is_focused,
549 is_focused : is_focused,
550 log_ajax_error : log_ajax_error,
550 log_ajax_error : log_ajax_error,
551 };
551 };
552
552
553 // Backwards compatability.
553 // Backwards compatability.
554 IPython.Utils = Utils;
554 IPython.Utils = utils;
555
555
556 return Utils;
556 return utils;
557 });
557 });
@@ -1,111 +1,109 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 var ipython = ipython || {};
4 var ipython = ipython || {};
5 require([
5 require([
6 'base/js/namespace',
6 'base/js/namespace',
7 'jquery',
7 'jquery',
8 'notebook/js/notebook',
8 'notebook/js/notebook',
9 'base/js/utils',
9 'base/js/utils',
10 'base/js/page',
10 'base/js/page',
11 'notebook/js/layoutmanager',
11 'notebook/js/layoutmanager',
12 'base/js/events',
12 'base/js/events',
13 'auth/js/loginwidget',
13 'auth/js/loginwidget',
14 'notebook/js/maintoolbar',
14 'notebook/js/maintoolbar',
15 'notebook/js/pager',
15 'notebook/js/pager',
16 'notebook/js/quickhelp',
16 'notebook/js/quickhelp',
17 'notebook/js/menubar',
17 'notebook/js/menubar',
18 'notebook/js/notificationarea',
18 'notebook/js/notificationarea',
19 'notebook/js/savewidget',
19 'notebook/js/savewidget',
20 'notebook/js/keyboardmanager',
20 'notebook/js/keyboardmanager',
21 'notebook/js/config',
21 'notebook/js/config',
22 ], function(
22 ], function(
23 IPython,
23 IPython,
24 $,
24 $,
25 notebook,
25 notebook,
26 utils,
26 utils,
27 page,
27 page,
28 layoutmanager,
28 layoutmanager,
29 events,
29 events,
30 loginwidget,
30 loginwidget,
31 maintoolbar,
31 maintoolbar,
32 pager,
32 pager,
33 quickhelp,
33 quickhelp,
34 menubar,
34 menubar,
35 notificationarea,
35 notificationarea,
36 savewidget,
36 savewidget,
37 keyboardmanager,
37 keyboardmanager,
38 config
38 config
39 ) {
39 ) {
40 "use strict";
40 "use strict";
41
41
42 $('#ipython-main-app').addClass('border-box-sizing');
42 $('#ipython-main-app').addClass('border-box-sizing');
43 $('div#notebook_panel').addClass('border-box-sizing');
43 $('div#notebook_panel').addClass('border-box-sizing');
44
44
45 var options = {
45 var options = {
46 base_url : utils.get_body_data("baseUrl"),
46 base_url : utils.get_body_data("baseUrl"),
47 notebook_path : utils.get_body_data("notebookPath"),
47 notebook_path : utils.get_body_data("notebookPath"),
48 notebook_name : utils.get_body_data('notebookName')
48 notebook_name : utils.get_body_data('notebookName')
49 };
49 };
50
50
51 var user_config = $.extend({}, config.default_config);
51 var user_config = $.extend({}, config.default_config);
52 var page = new page.Page();
52 var page = new page.Page();
53 var layout_manager = new layoutmanager.LayoutManager();
53 var layout_manager = new layoutmanager.LayoutManager();
54 var events = $([new events.Events()]);
54 var events = $([new events.Events()]);
55 var pager = new pager.Pager('div#pager', 'div#pager_splitter', layout_manager, events);
55 var pager = new pager.Pager('div#pager', 'div#pager_splitter', layout_manager, events);
56 var keyboard_manager = new keyboardmanager.KeyboardManager(pager, events);
56 var keyboard_manager = new keyboardmanager.KeyboardManager(pager, events);
57 var save_widget = new savewidget.SaveWidget('span#save_widget', events);
57 var save_widget = new savewidget.SaveWidget('span#save_widget', events);
58 var notebook = new notebook.Notebook('div#notebook', options, events, keyboard_manager, save_widget, user_config);
58 var notebook = new notebook.Notebook('div#notebook', options, events, keyboard_manager, save_widget, user_config);
59 var login_widget = new loginwidget.LoginWidget('span#login_widget', options);
59 var login_widget = new loginwidget.LoginWidget('span#login_widget', options);
60 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', layout_manager, notebook, events);
60 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', layout_manager, notebook, events);
61 var quick_help = new quickhelp.QuickHelp(undefined, keyboard_manager, events);
61 var quick_help = new quickhelp.QuickHelp(undefined, keyboard_manager, events);
62 var menubar = new menubar.MenuBar('#menubar', options, notebook, layout_manager, events, save_widget, quick_help);
62 var menubar = new menubar.MenuBar('#menubar', options, notebook, layout_manager, events, save_widget, quick_help);
63 var notification_area = new notificationarea.NotificationArea('#notification_area', events, save_widget, notebook);
63 var notification_area = new notificationarea.NotificationArea('#notification_area', events, save_widget, notebook);
64 notification_area.init_notification_widgets();
64 notification_area.init_notification_widgets();
65
65
66 layout_manager.do_resize();
67
68 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
66 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
69 '<span id="test2" style="font-weight: bold;">x</span>'+
67 '<span id="test2" style="font-weight: bold;">x</span>'+
70 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
68 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
71 var nh = $('#test1').innerHeight();
69 var nh = $('#test1').innerHeight();
72 var bh = $('#test2').innerHeight();
70 var bh = $('#test2').innerHeight();
73 var ih = $('#test3').innerHeight();
71 var ih = $('#test3').innerHeight();
74 if(nh != bh || nh != ih) {
72 if(nh != bh || nh != ih) {
75 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
73 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
76 }
74 }
77 $('#fonttest').remove();
75 $('#fonttest').remove();
78
76
79 page.show();
77 page.show();
80
78
81 layout_manager.do_resize();
79 layout_manager.do_resize();
82 var first_load = function () {
80 var first_load = function () {
83 layout_manager.do_resize();
81 layout_manager.do_resize();
84 var hash = document.location.hash;
82 var hash = document.location.hash;
85 if (hash) {
83 if (hash) {
86 document.location.hash = '';
84 document.location.hash = '';
87 document.location.hash = hash;
85 document.location.hash = hash;
88 }
86 }
89 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
87 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
90 // only do this once
88 // only do this once
91 events.off('notebook_loaded.Notebook', first_load);
89 events.off('notebook_loaded.Notebook', first_load);
92 };
90 };
93
91
94 events.on('notebook_loaded.Notebook', first_load);
92 events.on('notebook_loaded.Notebook', first_load);
95 events.trigger('app_initialized.NotebookApp');
93 events.trigger('app_initialized.NotebookApp');
96 notebook.load_notebook(options.notebook_name, options.notebook_path);
94 notebook.load_notebook(options.notebook_name, options.notebook_path);
97
95
98 ipython.page = page;
96 ipython.page = page;
99 ipython.layout_manager = layout_manager;
97 ipython.layout_manager = layout_manager;
100 ipython.notebook = notebook;
98 ipython.notebook = notebook;
101 ipython.pager = pager;
99 ipython.pager = pager;
102 ipython.quick_help = quick_help;
100 ipython.quick_help = quick_help;
103 ipython.login_widget = login_widget;
101 ipython.login_widget = login_widget;
104 ipython.menubar = menubar;
102 ipython.menubar = menubar;
105 ipython.toolbar = toolbar;
103 ipython.toolbar = toolbar;
106 ipython.notification_area = notification_area;
104 ipython.notification_area = notification_area;
107 ipython.events = events;
105 ipython.events = events;
108 ipython.keyboard_manager = keyboard_manager;
106 ipython.keyboard_manager = keyboard_manager;
109 ipython.save_widget = save_widget;
107 ipython.save_widget = save_widget;
110 ipython.config = user_config;
108 ipython.config = user_config;
111 });
109 });
@@ -1,2481 +1,2481 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 'base/js/dialog',
8 'base/js/dialog',
9 'notebook/js/textcell',
9 'notebook/js/textcell',
10 'notebook/js/codecell',
10 'notebook/js/codecell',
11 'services/sessions/js/session',
11 'services/sessions/js/session',
12 'notebook/js/celltoolbar',
12 'notebook/js/celltoolbar',
13 'components/marked/lib/marked',
13 'components/marked/lib/marked',
14 'notebook/js/mathjaxutils',
14 'notebook/js/mathjaxutils',
15 'base/js/keyboard',
15 'base/js/keyboard',
16 'components/jquery-ui/ui/minified/jquery-ui.min',
16 'components/jquery-ui/ui/minified/jquery-ui.min',
17 'components/bootstrap/js/bootstrap.min',
17 'components/bootstrap/js/bootstrap.min',
18 ], function (
18 ], function (
19 IPython,
19 IPython,
20 $,
20 $,
21 utils,
21 utils,
22 dialog,
22 dialog,
23 cells,
23 cells,
24 codecell,
24 codecell,
25 session,
25 session,
26 celltoolbar,
26 celltoolbar,
27 marked,
27 marked,
28 mathjaxutils,
28 mathjaxutils,
29 keyboard
29 keyboard
30 ) {
30 ) {
31
31
32 /**
32 /**
33 * A notebook contains and manages cells.
33 * A notebook contains and manages cells.
34 *
34 *
35 * @class Notebook
35 * @class Notebook
36 * @constructor
36 * @constructor
37 * @param {String} selector A jQuery selector for the notebook's DOM element
37 * @param {String} selector A jQuery selector for the notebook's DOM element
38 * @param {Object} [options] A config object
38 * @param {Object} [options] A config object
39 * @param {Object} [events] An events object
39 * @param {Object} [events] An events object
40 */
40 */
41 var Notebook = function (selector, options, events, keyboard_manager, save_widget, config) {
41 var Notebook = function (selector, options, events, keyboard_manager, save_widget, config) {
42 this.config = config;
42 this.config = config;
43 this.events = events;
43 this.events = events;
44 this.keyboard_manager = keyboard_manager;
44 this.keyboard_manager = keyboard_manager;
45 // TODO: This code smells (and the other `= this` line a couple lines down)
46 // We need a better way to deal with circular instance references.
45 keyboard_manager.notebook = this;
47 keyboard_manager.notebook = this;
46 this.save_widget = save_widget;
48 this.save_widget = save_widget;
47 save_widget.notebook = this;
49 save_widget.notebook = this;
48
50
49 mathjaxutils.init();
51 mathjaxutils.init();
50
52
51
52 window.marked = window.marked || marked;
53 if (marked) {
53 if (marked) {
54 marked.setOptions({
54 marked.setOptions({
55 gfm : true,
55 gfm : true,
56 tables: true,
56 tables: true,
57 langPrefix: "language-",
57 langPrefix: "language-",
58 highlight: function(code, lang) {
58 highlight: function(code, lang) {
59 if (!lang) {
59 if (!lang) {
60 // no language, no highlight
60 // no language, no highlight
61 return code;
61 return code;
62 }
62 }
63 var highlighted;
63 var highlighted;
64 try {
64 try {
65 highlighted = hljs.highlight(lang, code, false);
65 highlighted = hljs.highlight(lang, code, false);
66 } catch(err) {
66 } catch(err) {
67 highlighted = hljs.highlightAuto(code);
67 highlighted = hljs.highlightAuto(code);
68 }
68 }
69 return highlighted.value;
69 return highlighted.value;
70 }
70 }
71 });
71 });
72 }
72 }
73
73
74 // Backwards compatability.
74 // Backwards compatability.
75 IPython.keyboard_manager = this.keyboard_manager;
75 IPython.keyboard_manager = this.keyboard_manager;
76 IPython.save_widget = this.save_widget;
76 IPython.save_widget = this.save_widget;
77 IPython.keyboard = this.keyboard;
77 IPython.keyboard = this.keyboard;
78
78
79 this.options = options = options || {};
79 this.options = options = options || {};
80 this.base_url = options.base_url;
80 this.base_url = options.base_url;
81 this.notebook_path = options.notebook_path;
81 this.notebook_path = options.notebook_path;
82 this.notebook_name = options.notebook_name;
82 this.notebook_name = options.notebook_name;
83 this.element = $(selector);
83 this.element = $(selector);
84 this.element.scroll();
84 this.element.scroll();
85 this.element.data("notebook", this);
85 this.element.data("notebook", this);
86 this.next_prompt_number = 1;
86 this.next_prompt_number = 1;
87 this.session = null;
87 this.session = null;
88 this.kernel = null;
88 this.kernel = null;
89 this.clipboard = null;
89 this.clipboard = null;
90 this.undelete_backup = null;
90 this.undelete_backup = null;
91 this.undelete_index = null;
91 this.undelete_index = null;
92 this.undelete_below = false;
92 this.undelete_below = false;
93 this.paste_enabled = false;
93 this.paste_enabled = false;
94 // It is important to start out in command mode to match the intial mode
94 // It is important to start out in command mode to match the intial mode
95 // of the KeyboardManager.
95 // of the KeyboardManager.
96 this.mode = 'command';
96 this.mode = 'command';
97 this.set_dirty(false);
97 this.set_dirty(false);
98 this.metadata = {};
98 this.metadata = {};
99 this._checkpoint_after_save = false;
99 this._checkpoint_after_save = false;
100 this.last_checkpoint = null;
100 this.last_checkpoint = null;
101 this.checkpoints = [];
101 this.checkpoints = [];
102 this.autosave_interval = 0;
102 this.autosave_interval = 0;
103 this.autosave_timer = null;
103 this.autosave_timer = null;
104 // autosave *at most* every two minutes
104 // autosave *at most* every two minutes
105 this.minimum_autosave_interval = 120000;
105 this.minimum_autosave_interval = 120000;
106 // single worksheet for now
106 // single worksheet for now
107 this.worksheet_metadata = {};
107 this.worksheet_metadata = {};
108 this.notebook_name_blacklist_re = /[\/\\:]/;
108 this.notebook_name_blacklist_re = /[\/\\:]/;
109 this.nbformat = 3; // Increment this when changing the nbformat
109 this.nbformat = 3; // Increment this when changing the nbformat
110 this.nbformat_minor = 0; // Increment this when changing the nbformat
110 this.nbformat_minor = 0; // Increment this when changing the nbformat
111 this.style();
111 this.style();
112 this.create_elements();
112 this.create_elements();
113 this.bind_events();
113 this.bind_events();
114 this.save_notebook = function() { // don't allow save until notebook_loaded
114 this.save_notebook = function() { // don't allow save until notebook_loaded
115 this.save_notebook_error(null, null, "Load failed, save is disabled");
115 this.save_notebook_error(null, null, "Load failed, save is disabled");
116 };
116 };
117 };
117 };
118
118
119 /**
119 /**
120 * Tweak the notebook's CSS style.
120 * Tweak the notebook's CSS style.
121 *
121 *
122 * @method style
122 * @method style
123 */
123 */
124 Notebook.prototype.style = function () {
124 Notebook.prototype.style = function () {
125 $('div#notebook').addClass('border-box-sizing');
125 $('div#notebook').addClass('border-box-sizing');
126 };
126 };
127
127
128 /**
128 /**
129 * Create an HTML and CSS representation of the notebook.
129 * Create an HTML and CSS representation of the notebook.
130 *
130 *
131 * @method create_elements
131 * @method create_elements
132 */
132 */
133 Notebook.prototype.create_elements = function () {
133 Notebook.prototype.create_elements = function () {
134 var that = this;
134 var that = this;
135 this.element.attr('tabindex','-1');
135 this.element.attr('tabindex','-1');
136 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
136 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
137 // We add this end_space div to the end of the notebook div to:
137 // We add this end_space div to the end of the notebook div to:
138 // i) provide a margin between the last cell and the end of the notebook
138 // i) provide a margin between the last cell and the end of the notebook
139 // ii) to prevent the div from scrolling up when the last cell is being
139 // ii) to prevent the div from scrolling up when the last cell is being
140 // edited, but is too low on the page, which browsers will do automatically.
140 // edited, but is too low on the page, which browsers will do automatically.
141 var end_space = $('<div/>').addClass('end_space');
141 var end_space = $('<div/>').addClass('end_space');
142 end_space.dblclick(function (e) {
142 end_space.dblclick(function (e) {
143 var ncells = that.ncells();
143 var ncells = that.ncells();
144 that.insert_cell_below('code',ncells-1);
144 that.insert_cell_below('code',ncells-1);
145 });
145 });
146 this.element.append(this.container);
146 this.element.append(this.container);
147 this.container.append(end_space);
147 this.container.append(end_space);
148 };
148 };
149
149
150 /**
150 /**
151 * Bind JavaScript events: key presses and custom IPython events.
151 * Bind JavaScript events: key presses and custom IPython events.
152 *
152 *
153 * @method bind_events
153 * @method bind_events
154 */
154 */
155 Notebook.prototype.bind_events = function () {
155 Notebook.prototype.bind_events = function () {
156 var that = this;
156 var that = this;
157
157
158 this.events.on('set_next_input.Notebook', function (event, data) {
158 this.events.on('set_next_input.Notebook', function (event, data) {
159 var index = that.find_cell_index(data.cell);
159 var index = that.find_cell_index(data.cell);
160 var new_cell = that.insert_cell_below('code',index);
160 var new_cell = that.insert_cell_below('code',index);
161 new_cell.set_text(data.text);
161 new_cell.set_text(data.text);
162 that.dirty = true;
162 that.dirty = true;
163 });
163 });
164
164
165 this.events.on('set_dirty.Notebook', function (event, data) {
165 this.events.on('set_dirty.Notebook', function (event, data) {
166 that.dirty = data.value;
166 that.dirty = data.value;
167 });
167 });
168
168
169 this.events.on('trust_changed.Notebook', function (event, data) {
169 this.events.on('trust_changed.Notebook', function (event, data) {
170 that.trusted = data.value;
170 that.trusted = data.value;
171 });
171 });
172
172
173 this.events.on('select.Cell', function (event, data) {
173 this.events.on('select.Cell', function (event, data) {
174 var index = that.find_cell_index(data.cell);
174 var index = that.find_cell_index(data.cell);
175 that.select(index);
175 that.select(index);
176 });
176 });
177
177
178 this.events.on('edit_mode.Cell', function (event, data) {
178 this.events.on('edit_mode.Cell', function (event, data) {
179 that.handle_edit_mode(data.cell);
179 that.handle_edit_mode(data.cell);
180 });
180 });
181
181
182 this.events.on('command_mode.Cell', function (event, data) {
182 this.events.on('command_mode.Cell', function (event, data) {
183 that.handle_command_mode(data.cell);
183 that.handle_command_mode(data.cell);
184 });
184 });
185
185
186 this.events.on('status_autorestarting.Kernel', function () {
186 this.events.on('status_autorestarting.Kernel', function () {
187 dialog.modal({
187 dialog.modal({
188 title: "Kernel Restarting",
188 title: "Kernel Restarting",
189 body: "The kernel appears to have died. It will restart automatically.",
189 body: "The kernel appears to have died. It will restart automatically.",
190 buttons: {
190 buttons: {
191 OK : {
191 OK : {
192 class : "btn-primary"
192 class : "btn-primary"
193 }
193 }
194 }
194 }
195 });
195 });
196 });
196 });
197
197
198 var collapse_time = function (time) {
198 var collapse_time = function (time) {
199 var app_height = $('#ipython-main-app').height(); // content height
199 var app_height = $('#ipython-main-app').height(); // content height
200 var splitter_height = $('div#pager_splitter').outerHeight(true);
200 var splitter_height = $('div#pager_splitter').outerHeight(true);
201 var new_height = app_height - splitter_height;
201 var new_height = app_height - splitter_height;
202 that.element.animate({height : new_height + 'px'}, time);
202 that.element.animate({height : new_height + 'px'}, time);
203 };
203 };
204
204
205 this.element.bind('collapse_pager', function (event, extrap) {
205 this.element.bind('collapse_pager', function (event, extrap) {
206 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
206 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
207 collapse_time(time);
207 collapse_time(time);
208 });
208 });
209
209
210 var expand_time = function (time) {
210 var expand_time = function (time) {
211 var app_height = $('#ipython-main-app').height(); // content height
211 var app_height = $('#ipython-main-app').height(); // content height
212 var splitter_height = $('div#pager_splitter').outerHeight(true);
212 var splitter_height = $('div#pager_splitter').outerHeight(true);
213 var pager_height = $('div#pager').outerHeight(true);
213 var pager_height = $('div#pager').outerHeight(true);
214 var new_height = app_height - pager_height - splitter_height;
214 var new_height = app_height - pager_height - splitter_height;
215 that.element.animate({height : new_height + 'px'}, time);
215 that.element.animate({height : new_height + 'px'}, time);
216 };
216 };
217
217
218 this.element.bind('expand_pager', function (event, extrap) {
218 this.element.bind('expand_pager', function (event, extrap) {
219 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
219 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
220 expand_time(time);
220 expand_time(time);
221 });
221 });
222
222
223 // Firefox 22 broke $(window).on("beforeunload")
223 // Firefox 22 broke $(window).on("beforeunload")
224 // I'm not sure why or how.
224 // I'm not sure why or how.
225 window.onbeforeunload = function (e) {
225 window.onbeforeunload = function (e) {
226 // TODO: Make killing the kernel configurable.
226 // TODO: Make killing the kernel configurable.
227 var kill_kernel = false;
227 var kill_kernel = false;
228 if (kill_kernel) {
228 if (kill_kernel) {
229 that.session.kill_kernel();
229 that.session.kill_kernel();
230 }
230 }
231 // if we are autosaving, trigger an autosave on nav-away.
231 // if we are autosaving, trigger an autosave on nav-away.
232 // still warn, because if we don't the autosave may fail.
232 // still warn, because if we don't the autosave may fail.
233 if (that.dirty) {
233 if (that.dirty) {
234 if ( that.autosave_interval ) {
234 if ( that.autosave_interval ) {
235 // schedule autosave in a timeout
235 // schedule autosave in a timeout
236 // this gives you a chance to forcefully discard changes
236 // this gives you a chance to forcefully discard changes
237 // by reloading the page if you *really* want to.
237 // by reloading the page if you *really* want to.
238 // the timer doesn't start until you *dismiss* the dialog.
238 // the timer doesn't start until you *dismiss* the dialog.
239 setTimeout(function () {
239 setTimeout(function () {
240 if (that.dirty) {
240 if (that.dirty) {
241 that.save_notebook();
241 that.save_notebook();
242 }
242 }
243 }, 1000);
243 }, 1000);
244 return "Autosave in progress, latest changes may be lost.";
244 return "Autosave in progress, latest changes may be lost.";
245 } else {
245 } else {
246 return "Unsaved changes will be lost.";
246 return "Unsaved changes will be lost.";
247 }
247 }
248 }
248 }
249 // Null is the *only* return value that will make the browser not
249 // Null is the *only* return value that will make the browser not
250 // pop up the "don't leave" dialog.
250 // pop up the "don't leave" dialog.
251 return null;
251 return null;
252 };
252 };
253 };
253 };
254
254
255 /**
255 /**
256 * Set the dirty flag, and trigger the set_dirty.Notebook event
256 * Set the dirty flag, and trigger the set_dirty.Notebook event
257 *
257 *
258 * @method set_dirty
258 * @method set_dirty
259 */
259 */
260 Notebook.prototype.set_dirty = function (value) {
260 Notebook.prototype.set_dirty = function (value) {
261 if (value === undefined) {
261 if (value === undefined) {
262 value = true;
262 value = true;
263 }
263 }
264 if (this.dirty == value) {
264 if (this.dirty == value) {
265 return;
265 return;
266 }
266 }
267 this.events.trigger('set_dirty.Notebook', {value: value});
267 this.events.trigger('set_dirty.Notebook', {value: value});
268 };
268 };
269
269
270 /**
270 /**
271 * Scroll the top of the page to a given cell.
271 * Scroll the top of the page to a given cell.
272 *
272 *
273 * @method scroll_to_cell
273 * @method scroll_to_cell
274 * @param {Number} cell_number An index of the cell to view
274 * @param {Number} cell_number An index of the cell to view
275 * @param {Number} time Animation time in milliseconds
275 * @param {Number} time Animation time in milliseconds
276 * @return {Number} Pixel offset from the top of the container
276 * @return {Number} Pixel offset from the top of the container
277 */
277 */
278 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
278 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
279 var cells = this.get_cells();
279 var cells = this.get_cells();
280 time = time || 0;
280 time = time || 0;
281 cell_number = Math.min(cells.length-1,cell_number);
281 cell_number = Math.min(cells.length-1,cell_number);
282 cell_number = Math.max(0 ,cell_number);
282 cell_number = Math.max(0 ,cell_number);
283 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
283 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
284 this.element.animate({scrollTop:scroll_value}, time);
284 this.element.animate({scrollTop:scroll_value}, time);
285 return scroll_value;
285 return scroll_value;
286 };
286 };
287
287
288 /**
288 /**
289 * Scroll to the bottom of the page.
289 * Scroll to the bottom of the page.
290 *
290 *
291 * @method scroll_to_bottom
291 * @method scroll_to_bottom
292 */
292 */
293 Notebook.prototype.scroll_to_bottom = function () {
293 Notebook.prototype.scroll_to_bottom = function () {
294 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
294 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
295 };
295 };
296
296
297 /**
297 /**
298 * Scroll to the top of the page.
298 * Scroll to the top of the page.
299 *
299 *
300 * @method scroll_to_top
300 * @method scroll_to_top
301 */
301 */
302 Notebook.prototype.scroll_to_top = function () {
302 Notebook.prototype.scroll_to_top = function () {
303 this.element.animate({scrollTop:0}, 0);
303 this.element.animate({scrollTop:0}, 0);
304 };
304 };
305
305
306 // Edit Notebook metadata
306 // Edit Notebook metadata
307
307
308 Notebook.prototype.edit_metadata = function () {
308 Notebook.prototype.edit_metadata = function () {
309 var that = this;
309 var that = this;
310 dialog.edit_metadata(this.metadata, function (md) {
310 dialog.edit_metadata(this.metadata, function (md) {
311 that.metadata = md;
311 that.metadata = md;
312 }, 'Notebook');
312 }, 'Notebook');
313 };
313 };
314
314
315 // Cell indexing, retrieval, etc.
315 // Cell indexing, retrieval, etc.
316
316
317 /**
317 /**
318 * Get all cell elements in the notebook.
318 * Get all cell elements in the notebook.
319 *
319 *
320 * @method get_cell_elements
320 * @method get_cell_elements
321 * @return {jQuery} A selector of all cell elements
321 * @return {jQuery} A selector of all cell elements
322 */
322 */
323 Notebook.prototype.get_cell_elements = function () {
323 Notebook.prototype.get_cell_elements = function () {
324 return this.container.children("div.cell");
324 return this.container.children("div.cell");
325 };
325 };
326
326
327 /**
327 /**
328 * Get a particular cell element.
328 * Get a particular cell element.
329 *
329 *
330 * @method get_cell_element
330 * @method get_cell_element
331 * @param {Number} index An index of a cell to select
331 * @param {Number} index An index of a cell to select
332 * @return {jQuery} A selector of the given cell.
332 * @return {jQuery} A selector of the given cell.
333 */
333 */
334 Notebook.prototype.get_cell_element = function (index) {
334 Notebook.prototype.get_cell_element = function (index) {
335 var result = null;
335 var result = null;
336 var e = this.get_cell_elements().eq(index);
336 var e = this.get_cell_elements().eq(index);
337 if (e.length !== 0) {
337 if (e.length !== 0) {
338 result = e;
338 result = e;
339 }
339 }
340 return result;
340 return result;
341 };
341 };
342
342
343 /**
343 /**
344 * Try to get a particular cell by msg_id.
344 * Try to get a particular cell by msg_id.
345 *
345 *
346 * @method get_msg_cell
346 * @method get_msg_cell
347 * @param {String} msg_id A message UUID
347 * @param {String} msg_id A message UUID
348 * @return {Cell} Cell or null if no cell was found.
348 * @return {Cell} Cell or null if no cell was found.
349 */
349 */
350 Notebook.prototype.get_msg_cell = function (msg_id) {
350 Notebook.prototype.get_msg_cell = function (msg_id) {
351 return codecell.CodeCell.msg_cells[msg_id] || null;
351 return codecell.CodeCell.msg_cells[msg_id] || null;
352 };
352 };
353
353
354 /**
354 /**
355 * Count the cells in this notebook.
355 * Count the cells in this notebook.
356 *
356 *
357 * @method ncells
357 * @method ncells
358 * @return {Number} The number of cells in this notebook
358 * @return {Number} The number of cells in this notebook
359 */
359 */
360 Notebook.prototype.ncells = function () {
360 Notebook.prototype.ncells = function () {
361 return this.get_cell_elements().length;
361 return this.get_cell_elements().length;
362 };
362 };
363
363
364 /**
364 /**
365 * Get all Cell objects in this notebook.
365 * Get all Cell objects in this notebook.
366 *
366 *
367 * @method get_cells
367 * @method get_cells
368 * @return {Array} This notebook's Cell objects
368 * @return {Array} This notebook's Cell objects
369 */
369 */
370 // TODO: we are often calling cells as cells()[i], which we should optimize
370 // TODO: we are often calling cells as cells()[i], which we should optimize
371 // to cells(i) or a new method.
371 // to cells(i) or a new method.
372 Notebook.prototype.get_cells = function () {
372 Notebook.prototype.get_cells = function () {
373 return this.get_cell_elements().toArray().map(function (e) {
373 return this.get_cell_elements().toArray().map(function (e) {
374 return $(e).data("cell");
374 return $(e).data("cell");
375 });
375 });
376 };
376 };
377
377
378 /**
378 /**
379 * Get a Cell object from this notebook.
379 * Get a Cell object from this notebook.
380 *
380 *
381 * @method get_cell
381 * @method get_cell
382 * @param {Number} index An index of a cell to retrieve
382 * @param {Number} index An index of a cell to retrieve
383 * @return {Cell} A particular cell
383 * @return {Cell} A particular cell
384 */
384 */
385 Notebook.prototype.get_cell = function (index) {
385 Notebook.prototype.get_cell = function (index) {
386 var result = null;
386 var result = null;
387 var ce = this.get_cell_element(index);
387 var ce = this.get_cell_element(index);
388 if (ce !== null) {
388 if (ce !== null) {
389 result = ce.data('cell');
389 result = ce.data('cell');
390 }
390 }
391 return result;
391 return result;
392 };
392 };
393
393
394 /**
394 /**
395 * Get the cell below a given cell.
395 * Get the cell below a given cell.
396 *
396 *
397 * @method get_next_cell
397 * @method get_next_cell
398 * @param {Cell} cell The provided cell
398 * @param {Cell} cell The provided cell
399 * @return {Cell} The next cell
399 * @return {Cell} The next cell
400 */
400 */
401 Notebook.prototype.get_next_cell = function (cell) {
401 Notebook.prototype.get_next_cell = function (cell) {
402 var result = null;
402 var result = null;
403 var index = this.find_cell_index(cell);
403 var index = this.find_cell_index(cell);
404 if (this.is_valid_cell_index(index+1)) {
404 if (this.is_valid_cell_index(index+1)) {
405 result = this.get_cell(index+1);
405 result = this.get_cell(index+1);
406 }
406 }
407 return result;
407 return result;
408 };
408 };
409
409
410 /**
410 /**
411 * Get the cell above a given cell.
411 * Get the cell above a given cell.
412 *
412 *
413 * @method get_prev_cell
413 * @method get_prev_cell
414 * @param {Cell} cell The provided cell
414 * @param {Cell} cell The provided cell
415 * @return {Cell} The previous cell
415 * @return {Cell} The previous cell
416 */
416 */
417 Notebook.prototype.get_prev_cell = function (cell) {
417 Notebook.prototype.get_prev_cell = function (cell) {
418 // TODO: off-by-one
418 // TODO: off-by-one
419 // nb.get_prev_cell(nb.get_cell(1)) is null
419 // nb.get_prev_cell(nb.get_cell(1)) is null
420 var result = null;
420 var result = null;
421 var index = this.find_cell_index(cell);
421 var index = this.find_cell_index(cell);
422 if (index !== null && index > 1) {
422 if (index !== null && index > 1) {
423 result = this.get_cell(index-1);
423 result = this.get_cell(index-1);
424 }
424 }
425 return result;
425 return result;
426 };
426 };
427
427
428 /**
428 /**
429 * Get the numeric index of a given cell.
429 * Get the numeric index of a given cell.
430 *
430 *
431 * @method find_cell_index
431 * @method find_cell_index
432 * @param {Cell} cell The provided cell
432 * @param {Cell} cell The provided cell
433 * @return {Number} The cell's numeric index
433 * @return {Number} The cell's numeric index
434 */
434 */
435 Notebook.prototype.find_cell_index = function (cell) {
435 Notebook.prototype.find_cell_index = function (cell) {
436 var result = null;
436 var result = null;
437 this.get_cell_elements().filter(function (index) {
437 this.get_cell_elements().filter(function (index) {
438 if ($(this).data("cell") === cell) {
438 if ($(this).data("cell") === cell) {
439 result = index;
439 result = index;
440 }
440 }
441 });
441 });
442 return result;
442 return result;
443 };
443 };
444
444
445 /**
445 /**
446 * Get a given index , or the selected index if none is provided.
446 * Get a given index , or the selected index if none is provided.
447 *
447 *
448 * @method index_or_selected
448 * @method index_or_selected
449 * @param {Number} index A cell's index
449 * @param {Number} index A cell's index
450 * @return {Number} The given index, or selected index if none is provided.
450 * @return {Number} The given index, or selected index if none is provided.
451 */
451 */
452 Notebook.prototype.index_or_selected = function (index) {
452 Notebook.prototype.index_or_selected = function (index) {
453 var i;
453 var i;
454 if (index === undefined || index === null) {
454 if (index === undefined || index === null) {
455 i = this.get_selected_index();
455 i = this.get_selected_index();
456 if (i === null) {
456 if (i === null) {
457 i = 0;
457 i = 0;
458 }
458 }
459 } else {
459 } else {
460 i = index;
460 i = index;
461 }
461 }
462 return i;
462 return i;
463 };
463 };
464
464
465 /**
465 /**
466 * Get the currently selected cell.
466 * Get the currently selected cell.
467 * @method get_selected_cell
467 * @method get_selected_cell
468 * @return {Cell} The selected cell
468 * @return {Cell} The selected cell
469 */
469 */
470 Notebook.prototype.get_selected_cell = function () {
470 Notebook.prototype.get_selected_cell = function () {
471 var index = this.get_selected_index();
471 var index = this.get_selected_index();
472 return this.get_cell(index);
472 return this.get_cell(index);
473 };
473 };
474
474
475 /**
475 /**
476 * Check whether a cell index is valid.
476 * Check whether a cell index is valid.
477 *
477 *
478 * @method is_valid_cell_index
478 * @method is_valid_cell_index
479 * @param {Number} index A cell index
479 * @param {Number} index A cell index
480 * @return True if the index is valid, false otherwise
480 * @return True if the index is valid, false otherwise
481 */
481 */
482 Notebook.prototype.is_valid_cell_index = function (index) {
482 Notebook.prototype.is_valid_cell_index = function (index) {
483 if (index !== null && index >= 0 && index < this.ncells()) {
483 if (index !== null && index >= 0 && index < this.ncells()) {
484 return true;
484 return true;
485 } else {
485 } else {
486 return false;
486 return false;
487 }
487 }
488 };
488 };
489
489
490 /**
490 /**
491 * Get the index of the currently selected cell.
491 * Get the index of the currently selected cell.
492
492
493 * @method get_selected_index
493 * @method get_selected_index
494 * @return {Number} The selected cell's numeric index
494 * @return {Number} The selected cell's numeric index
495 */
495 */
496 Notebook.prototype.get_selected_index = function () {
496 Notebook.prototype.get_selected_index = function () {
497 var result = null;
497 var result = null;
498 this.get_cell_elements().filter(function (index) {
498 this.get_cell_elements().filter(function (index) {
499 if ($(this).data("cell").selected === true) {
499 if ($(this).data("cell").selected === true) {
500 result = index;
500 result = index;
501 }
501 }
502 });
502 });
503 return result;
503 return result;
504 };
504 };
505
505
506
506
507 // Cell selection.
507 // Cell selection.
508
508
509 /**
509 /**
510 * Programmatically select a cell.
510 * Programmatically select a cell.
511 *
511 *
512 * @method select
512 * @method select
513 * @param {Number} index A cell's index
513 * @param {Number} index A cell's index
514 * @return {Notebook} This notebook
514 * @return {Notebook} This notebook
515 */
515 */
516 Notebook.prototype.select = function (index) {
516 Notebook.prototype.select = function (index) {
517 if (this.is_valid_cell_index(index)) {
517 if (this.is_valid_cell_index(index)) {
518 var sindex = this.get_selected_index();
518 var sindex = this.get_selected_index();
519 if (sindex !== null && index !== sindex) {
519 if (sindex !== null && index !== sindex) {
520 // If we are about to select a different cell, make sure we are
520 // If we are about to select a different cell, make sure we are
521 // first in command mode.
521 // first in command mode.
522 if (this.mode !== 'command') {
522 if (this.mode !== 'command') {
523 this.command_mode();
523 this.command_mode();
524 }
524 }
525 this.get_cell(sindex).unselect();
525 this.get_cell(sindex).unselect();
526 }
526 }
527 var cell = this.get_cell(index);
527 var cell = this.get_cell(index);
528 cell.select();
528 cell.select();
529 if (cell.cell_type === 'heading') {
529 if (cell.cell_type === 'heading') {
530 this.events.trigger('selected_cell_type_changed.Notebook',
530 this.events.trigger('selected_cell_type_changed.Notebook',
531 {'cell_type':cell.cell_type,level:cell.level}
531 {'cell_type':cell.cell_type,level:cell.level}
532 );
532 );
533 } else {
533 } else {
534 this.events.trigger('selected_cell_type_changed.Notebook',
534 this.events.trigger('selected_cell_type_changed.Notebook',
535 {'cell_type':cell.cell_type}
535 {'cell_type':cell.cell_type}
536 );
536 );
537 }
537 }
538 }
538 }
539 return this;
539 return this;
540 };
540 };
541
541
542 /**
542 /**
543 * Programmatically select the next cell.
543 * Programmatically select the next cell.
544 *
544 *
545 * @method select_next
545 * @method select_next
546 * @return {Notebook} This notebook
546 * @return {Notebook} This notebook
547 */
547 */
548 Notebook.prototype.select_next = function () {
548 Notebook.prototype.select_next = function () {
549 var index = this.get_selected_index();
549 var index = this.get_selected_index();
550 this.select(index+1);
550 this.select(index+1);
551 return this;
551 return this;
552 };
552 };
553
553
554 /**
554 /**
555 * Programmatically select the previous cell.
555 * Programmatically select the previous cell.
556 *
556 *
557 * @method select_prev
557 * @method select_prev
558 * @return {Notebook} This notebook
558 * @return {Notebook} This notebook
559 */
559 */
560 Notebook.prototype.select_prev = function () {
560 Notebook.prototype.select_prev = function () {
561 var index = this.get_selected_index();
561 var index = this.get_selected_index();
562 this.select(index-1);
562 this.select(index-1);
563 return this;
563 return this;
564 };
564 };
565
565
566
566
567 // Edit/Command mode
567 // Edit/Command mode
568
568
569 /**
569 /**
570 * Gets the index of the cell that is in edit mode.
570 * Gets the index of the cell that is in edit mode.
571 *
571 *
572 * @method get_edit_index
572 * @method get_edit_index
573 *
573 *
574 * @return index {int}
574 * @return index {int}
575 **/
575 **/
576 Notebook.prototype.get_edit_index = function () {
576 Notebook.prototype.get_edit_index = function () {
577 var result = null;
577 var result = null;
578 this.get_cell_elements().filter(function (index) {
578 this.get_cell_elements().filter(function (index) {
579 if ($(this).data("cell").mode === 'edit') {
579 if ($(this).data("cell").mode === 'edit') {
580 result = index;
580 result = index;
581 }
581 }
582 });
582 });
583 return result;
583 return result;
584 };
584 };
585
585
586 /**
586 /**
587 * Handle when a a cell blurs and the notebook should enter command mode.
587 * Handle when a a cell blurs and the notebook should enter command mode.
588 *
588 *
589 * @method handle_command_mode
589 * @method handle_command_mode
590 * @param [cell] {Cell} Cell to enter command mode on.
590 * @param [cell] {Cell} Cell to enter command mode on.
591 **/
591 **/
592 Notebook.prototype.handle_command_mode = function (cell) {
592 Notebook.prototype.handle_command_mode = function (cell) {
593 if (this.mode !== 'command') {
593 if (this.mode !== 'command') {
594 cell.command_mode();
594 cell.command_mode();
595 this.mode = 'command';
595 this.mode = 'command';
596 this.events.trigger('command_mode.Notebook');
596 this.events.trigger('command_mode.Notebook');
597 this.keyboard_manager.command_mode();
597 this.keyboard_manager.command_mode();
598 }
598 }
599 };
599 };
600
600
601 /**
601 /**
602 * Make the notebook enter command mode.
602 * Make the notebook enter command mode.
603 *
603 *
604 * @method command_mode
604 * @method command_mode
605 **/
605 **/
606 Notebook.prototype.command_mode = function () {
606 Notebook.prototype.command_mode = function () {
607 var cell = this.get_cell(this.get_edit_index());
607 var cell = this.get_cell(this.get_edit_index());
608 if (cell && this.mode !== 'command') {
608 if (cell && this.mode !== 'command') {
609 // We don't call cell.command_mode, but rather call cell.focus_cell()
609 // We don't call cell.command_mode, but rather call cell.focus_cell()
610 // which will blur and CM editor and trigger the call to
610 // which will blur and CM editor and trigger the call to
611 // handle_command_mode.
611 // handle_command_mode.
612 cell.focus_cell();
612 cell.focus_cell();
613 }
613 }
614 };
614 };
615
615
616 /**
616 /**
617 * Handle when a cell fires it's edit_mode event.
617 * Handle when a cell fires it's edit_mode event.
618 *
618 *
619 * @method handle_edit_mode
619 * @method handle_edit_mode
620 * @param [cell] {Cell} Cell to enter edit mode on.
620 * @param [cell] {Cell} Cell to enter edit mode on.
621 **/
621 **/
622 Notebook.prototype.handle_edit_mode = function (cell) {
622 Notebook.prototype.handle_edit_mode = function (cell) {
623 if (cell && this.mode !== 'edit') {
623 if (cell && this.mode !== 'edit') {
624 cell.edit_mode();
624 cell.edit_mode();
625 this.mode = 'edit';
625 this.mode = 'edit';
626 this.events.trigger('edit_mode.Notebook');
626 this.events.trigger('edit_mode.Notebook');
627 this.keyboard_manager.edit_mode();
627 this.keyboard_manager.edit_mode();
628 }
628 }
629 };
629 };
630
630
631 /**
631 /**
632 * Make a cell enter edit mode.
632 * Make a cell enter edit mode.
633 *
633 *
634 * @method edit_mode
634 * @method edit_mode
635 **/
635 **/
636 Notebook.prototype.edit_mode = function () {
636 Notebook.prototype.edit_mode = function () {
637 var cell = this.get_selected_cell();
637 var cell = this.get_selected_cell();
638 if (cell && this.mode !== 'edit') {
638 if (cell && this.mode !== 'edit') {
639 cell.unrender();
639 cell.unrender();
640 cell.focus_editor();
640 cell.focus_editor();
641 }
641 }
642 };
642 };
643
643
644 /**
644 /**
645 * Focus the currently selected cell.
645 * Focus the currently selected cell.
646 *
646 *
647 * @method focus_cell
647 * @method focus_cell
648 **/
648 **/
649 Notebook.prototype.focus_cell = function () {
649 Notebook.prototype.focus_cell = function () {
650 var cell = this.get_selected_cell();
650 var cell = this.get_selected_cell();
651 if (cell === null) {return;} // No cell is selected
651 if (cell === null) {return;} // No cell is selected
652 cell.focus_cell();
652 cell.focus_cell();
653 };
653 };
654
654
655 // Cell movement
655 // Cell movement
656
656
657 /**
657 /**
658 * Move given (or selected) cell up and select it.
658 * Move given (or selected) cell up and select it.
659 *
659 *
660 * @method move_cell_up
660 * @method move_cell_up
661 * @param [index] {integer} cell index
661 * @param [index] {integer} cell index
662 * @return {Notebook} This notebook
662 * @return {Notebook} This notebook
663 **/
663 **/
664 Notebook.prototype.move_cell_up = function (index) {
664 Notebook.prototype.move_cell_up = function (index) {
665 var i = this.index_or_selected(index);
665 var i = this.index_or_selected(index);
666 if (this.is_valid_cell_index(i) && i > 0) {
666 if (this.is_valid_cell_index(i) && i > 0) {
667 var pivot = this.get_cell_element(i-1);
667 var pivot = this.get_cell_element(i-1);
668 var tomove = this.get_cell_element(i);
668 var tomove = this.get_cell_element(i);
669 if (pivot !== null && tomove !== null) {
669 if (pivot !== null && tomove !== null) {
670 tomove.detach();
670 tomove.detach();
671 pivot.before(tomove);
671 pivot.before(tomove);
672 this.select(i-1);
672 this.select(i-1);
673 var cell = this.get_selected_cell();
673 var cell = this.get_selected_cell();
674 cell.focus_cell();
674 cell.focus_cell();
675 }
675 }
676 this.set_dirty(true);
676 this.set_dirty(true);
677 }
677 }
678 return this;
678 return this;
679 };
679 };
680
680
681
681
682 /**
682 /**
683 * Move given (or selected) cell down and select it
683 * Move given (or selected) cell down and select it
684 *
684 *
685 * @method move_cell_down
685 * @method move_cell_down
686 * @param [index] {integer} cell index
686 * @param [index] {integer} cell index
687 * @return {Notebook} This notebook
687 * @return {Notebook} This notebook
688 **/
688 **/
689 Notebook.prototype.move_cell_down = function (index) {
689 Notebook.prototype.move_cell_down = function (index) {
690 var i = this.index_or_selected(index);
690 var i = this.index_or_selected(index);
691 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
691 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
692 var pivot = this.get_cell_element(i+1);
692 var pivot = this.get_cell_element(i+1);
693 var tomove = this.get_cell_element(i);
693 var tomove = this.get_cell_element(i);
694 if (pivot !== null && tomove !== null) {
694 if (pivot !== null && tomove !== null) {
695 tomove.detach();
695 tomove.detach();
696 pivot.after(tomove);
696 pivot.after(tomove);
697 this.select(i+1);
697 this.select(i+1);
698 var cell = this.get_selected_cell();
698 var cell = this.get_selected_cell();
699 cell.focus_cell();
699 cell.focus_cell();
700 }
700 }
701 }
701 }
702 this.set_dirty();
702 this.set_dirty();
703 return this;
703 return this;
704 };
704 };
705
705
706
706
707 // Insertion, deletion.
707 // Insertion, deletion.
708
708
709 /**
709 /**
710 * Delete a cell from the notebook.
710 * Delete a cell from the notebook.
711 *
711 *
712 * @method delete_cell
712 * @method delete_cell
713 * @param [index] A cell's numeric index
713 * @param [index] A cell's numeric index
714 * @return {Notebook} This notebook
714 * @return {Notebook} This notebook
715 */
715 */
716 Notebook.prototype.delete_cell = function (index) {
716 Notebook.prototype.delete_cell = function (index) {
717 var i = this.index_or_selected(index);
717 var i = this.index_or_selected(index);
718 var cell = this.get_selected_cell();
718 var cell = this.get_selected_cell();
719 this.undelete_backup = cell.toJSON();
719 this.undelete_backup = cell.toJSON();
720 $('#undelete_cell').removeClass('disabled');
720 $('#undelete_cell').removeClass('disabled');
721 if (this.is_valid_cell_index(i)) {
721 if (this.is_valid_cell_index(i)) {
722 var old_ncells = this.ncells();
722 var old_ncells = this.ncells();
723 var ce = this.get_cell_element(i);
723 var ce = this.get_cell_element(i);
724 ce.remove();
724 ce.remove();
725 if (i === 0) {
725 if (i === 0) {
726 // Always make sure we have at least one cell.
726 // Always make sure we have at least one cell.
727 if (old_ncells === 1) {
727 if (old_ncells === 1) {
728 this.insert_cell_below('code');
728 this.insert_cell_below('code');
729 }
729 }
730 this.select(0);
730 this.select(0);
731 this.undelete_index = 0;
731 this.undelete_index = 0;
732 this.undelete_below = false;
732 this.undelete_below = false;
733 } else if (i === old_ncells-1 && i !== 0) {
733 } else if (i === old_ncells-1 && i !== 0) {
734 this.select(i-1);
734 this.select(i-1);
735 this.undelete_index = i - 1;
735 this.undelete_index = i - 1;
736 this.undelete_below = true;
736 this.undelete_below = true;
737 } else {
737 } else {
738 this.select(i);
738 this.select(i);
739 this.undelete_index = i;
739 this.undelete_index = i;
740 this.undelete_below = false;
740 this.undelete_below = false;
741 }
741 }
742 this.events.trigger('delete.Cell', {'cell': cell, 'index': i});
742 this.events.trigger('delete.Cell', {'cell': cell, 'index': i});
743 this.set_dirty(true);
743 this.set_dirty(true);
744 }
744 }
745 return this;
745 return this;
746 };
746 };
747
747
748 /**
748 /**
749 * Restore the most recently deleted cell.
749 * Restore the most recently deleted cell.
750 *
750 *
751 * @method undelete
751 * @method undelete
752 */
752 */
753 Notebook.prototype.undelete_cell = function() {
753 Notebook.prototype.undelete_cell = function() {
754 if (this.undelete_backup !== null && this.undelete_index !== null) {
754 if (this.undelete_backup !== null && this.undelete_index !== null) {
755 var current_index = this.get_selected_index();
755 var current_index = this.get_selected_index();
756 if (this.undelete_index < current_index) {
756 if (this.undelete_index < current_index) {
757 current_index = current_index + 1;
757 current_index = current_index + 1;
758 }
758 }
759 if (this.undelete_index >= this.ncells()) {
759 if (this.undelete_index >= this.ncells()) {
760 this.select(this.ncells() - 1);
760 this.select(this.ncells() - 1);
761 }
761 }
762 else {
762 else {
763 this.select(this.undelete_index);
763 this.select(this.undelete_index);
764 }
764 }
765 var cell_data = this.undelete_backup;
765 var cell_data = this.undelete_backup;
766 var new_cell = null;
766 var new_cell = null;
767 if (this.undelete_below) {
767 if (this.undelete_below) {
768 new_cell = this.insert_cell_below(cell_data.cell_type);
768 new_cell = this.insert_cell_below(cell_data.cell_type);
769 } else {
769 } else {
770 new_cell = this.insert_cell_above(cell_data.cell_type);
770 new_cell = this.insert_cell_above(cell_data.cell_type);
771 }
771 }
772 new_cell.fromJSON(cell_data);
772 new_cell.fromJSON(cell_data);
773 if (this.undelete_below) {
773 if (this.undelete_below) {
774 this.select(current_index+1);
774 this.select(current_index+1);
775 } else {
775 } else {
776 this.select(current_index);
776 this.select(current_index);
777 }
777 }
778 this.undelete_backup = null;
778 this.undelete_backup = null;
779 this.undelete_index = null;
779 this.undelete_index = null;
780 }
780 }
781 $('#undelete_cell').addClass('disabled');
781 $('#undelete_cell').addClass('disabled');
782 };
782 };
783
783
784 /**
784 /**
785 * Insert a cell so that after insertion the cell is at given index.
785 * Insert a cell so that after insertion the cell is at given index.
786 *
786 *
787 * If cell type is not provided, it will default to the type of the
787 * If cell type is not provided, it will default to the type of the
788 * currently active cell.
788 * currently active cell.
789 *
789 *
790 * Similar to insert_above, but index parameter is mandatory
790 * Similar to insert_above, but index parameter is mandatory
791 *
791 *
792 * Index will be brought back into the accessible range [0,n]
792 * Index will be brought back into the accessible range [0,n]
793 *
793 *
794 * @method insert_cell_at_index
794 * @method insert_cell_at_index
795 * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
795 * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
796 * @param [index] {int} a valid index where to insert cell
796 * @param [index] {int} a valid index where to insert cell
797 *
797 *
798 * @return cell {cell|null} created cell or null
798 * @return cell {cell|null} created cell or null
799 **/
799 **/
800 Notebook.prototype.insert_cell_at_index = function(type, index){
800 Notebook.prototype.insert_cell_at_index = function(type, index){
801
801
802 var ncells = this.ncells();
802 var ncells = this.ncells();
803 index = Math.min(index,ncells);
803 index = Math.min(index,ncells);
804 index = Math.max(index,0);
804 index = Math.max(index,0);
805 var cell = null;
805 var cell = null;
806 type = type || this.get_selected_cell().cell_type;
806 type = type || this.get_selected_cell().cell_type;
807
807
808 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
808 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
809 if (type === 'code') {
809 if (type === 'code') {
810 cell = new codecell.CodeCell(this.kernel, this.options, this.events, this.config, this.keyboard_manager, this);
810 cell = new codecell.CodeCell(this.kernel, this.options, this.events, this.config, this.keyboard_manager, this);
811 cell.set_input_prompt();
811 cell.set_input_prompt();
812 } else if (type === 'markdown') {
812 } else if (type === 'markdown') {
813 cell = new cells.MarkdownCell(this.options, this.events, this.config, this.keyboard_manager, this);
813 cell = new cells.MarkdownCell(this.options, this.events, this.config, this.keyboard_manager, this);
814 } else if (type === 'raw') {
814 } else if (type === 'raw') {
815 cell = new cells.RawCell(this.options, this.events, this.config, this.keyboard_manager, this);
815 cell = new cells.RawCell(this.options, this.events, this.config, this.keyboard_manager, this);
816 } else if (type === 'heading') {
816 } else if (type === 'heading') {
817 cell = new cells.HeadingCell(this.options, this.events, this.config, this.keyboard_manager, this);
817 cell = new cells.HeadingCell(this.options, this.events, this.config, this.keyboard_manager, this);
818 }
818 }
819
819
820 if(this._insert_element_at_index(cell.element,index)) {
820 if(this._insert_element_at_index(cell.element,index)) {
821 cell.render();
821 cell.render();
822 this.events.trigger('create.Cell', {'cell': cell, 'index': index});
822 this.events.trigger('create.Cell', {'cell': cell, 'index': index});
823 cell.refresh();
823 cell.refresh();
824 // We used to select the cell after we refresh it, but there
824 // We used to select the cell after we refresh it, but there
825 // are now cases were this method is called where select is
825 // are now cases were this method is called where select is
826 // not appropriate. The selection logic should be handled by the
826 // not appropriate. The selection logic should be handled by the
827 // caller of the the top level insert_cell methods.
827 // caller of the the top level insert_cell methods.
828 this.set_dirty(true);
828 this.set_dirty(true);
829 }
829 }
830 }
830 }
831 return cell;
831 return cell;
832
832
833 };
833 };
834
834
835 /**
835 /**
836 * Insert an element at given cell index.
836 * Insert an element at given cell index.
837 *
837 *
838 * @method _insert_element_at_index
838 * @method _insert_element_at_index
839 * @param element {dom element} a cell element
839 * @param element {dom element} a cell element
840 * @param [index] {int} a valid index where to inser cell
840 * @param [index] {int} a valid index where to inser cell
841 * @private
841 * @private
842 *
842 *
843 * return true if everything whent fine.
843 * return true if everything whent fine.
844 **/
844 **/
845 Notebook.prototype._insert_element_at_index = function(element, index){
845 Notebook.prototype._insert_element_at_index = function(element, index){
846 if (element === undefined){
846 if (element === undefined){
847 return false;
847 return false;
848 }
848 }
849
849
850 var ncells = this.ncells();
850 var ncells = this.ncells();
851
851
852 if (ncells === 0) {
852 if (ncells === 0) {
853 // special case append if empty
853 // special case append if empty
854 this.element.find('div.end_space').before(element);
854 this.element.find('div.end_space').before(element);
855 } else if ( ncells === index ) {
855 } else if ( ncells === index ) {
856 // special case append it the end, but not empty
856 // special case append it the end, but not empty
857 this.get_cell_element(index-1).after(element);
857 this.get_cell_element(index-1).after(element);
858 } else if (this.is_valid_cell_index(index)) {
858 } else if (this.is_valid_cell_index(index)) {
859 // otherwise always somewhere to append to
859 // otherwise always somewhere to append to
860 this.get_cell_element(index).before(element);
860 this.get_cell_element(index).before(element);
861 } else {
861 } else {
862 return false;
862 return false;
863 }
863 }
864
864
865 if (this.undelete_index !== null && index <= this.undelete_index) {
865 if (this.undelete_index !== null && index <= this.undelete_index) {
866 this.undelete_index = this.undelete_index + 1;
866 this.undelete_index = this.undelete_index + 1;
867 this.set_dirty(true);
867 this.set_dirty(true);
868 }
868 }
869 return true;
869 return true;
870 };
870 };
871
871
872 /**
872 /**
873 * Insert a cell of given type above given index, or at top
873 * Insert a cell of given type above given index, or at top
874 * of notebook if index smaller than 0.
874 * of notebook if index smaller than 0.
875 *
875 *
876 * default index value is the one of currently selected cell
876 * default index value is the one of currently selected cell
877 *
877 *
878 * @method insert_cell_above
878 * @method insert_cell_above
879 * @param [type] {string} cell type
879 * @param [type] {string} cell type
880 * @param [index] {integer}
880 * @param [index] {integer}
881 *
881 *
882 * @return handle to created cell or null
882 * @return handle to created cell or null
883 **/
883 **/
884 Notebook.prototype.insert_cell_above = function (type, index) {
884 Notebook.prototype.insert_cell_above = function (type, index) {
885 index = this.index_or_selected(index);
885 index = this.index_or_selected(index);
886 return this.insert_cell_at_index(type, index);
886 return this.insert_cell_at_index(type, index);
887 };
887 };
888
888
889 /**
889 /**
890 * Insert a cell of given type below given index, or at bottom
890 * Insert a cell of given type below given index, or at bottom
891 * of notebook if index greater than number of cells
891 * of notebook if index greater than number of cells
892 *
892 *
893 * default index value is the one of currently selected cell
893 * default index value is the one of currently selected cell
894 *
894 *
895 * @method insert_cell_below
895 * @method insert_cell_below
896 * @param [type] {string} cell type
896 * @param [type] {string} cell type
897 * @param [index] {integer}
897 * @param [index] {integer}
898 *
898 *
899 * @return handle to created cell or null
899 * @return handle to created cell or null
900 *
900 *
901 **/
901 **/
902 Notebook.prototype.insert_cell_below = function (type, index) {
902 Notebook.prototype.insert_cell_below = function (type, index) {
903 index = this.index_or_selected(index);
903 index = this.index_or_selected(index);
904 return this.insert_cell_at_index(type, index+1);
904 return this.insert_cell_at_index(type, index+1);
905 };
905 };
906
906
907
907
908 /**
908 /**
909 * Insert cell at end of notebook
909 * Insert cell at end of notebook
910 *
910 *
911 * @method insert_cell_at_bottom
911 * @method insert_cell_at_bottom
912 * @param {String} type cell type
912 * @param {String} type cell type
913 *
913 *
914 * @return the added cell; or null
914 * @return the added cell; or null
915 **/
915 **/
916 Notebook.prototype.insert_cell_at_bottom = function (type){
916 Notebook.prototype.insert_cell_at_bottom = function (type){
917 var len = this.ncells();
917 var len = this.ncells();
918 return this.insert_cell_below(type,len-1);
918 return this.insert_cell_below(type,len-1);
919 };
919 };
920
920
921 /**
921 /**
922 * Turn a cell into a code cell.
922 * Turn a cell into a code cell.
923 *
923 *
924 * @method to_code
924 * @method to_code
925 * @param {Number} [index] A cell's index
925 * @param {Number} [index] A cell's index
926 */
926 */
927 Notebook.prototype.to_code = function (index) {
927 Notebook.prototype.to_code = function (index) {
928 var i = this.index_or_selected(index);
928 var i = this.index_or_selected(index);
929 if (this.is_valid_cell_index(i)) {
929 if (this.is_valid_cell_index(i)) {
930 var source_element = this.get_cell_element(i);
930 var source_element = this.get_cell_element(i);
931 var source_cell = source_element.data("cell");
931 var source_cell = source_element.data("cell");
932 if (!(source_cell instanceof codecell.CodeCell)) {
932 if (!(source_cell instanceof codecell.CodeCell)) {
933 var target_cell = this.insert_cell_below('code',i);
933 var target_cell = this.insert_cell_below('code',i);
934 var text = source_cell.get_text();
934 var text = source_cell.get_text();
935 if (text === source_cell.placeholder) {
935 if (text === source_cell.placeholder) {
936 text = '';
936 text = '';
937 }
937 }
938 target_cell.set_text(text);
938 target_cell.set_text(text);
939 // make this value the starting point, so that we can only undo
939 // make this value the starting point, so that we can only undo
940 // to this state, instead of a blank cell
940 // to this state, instead of a blank cell
941 target_cell.code_mirror.clearHistory();
941 target_cell.code_mirror.clearHistory();
942 source_element.remove();
942 source_element.remove();
943 this.select(i);
943 this.select(i);
944 var cursor = source_cell.code_mirror.getCursor();
944 var cursor = source_cell.code_mirror.getCursor();
945 target_cell.code_mirror.setCursor(cursor);
945 target_cell.code_mirror.setCursor(cursor);
946 this.set_dirty(true);
946 this.set_dirty(true);
947 }
947 }
948 }
948 }
949 };
949 };
950
950
951 /**
951 /**
952 * Turn a cell into a Markdown cell.
952 * Turn a cell into a Markdown cell.
953 *
953 *
954 * @method to_markdown
954 * @method to_markdown
955 * @param {Number} [index] A cell's index
955 * @param {Number} [index] A cell's index
956 */
956 */
957 Notebook.prototype.to_markdown = function (index) {
957 Notebook.prototype.to_markdown = function (index) {
958 var i = this.index_or_selected(index);
958 var i = this.index_or_selected(index);
959 if (this.is_valid_cell_index(i)) {
959 if (this.is_valid_cell_index(i)) {
960 var source_element = this.get_cell_element(i);
960 var source_element = this.get_cell_element(i);
961 var source_cell = source_element.data("cell");
961 var source_cell = source_element.data("cell");
962 if (!(source_cell instanceof cells.MarkdownCell)) {
962 if (!(source_cell instanceof cells.MarkdownCell)) {
963 var target_cell = this.insert_cell_below('markdown',i);
963 var target_cell = this.insert_cell_below('markdown',i);
964 var text = source_cell.get_text();
964 var text = source_cell.get_text();
965 if (text === source_cell.placeholder) {
965 if (text === source_cell.placeholder) {
966 text = '';
966 text = '';
967 }
967 }
968 // We must show the editor before setting its contents
968 // We must show the editor before setting its contents
969 target_cell.unrender();
969 target_cell.unrender();
970 target_cell.set_text(text);
970 target_cell.set_text(text);
971 // make this value the starting point, so that we can only undo
971 // make this value the starting point, so that we can only undo
972 // to this state, instead of a blank cell
972 // to this state, instead of a blank cell
973 target_cell.code_mirror.clearHistory();
973 target_cell.code_mirror.clearHistory();
974 source_element.remove();
974 source_element.remove();
975 this.select(i);
975 this.select(i);
976 if ((source_cell instanceof cells.TextCell) && source_cell.rendered) {
976 if ((source_cell instanceof cells.TextCell) && source_cell.rendered) {
977 target_cell.render();
977 target_cell.render();
978 }
978 }
979 var cursor = source_cell.code_mirror.getCursor();
979 var cursor = source_cell.code_mirror.getCursor();
980 target_cell.code_mirror.setCursor(cursor);
980 target_cell.code_mirror.setCursor(cursor);
981 this.set_dirty(true);
981 this.set_dirty(true);
982 }
982 }
983 }
983 }
984 };
984 };
985
985
986 /**
986 /**
987 * Turn a cell into a raw text cell.
987 * Turn a cell into a raw text cell.
988 *
988 *
989 * @method to_raw
989 * @method to_raw
990 * @param {Number} [index] A cell's index
990 * @param {Number} [index] A cell's index
991 */
991 */
992 Notebook.prototype.to_raw = function (index) {
992 Notebook.prototype.to_raw = function (index) {
993 var i = this.index_or_selected(index);
993 var i = this.index_or_selected(index);
994 if (this.is_valid_cell_index(i)) {
994 if (this.is_valid_cell_index(i)) {
995 var source_element = this.get_cell_element(i);
995 var source_element = this.get_cell_element(i);
996 var source_cell = source_element.data("cell");
996 var source_cell = source_element.data("cell");
997 var target_cell = null;
997 var target_cell = null;
998 if (!(source_cell instanceof cells.RawCell)) {
998 if (!(source_cell instanceof cells.RawCell)) {
999 target_cell = this.insert_cell_below('raw',i);
999 target_cell = this.insert_cell_below('raw',i);
1000 var text = source_cell.get_text();
1000 var text = source_cell.get_text();
1001 if (text === source_cell.placeholder) {
1001 if (text === source_cell.placeholder) {
1002 text = '';
1002 text = '';
1003 }
1003 }
1004 // We must show the editor before setting its contents
1004 // We must show the editor before setting its contents
1005 target_cell.unrender();
1005 target_cell.unrender();
1006 target_cell.set_text(text);
1006 target_cell.set_text(text);
1007 // make this value the starting point, so that we can only undo
1007 // make this value the starting point, so that we can only undo
1008 // to this state, instead of a blank cell
1008 // to this state, instead of a blank cell
1009 target_cell.code_mirror.clearHistory();
1009 target_cell.code_mirror.clearHistory();
1010 source_element.remove();
1010 source_element.remove();
1011 this.select(i);
1011 this.select(i);
1012 var cursor = source_cell.code_mirror.getCursor();
1012 var cursor = source_cell.code_mirror.getCursor();
1013 target_cell.code_mirror.setCursor(cursor);
1013 target_cell.code_mirror.setCursor(cursor);
1014 this.set_dirty(true);
1014 this.set_dirty(true);
1015 }
1015 }
1016 }
1016 }
1017 };
1017 };
1018
1018
1019 /**
1019 /**
1020 * Turn a cell into a heading cell.
1020 * Turn a cell into a heading cell.
1021 *
1021 *
1022 * @method to_heading
1022 * @method to_heading
1023 * @param {Number} [index] A cell's index
1023 * @param {Number} [index] A cell's index
1024 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
1024 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
1025 */
1025 */
1026 Notebook.prototype.to_heading = function (index, level) {
1026 Notebook.prototype.to_heading = function (index, level) {
1027 level = level || 1;
1027 level = level || 1;
1028 var i = this.index_or_selected(index);
1028 var i = this.index_or_selected(index);
1029 if (this.is_valid_cell_index(i)) {
1029 if (this.is_valid_cell_index(i)) {
1030 var source_element = this.get_cell_element(i);
1030 var source_element = this.get_cell_element(i);
1031 var source_cell = source_element.data("cell");
1031 var source_cell = source_element.data("cell");
1032 var target_cell = null;
1032 var target_cell = null;
1033 if (source_cell instanceof cells.HeadingCell) {
1033 if (source_cell instanceof cells.HeadingCell) {
1034 source_cell.set_level(level);
1034 source_cell.set_level(level);
1035 } else {
1035 } else {
1036 target_cell = this.insert_cell_below('heading',i);
1036 target_cell = this.insert_cell_below('heading',i);
1037 var text = source_cell.get_text();
1037 var text = source_cell.get_text();
1038 if (text === source_cell.placeholder) {
1038 if (text === source_cell.placeholder) {
1039 text = '';
1039 text = '';
1040 }
1040 }
1041 // We must show the editor before setting its contents
1041 // We must show the editor before setting its contents
1042 target_cell.set_level(level);
1042 target_cell.set_level(level);
1043 target_cell.unrender();
1043 target_cell.unrender();
1044 target_cell.set_text(text);
1044 target_cell.set_text(text);
1045 // make this value the starting point, so that we can only undo
1045 // make this value the starting point, so that we can only undo
1046 // to this state, instead of a blank cell
1046 // to this state, instead of a blank cell
1047 target_cell.code_mirror.clearHistory();
1047 target_cell.code_mirror.clearHistory();
1048 source_element.remove();
1048 source_element.remove();
1049 this.select(i);
1049 this.select(i);
1050 var cursor = source_cell.code_mirror.getCursor();
1050 var cursor = source_cell.code_mirror.getCursor();
1051 target_cell.code_mirror.setCursor(cursor);
1051 target_cell.code_mirror.setCursor(cursor);
1052 if ((source_cell instanceof cells.TextCell) && source_cell.rendered) {
1052 if ((source_cell instanceof cells.TextCell) && source_cell.rendered) {
1053 target_cell.render();
1053 target_cell.render();
1054 }
1054 }
1055 }
1055 }
1056 this.set_dirty(true);
1056 this.set_dirty(true);
1057 this.events.trigger('selected_cell_type_changed.Notebook',
1057 this.events.trigger('selected_cell_type_changed.Notebook',
1058 {'cell_type':'heading',level:level}
1058 {'cell_type':'heading',level:level}
1059 );
1059 );
1060 }
1060 }
1061 };
1061 };
1062
1062
1063
1063
1064 // Cut/Copy/Paste
1064 // Cut/Copy/Paste
1065
1065
1066 /**
1066 /**
1067 * Enable UI elements for pasting cells.
1067 * Enable UI elements for pasting cells.
1068 *
1068 *
1069 * @method enable_paste
1069 * @method enable_paste
1070 */
1070 */
1071 Notebook.prototype.enable_paste = function () {
1071 Notebook.prototype.enable_paste = function () {
1072 var that = this;
1072 var that = this;
1073 if (!this.paste_enabled) {
1073 if (!this.paste_enabled) {
1074 $('#paste_cell_replace').removeClass('disabled')
1074 $('#paste_cell_replace').removeClass('disabled')
1075 .on('click', function () {that.paste_cell_replace();});
1075 .on('click', function () {that.paste_cell_replace();});
1076 $('#paste_cell_above').removeClass('disabled')
1076 $('#paste_cell_above').removeClass('disabled')
1077 .on('click', function () {that.paste_cell_above();});
1077 .on('click', function () {that.paste_cell_above();});
1078 $('#paste_cell_below').removeClass('disabled')
1078 $('#paste_cell_below').removeClass('disabled')
1079 .on('click', function () {that.paste_cell_below();});
1079 .on('click', function () {that.paste_cell_below();});
1080 this.paste_enabled = true;
1080 this.paste_enabled = true;
1081 }
1081 }
1082 };
1082 };
1083
1083
1084 /**
1084 /**
1085 * Disable UI elements for pasting cells.
1085 * Disable UI elements for pasting cells.
1086 *
1086 *
1087 * @method disable_paste
1087 * @method disable_paste
1088 */
1088 */
1089 Notebook.prototype.disable_paste = function () {
1089 Notebook.prototype.disable_paste = function () {
1090 if (this.paste_enabled) {
1090 if (this.paste_enabled) {
1091 $('#paste_cell_replace').addClass('disabled').off('click');
1091 $('#paste_cell_replace').addClass('disabled').off('click');
1092 $('#paste_cell_above').addClass('disabled').off('click');
1092 $('#paste_cell_above').addClass('disabled').off('click');
1093 $('#paste_cell_below').addClass('disabled').off('click');
1093 $('#paste_cell_below').addClass('disabled').off('click');
1094 this.paste_enabled = false;
1094 this.paste_enabled = false;
1095 }
1095 }
1096 };
1096 };
1097
1097
1098 /**
1098 /**
1099 * Cut a cell.
1099 * Cut a cell.
1100 *
1100 *
1101 * @method cut_cell
1101 * @method cut_cell
1102 */
1102 */
1103 Notebook.prototype.cut_cell = function () {
1103 Notebook.prototype.cut_cell = function () {
1104 this.copy_cell();
1104 this.copy_cell();
1105 this.delete_cell();
1105 this.delete_cell();
1106 };
1106 };
1107
1107
1108 /**
1108 /**
1109 * Copy a cell.
1109 * Copy a cell.
1110 *
1110 *
1111 * @method copy_cell
1111 * @method copy_cell
1112 */
1112 */
1113 Notebook.prototype.copy_cell = function () {
1113 Notebook.prototype.copy_cell = function () {
1114 var cell = this.get_selected_cell();
1114 var cell = this.get_selected_cell();
1115 this.clipboard = cell.toJSON();
1115 this.clipboard = cell.toJSON();
1116 this.enable_paste();
1116 this.enable_paste();
1117 };
1117 };
1118
1118
1119 /**
1119 /**
1120 * Replace the selected cell with a cell in the clipboard.
1120 * Replace the selected cell with a cell in the clipboard.
1121 *
1121 *
1122 * @method paste_cell_replace
1122 * @method paste_cell_replace
1123 */
1123 */
1124 Notebook.prototype.paste_cell_replace = function () {
1124 Notebook.prototype.paste_cell_replace = function () {
1125 if (this.clipboard !== null && this.paste_enabled) {
1125 if (this.clipboard !== null && this.paste_enabled) {
1126 var cell_data = this.clipboard;
1126 var cell_data = this.clipboard;
1127 var new_cell = this.insert_cell_above(cell_data.cell_type);
1127 var new_cell = this.insert_cell_above(cell_data.cell_type);
1128 new_cell.fromJSON(cell_data);
1128 new_cell.fromJSON(cell_data);
1129 var old_cell = this.get_next_cell(new_cell);
1129 var old_cell = this.get_next_cell(new_cell);
1130 this.delete_cell(this.find_cell_index(old_cell));
1130 this.delete_cell(this.find_cell_index(old_cell));
1131 this.select(this.find_cell_index(new_cell));
1131 this.select(this.find_cell_index(new_cell));
1132 }
1132 }
1133 };
1133 };
1134
1134
1135 /**
1135 /**
1136 * Paste a cell from the clipboard above the selected cell.
1136 * Paste a cell from the clipboard above the selected cell.
1137 *
1137 *
1138 * @method paste_cell_above
1138 * @method paste_cell_above
1139 */
1139 */
1140 Notebook.prototype.paste_cell_above = function () {
1140 Notebook.prototype.paste_cell_above = function () {
1141 if (this.clipboard !== null && this.paste_enabled) {
1141 if (this.clipboard !== null && this.paste_enabled) {
1142 var cell_data = this.clipboard;
1142 var cell_data = this.clipboard;
1143 var new_cell = this.insert_cell_above(cell_data.cell_type);
1143 var new_cell = this.insert_cell_above(cell_data.cell_type);
1144 new_cell.fromJSON(cell_data);
1144 new_cell.fromJSON(cell_data);
1145 new_cell.focus_cell();
1145 new_cell.focus_cell();
1146 }
1146 }
1147 };
1147 };
1148
1148
1149 /**
1149 /**
1150 * Paste a cell from the clipboard below the selected cell.
1150 * Paste a cell from the clipboard below the selected cell.
1151 *
1151 *
1152 * @method paste_cell_below
1152 * @method paste_cell_below
1153 */
1153 */
1154 Notebook.prototype.paste_cell_below = function () {
1154 Notebook.prototype.paste_cell_below = function () {
1155 if (this.clipboard !== null && this.paste_enabled) {
1155 if (this.clipboard !== null && this.paste_enabled) {
1156 var cell_data = this.clipboard;
1156 var cell_data = this.clipboard;
1157 var new_cell = this.insert_cell_below(cell_data.cell_type);
1157 var new_cell = this.insert_cell_below(cell_data.cell_type);
1158 new_cell.fromJSON(cell_data);
1158 new_cell.fromJSON(cell_data);
1159 new_cell.focus_cell();
1159 new_cell.focus_cell();
1160 }
1160 }
1161 };
1161 };
1162
1162
1163 // Split/merge
1163 // Split/merge
1164
1164
1165 /**
1165 /**
1166 * Split the selected cell into two, at the cursor.
1166 * Split the selected cell into two, at the cursor.
1167 *
1167 *
1168 * @method split_cell
1168 * @method split_cell
1169 */
1169 */
1170 Notebook.prototype.split_cell = function () {
1170 Notebook.prototype.split_cell = function () {
1171 var mdc = cells.MarkdownCell;
1171 var mdc = cells.MarkdownCell;
1172 var rc = cells.RawCell;
1172 var rc = cells.RawCell;
1173 var cell = this.get_selected_cell();
1173 var cell = this.get_selected_cell();
1174 if (cell.is_splittable()) {
1174 if (cell.is_splittable()) {
1175 var texta = cell.get_pre_cursor();
1175 var texta = cell.get_pre_cursor();
1176 var textb = cell.get_post_cursor();
1176 var textb = cell.get_post_cursor();
1177 if (cell instanceof codecell.CodeCell) {
1177 if (cell instanceof codecell.CodeCell) {
1178 // In this case the operations keep the notebook in its existing mode
1178 // In this case the operations keep the notebook in its existing mode
1179 // so we don't need to do any post-op mode changes.
1179 // so we don't need to do any post-op mode changes.
1180 cell.set_text(textb);
1180 cell.set_text(textb);
1181 var new_cell = this.insert_cell_above('code');
1181 var new_cell = this.insert_cell_above('code');
1182 new_cell.set_text(texta);
1182 new_cell.set_text(texta);
1183 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1183 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1184 // We know cell is !rendered so we can use set_text.
1184 // We know cell is !rendered so we can use set_text.
1185 cell.set_text(textb);
1185 cell.set_text(textb);
1186 var new_cell = this.insert_cell_above(cell.cell_type);
1186 var new_cell = this.insert_cell_above(cell.cell_type);
1187 // Unrender the new cell so we can call set_text.
1187 // Unrender the new cell so we can call set_text.
1188 new_cell.unrender();
1188 new_cell.unrender();
1189 new_cell.set_text(texta);
1189 new_cell.set_text(texta);
1190 }
1190 }
1191 }
1191 }
1192 };
1192 };
1193
1193
1194 /**
1194 /**
1195 * Combine the selected cell into the cell above it.
1195 * Combine the selected cell into the cell above it.
1196 *
1196 *
1197 * @method merge_cell_above
1197 * @method merge_cell_above
1198 */
1198 */
1199 Notebook.prototype.merge_cell_above = function () {
1199 Notebook.prototype.merge_cell_above = function () {
1200 var mdc = cells.MarkdownCell;
1200 var mdc = cells.MarkdownCell;
1201 var rc = cells.RawCell;
1201 var rc = cells.RawCell;
1202 var index = this.get_selected_index();
1202 var index = this.get_selected_index();
1203 var cell = this.get_cell(index);
1203 var cell = this.get_cell(index);
1204 var render = cell.rendered;
1204 var render = cell.rendered;
1205 if (!cell.is_mergeable()) {
1205 if (!cell.is_mergeable()) {
1206 return;
1206 return;
1207 }
1207 }
1208 if (index > 0) {
1208 if (index > 0) {
1209 var upper_cell = this.get_cell(index-1);
1209 var upper_cell = this.get_cell(index-1);
1210 if (!upper_cell.is_mergeable()) {
1210 if (!upper_cell.is_mergeable()) {
1211 return;
1211 return;
1212 }
1212 }
1213 var upper_text = upper_cell.get_text();
1213 var upper_text = upper_cell.get_text();
1214 var text = cell.get_text();
1214 var text = cell.get_text();
1215 if (cell instanceof codecell.CodeCell) {
1215 if (cell instanceof codecell.CodeCell) {
1216 cell.set_text(upper_text+'\n'+text);
1216 cell.set_text(upper_text+'\n'+text);
1217 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1217 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1218 cell.unrender(); // Must unrender before we set_text.
1218 cell.unrender(); // Must unrender before we set_text.
1219 cell.set_text(upper_text+'\n\n'+text);
1219 cell.set_text(upper_text+'\n\n'+text);
1220 if (render) {
1220 if (render) {
1221 // The rendered state of the final cell should match
1221 // The rendered state of the final cell should match
1222 // that of the original selected cell;
1222 // that of the original selected cell;
1223 cell.render();
1223 cell.render();
1224 }
1224 }
1225 }
1225 }
1226 this.delete_cell(index-1);
1226 this.delete_cell(index-1);
1227 this.select(this.find_cell_index(cell));
1227 this.select(this.find_cell_index(cell));
1228 }
1228 }
1229 };
1229 };
1230
1230
1231 /**
1231 /**
1232 * Combine the selected cell into the cell below it.
1232 * Combine the selected cell into the cell below it.
1233 *
1233 *
1234 * @method merge_cell_below
1234 * @method merge_cell_below
1235 */
1235 */
1236 Notebook.prototype.merge_cell_below = function () {
1236 Notebook.prototype.merge_cell_below = function () {
1237 var mdc = cells.MarkdownCell;
1237 var mdc = cells.MarkdownCell;
1238 var rc = cells.RawCell;
1238 var rc = cells.RawCell;
1239 var index = this.get_selected_index();
1239 var index = this.get_selected_index();
1240 var cell = this.get_cell(index);
1240 var cell = this.get_cell(index);
1241 var render = cell.rendered;
1241 var render = cell.rendered;
1242 if (!cell.is_mergeable()) {
1242 if (!cell.is_mergeable()) {
1243 return;
1243 return;
1244 }
1244 }
1245 if (index < this.ncells()-1) {
1245 if (index < this.ncells()-1) {
1246 var lower_cell = this.get_cell(index+1);
1246 var lower_cell = this.get_cell(index+1);
1247 if (!lower_cell.is_mergeable()) {
1247 if (!lower_cell.is_mergeable()) {
1248 return;
1248 return;
1249 }
1249 }
1250 var lower_text = lower_cell.get_text();
1250 var lower_text = lower_cell.get_text();
1251 var text = cell.get_text();
1251 var text = cell.get_text();
1252 if (cell instanceof codecell.CodeCell) {
1252 if (cell instanceof codecell.CodeCell) {
1253 cell.set_text(text+'\n'+lower_text);
1253 cell.set_text(text+'\n'+lower_text);
1254 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1254 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1255 cell.unrender(); // Must unrender before we set_text.
1255 cell.unrender(); // Must unrender before we set_text.
1256 cell.set_text(text+'\n\n'+lower_text);
1256 cell.set_text(text+'\n\n'+lower_text);
1257 if (render) {
1257 if (render) {
1258 // The rendered state of the final cell should match
1258 // The rendered state of the final cell should match
1259 // that of the original selected cell;
1259 // that of the original selected cell;
1260 cell.render();
1260 cell.render();
1261 }
1261 }
1262 }
1262 }
1263 this.delete_cell(index+1);
1263 this.delete_cell(index+1);
1264 this.select(this.find_cell_index(cell));
1264 this.select(this.find_cell_index(cell));
1265 }
1265 }
1266 };
1266 };
1267
1267
1268
1268
1269 // Cell collapsing and output clearing
1269 // Cell collapsing and output clearing
1270
1270
1271 /**
1271 /**
1272 * Hide a cell's output.
1272 * Hide a cell's output.
1273 *
1273 *
1274 * @method collapse_output
1274 * @method collapse_output
1275 * @param {Number} index A cell's numeric index
1275 * @param {Number} index A cell's numeric index
1276 */
1276 */
1277 Notebook.prototype.collapse_output = function (index) {
1277 Notebook.prototype.collapse_output = function (index) {
1278 var i = this.index_or_selected(index);
1278 var i = this.index_or_selected(index);
1279 var cell = this.get_cell(i);
1279 var cell = this.get_cell(i);
1280 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1280 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1281 cell.collapse_output();
1281 cell.collapse_output();
1282 this.set_dirty(true);
1282 this.set_dirty(true);
1283 }
1283 }
1284 };
1284 };
1285
1285
1286 /**
1286 /**
1287 * Hide each code cell's output area.
1287 * Hide each code cell's output area.
1288 *
1288 *
1289 * @method collapse_all_output
1289 * @method collapse_all_output
1290 */
1290 */
1291 Notebook.prototype.collapse_all_output = function () {
1291 Notebook.prototype.collapse_all_output = function () {
1292 $.map(this.get_cells(), function (cell, i) {
1292 $.map(this.get_cells(), function (cell, i) {
1293 if (cell instanceof codecell.CodeCell) {
1293 if (cell instanceof codecell.CodeCell) {
1294 cell.collapse_output();
1294 cell.collapse_output();
1295 }
1295 }
1296 });
1296 });
1297 // this should not be set if the `collapse` key is removed from nbformat
1297 // this should not be set if the `collapse` key is removed from nbformat
1298 this.set_dirty(true);
1298 this.set_dirty(true);
1299 };
1299 };
1300
1300
1301 /**
1301 /**
1302 * Show a cell's output.
1302 * Show a cell's output.
1303 *
1303 *
1304 * @method expand_output
1304 * @method expand_output
1305 * @param {Number} index A cell's numeric index
1305 * @param {Number} index A cell's numeric index
1306 */
1306 */
1307 Notebook.prototype.expand_output = function (index) {
1307 Notebook.prototype.expand_output = function (index) {
1308 var i = this.index_or_selected(index);
1308 var i = this.index_or_selected(index);
1309 var cell = this.get_cell(i);
1309 var cell = this.get_cell(i);
1310 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1310 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1311 cell.expand_output();
1311 cell.expand_output();
1312 this.set_dirty(true);
1312 this.set_dirty(true);
1313 }
1313 }
1314 };
1314 };
1315
1315
1316 /**
1316 /**
1317 * Expand each code cell's output area, and remove scrollbars.
1317 * Expand each code cell's output area, and remove scrollbars.
1318 *
1318 *
1319 * @method expand_all_output
1319 * @method expand_all_output
1320 */
1320 */
1321 Notebook.prototype.expand_all_output = function () {
1321 Notebook.prototype.expand_all_output = function () {
1322 $.map(this.get_cells(), function (cell, i) {
1322 $.map(this.get_cells(), function (cell, i) {
1323 if (cell instanceof codecell.CodeCell) {
1323 if (cell instanceof codecell.CodeCell) {
1324 cell.expand_output();
1324 cell.expand_output();
1325 }
1325 }
1326 });
1326 });
1327 // this should not be set if the `collapse` key is removed from nbformat
1327 // this should not be set if the `collapse` key is removed from nbformat
1328 this.set_dirty(true);
1328 this.set_dirty(true);
1329 };
1329 };
1330
1330
1331 /**
1331 /**
1332 * Clear the selected CodeCell's output area.
1332 * Clear the selected CodeCell's output area.
1333 *
1333 *
1334 * @method clear_output
1334 * @method clear_output
1335 * @param {Number} index A cell's numeric index
1335 * @param {Number} index A cell's numeric index
1336 */
1336 */
1337 Notebook.prototype.clear_output = function (index) {
1337 Notebook.prototype.clear_output = function (index) {
1338 var i = this.index_or_selected(index);
1338 var i = this.index_or_selected(index);
1339 var cell = this.get_cell(i);
1339 var cell = this.get_cell(i);
1340 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1340 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1341 cell.clear_output();
1341 cell.clear_output();
1342 this.set_dirty(true);
1342 this.set_dirty(true);
1343 }
1343 }
1344 };
1344 };
1345
1345
1346 /**
1346 /**
1347 * Clear each code cell's output area.
1347 * Clear each code cell's output area.
1348 *
1348 *
1349 * @method clear_all_output
1349 * @method clear_all_output
1350 */
1350 */
1351 Notebook.prototype.clear_all_output = function () {
1351 Notebook.prototype.clear_all_output = function () {
1352 $.map(this.get_cells(), function (cell, i) {
1352 $.map(this.get_cells(), function (cell, i) {
1353 if (cell instanceof codecell.CodeCell) {
1353 if (cell instanceof codecell.CodeCell) {
1354 cell.clear_output();
1354 cell.clear_output();
1355 }
1355 }
1356 });
1356 });
1357 this.set_dirty(true);
1357 this.set_dirty(true);
1358 };
1358 };
1359
1359
1360 /**
1360 /**
1361 * Scroll the selected CodeCell's output area.
1361 * Scroll the selected CodeCell's output area.
1362 *
1362 *
1363 * @method scroll_output
1363 * @method scroll_output
1364 * @param {Number} index A cell's numeric index
1364 * @param {Number} index A cell's numeric index
1365 */
1365 */
1366 Notebook.prototype.scroll_output = function (index) {
1366 Notebook.prototype.scroll_output = function (index) {
1367 var i = this.index_or_selected(index);
1367 var i = this.index_or_selected(index);
1368 var cell = this.get_cell(i);
1368 var cell = this.get_cell(i);
1369 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1369 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1370 cell.scroll_output();
1370 cell.scroll_output();
1371 this.set_dirty(true);
1371 this.set_dirty(true);
1372 }
1372 }
1373 };
1373 };
1374
1374
1375 /**
1375 /**
1376 * Expand each code cell's output area, and add a scrollbar for long output.
1376 * Expand each code cell's output area, and add a scrollbar for long output.
1377 *
1377 *
1378 * @method scroll_all_output
1378 * @method scroll_all_output
1379 */
1379 */
1380 Notebook.prototype.scroll_all_output = function () {
1380 Notebook.prototype.scroll_all_output = function () {
1381 $.map(this.get_cells(), function (cell, i) {
1381 $.map(this.get_cells(), function (cell, i) {
1382 if (cell instanceof codecell.CodeCell) {
1382 if (cell instanceof codecell.CodeCell) {
1383 cell.scroll_output();
1383 cell.scroll_output();
1384 }
1384 }
1385 });
1385 });
1386 // this should not be set if the `collapse` key is removed from nbformat
1386 // this should not be set if the `collapse` key is removed from nbformat
1387 this.set_dirty(true);
1387 this.set_dirty(true);
1388 };
1388 };
1389
1389
1390 /** Toggle whether a cell's output is collapsed or expanded.
1390 /** Toggle whether a cell's output is collapsed or expanded.
1391 *
1391 *
1392 * @method toggle_output
1392 * @method toggle_output
1393 * @param {Number} index A cell's numeric index
1393 * @param {Number} index A cell's numeric index
1394 */
1394 */
1395 Notebook.prototype.toggle_output = function (index) {
1395 Notebook.prototype.toggle_output = function (index) {
1396 var i = this.index_or_selected(index);
1396 var i = this.index_or_selected(index);
1397 var cell = this.get_cell(i);
1397 var cell = this.get_cell(i);
1398 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1398 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1399 cell.toggle_output();
1399 cell.toggle_output();
1400 this.set_dirty(true);
1400 this.set_dirty(true);
1401 }
1401 }
1402 };
1402 };
1403
1403
1404 /**
1404 /**
1405 * Hide/show the output of all cells.
1405 * Hide/show the output of all cells.
1406 *
1406 *
1407 * @method toggle_all_output
1407 * @method toggle_all_output
1408 */
1408 */
1409 Notebook.prototype.toggle_all_output = function () {
1409 Notebook.prototype.toggle_all_output = function () {
1410 $.map(this.get_cells(), function (cell, i) {
1410 $.map(this.get_cells(), function (cell, i) {
1411 if (cell instanceof codecell.CodeCell) {
1411 if (cell instanceof codecell.CodeCell) {
1412 cell.toggle_output();
1412 cell.toggle_output();
1413 }
1413 }
1414 });
1414 });
1415 // this should not be set if the `collapse` key is removed from nbformat
1415 // this should not be set if the `collapse` key is removed from nbformat
1416 this.set_dirty(true);
1416 this.set_dirty(true);
1417 };
1417 };
1418
1418
1419 /**
1419 /**
1420 * Toggle a scrollbar for long cell outputs.
1420 * Toggle a scrollbar for long cell outputs.
1421 *
1421 *
1422 * @method toggle_output_scroll
1422 * @method toggle_output_scroll
1423 * @param {Number} index A cell's numeric index
1423 * @param {Number} index A cell's numeric index
1424 */
1424 */
1425 Notebook.prototype.toggle_output_scroll = function (index) {
1425 Notebook.prototype.toggle_output_scroll = function (index) {
1426 var i = this.index_or_selected(index);
1426 var i = this.index_or_selected(index);
1427 var cell = this.get_cell(i);
1427 var cell = this.get_cell(i);
1428 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1428 if (cell !== null && (cell instanceof codecell.CodeCell)) {
1429 cell.toggle_output_scroll();
1429 cell.toggle_output_scroll();
1430 this.set_dirty(true);
1430 this.set_dirty(true);
1431 }
1431 }
1432 };
1432 };
1433
1433
1434 /**
1434 /**
1435 * Toggle the scrolling of long output on all cells.
1435 * Toggle the scrolling of long output on all cells.
1436 *
1436 *
1437 * @method toggle_all_output_scrolling
1437 * @method toggle_all_output_scrolling
1438 */
1438 */
1439 Notebook.prototype.toggle_all_output_scroll = function () {
1439 Notebook.prototype.toggle_all_output_scroll = function () {
1440 $.map(this.get_cells(), function (cell, i) {
1440 $.map(this.get_cells(), function (cell, i) {
1441 if (cell instanceof codecell.CodeCell) {
1441 if (cell instanceof codecell.CodeCell) {
1442 cell.toggle_output_scroll();
1442 cell.toggle_output_scroll();
1443 }
1443 }
1444 });
1444 });
1445 // this should not be set if the `collapse` key is removed from nbformat
1445 // this should not be set if the `collapse` key is removed from nbformat
1446 this.set_dirty(true);
1446 this.set_dirty(true);
1447 };
1447 };
1448
1448
1449 // Other cell functions: line numbers, ...
1449 // Other cell functions: line numbers, ...
1450
1450
1451 /**
1451 /**
1452 * Toggle line numbers in the selected cell's input area.
1452 * Toggle line numbers in the selected cell's input area.
1453 *
1453 *
1454 * @method cell_toggle_line_numbers
1454 * @method cell_toggle_line_numbers
1455 */
1455 */
1456 Notebook.prototype.cell_toggle_line_numbers = function() {
1456 Notebook.prototype.cell_toggle_line_numbers = function() {
1457 this.get_selected_cell().toggle_line_numbers();
1457 this.get_selected_cell().toggle_line_numbers();
1458 };
1458 };
1459
1459
1460 // Session related things
1460 // Session related things
1461
1461
1462 /**
1462 /**
1463 * Start a new session and set it on each code cell.
1463 * Start a new session and set it on each code cell.
1464 *
1464 *
1465 * @method start_session
1465 * @method start_session
1466 */
1466 */
1467 Notebook.prototype.start_session = function () {
1467 Notebook.prototype.start_session = function () {
1468 this.session = new session.Session(this, this.options);
1468 this.session = new session.Session(this, this.options);
1469 this.session.start($.proxy(this._session_started, this));
1469 this.session.start($.proxy(this._session_started, this));
1470 };
1470 };
1471
1471
1472
1472
1473 /**
1473 /**
1474 * Once a session is started, link the code cells to the kernel and pass the
1474 * Once a session is started, link the code cells to the kernel and pass the
1475 * comm manager to the widget manager
1475 * comm manager to the widget manager
1476 *
1476 *
1477 */
1477 */
1478 Notebook.prototype._session_started = function(){
1478 Notebook.prototype._session_started = function(){
1479 this.kernel = this.session.kernel;
1479 this.kernel = this.session.kernel;
1480 var ncells = this.ncells();
1480 var ncells = this.ncells();
1481 for (var i=0; i<ncells; i++) {
1481 for (var i=0; i<ncells; i++) {
1482 var cell = this.get_cell(i);
1482 var cell = this.get_cell(i);
1483 if (cell instanceof codecell.CodeCell) {
1483 if (cell instanceof codecell.CodeCell) {
1484 cell.set_kernel(this.session.kernel);
1484 cell.set_kernel(this.session.kernel);
1485 }
1485 }
1486 }
1486 }
1487 };
1487 };
1488
1488
1489 /**
1489 /**
1490 * Prompt the user to restart the IPython kernel.
1490 * Prompt the user to restart the IPython kernel.
1491 *
1491 *
1492 * @method restart_kernel
1492 * @method restart_kernel
1493 */
1493 */
1494 Notebook.prototype.restart_kernel = function () {
1494 Notebook.prototype.restart_kernel = function () {
1495 var that = this;
1495 var that = this;
1496 dialog.modal({
1496 dialog.modal({
1497 title : "Restart kernel or continue running?",
1497 title : "Restart kernel or continue running?",
1498 body : $("<p/>").text(
1498 body : $("<p/>").text(
1499 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1499 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1500 ),
1500 ),
1501 buttons : {
1501 buttons : {
1502 "Continue running" : {},
1502 "Continue running" : {},
1503 "Restart" : {
1503 "Restart" : {
1504 "class" : "btn-danger",
1504 "class" : "btn-danger",
1505 "click" : function() {
1505 "click" : function() {
1506 that.session.restart_kernel();
1506 that.session.restart_kernel();
1507 }
1507 }
1508 }
1508 }
1509 }
1509 }
1510 });
1510 });
1511 };
1511 };
1512
1512
1513 /**
1513 /**
1514 * Execute or render cell outputs and go into command mode.
1514 * Execute or render cell outputs and go into command mode.
1515 *
1515 *
1516 * @method execute_cell
1516 * @method execute_cell
1517 */
1517 */
1518 Notebook.prototype.execute_cell = function () {
1518 Notebook.prototype.execute_cell = function () {
1519 // mode = shift, ctrl, alt
1519 // mode = shift, ctrl, alt
1520 var cell = this.get_selected_cell();
1520 var cell = this.get_selected_cell();
1521 var cell_index = this.find_cell_index(cell);
1521 var cell_index = this.find_cell_index(cell);
1522
1522
1523 cell.execute();
1523 cell.execute();
1524 this.command_mode();
1524 this.command_mode();
1525 this.set_dirty(true);
1525 this.set_dirty(true);
1526 };
1526 };
1527
1527
1528 /**
1528 /**
1529 * Execute or render cell outputs and insert a new cell below.
1529 * Execute or render cell outputs and insert a new cell below.
1530 *
1530 *
1531 * @method execute_cell_and_insert_below
1531 * @method execute_cell_and_insert_below
1532 */
1532 */
1533 Notebook.prototype.execute_cell_and_insert_below = function () {
1533 Notebook.prototype.execute_cell_and_insert_below = function () {
1534 var cell = this.get_selected_cell();
1534 var cell = this.get_selected_cell();
1535 var cell_index = this.find_cell_index(cell);
1535 var cell_index = this.find_cell_index(cell);
1536
1536
1537 cell.execute();
1537 cell.execute();
1538
1538
1539 // If we are at the end always insert a new cell and return
1539 // If we are at the end always insert a new cell and return
1540 if (cell_index === (this.ncells()-1)) {
1540 if (cell_index === (this.ncells()-1)) {
1541 this.command_mode();
1541 this.command_mode();
1542 this.insert_cell_below();
1542 this.insert_cell_below();
1543 this.select(cell_index+1);
1543 this.select(cell_index+1);
1544 this.edit_mode();
1544 this.edit_mode();
1545 this.scroll_to_bottom();
1545 this.scroll_to_bottom();
1546 this.set_dirty(true);
1546 this.set_dirty(true);
1547 return;
1547 return;
1548 }
1548 }
1549
1549
1550 this.command_mode();
1550 this.command_mode();
1551 this.insert_cell_below();
1551 this.insert_cell_below();
1552 this.select(cell_index+1);
1552 this.select(cell_index+1);
1553 this.edit_mode();
1553 this.edit_mode();
1554 this.set_dirty(true);
1554 this.set_dirty(true);
1555 };
1555 };
1556
1556
1557 /**
1557 /**
1558 * Execute or render cell outputs and select the next cell.
1558 * Execute or render cell outputs and select the next cell.
1559 *
1559 *
1560 * @method execute_cell_and_select_below
1560 * @method execute_cell_and_select_below
1561 */
1561 */
1562 Notebook.prototype.execute_cell_and_select_below = function () {
1562 Notebook.prototype.execute_cell_and_select_below = function () {
1563
1563
1564 var cell = this.get_selected_cell();
1564 var cell = this.get_selected_cell();
1565 var cell_index = this.find_cell_index(cell);
1565 var cell_index = this.find_cell_index(cell);
1566
1566
1567 cell.execute();
1567 cell.execute();
1568
1568
1569 // If we are at the end always insert a new cell and return
1569 // If we are at the end always insert a new cell and return
1570 if (cell_index === (this.ncells()-1)) {
1570 if (cell_index === (this.ncells()-1)) {
1571 this.command_mode();
1571 this.command_mode();
1572 this.insert_cell_below();
1572 this.insert_cell_below();
1573 this.select(cell_index+1);
1573 this.select(cell_index+1);
1574 this.edit_mode();
1574 this.edit_mode();
1575 this.scroll_to_bottom();
1575 this.scroll_to_bottom();
1576 this.set_dirty(true);
1576 this.set_dirty(true);
1577 return;
1577 return;
1578 }
1578 }
1579
1579
1580 this.command_mode();
1580 this.command_mode();
1581 this.select(cell_index+1);
1581 this.select(cell_index+1);
1582 this.focus_cell();
1582 this.focus_cell();
1583 this.set_dirty(true);
1583 this.set_dirty(true);
1584 };
1584 };
1585
1585
1586 /**
1586 /**
1587 * Execute all cells below the selected cell.
1587 * Execute all cells below the selected cell.
1588 *
1588 *
1589 * @method execute_cells_below
1589 * @method execute_cells_below
1590 */
1590 */
1591 Notebook.prototype.execute_cells_below = function () {
1591 Notebook.prototype.execute_cells_below = function () {
1592 this.execute_cell_range(this.get_selected_index(), this.ncells());
1592 this.execute_cell_range(this.get_selected_index(), this.ncells());
1593 this.scroll_to_bottom();
1593 this.scroll_to_bottom();
1594 };
1594 };
1595
1595
1596 /**
1596 /**
1597 * Execute all cells above the selected cell.
1597 * Execute all cells above the selected cell.
1598 *
1598 *
1599 * @method execute_cells_above
1599 * @method execute_cells_above
1600 */
1600 */
1601 Notebook.prototype.execute_cells_above = function () {
1601 Notebook.prototype.execute_cells_above = function () {
1602 this.execute_cell_range(0, this.get_selected_index());
1602 this.execute_cell_range(0, this.get_selected_index());
1603 };
1603 };
1604
1604
1605 /**
1605 /**
1606 * Execute all cells.
1606 * Execute all cells.
1607 *
1607 *
1608 * @method execute_all_cells
1608 * @method execute_all_cells
1609 */
1609 */
1610 Notebook.prototype.execute_all_cells = function () {
1610 Notebook.prototype.execute_all_cells = function () {
1611 this.execute_cell_range(0, this.ncells());
1611 this.execute_cell_range(0, this.ncells());
1612 this.scroll_to_bottom();
1612 this.scroll_to_bottom();
1613 };
1613 };
1614
1614
1615 /**
1615 /**
1616 * Execute a contiguous range of cells.
1616 * Execute a contiguous range of cells.
1617 *
1617 *
1618 * @method execute_cell_range
1618 * @method execute_cell_range
1619 * @param {Number} start Index of the first cell to execute (inclusive)
1619 * @param {Number} start Index of the first cell to execute (inclusive)
1620 * @param {Number} end Index of the last cell to execute (exclusive)
1620 * @param {Number} end Index of the last cell to execute (exclusive)
1621 */
1621 */
1622 Notebook.prototype.execute_cell_range = function (start, end) {
1622 Notebook.prototype.execute_cell_range = function (start, end) {
1623 this.command_mode();
1623 this.command_mode();
1624 for (var i=start; i<end; i++) {
1624 for (var i=start; i<end; i++) {
1625 this.select(i);
1625 this.select(i);
1626 this.execute_cell();
1626 this.execute_cell();
1627 }
1627 }
1628 };
1628 };
1629
1629
1630 // Persistance and loading
1630 // Persistance and loading
1631
1631
1632 /**
1632 /**
1633 * Getter method for this notebook's name.
1633 * Getter method for this notebook's name.
1634 *
1634 *
1635 * @method get_notebook_name
1635 * @method get_notebook_name
1636 * @return {String} This notebook's name (excluding file extension)
1636 * @return {String} This notebook's name (excluding file extension)
1637 */
1637 */
1638 Notebook.prototype.get_notebook_name = function () {
1638 Notebook.prototype.get_notebook_name = function () {
1639 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1639 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1640 return nbname;
1640 return nbname;
1641 };
1641 };
1642
1642
1643 /**
1643 /**
1644 * Setter method for this notebook's name.
1644 * Setter method for this notebook's name.
1645 *
1645 *
1646 * @method set_notebook_name
1646 * @method set_notebook_name
1647 * @param {String} name A new name for this notebook
1647 * @param {String} name A new name for this notebook
1648 */
1648 */
1649 Notebook.prototype.set_notebook_name = function (name) {
1649 Notebook.prototype.set_notebook_name = function (name) {
1650 this.notebook_name = name;
1650 this.notebook_name = name;
1651 };
1651 };
1652
1652
1653 /**
1653 /**
1654 * Check that a notebook's name is valid.
1654 * Check that a notebook's name is valid.
1655 *
1655 *
1656 * @method test_notebook_name
1656 * @method test_notebook_name
1657 * @param {String} nbname A name for this notebook
1657 * @param {String} nbname A name for this notebook
1658 * @return {Boolean} True if the name is valid, false if invalid
1658 * @return {Boolean} True if the name is valid, false if invalid
1659 */
1659 */
1660 Notebook.prototype.test_notebook_name = function (nbname) {
1660 Notebook.prototype.test_notebook_name = function (nbname) {
1661 nbname = nbname || '';
1661 nbname = nbname || '';
1662 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1662 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1663 return true;
1663 return true;
1664 } else {
1664 } else {
1665 return false;
1665 return false;
1666 }
1666 }
1667 };
1667 };
1668
1668
1669 /**
1669 /**
1670 * Load a notebook from JSON (.ipynb).
1670 * Load a notebook from JSON (.ipynb).
1671 *
1671 *
1672 * This currently handles one worksheet: others are deleted.
1672 * This currently handles one worksheet: others are deleted.
1673 *
1673 *
1674 * @method fromJSON
1674 * @method fromJSON
1675 * @param {Object} data JSON representation of a notebook
1675 * @param {Object} data JSON representation of a notebook
1676 */
1676 */
1677 Notebook.prototype.fromJSON = function (data) {
1677 Notebook.prototype.fromJSON = function (data) {
1678 var content = data.content;
1678 var content = data.content;
1679 var ncells = this.ncells();
1679 var ncells = this.ncells();
1680 var i;
1680 var i;
1681 for (i=0; i<ncells; i++) {
1681 for (i=0; i<ncells; i++) {
1682 // Always delete cell 0 as they get renumbered as they are deleted.
1682 // Always delete cell 0 as they get renumbered as they are deleted.
1683 this.delete_cell(0);
1683 this.delete_cell(0);
1684 }
1684 }
1685 // Save the metadata and name.
1685 // Save the metadata and name.
1686 this.metadata = content.metadata;
1686 this.metadata = content.metadata;
1687 this.notebook_name = data.name;
1687 this.notebook_name = data.name;
1688 var trusted = true;
1688 var trusted = true;
1689 // Only handle 1 worksheet for now.
1689 // Only handle 1 worksheet for now.
1690 var worksheet = content.worksheets[0];
1690 var worksheet = content.worksheets[0];
1691 if (worksheet !== undefined) {
1691 if (worksheet !== undefined) {
1692 if (worksheet.metadata) {
1692 if (worksheet.metadata) {
1693 this.worksheet_metadata = worksheet.metadata;
1693 this.worksheet_metadata = worksheet.metadata;
1694 }
1694 }
1695 var new_cells = worksheet.cells;
1695 var new_cells = worksheet.cells;
1696 ncells = new_cells.length;
1696 ncells = new_cells.length;
1697 var cell_data = null;
1697 var cell_data = null;
1698 var new_cell = null;
1698 var new_cell = null;
1699 for (i=0; i<ncells; i++) {
1699 for (i=0; i<ncells; i++) {
1700 cell_data = new_cells[i];
1700 cell_data = new_cells[i];
1701 // VERSIONHACK: plaintext -> raw
1701 // VERSIONHACK: plaintext -> raw
1702 // handle never-released plaintext name for raw cells
1702 // handle never-released plaintext name for raw cells
1703 if (cell_data.cell_type === 'plaintext'){
1703 if (cell_data.cell_type === 'plaintext'){
1704 cell_data.cell_type = 'raw';
1704 cell_data.cell_type = 'raw';
1705 }
1705 }
1706
1706
1707 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1707 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1708 new_cell.fromJSON(cell_data);
1708 new_cell.fromJSON(cell_data);
1709 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1709 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1710 trusted = false;
1710 trusted = false;
1711 }
1711 }
1712 }
1712 }
1713 }
1713 }
1714 if (trusted != this.trusted) {
1714 if (trusted != this.trusted) {
1715 this.trusted = trusted;
1715 this.trusted = trusted;
1716 this.events.trigger("trust_changed.Notebook", trusted);
1716 this.events.trigger("trust_changed.Notebook", trusted);
1717 }
1717 }
1718 if (content.worksheets.length > 1) {
1718 if (content.worksheets.length > 1) {
1719 dialog.modal({
1719 dialog.modal({
1720 title : "Multiple worksheets",
1720 title : "Multiple worksheets",
1721 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1721 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1722 "but this version of IPython can only handle the first. " +
1722 "but this version of IPython can only handle the first. " +
1723 "If you save this notebook, worksheets after the first will be lost.",
1723 "If you save this notebook, worksheets after the first will be lost.",
1724 buttons : {
1724 buttons : {
1725 OK : {
1725 OK : {
1726 class : "btn-danger"
1726 class : "btn-danger"
1727 }
1727 }
1728 }
1728 }
1729 });
1729 });
1730 }
1730 }
1731 };
1731 };
1732
1732
1733 /**
1733 /**
1734 * Dump this notebook into a JSON-friendly object.
1734 * Dump this notebook into a JSON-friendly object.
1735 *
1735 *
1736 * @method toJSON
1736 * @method toJSON
1737 * @return {Object} A JSON-friendly representation of this notebook.
1737 * @return {Object} A JSON-friendly representation of this notebook.
1738 */
1738 */
1739 Notebook.prototype.toJSON = function () {
1739 Notebook.prototype.toJSON = function () {
1740 var cells = this.get_cells();
1740 var cells = this.get_cells();
1741 var ncells = cells.length;
1741 var ncells = cells.length;
1742 var cell_array = new Array(ncells);
1742 var cell_array = new Array(ncells);
1743 var trusted = true;
1743 var trusted = true;
1744 for (var i=0; i<ncells; i++) {
1744 for (var i=0; i<ncells; i++) {
1745 var cell = cells[i];
1745 var cell = cells[i];
1746 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1746 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1747 trusted = false;
1747 trusted = false;
1748 }
1748 }
1749 cell_array[i] = cell.toJSON();
1749 cell_array[i] = cell.toJSON();
1750 }
1750 }
1751 var data = {
1751 var data = {
1752 // Only handle 1 worksheet for now.
1752 // Only handle 1 worksheet for now.
1753 worksheets : [{
1753 worksheets : [{
1754 cells: cell_array,
1754 cells: cell_array,
1755 metadata: this.worksheet_metadata
1755 metadata: this.worksheet_metadata
1756 }],
1756 }],
1757 metadata : this.metadata
1757 metadata : this.metadata
1758 };
1758 };
1759 if (trusted != this.trusted) {
1759 if (trusted != this.trusted) {
1760 this.trusted = trusted;
1760 this.trusted = trusted;
1761 this.events.trigger("trust_changed.Notebook", trusted);
1761 this.events.trigger("trust_changed.Notebook", trusted);
1762 }
1762 }
1763 return data;
1763 return data;
1764 };
1764 };
1765
1765
1766 /**
1766 /**
1767 * Start an autosave timer, for periodically saving the notebook.
1767 * Start an autosave timer, for periodically saving the notebook.
1768 *
1768 *
1769 * @method set_autosave_interval
1769 * @method set_autosave_interval
1770 * @param {Integer} interval the autosave interval in milliseconds
1770 * @param {Integer} interval the autosave interval in milliseconds
1771 */
1771 */
1772 Notebook.prototype.set_autosave_interval = function (interval) {
1772 Notebook.prototype.set_autosave_interval = function (interval) {
1773 var that = this;
1773 var that = this;
1774 // clear previous interval, so we don't get simultaneous timers
1774 // clear previous interval, so we don't get simultaneous timers
1775 if (this.autosave_timer) {
1775 if (this.autosave_timer) {
1776 clearInterval(this.autosave_timer);
1776 clearInterval(this.autosave_timer);
1777 }
1777 }
1778
1778
1779 this.autosave_interval = this.minimum_autosave_interval = interval;
1779 this.autosave_interval = this.minimum_autosave_interval = interval;
1780 if (interval) {
1780 if (interval) {
1781 this.autosave_timer = setInterval(function() {
1781 this.autosave_timer = setInterval(function() {
1782 if (that.dirty) {
1782 if (that.dirty) {
1783 that.save_notebook();
1783 that.save_notebook();
1784 }
1784 }
1785 }, interval);
1785 }, interval);
1786 this.events.trigger("autosave_enabled.Notebook", interval);
1786 this.events.trigger("autosave_enabled.Notebook", interval);
1787 } else {
1787 } else {
1788 this.autosave_timer = null;
1788 this.autosave_timer = null;
1789 this.events.trigger("autosave_disabled.Notebook");
1789 this.events.trigger("autosave_disabled.Notebook");
1790 }
1790 }
1791 };
1791 };
1792
1792
1793 /**
1793 /**
1794 * Save this notebook on the server. This becomes a notebook instance's
1794 * Save this notebook on the server. This becomes a notebook instance's
1795 * .save_notebook method *after* the entire notebook has been loaded.
1795 * .save_notebook method *after* the entire notebook has been loaded.
1796 *
1796 *
1797 * @method save_notebook
1797 * @method save_notebook
1798 */
1798 */
1799 Notebook.prototype.save_notebook = function (extra_settings) {
1799 Notebook.prototype.save_notebook = function (extra_settings) {
1800 // Create a JSON model to be sent to the server.
1800 // Create a JSON model to be sent to the server.
1801 var model = {};
1801 var model = {};
1802 model.name = this.notebook_name;
1802 model.name = this.notebook_name;
1803 model.path = this.notebook_path;
1803 model.path = this.notebook_path;
1804 model.content = this.toJSON();
1804 model.content = this.toJSON();
1805 model.content.nbformat = this.nbformat;
1805 model.content.nbformat = this.nbformat;
1806 model.content.nbformat_minor = this.nbformat_minor;
1806 model.content.nbformat_minor = this.nbformat_minor;
1807 // time the ajax call for autosave tuning purposes.
1807 // time the ajax call for autosave tuning purposes.
1808 var start = new Date().getTime();
1808 var start = new Date().getTime();
1809 // We do the call with settings so we can set cache to false.
1809 // We do the call with settings so we can set cache to false.
1810 var settings = {
1810 var settings = {
1811 processData : false,
1811 processData : false,
1812 cache : false,
1812 cache : false,
1813 type : "PUT",
1813 type : "PUT",
1814 data : JSON.stringify(model),
1814 data : JSON.stringify(model),
1815 headers : {'Content-Type': 'application/json'},
1815 headers : {'Content-Type': 'application/json'},
1816 success : $.proxy(this.save_notebook_success, this, start),
1816 success : $.proxy(this.save_notebook_success, this, start),
1817 error : $.proxy(this.save_notebook_error, this)
1817 error : $.proxy(this.save_notebook_error, this)
1818 };
1818 };
1819 if (extra_settings) {
1819 if (extra_settings) {
1820 for (var key in extra_settings) {
1820 for (var key in extra_settings) {
1821 settings[key] = extra_settings[key];
1821 settings[key] = extra_settings[key];
1822 }
1822 }
1823 }
1823 }
1824 this.events.trigger('notebook_saving.Notebook');
1824 this.events.trigger('notebook_saving.Notebook');
1825 var url = utils.url_join_encode(
1825 var url = utils.url_join_encode(
1826 this.base_url,
1826 this.base_url,
1827 'api/notebooks',
1827 'api/notebooks',
1828 this.notebook_path,
1828 this.notebook_path,
1829 this.notebook_name
1829 this.notebook_name
1830 );
1830 );
1831 $.ajax(url, settings);
1831 $.ajax(url, settings);
1832 };
1832 };
1833
1833
1834 /**
1834 /**
1835 * Success callback for saving a notebook.
1835 * Success callback for saving a notebook.
1836 *
1836 *
1837 * @method save_notebook_success
1837 * @method save_notebook_success
1838 * @param {Integer} start the time when the save request started
1838 * @param {Integer} start the time when the save request started
1839 * @param {Object} data JSON representation of a notebook
1839 * @param {Object} data JSON representation of a notebook
1840 * @param {String} status Description of response status
1840 * @param {String} status Description of response status
1841 * @param {jqXHR} xhr jQuery Ajax object
1841 * @param {jqXHR} xhr jQuery Ajax object
1842 */
1842 */
1843 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1843 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1844 this.set_dirty(false);
1844 this.set_dirty(false);
1845 this.events.trigger('notebook_saved.Notebook');
1845 this.events.trigger('notebook_saved.Notebook');
1846 this._update_autosave_interval(start);
1846 this._update_autosave_interval(start);
1847 if (this._checkpoint_after_save) {
1847 if (this._checkpoint_after_save) {
1848 this.create_checkpoint();
1848 this.create_checkpoint();
1849 this._checkpoint_after_save = false;
1849 this._checkpoint_after_save = false;
1850 }
1850 }
1851 };
1851 };
1852
1852
1853 /**
1853 /**
1854 * update the autosave interval based on how long the last save took
1854 * update the autosave interval based on how long the last save took
1855 *
1855 *
1856 * @method _update_autosave_interval
1856 * @method _update_autosave_interval
1857 * @param {Integer} timestamp when the save request started
1857 * @param {Integer} timestamp when the save request started
1858 */
1858 */
1859 Notebook.prototype._update_autosave_interval = function (start) {
1859 Notebook.prototype._update_autosave_interval = function (start) {
1860 var duration = (new Date().getTime() - start);
1860 var duration = (new Date().getTime() - start);
1861 if (this.autosave_interval) {
1861 if (this.autosave_interval) {
1862 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1862 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1863 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1863 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1864 // round to 10 seconds, otherwise we will be setting a new interval too often
1864 // round to 10 seconds, otherwise we will be setting a new interval too often
1865 interval = 10000 * Math.round(interval / 10000);
1865 interval = 10000 * Math.round(interval / 10000);
1866 // set new interval, if it's changed
1866 // set new interval, if it's changed
1867 if (interval != this.autosave_interval) {
1867 if (interval != this.autosave_interval) {
1868 this.set_autosave_interval(interval);
1868 this.set_autosave_interval(interval);
1869 }
1869 }
1870 }
1870 }
1871 };
1871 };
1872
1872
1873 /**
1873 /**
1874 * Failure callback for saving a notebook.
1874 * Failure callback for saving a notebook.
1875 *
1875 *
1876 * @method save_notebook_error
1876 * @method save_notebook_error
1877 * @param {jqXHR} xhr jQuery Ajax object
1877 * @param {jqXHR} xhr jQuery Ajax object
1878 * @param {String} status Description of response status
1878 * @param {String} status Description of response status
1879 * @param {String} error HTTP error message
1879 * @param {String} error HTTP error message
1880 */
1880 */
1881 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1881 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1882 this.events.trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1882 this.events.trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1883 };
1883 };
1884
1884
1885 /**
1885 /**
1886 * Explicitly trust the output of this notebook.
1886 * Explicitly trust the output of this notebook.
1887 *
1887 *
1888 * @method trust_notebook
1888 * @method trust_notebook
1889 */
1889 */
1890 Notebook.prototype.trust_notebook = function (extra_settings) {
1890 Notebook.prototype.trust_notebook = function (extra_settings) {
1891 var body = $("<div>").append($("<p>")
1891 var body = $("<div>").append($("<p>")
1892 .text("A trusted IPython notebook may execute hidden malicious code ")
1892 .text("A trusted IPython notebook may execute hidden malicious code ")
1893 .append($("<strong>")
1893 .append($("<strong>")
1894 .append(
1894 .append(
1895 $("<em>").text("when you open it")
1895 $("<em>").text("when you open it")
1896 )
1896 )
1897 ).append(".").append(
1897 ).append(".").append(
1898 " Selecting trust will immediately reload this notebook in a trusted state."
1898 " Selecting trust will immediately reload this notebook in a trusted state."
1899 ).append(
1899 ).append(
1900 " For more information, see the "
1900 " For more information, see the "
1901 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
1901 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
1902 .text("IPython security documentation")
1902 .text("IPython security documentation")
1903 ).append(".")
1903 ).append(".")
1904 );
1904 );
1905
1905
1906 var nb = this;
1906 var nb = this;
1907 dialog.modal({
1907 dialog.modal({
1908 title: "Trust this notebook?",
1908 title: "Trust this notebook?",
1909 body: body,
1909 body: body,
1910
1910
1911 buttons: {
1911 buttons: {
1912 Cancel : {},
1912 Cancel : {},
1913 Trust : {
1913 Trust : {
1914 class : "btn-danger",
1914 class : "btn-danger",
1915 click : function () {
1915 click : function () {
1916 var cells = nb.get_cells();
1916 var cells = nb.get_cells();
1917 for (var i = 0; i < cells.length; i++) {
1917 for (var i = 0; i < cells.length; i++) {
1918 var cell = cells[i];
1918 var cell = cells[i];
1919 if (cell.cell_type == 'code') {
1919 if (cell.cell_type == 'code') {
1920 cell.output_area.trusted = true;
1920 cell.output_area.trusted = true;
1921 }
1921 }
1922 }
1922 }
1923 this.events.on('notebook_saved.Notebook', function () {
1923 this.events.on('notebook_saved.Notebook', function () {
1924 window.location.reload();
1924 window.location.reload();
1925 });
1925 });
1926 nb.save_notebook();
1926 nb.save_notebook();
1927 }
1927 }
1928 }
1928 }
1929 }
1929 }
1930 });
1930 });
1931 };
1931 };
1932
1932
1933 Notebook.prototype.new_notebook = function(){
1933 Notebook.prototype.new_notebook = function(){
1934 var path = this.notebook_path;
1934 var path = this.notebook_path;
1935 var base_url = this.base_url;
1935 var base_url = this.base_url;
1936 var settings = {
1936 var settings = {
1937 processData : false,
1937 processData : false,
1938 cache : false,
1938 cache : false,
1939 type : "POST",
1939 type : "POST",
1940 dataType : "json",
1940 dataType : "json",
1941 async : false,
1941 async : false,
1942 success : function (data, status, xhr){
1942 success : function (data, status, xhr){
1943 var notebook_name = data.name;
1943 var notebook_name = data.name;
1944 window.open(
1944 window.open(
1945 utils.url_join_encode(
1945 utils.url_join_encode(
1946 base_url,
1946 base_url,
1947 'notebooks',
1947 'notebooks',
1948 path,
1948 path,
1949 notebook_name
1949 notebook_name
1950 ),
1950 ),
1951 '_blank'
1951 '_blank'
1952 );
1952 );
1953 },
1953 },
1954 error : utils.log_ajax_error,
1954 error : utils.log_ajax_error,
1955 };
1955 };
1956 var url = utils.url_join_encode(
1956 var url = utils.url_join_encode(
1957 base_url,
1957 base_url,
1958 'api/notebooks',
1958 'api/notebooks',
1959 path
1959 path
1960 );
1960 );
1961 $.ajax(url,settings);
1961 $.ajax(url,settings);
1962 };
1962 };
1963
1963
1964
1964
1965 Notebook.prototype.copy_notebook = function(){
1965 Notebook.prototype.copy_notebook = function(){
1966 var path = this.notebook_path;
1966 var path = this.notebook_path;
1967 var base_url = this.base_url;
1967 var base_url = this.base_url;
1968 var settings = {
1968 var settings = {
1969 processData : false,
1969 processData : false,
1970 cache : false,
1970 cache : false,
1971 type : "POST",
1971 type : "POST",
1972 dataType : "json",
1972 dataType : "json",
1973 data : JSON.stringify({copy_from : this.notebook_name}),
1973 data : JSON.stringify({copy_from : this.notebook_name}),
1974 async : false,
1974 async : false,
1975 success : function (data, status, xhr) {
1975 success : function (data, status, xhr) {
1976 window.open(utils.url_join_encode(
1976 window.open(utils.url_join_encode(
1977 base_url,
1977 base_url,
1978 'notebooks',
1978 'notebooks',
1979 data.path,
1979 data.path,
1980 data.name
1980 data.name
1981 ), '_blank');
1981 ), '_blank');
1982 },
1982 },
1983 error : utils.log_ajax_error,
1983 error : utils.log_ajax_error,
1984 };
1984 };
1985 var url = utils.url_join_encode(
1985 var url = utils.url_join_encode(
1986 base_url,
1986 base_url,
1987 'api/notebooks',
1987 'api/notebooks',
1988 path
1988 path
1989 );
1989 );
1990 $.ajax(url,settings);
1990 $.ajax(url,settings);
1991 };
1991 };
1992
1992
1993 Notebook.prototype.rename = function (nbname) {
1993 Notebook.prototype.rename = function (nbname) {
1994 var that = this;
1994 var that = this;
1995 if (!nbname.match(/\.ipynb$/)) {
1995 if (!nbname.match(/\.ipynb$/)) {
1996 nbname = nbname + ".ipynb";
1996 nbname = nbname + ".ipynb";
1997 }
1997 }
1998 var data = {name: nbname};
1998 var data = {name: nbname};
1999 var settings = {
1999 var settings = {
2000 processData : false,
2000 processData : false,
2001 cache : false,
2001 cache : false,
2002 type : "PATCH",
2002 type : "PATCH",
2003 data : JSON.stringify(data),
2003 data : JSON.stringify(data),
2004 dataType: "json",
2004 dataType: "json",
2005 headers : {'Content-Type': 'application/json'},
2005 headers : {'Content-Type': 'application/json'},
2006 success : $.proxy(that.rename_success, this),
2006 success : $.proxy(that.rename_success, this),
2007 error : $.proxy(that.rename_error, this)
2007 error : $.proxy(that.rename_error, this)
2008 };
2008 };
2009 this.events.trigger('rename_notebook.Notebook', data);
2009 this.events.trigger('rename_notebook.Notebook', data);
2010 var url = utils.url_join_encode(
2010 var url = utils.url_join_encode(
2011 this.base_url,
2011 this.base_url,
2012 'api/notebooks',
2012 'api/notebooks',
2013 this.notebook_path,
2013 this.notebook_path,
2014 this.notebook_name
2014 this.notebook_name
2015 );
2015 );
2016 $.ajax(url, settings);
2016 $.ajax(url, settings);
2017 };
2017 };
2018
2018
2019 Notebook.prototype.delete = function () {
2019 Notebook.prototype.delete = function () {
2020 var that = this;
2020 var that = this;
2021 var settings = {
2021 var settings = {
2022 processData : false,
2022 processData : false,
2023 cache : false,
2023 cache : false,
2024 type : "DELETE",
2024 type : "DELETE",
2025 dataType: "json",
2025 dataType: "json",
2026 error : utils.log_ajax_error,
2026 error : utils.log_ajax_error,
2027 };
2027 };
2028 var url = utils.url_join_encode(
2028 var url = utils.url_join_encode(
2029 this.base_url,
2029 this.base_url,
2030 'api/notebooks',
2030 'api/notebooks',
2031 this.notebook_path,
2031 this.notebook_path,
2032 this.notebook_name
2032 this.notebook_name
2033 );
2033 );
2034 $.ajax(url, settings);
2034 $.ajax(url, settings);
2035 };
2035 };
2036
2036
2037
2037
2038 Notebook.prototype.rename_success = function (json, status, xhr) {
2038 Notebook.prototype.rename_success = function (json, status, xhr) {
2039 var name = this.notebook_name = json.name;
2039 var name = this.notebook_name = json.name;
2040 var path = json.path;
2040 var path = json.path;
2041 this.session.rename_notebook(name, path);
2041 this.session.rename_notebook(name, path);
2042 this.events.trigger('notebook_renamed.Notebook', json);
2042 this.events.trigger('notebook_renamed.Notebook', json);
2043 };
2043 };
2044
2044
2045 Notebook.prototype.rename_error = function (xhr, status, error) {
2045 Notebook.prototype.rename_error = function (xhr, status, error) {
2046 var that = this;
2046 var that = this;
2047 var dialog_body = $('<div/>').append(
2047 var dialog_body = $('<div/>').append(
2048 $("<p/>").addClass("rename-message")
2048 $("<p/>").addClass("rename-message")
2049 .text('This notebook name already exists.')
2049 .text('This notebook name already exists.')
2050 );
2050 );
2051 this.events.trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
2051 this.events.trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
2052 dialog.modal({
2052 dialog.modal({
2053 title: "Notebook Rename Error!",
2053 title: "Notebook Rename Error!",
2054 body: dialog_body,
2054 body: dialog_body,
2055 buttons : {
2055 buttons : {
2056 "Cancel": {},
2056 "Cancel": {},
2057 "OK": {
2057 "OK": {
2058 class: "btn-primary",
2058 class: "btn-primary",
2059 click: function () {
2059 click: function () {
2060 this.save_widget.rename_notebook();
2060 this.save_widget.rename_notebook();
2061 }}
2061 }}
2062 },
2062 },
2063 open : function (event, ui) {
2063 open : function (event, ui) {
2064 var that = $(this);
2064 var that = $(this);
2065 // Upon ENTER, click the OK button.
2065 // Upon ENTER, click the OK button.
2066 that.find('input[type="text"]').keydown(function (event, ui) {
2066 that.find('input[type="text"]').keydown(function (event, ui) {
2067 if (event.which === this.keyboard.keycodes.enter) {
2067 if (event.which === this.keyboard.keycodes.enter) {
2068 that.find('.btn-primary').first().click();
2068 that.find('.btn-primary').first().click();
2069 }
2069 }
2070 });
2070 });
2071 that.find('input[type="text"]').focus();
2071 that.find('input[type="text"]').focus();
2072 }
2072 }
2073 });
2073 });
2074 };
2074 };
2075
2075
2076 /**
2076 /**
2077 * Request a notebook's data from the server.
2077 * Request a notebook's data from the server.
2078 *
2078 *
2079 * @method load_notebook
2079 * @method load_notebook
2080 * @param {String} notebook_name and path A notebook to load
2080 * @param {String} notebook_name and path A notebook to load
2081 */
2081 */
2082 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
2082 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
2083 var that = this;
2083 var that = this;
2084 this.notebook_name = notebook_name;
2084 this.notebook_name = notebook_name;
2085 this.notebook_path = notebook_path;
2085 this.notebook_path = notebook_path;
2086 // We do the call with settings so we can set cache to false.
2086 // We do the call with settings so we can set cache to false.
2087 var settings = {
2087 var settings = {
2088 processData : false,
2088 processData : false,
2089 cache : false,
2089 cache : false,
2090 type : "GET",
2090 type : "GET",
2091 dataType : "json",
2091 dataType : "json",
2092 success : $.proxy(this.load_notebook_success,this),
2092 success : $.proxy(this.load_notebook_success,this),
2093 error : $.proxy(this.load_notebook_error,this),
2093 error : $.proxy(this.load_notebook_error,this),
2094 };
2094 };
2095 this.events.trigger('notebook_loading.Notebook');
2095 this.events.trigger('notebook_loading.Notebook');
2096 var url = utils.url_join_encode(
2096 var url = utils.url_join_encode(
2097 this.base_url,
2097 this.base_url,
2098 'api/notebooks',
2098 'api/notebooks',
2099 this.notebook_path,
2099 this.notebook_path,
2100 this.notebook_name
2100 this.notebook_name
2101 );
2101 );
2102 $.ajax(url, settings);
2102 $.ajax(url, settings);
2103 };
2103 };
2104
2104
2105 /**
2105 /**
2106 * Success callback for loading a notebook from the server.
2106 * Success callback for loading a notebook from the server.
2107 *
2107 *
2108 * Load notebook data from the JSON response.
2108 * Load notebook data from the JSON response.
2109 *
2109 *
2110 * @method load_notebook_success
2110 * @method load_notebook_success
2111 * @param {Object} data JSON representation of a notebook
2111 * @param {Object} data JSON representation of a notebook
2112 * @param {String} status Description of response status
2112 * @param {String} status Description of response status
2113 * @param {jqXHR} xhr jQuery Ajax object
2113 * @param {jqXHR} xhr jQuery Ajax object
2114 */
2114 */
2115 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
2115 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
2116 this.fromJSON(data);
2116 this.fromJSON(data);
2117 if (this.ncells() === 0) {
2117 if (this.ncells() === 0) {
2118 this.insert_cell_below('code');
2118 this.insert_cell_below('code');
2119 this.edit_mode(0);
2119 this.edit_mode(0);
2120 } else {
2120 } else {
2121 this.select(0);
2121 this.select(0);
2122 this.handle_command_mode(this.get_cell(0));
2122 this.handle_command_mode(this.get_cell(0));
2123 }
2123 }
2124 this.set_dirty(false);
2124 this.set_dirty(false);
2125 this.scroll_to_top();
2125 this.scroll_to_top();
2126 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
2126 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
2127 var msg = "This notebook has been converted from an older " +
2127 var msg = "This notebook has been converted from an older " +
2128 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
2128 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
2129 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
2129 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
2130 "newer notebook format will be used and older versions of IPython " +
2130 "newer notebook format will be used and older versions of IPython " +
2131 "may not be able to read it. To keep the older version, close the " +
2131 "may not be able to read it. To keep the older version, close the " +
2132 "notebook without saving it.";
2132 "notebook without saving it.";
2133 dialog.modal({
2133 dialog.modal({
2134 title : "Notebook converted",
2134 title : "Notebook converted",
2135 body : msg,
2135 body : msg,
2136 buttons : {
2136 buttons : {
2137 OK : {
2137 OK : {
2138 class : "btn-primary"
2138 class : "btn-primary"
2139 }
2139 }
2140 }
2140 }
2141 });
2141 });
2142 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
2142 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
2143 var that = this;
2143 var that = this;
2144 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
2144 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
2145 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
2145 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
2146 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
2146 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
2147 this_vs + ". You can still work with this notebook, but some features " +
2147 this_vs + ". You can still work with this notebook, but some features " +
2148 "introduced in later notebook versions may not be available.";
2148 "introduced in later notebook versions may not be available.";
2149
2149
2150 dialog.modal({
2150 dialog.modal({
2151 title : "Newer Notebook",
2151 title : "Newer Notebook",
2152 body : msg,
2152 body : msg,
2153 buttons : {
2153 buttons : {
2154 OK : {
2154 OK : {
2155 class : "btn-danger"
2155 class : "btn-danger"
2156 }
2156 }
2157 }
2157 }
2158 });
2158 });
2159
2159
2160 }
2160 }
2161
2161
2162 // Create the session after the notebook is completely loaded to prevent
2162 // Create the session after the notebook is completely loaded to prevent
2163 // code execution upon loading, which is a security risk.
2163 // code execution upon loading, which is a security risk.
2164 if (this.session === null) {
2164 if (this.session === null) {
2165 this.start_session();
2165 this.start_session();
2166 }
2166 }
2167 // load our checkpoint list
2167 // load our checkpoint list
2168 this.list_checkpoints();
2168 this.list_checkpoints();
2169
2169
2170 // load toolbar state
2170 // load toolbar state
2171 if (this.metadata.celltoolbar) {
2171 if (this.metadata.celltoolbar) {
2172 celltoolbar.CellToolbar.global_show();
2172 celltoolbar.CellToolbar.global_show();
2173 celltoolbar.CellToolbar.activate_preset(this.metadata.celltoolbar);
2173 celltoolbar.CellToolbar.activate_preset(this.metadata.celltoolbar);
2174 } else {
2174 } else {
2175 celltoolbar.CellToolbar.global_hide();
2175 celltoolbar.CellToolbar.global_hide();
2176 }
2176 }
2177
2177
2178 // now that we're fully loaded, it is safe to restore save functionality
2178 // now that we're fully loaded, it is safe to restore save functionality
2179 delete(this.save_notebook);
2179 delete(this.save_notebook);
2180 this.events.trigger('notebook_loaded.Notebook');
2180 this.events.trigger('notebook_loaded.Notebook');
2181 };
2181 };
2182
2182
2183 /**
2183 /**
2184 * Failure callback for loading a notebook from the server.
2184 * Failure callback for loading a notebook from the server.
2185 *
2185 *
2186 * @method load_notebook_error
2186 * @method load_notebook_error
2187 * @param {jqXHR} xhr jQuery Ajax object
2187 * @param {jqXHR} xhr jQuery Ajax object
2188 * @param {String} status Description of response status
2188 * @param {String} status Description of response status
2189 * @param {String} error HTTP error message
2189 * @param {String} error HTTP error message
2190 */
2190 */
2191 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
2191 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
2192 this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
2192 this.events.trigger('notebook_load_failed.Notebook', [xhr, status, error]);
2193 var msg;
2193 var msg;
2194 if (xhr.status === 400) {
2194 if (xhr.status === 400) {
2195 msg = error;
2195 msg = error;
2196 } else if (xhr.status === 500) {
2196 } else if (xhr.status === 500) {
2197 msg = "An unknown error occurred while loading this notebook. " +
2197 msg = "An unknown error occurred while loading this notebook. " +
2198 "This version can load notebook formats " +
2198 "This version can load notebook formats " +
2199 "v" + this.nbformat + " or earlier.";
2199 "v" + this.nbformat + " or earlier.";
2200 }
2200 }
2201 dialog.modal({
2201 dialog.modal({
2202 title: "Error loading notebook",
2202 title: "Error loading notebook",
2203 body : msg,
2203 body : msg,
2204 buttons : {
2204 buttons : {
2205 "OK": {}
2205 "OK": {}
2206 }
2206 }
2207 });
2207 });
2208 };
2208 };
2209
2209
2210 /********************* checkpoint-related *********************/
2210 /********************* checkpoint-related *********************/
2211
2211
2212 /**
2212 /**
2213 * Save the notebook then immediately create a checkpoint.
2213 * Save the notebook then immediately create a checkpoint.
2214 *
2214 *
2215 * @method save_checkpoint
2215 * @method save_checkpoint
2216 */
2216 */
2217 Notebook.prototype.save_checkpoint = function () {
2217 Notebook.prototype.save_checkpoint = function () {
2218 this._checkpoint_after_save = true;
2218 this._checkpoint_after_save = true;
2219 this.save_notebook();
2219 this.save_notebook();
2220 };
2220 };
2221
2221
2222 /**
2222 /**
2223 * Add a checkpoint for this notebook.
2223 * Add a checkpoint for this notebook.
2224 * for use as a callback from checkpoint creation.
2224 * for use as a callback from checkpoint creation.
2225 *
2225 *
2226 * @method add_checkpoint
2226 * @method add_checkpoint
2227 */
2227 */
2228 Notebook.prototype.add_checkpoint = function (checkpoint) {
2228 Notebook.prototype.add_checkpoint = function (checkpoint) {
2229 var found = false;
2229 var found = false;
2230 for (var i = 0; i < this.checkpoints.length; i++) {
2230 for (var i = 0; i < this.checkpoints.length; i++) {
2231 var existing = this.checkpoints[i];
2231 var existing = this.checkpoints[i];
2232 if (existing.id == checkpoint.id) {
2232 if (existing.id == checkpoint.id) {
2233 found = true;
2233 found = true;
2234 this.checkpoints[i] = checkpoint;
2234 this.checkpoints[i] = checkpoint;
2235 break;
2235 break;
2236 }
2236 }
2237 }
2237 }
2238 if (!found) {
2238 if (!found) {
2239 this.checkpoints.push(checkpoint);
2239 this.checkpoints.push(checkpoint);
2240 }
2240 }
2241 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2241 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2242 };
2242 };
2243
2243
2244 /**
2244 /**
2245 * List checkpoints for this notebook.
2245 * List checkpoints for this notebook.
2246 *
2246 *
2247 * @method list_checkpoints
2247 * @method list_checkpoints
2248 */
2248 */
2249 Notebook.prototype.list_checkpoints = function () {
2249 Notebook.prototype.list_checkpoints = function () {
2250 var url = utils.url_join_encode(
2250 var url = utils.url_join_encode(
2251 this.base_url,
2251 this.base_url,
2252 'api/notebooks',
2252 'api/notebooks',
2253 this.notebook_path,
2253 this.notebook_path,
2254 this.notebook_name,
2254 this.notebook_name,
2255 'checkpoints'
2255 'checkpoints'
2256 );
2256 );
2257 $.get(url).done(
2257 $.get(url).done(
2258 $.proxy(this.list_checkpoints_success, this)
2258 $.proxy(this.list_checkpoints_success, this)
2259 ).fail(
2259 ).fail(
2260 $.proxy(this.list_checkpoints_error, this)
2260 $.proxy(this.list_checkpoints_error, this)
2261 );
2261 );
2262 };
2262 };
2263
2263
2264 /**
2264 /**
2265 * Success callback for listing checkpoints.
2265 * Success callback for listing checkpoints.
2266 *
2266 *
2267 * @method list_checkpoint_success
2267 * @method list_checkpoint_success
2268 * @param {Object} data JSON representation of a checkpoint
2268 * @param {Object} data JSON representation of a checkpoint
2269 * @param {String} status Description of response status
2269 * @param {String} status Description of response status
2270 * @param {jqXHR} xhr jQuery Ajax object
2270 * @param {jqXHR} xhr jQuery Ajax object
2271 */
2271 */
2272 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2272 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2273 data = $.parseJSON(data);
2273 data = $.parseJSON(data);
2274 this.checkpoints = data;
2274 this.checkpoints = data;
2275 if (data.length) {
2275 if (data.length) {
2276 this.last_checkpoint = data[data.length - 1];
2276 this.last_checkpoint = data[data.length - 1];
2277 } else {
2277 } else {
2278 this.last_checkpoint = null;
2278 this.last_checkpoint = null;
2279 }
2279 }
2280 this.events.trigger('checkpoints_listed.Notebook', [data]);
2280 this.events.trigger('checkpoints_listed.Notebook', [data]);
2281 };
2281 };
2282
2282
2283 /**
2283 /**
2284 * Failure callback for listing a checkpoint.
2284 * Failure callback for listing a checkpoint.
2285 *
2285 *
2286 * @method list_checkpoint_error
2286 * @method list_checkpoint_error
2287 * @param {jqXHR} xhr jQuery Ajax object
2287 * @param {jqXHR} xhr jQuery Ajax object
2288 * @param {String} status Description of response status
2288 * @param {String} status Description of response status
2289 * @param {String} error_msg HTTP error message
2289 * @param {String} error_msg HTTP error message
2290 */
2290 */
2291 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2291 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2292 this.events.trigger('list_checkpoints_failed.Notebook');
2292 this.events.trigger('list_checkpoints_failed.Notebook');
2293 };
2293 };
2294
2294
2295 /**
2295 /**
2296 * Create a checkpoint of this notebook on the server from the most recent save.
2296 * Create a checkpoint of this notebook on the server from the most recent save.
2297 *
2297 *
2298 * @method create_checkpoint
2298 * @method create_checkpoint
2299 */
2299 */
2300 Notebook.prototype.create_checkpoint = function () {
2300 Notebook.prototype.create_checkpoint = function () {
2301 var url = utils.url_join_encode(
2301 var url = utils.url_join_encode(
2302 this.base_url,
2302 this.base_url,
2303 'api/notebooks',
2303 'api/notebooks',
2304 this.notebook_path,
2304 this.notebook_path,
2305 this.notebook_name,
2305 this.notebook_name,
2306 'checkpoints'
2306 'checkpoints'
2307 );
2307 );
2308 $.post(url).done(
2308 $.post(url).done(
2309 $.proxy(this.create_checkpoint_success, this)
2309 $.proxy(this.create_checkpoint_success, this)
2310 ).fail(
2310 ).fail(
2311 $.proxy(this.create_checkpoint_error, this)
2311 $.proxy(this.create_checkpoint_error, this)
2312 );
2312 );
2313 };
2313 };
2314
2314
2315 /**
2315 /**
2316 * Success callback for creating a checkpoint.
2316 * Success callback for creating a checkpoint.
2317 *
2317 *
2318 * @method create_checkpoint_success
2318 * @method create_checkpoint_success
2319 * @param {Object} data JSON representation of a checkpoint
2319 * @param {Object} data JSON representation of a checkpoint
2320 * @param {String} status Description of response status
2320 * @param {String} status Description of response status
2321 * @param {jqXHR} xhr jQuery Ajax object
2321 * @param {jqXHR} xhr jQuery Ajax object
2322 */
2322 */
2323 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2323 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2324 data = $.parseJSON(data);
2324 data = $.parseJSON(data);
2325 this.add_checkpoint(data);
2325 this.add_checkpoint(data);
2326 this.events.trigger('checkpoint_created.Notebook', data);
2326 this.events.trigger('checkpoint_created.Notebook', data);
2327 };
2327 };
2328
2328
2329 /**
2329 /**
2330 * Failure callback for creating a checkpoint.
2330 * Failure callback for creating a checkpoint.
2331 *
2331 *
2332 * @method create_checkpoint_error
2332 * @method create_checkpoint_error
2333 * @param {jqXHR} xhr jQuery Ajax object
2333 * @param {jqXHR} xhr jQuery Ajax object
2334 * @param {String} status Description of response status
2334 * @param {String} status Description of response status
2335 * @param {String} error_msg HTTP error message
2335 * @param {String} error_msg HTTP error message
2336 */
2336 */
2337 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2337 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2338 this.events.trigger('checkpoint_failed.Notebook');
2338 this.events.trigger('checkpoint_failed.Notebook');
2339 };
2339 };
2340
2340
2341 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2341 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2342 var that = this;
2342 var that = this;
2343 checkpoint = checkpoint || this.last_checkpoint;
2343 checkpoint = checkpoint || this.last_checkpoint;
2344 if ( ! checkpoint ) {
2344 if ( ! checkpoint ) {
2345 console.log("restore dialog, but no checkpoint to restore to!");
2345 console.log("restore dialog, but no checkpoint to restore to!");
2346 return;
2346 return;
2347 }
2347 }
2348 var body = $('<div/>').append(
2348 var body = $('<div/>').append(
2349 $('<p/>').addClass("p-space").text(
2349 $('<p/>').addClass("p-space").text(
2350 "Are you sure you want to revert the notebook to " +
2350 "Are you sure you want to revert the notebook to " +
2351 "the latest checkpoint?"
2351 "the latest checkpoint?"
2352 ).append(
2352 ).append(
2353 $("<strong/>").text(
2353 $("<strong/>").text(
2354 " This cannot be undone."
2354 " This cannot be undone."
2355 )
2355 )
2356 )
2356 )
2357 ).append(
2357 ).append(
2358 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2358 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2359 ).append(
2359 ).append(
2360 $('<p/>').addClass("p-space").text(
2360 $('<p/>').addClass("p-space").text(
2361 Date(checkpoint.last_modified)
2361 Date(checkpoint.last_modified)
2362 ).css("text-align", "center")
2362 ).css("text-align", "center")
2363 );
2363 );
2364
2364
2365 dialog.modal({
2365 dialog.modal({
2366 title : "Revert notebook to checkpoint",
2366 title : "Revert notebook to checkpoint",
2367 body : body,
2367 body : body,
2368 buttons : {
2368 buttons : {
2369 Revert : {
2369 Revert : {
2370 class : "btn-danger",
2370 class : "btn-danger",
2371 click : function () {
2371 click : function () {
2372 that.restore_checkpoint(checkpoint.id);
2372 that.restore_checkpoint(checkpoint.id);
2373 }
2373 }
2374 },
2374 },
2375 Cancel : {}
2375 Cancel : {}
2376 }
2376 }
2377 });
2377 });
2378 };
2378 };
2379
2379
2380 /**
2380 /**
2381 * Restore the notebook to a checkpoint state.
2381 * Restore the notebook to a checkpoint state.
2382 *
2382 *
2383 * @method restore_checkpoint
2383 * @method restore_checkpoint
2384 * @param {String} checkpoint ID
2384 * @param {String} checkpoint ID
2385 */
2385 */
2386 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2386 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2387 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2387 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2388 var url = utils.url_join_encode(
2388 var url = utils.url_join_encode(
2389 this.base_url,
2389 this.base_url,
2390 'api/notebooks',
2390 'api/notebooks',
2391 this.notebook_path,
2391 this.notebook_path,
2392 this.notebook_name,
2392 this.notebook_name,
2393 'checkpoints',
2393 'checkpoints',
2394 checkpoint
2394 checkpoint
2395 );
2395 );
2396 $.post(url).done(
2396 $.post(url).done(
2397 $.proxy(this.restore_checkpoint_success, this)
2397 $.proxy(this.restore_checkpoint_success, this)
2398 ).fail(
2398 ).fail(
2399 $.proxy(this.restore_checkpoint_error, this)
2399 $.proxy(this.restore_checkpoint_error, this)
2400 );
2400 );
2401 };
2401 };
2402
2402
2403 /**
2403 /**
2404 * Success callback for restoring a notebook to a checkpoint.
2404 * Success callback for restoring a notebook to a checkpoint.
2405 *
2405 *
2406 * @method restore_checkpoint_success
2406 * @method restore_checkpoint_success
2407 * @param {Object} data (ignored, should be empty)
2407 * @param {Object} data (ignored, should be empty)
2408 * @param {String} status Description of response status
2408 * @param {String} status Description of response status
2409 * @param {jqXHR} xhr jQuery Ajax object
2409 * @param {jqXHR} xhr jQuery Ajax object
2410 */
2410 */
2411 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2411 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2412 this.events.trigger('checkpoint_restored.Notebook');
2412 this.events.trigger('checkpoint_restored.Notebook');
2413 this.load_notebook(this.notebook_name, this.notebook_path);
2413 this.load_notebook(this.notebook_name, this.notebook_path);
2414 };
2414 };
2415
2415
2416 /**
2416 /**
2417 * Failure callback for restoring a notebook to a checkpoint.
2417 * Failure callback for restoring a notebook to a checkpoint.
2418 *
2418 *
2419 * @method restore_checkpoint_error
2419 * @method restore_checkpoint_error
2420 * @param {jqXHR} xhr jQuery Ajax object
2420 * @param {jqXHR} xhr jQuery Ajax object
2421 * @param {String} status Description of response status
2421 * @param {String} status Description of response status
2422 * @param {String} error_msg HTTP error message
2422 * @param {String} error_msg HTTP error message
2423 */
2423 */
2424 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2424 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2425 this.events.trigger('checkpoint_restore_failed.Notebook');
2425 this.events.trigger('checkpoint_restore_failed.Notebook');
2426 };
2426 };
2427
2427
2428 /**
2428 /**
2429 * Delete a notebook checkpoint.
2429 * Delete a notebook checkpoint.
2430 *
2430 *
2431 * @method delete_checkpoint
2431 * @method delete_checkpoint
2432 * @param {String} checkpoint ID
2432 * @param {String} checkpoint ID
2433 */
2433 */
2434 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2434 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2435 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2435 this.events.trigger('notebook_restoring.Notebook', checkpoint);
2436 var url = utils.url_join_encode(
2436 var url = utils.url_join_encode(
2437 this.base_url,
2437 this.base_url,
2438 'api/notebooks',
2438 'api/notebooks',
2439 this.notebook_path,
2439 this.notebook_path,
2440 this.notebook_name,
2440 this.notebook_name,
2441 'checkpoints',
2441 'checkpoints',
2442 checkpoint
2442 checkpoint
2443 );
2443 );
2444 $.ajax(url, {
2444 $.ajax(url, {
2445 type: 'DELETE',
2445 type: 'DELETE',
2446 success: $.proxy(this.delete_checkpoint_success, this),
2446 success: $.proxy(this.delete_checkpoint_success, this),
2447 error: $.proxy(this.delete_checkpoint_error, this)
2447 error: $.proxy(this.delete_checkpoint_error, this)
2448 });
2448 });
2449 };
2449 };
2450
2450
2451 /**
2451 /**
2452 * Success callback for deleting a notebook checkpoint
2452 * Success callback for deleting a notebook checkpoint
2453 *
2453 *
2454 * @method delete_checkpoint_success
2454 * @method delete_checkpoint_success
2455 * @param {Object} data (ignored, should be empty)
2455 * @param {Object} data (ignored, should be empty)
2456 * @param {String} status Description of response status
2456 * @param {String} status Description of response status
2457 * @param {jqXHR} xhr jQuery Ajax object
2457 * @param {jqXHR} xhr jQuery Ajax object
2458 */
2458 */
2459 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2459 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2460 this.events.trigger('checkpoint_deleted.Notebook', data);
2460 this.events.trigger('checkpoint_deleted.Notebook', data);
2461 this.load_notebook(this.notebook_name, this.notebook_path);
2461 this.load_notebook(this.notebook_name, this.notebook_path);
2462 };
2462 };
2463
2463
2464 /**
2464 /**
2465 * Failure callback for deleting a notebook checkpoint.
2465 * Failure callback for deleting a notebook checkpoint.
2466 *
2466 *
2467 * @method delete_checkpoint_error
2467 * @method delete_checkpoint_error
2468 * @param {jqXHR} xhr jQuery Ajax object
2468 * @param {jqXHR} xhr jQuery Ajax object
2469 * @param {String} status Description of response status
2469 * @param {String} status Description of response status
2470 * @param {String} error_msg HTTP error message
2470 * @param {String} error_msg HTTP error message
2471 */
2471 */
2472 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2472 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2473 this.events.trigger('checkpoint_delete_failed.Notebook');
2473 this.events.trigger('checkpoint_delete_failed.Notebook');
2474 };
2474 };
2475
2475
2476
2476
2477 // For backwards compatability.
2477 // For backwards compatability.
2478 IPython.Notebook = Notebook;
2478 IPython.Notebook = Notebook;
2479
2479
2480 return {'Notebook': Notebook};
2480 return {'Notebook': Notebook};
2481 });
2481 });
@@ -1,52 +1,53 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3
3
4 {% block stylesheet %}
4 {% block stylesheet %}
5 {{super()}}
5 {{super()}}
6 <link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
6 <link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
7 {% endblock %}
7 {% endblock %}
8
8
9 {% block login_widget %}
9 {% block login_widget %}
10 {% endblock %}
10 {% endblock %}
11
11
12 {% block site %}
12 {% block site %}
13
13
14 <div id="ipython-main-app" class="container">
14 <div id="ipython-main-app" class="container">
15
15
16 {% if login_available %}
16 {% if login_available %}
17 <div class="row">
17 <div class="row">
18 <div class="navbar col-md-8 col-md-offset2">
18 <div class="navbar col-md-8 col-md-offset2">
19 <div class="navbar-inner">
19 <div class="navbar-inner">
20 <div class="container">
20 <div class="container">
21 <div class="center-nav">
21 <div class="center-nav">
22 <p class="navbar-text nav">Password:</p>
22 <p class="navbar-text nav">Password:</p>
23 <form action="{{base_url}}login?next={{next}}" method="post" class="navbar-form pull-left">
23 <form action="{{base_url}}login?next={{next}}" method="post" class="navbar-form pull-left">
24 <input type="password" name="password" id="password_input">
24 <input type="password" name="password" id="password_input">
25 <button type="submit" id="login_submit">Log in</button>
25 <button type="submit" id="login_submit">Log in</button>
26 </form>
26 </form>
27 </div>
27 </div>
28 </div>
28 </div>
29 </div>
29 </div>
30 </div>
30 </div>
31 </div>
31 </div>
32 {% endif %}
32 {% endif %}
33 {% if message %}
33 {% if message %}
34 <div class="row">
34 <div class="row">
35 {% for key in message %}
35 {% for key in message %}
36 <div class="message {{key}}">
36 <div class="message {{key}}">
37 {{message[key]}}
37 {{message[key]}}
38 </div>
38 </div>
39 {% endfor %}
39 {% endfor %}
40 </div>
40 </div>
41 {% endif %}
41 {% endif %}
42
42
43 <div/>
43 <div/>
44
44
45 {% endblock %}
45 {% endblock %}
46
46
47
47
48 {% block script %}
48 {% block script %}
49 {{super()}}
49
50
50 <script src="{{static_url("auth/js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
51 <script src="{{static_url("auth/js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
51
52
52 {% endblock %}
53 {% endblock %}
@@ -1,38 +1,39 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4 {{super()}}
4 {{super()}}
5 <link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
5 <link rel="stylesheet" href="{{ static_url("auth/css/override.css") }}" type="text/css" />
6 {% endblock %}
6 {% endblock %}
7
7
8 {% block login_widget %}
8 {% block login_widget %}
9 {% endblock %}
9 {% endblock %}
10
10
11 {% block site %}
11 {% block site %}
12
12
13 <div id="ipython-main-app" class="container">
13 <div id="ipython-main-app" class="container">
14
14
15 {% if message %}
15 {% if message %}
16 {% for key in message %}
16 {% for key in message %}
17 <div class="message {{key}}">
17 <div class="message {{key}}">
18 {{message[key]}}
18 {{message[key]}}
19 </div>
19 </div>
20 {% endfor %}
20 {% endfor %}
21 {% endif %}
21 {% endif %}
22
22
23 {% if not login_available %}
23 {% if not login_available %}
24 Proceed to the <a href="{{base_url}}">dashboard</a>.
24 Proceed to the <a href="{{base_url}}">dashboard</a>.
25 {% else %}
25 {% else %}
26 Proceed to the <a href="{{base_url}}login">login page</a>.
26 Proceed to the <a href="{{base_url}}login">login page</a>.
27 {% endif %}
27 {% endif %}
28
28
29
29
30 <div/>
30 <div/>
31
31
32 {% endblock %}
32 {% endblock %}
33
33
34 {% block script %}
34 {% block script %}
35 {{super()}}
35
36
36 <script src="{{static_url("auth/js/logoutmain.js") }}" type="text/javascript" charset="utf-8"></script>
37 <script src="{{static_url("auth/js/logoutmain.js") }}" type="text/javascript" charset="utf-8"></script>
37
38
38 {% endblock %}
39 {% endblock %}
@@ -1,319 +1,320 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% endif %}
7 {% endif %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
12 </script>
13
13
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
16
16
17 {{super()}}
17 {{super()}}
18
18
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
20
20
21 {% endblock %}
21 {% endblock %}
22
22
23 {% block params %}
23 {% block params %}
24
24
25 data-project="{{project}}"
25 data-project="{{project}}"
26 data-base-url="{{base_url}}"
26 data-base-url="{{base_url}}"
27 data-notebook-name="{{notebook_name}}"
27 data-notebook-name="{{notebook_name}}"
28 data-notebook-path="{{notebook_path}}"
28 data-notebook-path="{{notebook_path}}"
29 class="notebook_app"
29 class="notebook_app"
30
30
31 {% endblock %}
31 {% endblock %}
32
32
33
33
34 {% block header %}
34 {% block header %}
35
35
36 <span id="save_widget" class="nav pull-left">
36 <span id="save_widget" class="nav pull-left">
37 <span id="notebook_name"></span>
37 <span id="notebook_name"></span>
38 <span id="checkpoint_status"></span>
38 <span id="checkpoint_status"></span>
39 <span id="autosave_status"></span>
39 <span id="autosave_status"></span>
40 </span>
40 </span>
41
41
42 {% endblock %}
42 {% endblock %}
43
43
44
44
45 {% block site %}
45 {% block site %}
46
46
47 <div id="menubar-container" class="container">
47 <div id="menubar-container" class="container">
48 <div id="menubar">
48 <div id="menubar">
49 <div id="menus" class="navbar navbar-default" role="navigation">
49 <div id="menus" class="navbar navbar-default" role="navigation">
50 <div class="container-fluid">
50 <div class="container-fluid">
51 <ul class="nav navbar-nav">
51 <ul class="nav navbar-nav">
52 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
52 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
53 <ul id="file_menu" class="dropdown-menu">
53 <ul id="file_menu" class="dropdown-menu">
54 <li id="new_notebook"
54 <li id="new_notebook"
55 title="Make a new notebook (Opens a new window)">
55 title="Make a new notebook (Opens a new window)">
56 <a href="#">New</a></li>
56 <a href="#">New</a></li>
57 <li id="open_notebook"
57 <li id="open_notebook"
58 title="Opens a new window with the Dashboard view">
58 title="Opens a new window with the Dashboard view">
59 <a href="#">Open...</a></li>
59 <a href="#">Open...</a></li>
60 <!-- <hr/> -->
60 <!-- <hr/> -->
61 <li class="divider"></li>
61 <li class="divider"></li>
62 <li id="copy_notebook"
62 <li id="copy_notebook"
63 title="Open a copy of this notebook's contents and start a new kernel">
63 title="Open a copy of this notebook's contents and start a new kernel">
64 <a href="#">Make a Copy...</a></li>
64 <a href="#">Make a Copy...</a></li>
65 <li id="rename_notebook"><a href="#">Rename...</a></li>
65 <li id="rename_notebook"><a href="#">Rename...</a></li>
66 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
66 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
67 <!-- <hr/> -->
67 <!-- <hr/> -->
68 <li class="divider"></li>
68 <li class="divider"></li>
69 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
69 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
70 <ul class="dropdown-menu">
70 <ul class="dropdown-menu">
71 <li><a href="#"></a></li>
71 <li><a href="#"></a></li>
72 <li><a href="#"></a></li>
72 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
76 </ul>
76 </ul>
77 </li>
77 </li>
78 <li class="divider"></li>
78 <li class="divider"></li>
79 <li id="print_preview"><a href="#">Print Preview</a></li>
79 <li id="print_preview"><a href="#">Print Preview</a></li>
80 <li class="dropdown-submenu"><a href="#">Download as</a>
80 <li class="dropdown-submenu"><a href="#">Download as</a>
81 <ul class="dropdown-menu">
81 <ul class="dropdown-menu">
82 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
82 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
83 <li id="download_py"><a href="#">Python (.py)</a></li>
83 <li id="download_py"><a href="#">Python (.py)</a></li>
84 <li id="download_html"><a href="#">HTML (.html)</a></li>
84 <li id="download_html"><a href="#">HTML (.html)</a></li>
85 <li id="download_rst"><a href="#">reST (.rst)</a></li>
85 <li id="download_rst"><a href="#">reST (.rst)</a></li>
86 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
86 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
87 </ul>
87 </ul>
88 </li>
88 </li>
89 <li class="divider"></li>
89 <li class="divider"></li>
90 <li id="trust_notebook"
90 <li id="trust_notebook"
91 title="Trust the output of this notebook">
91 title="Trust the output of this notebook">
92 <a href="#" >Trust Notebook</a></li>
92 <a href="#" >Trust Notebook</a></li>
93 <li class="divider"></li>
93 <li class="divider"></li>
94 <li id="kill_and_exit"
94 <li id="kill_and_exit"
95 title="Shutdown this notebook's kernel, and close this window">
95 title="Shutdown this notebook's kernel, and close this window">
96 <a href="#" >Close and halt</a></li>
96 <a href="#" >Close and halt</a></li>
97 </ul>
97 </ul>
98 </li>
98 </li>
99 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
99 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
100 <ul id="edit_menu" class="dropdown-menu">
100 <ul id="edit_menu" class="dropdown-menu">
101 <li id="cut_cell"><a href="#">Cut Cell</a></li>
101 <li id="cut_cell"><a href="#">Cut Cell</a></li>
102 <li id="copy_cell"><a href="#">Copy Cell</a></li>
102 <li id="copy_cell"><a href="#">Copy Cell</a></li>
103 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
103 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
104 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
104 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
105 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
105 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
106 <li id="delete_cell"><a href="#">Delete Cell</a></li>
106 <li id="delete_cell"><a href="#">Delete Cell</a></li>
107 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
107 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
108 <li class="divider"></li>
108 <li class="divider"></li>
109 <li id="split_cell"><a href="#">Split Cell</a></li>
109 <li id="split_cell"><a href="#">Split Cell</a></li>
110 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
110 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
111 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
111 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
112 <li class="divider"></li>
112 <li class="divider"></li>
113 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
113 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
114 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
114 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
115 <li class="divider"></li>
115 <li class="divider"></li>
116 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
116 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
117 </ul>
117 </ul>
118 </li>
118 </li>
119 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
119 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
120 <ul id="view_menu" class="dropdown-menu">
120 <ul id="view_menu" class="dropdown-menu">
121 <li id="toggle_header"
121 <li id="toggle_header"
122 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
122 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
123 <a href="#">Toggle Header</a></li>
123 <a href="#">Toggle Header</a></li>
124 <li id="toggle_toolbar"
124 <li id="toggle_toolbar"
125 title="Show/Hide the action icons (below menu bar)">
125 title="Show/Hide the action icons (below menu bar)">
126 <a href="#">Toggle Toolbar</a></li>
126 <a href="#">Toggle Toolbar</a></li>
127 </ul>
127 </ul>
128 </li>
128 </li>
129 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
129 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
130 <ul id="insert_menu" class="dropdown-menu">
130 <ul id="insert_menu" class="dropdown-menu">
131 <li id="insert_cell_above"
131 <li id="insert_cell_above"
132 title="Insert an empty Code cell above the currently active cell">
132 title="Insert an empty Code cell above the currently active cell">
133 <a href="#">Insert Cell Above</a></li>
133 <a href="#">Insert Cell Above</a></li>
134 <li id="insert_cell_below"
134 <li id="insert_cell_below"
135 title="Insert an empty Code cell below the currently active cell">
135 title="Insert an empty Code cell below the currently active cell">
136 <a href="#">Insert Cell Below</a></li>
136 <a href="#">Insert Cell Below</a></li>
137 </ul>
137 </ul>
138 </li>
138 </li>
139 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
139 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
140 <ul id="cell_menu" class="dropdown-menu">
140 <ul id="cell_menu" class="dropdown-menu">
141 <li id="run_cell" title="Run this cell, and move cursor to the next one">
141 <li id="run_cell" title="Run this cell, and move cursor to the next one">
142 <a href="#">Run</a></li>
142 <a href="#">Run</a></li>
143 <li id="run_cell_select_below" title="Run this cell, select below">
143 <li id="run_cell_select_below" title="Run this cell, select below">
144 <a href="#">Run and Select Below</a></li>
144 <a href="#">Run and Select Below</a></li>
145 <li id="run_cell_insert_below" title="Run this cell, insert below">
145 <li id="run_cell_insert_below" title="Run this cell, insert below">
146 <a href="#">Run and Insert Below</a></li>
146 <a href="#">Run and Insert Below</a></li>
147 <li id="run_all_cells" title="Run all cells in the notebook">
147 <li id="run_all_cells" title="Run all cells in the notebook">
148 <a href="#">Run All</a></li>
148 <a href="#">Run All</a></li>
149 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
149 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
150 <a href="#">Run All Above</a></li>
150 <a href="#">Run All Above</a></li>
151 <li id="run_all_cells_below" title="Run this cell and all cells below it">
151 <li id="run_all_cells_below" title="Run this cell and all cells below it">
152 <a href="#">Run All Below</a></li>
152 <a href="#">Run All Below</a></li>
153 <li class="divider"></li>
153 <li class="divider"></li>
154 <li id="change_cell_type" class="dropdown-submenu"
154 <li id="change_cell_type" class="dropdown-submenu"
155 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
155 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
156 <a href="#">Cell Type</a>
156 <a href="#">Cell Type</a>
157 <ul class="dropdown-menu">
157 <ul class="dropdown-menu">
158 <li id="to_code"
158 <li id="to_code"
159 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
159 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
160 <a href="#">Code</a></li>
160 <a href="#">Code</a></li>
161 <li id="to_markdown"
161 <li id="to_markdown"
162 title="Contents will be rendered as HTML and serve as explanatory text">
162 title="Contents will be rendered as HTML and serve as explanatory text">
163 <a href="#">Markdown</a></li>
163 <a href="#">Markdown</a></li>
164 <li id="to_raw"
164 <li id="to_raw"
165 title="Contents will pass through nbconvert unmodified">
165 title="Contents will pass through nbconvert unmodified">
166 <a href="#">Raw NBConvert</a></li>
166 <a href="#">Raw NBConvert</a></li>
167 <li id="to_heading1"><a href="#">Heading 1</a></li>
167 <li id="to_heading1"><a href="#">Heading 1</a></li>
168 <li id="to_heading2"><a href="#">Heading 2</a></li>
168 <li id="to_heading2"><a href="#">Heading 2</a></li>
169 <li id="to_heading3"><a href="#">Heading 3</a></li>
169 <li id="to_heading3"><a href="#">Heading 3</a></li>
170 <li id="to_heading4"><a href="#">Heading 4</a></li>
170 <li id="to_heading4"><a href="#">Heading 4</a></li>
171 <li id="to_heading5"><a href="#">Heading 5</a></li>
171 <li id="to_heading5"><a href="#">Heading 5</a></li>
172 <li id="to_heading6"><a href="#">Heading 6</a></li>
172 <li id="to_heading6"><a href="#">Heading 6</a></li>
173 </ul>
173 </ul>
174 </li>
174 </li>
175 <li class="divider"></li>
175 <li class="divider"></li>
176 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
176 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
177 <ul class="dropdown-menu">
177 <ul class="dropdown-menu">
178 <li id="toggle_current_output"
178 <li id="toggle_current_output"
179 title="Hide/Show the output of the current cell">
179 title="Hide/Show the output of the current cell">
180 <a href="#">Toggle</a>
180 <a href="#">Toggle</a>
181 </li>
181 </li>
182 <li id="toggle_current_output_scroll"
182 <li id="toggle_current_output_scroll"
183 title="Scroll the output of the current cell">
183 title="Scroll the output of the current cell">
184 <a href="#">Toggle Scrolling</a>
184 <a href="#">Toggle Scrolling</a>
185 </li>
185 </li>
186 <li id="clear_current_output"
186 <li id="clear_current_output"
187 title="Clear the output of the current cell">
187 title="Clear the output of the current cell">
188 <a href="#">Clear</a>
188 <a href="#">Clear</a>
189 </li>
189 </li>
190 </ul>
190 </ul>
191 </li>
191 </li>
192 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
192 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
193 <ul class="dropdown-menu">
193 <ul class="dropdown-menu">
194 <li id="toggle_all_output"
194 <li id="toggle_all_output"
195 title="Hide/Show the output of all cells">
195 title="Hide/Show the output of all cells">
196 <a href="#">Toggle</a>
196 <a href="#">Toggle</a>
197 </li>
197 </li>
198 <li id="toggle_all_output_scroll"
198 <li id="toggle_all_output_scroll"
199 title="Scroll the output of all cells">
199 title="Scroll the output of all cells">
200 <a href="#">Toggle Scrolling</a>
200 <a href="#">Toggle Scrolling</a>
201 </li>
201 </li>
202 <li id="clear_all_output"
202 <li id="clear_all_output"
203 title="Clear the output of all cells">
203 title="Clear the output of all cells">
204 <a href="#">Clear</a>
204 <a href="#">Clear</a>
205 </li>
205 </li>
206 </ul>
206 </ul>
207 </li>
207 </li>
208 </ul>
208 </ul>
209 </li>
209 </li>
210 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
210 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
211 <ul id="kernel_menu" class="dropdown-menu">
211 <ul id="kernel_menu" class="dropdown-menu">
212 <li id="int_kernel"
212 <li id="int_kernel"
213 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
213 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
214 <a href="#">Interrupt</a></li>
214 <a href="#">Interrupt</a></li>
215 <li id="restart_kernel"
215 <li id="restart_kernel"
216 title="Restart the Kernel">
216 title="Restart the Kernel">
217 <a href="#">Restart</a></li>
217 <a href="#">Restart</a></li>
218 </ul>
218 </ul>
219 </li>
219 </li>
220 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
220 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
221 <ul id="help_menu" class="dropdown-menu">
221 <ul id="help_menu" class="dropdown-menu">
222 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
222 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
223 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
223 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
224 <li class="divider"></li>
224 <li class="divider"></li>
225 {% set
225 {% set
226 sections = (
226 sections = (
227 (
227 (
228 ("http://ipython.org/documentation.html","IPython Help",True),
228 ("http://ipython.org/documentation.html","IPython Help",True),
229 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
229 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
230 ),(
230 ),(
231 ("http://docs.python.org","Python",True),
231 ("http://docs.python.org","Python",True),
232 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
232 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
233 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
233 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
234 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
234 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
235 ("http://matplotlib.org/contents.html","Matplotlib",True),
235 ("http://matplotlib.org/contents.html","Matplotlib",True),
236 ("http://docs.sympy.org/latest/index.html","SymPy",True),
236 ("http://docs.sympy.org/latest/index.html","SymPy",True),
237 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
237 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
238 )
238 )
239 )
239 )
240 %}
240 %}
241
241
242 {% for helplinks in sections %}
242 {% for helplinks in sections %}
243 {% for link in helplinks %}
243 {% for link in helplinks %}
244 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
244 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
245 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
245 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
246 {{link[1]}}
246 {{link[1]}}
247 </a></li>
247 </a></li>
248 {% endfor %}
248 {% endfor %}
249 {% if not loop.last %}
249 {% if not loop.last %}
250 <li class="divider"></li>
250 <li class="divider"></li>
251 {% endif %}
251 {% endif %}
252 {% endfor %}
252 {% endfor %}
253 </li>
253 </li>
254 </ul>
254 </ul>
255 </li>
255 </li>
256 </ul>
256 </ul>
257 <ul class="nav navbar-nav navbar-right">
257 <ul class="nav navbar-nav navbar-right">
258 <div id="kernel_indicator">
258 <div id="kernel_indicator">
259 <i id="kernel_indicator_icon"></i>
259 <i id="kernel_indicator_icon"></i>
260 </div>
260 </div>
261 <div id="modal_indicator">
261 <div id="modal_indicator">
262 <i id="modal_indicator_icon"></i>
262 <i id="modal_indicator_icon"></i>
263 </div>
263 </div>
264 <div id="notification_area"></div>
264 <div id="notification_area"></div>
265 </ul>
265 </ul>
266 </div>
266 </div>
267 </div>
267 </div>
268 </div>
268 </div>
269 <div id="maintoolbar" class="navbar">
269 <div id="maintoolbar" class="navbar">
270 <div class="toolbar-inner navbar-inner navbar-nobg">
270 <div class="toolbar-inner navbar-inner navbar-nobg">
271 <div id="maintoolbar-container" class="container"></div>
271 <div id="maintoolbar-container" class="container"></div>
272 </div>
272 </div>
273 </div>
273 </div>
274 </div>
274 </div>
275
275
276 <div id="ipython-main-app">
276 <div id="ipython-main-app">
277
277
278 <div id="notebook_panel">
278 <div id="notebook_panel">
279 <div id="notebook"></div>
279 <div id="notebook"></div>
280 <div id="pager_splitter"></div>
280 <div id="pager_splitter"></div>
281 <div id="pager">
281 <div id="pager">
282 <div id='pager_button_area'>
282 <div id='pager_button_area'>
283 </div>
283 </div>
284 <div id="pager-container" class="container"></div>
284 <div id="pager-container" class="container"></div>
285 </div>
285 </div>
286 </div>
286 </div>
287
287
288 </div>
288 </div>
289 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
289 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
290
290
291
291
292 {% endblock %}
292 {% endblock %}
293
293
294
294
295 {% block script %}
295 {% block script %}
296 {{super()}}
296
297
297 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
298 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
298 <script type="text/javascript">
299 <script type="text/javascript">
299 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
300 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
300 </script>
301 </script>
301 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
302 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
302 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
303 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
303 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
304 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
304 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
305 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
305 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
306 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
306 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
307 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
307 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
308 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
308 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
309 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
309 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
310 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
310 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
311 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
311 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
312 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
312 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
313 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
313 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
315 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
315 <script src="{{ static_url("notebook/js/codemirror-ipythongfm.js") }}" charset="utf-8"></script>
316 <script src="{{ static_url("notebook/js/codemirror-ipythongfm.js") }}" charset="utf-8"></script>
316
317
317 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
318 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
318
319
319 {% endblock %}
320 {% endblock %}
@@ -1,114 +1,116 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block title %}{{page_title}}{% endblock %}
3 {% block title %}{{page_title}}{% endblock %}
4
4
5
5
6 {% block stylesheet %}
6 {% block stylesheet %}
7 {{super()}}
7 {{super()}}
8 <link rel="stylesheet" href="{{ static_url("tree/css/override.css") }}" type="text/css" />
8 <link rel="stylesheet" href="{{ static_url("tree/css/override.css") }}" type="text/css" />
9 {% endblock %}
9 {% endblock %}
10
10
11 {% block params %}
11 {% block params %}
12
12
13 data-project="{{project}}"
13 data-project="{{project}}"
14 data-base-url="{{base_url}}"
14 data-base-url="{{base_url}}"
15 data-notebook-path="{{notebook_path}}"
15 data-notebook-path="{{notebook_path}}"
16
16
17 {% endblock %}
17 {% endblock %}
18
18
19
19
20 {% block site %}
20 {% block site %}
21
21
22 <div id="ipython-main-app" class="container">
22 <div id="ipython-main-app" class="container">
23
23
24 <div id="tab_content" class="tabbable">
24 <div id="tab_content" class="tabbable">
25 <ul id="tabs" class="nav nav-tabs">
25 <ul id="tabs" class="nav nav-tabs">
26 <li class="active"><a href="#notebooks" data-toggle="tab">Notebooks</a></li>
26 <li class="active"><a href="#notebooks" data-toggle="tab">Notebooks</a></li>
27 <li><a href="#running" data-toggle="tab">Running</a></li>
27 <li><a href="#running" data-toggle="tab">Running</a></li>
28 <li><a href="#clusters" data-toggle="tab">Clusters</a></li>
28 <li><a href="#clusters" data-toggle="tab">Clusters</a></li>
29 </ul>
29 </ul>
30
30
31 <div class="tab-content">
31 <div class="tab-content">
32 <div id="notebooks" class="tab-pane active">
32 <div id="notebooks" class="tab-pane active">
33 <div id="notebook_toolbar" class="row">
33 <div id="notebook_toolbar" class="row">
34 <div class="col-md-8 no-padding">
34 <div class="col-md-8 no-padding">
35 <form id='alternate_upload' class='alternate_upload' >
35 <form id='alternate_upload' class='alternate_upload' >
36 <span id="notebook_list_info" style="position:absolute" >
36 <span id="notebook_list_info" style="position:absolute" >
37 To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
37 To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
38 </span>
38 </span>
39 <input type="file" name="datafile" class="fileinput" multiple='multiple'>
39 <input type="file" name="datafile" class="fileinput" multiple='multiple'>
40 </form>
40 </form>
41 </div>
41 </div>
42 <div class="col-md-4 no-padding tree-buttons">
42 <div class="col-md-4 no-padding tree-buttons">
43 <span id="notebook_buttons" class="pull-right">
43 <span id="notebook_buttons" class="pull-right">
44 <button id="new_notebook" title="Create new notebook" class="btn btn-default btn-xs">New Notebook</button>
44 <button id="new_notebook" title="Create new notebook" class="btn btn-default btn-xs">New Notebook</button>
45 <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="icon-refresh"></i></button>
45 <button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="icon-refresh"></i></button>
46 </span>
46 </span>
47 </div>
47 </div>
48 </div>
48 </div>
49
49
50 <div id="notebook_list">
50 <div id="notebook_list">
51 <div id="notebook_list_header" class="row list_header">
51 <div id="notebook_list_header" class="row list_header">
52 <div id="project_name">
52 <div id="project_name">
53 <ul class="breadcrumb">
53 <ul class="breadcrumb">
54 <li><a href="{{breadcrumbs[0][0]}}"><i class="icon-home"></i></a></li>
54 <li><a href="{{breadcrumbs[0][0]}}"><i class="icon-home"></i></a></li>
55 {% for crumb in breadcrumbs[1:] %}
55 {% for crumb in breadcrumbs[1:] %}
56 <li><a href="{{crumb[0]}}">{{crumb[1]}}</a></li>
56 <li><a href="{{crumb[0]}}">{{crumb[1]}}</a></li>
57 {% endfor %}
57 {% endfor %}
58 </ul>
58 </ul>
59 </div>
59 </div>
60 </div>
60 </div>
61 </div>
61 </div>
62 </div>
62 </div>
63
63
64 <div id="running" class="tab-pane">
64 <div id="running" class="tab-pane">
65
65
66 <div id="running_toolbar" class="row">
66 <div id="running_toolbar" class="row">
67 <div class="col-md-8 no-padding">
67 <div class="col-md-8 no-padding">
68 <span id="running_list_info">Currently running IPython notebooks</span>
68 <span id="running_list_info">Currently running IPython notebooks</span>
69 </div>
69 </div>
70 <div class="col-md-4 no-padding tree-buttons">
70 <div class="col-md-4 no-padding tree-buttons">
71 <span id="running_buttons" class="pull-right">
71 <span id="running_buttons" class="pull-right">
72 <button id="refresh_running_list" title="Refresh running list" class="btn btn-default btn-xs"><i class="icon-refresh"></i></button>
72 <button id="refresh_running_list" title="Refresh running list" class="btn btn-default btn-xs"><i class="icon-refresh"></i></button>
73 </span>
73 </span>
74 </div>
74 </div>
75 </div>
75 </div>
76
76
77 <div id="running_list">
77 <div id="running_list">
78 <div id="running_list_header" class="row list_header">
78 <div id="running_list_header" class="row list_header">
79 <div> There are no notebooks running. </div>
79 <div> There are no notebooks running. </div>
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83
83
84 <div id="clusters" class="tab-pane">
84 <div id="clusters" class="tab-pane">
85
85
86 <div id="cluster_toolbar" class="row">
86 <div id="cluster_toolbar" class="row">
87 <div class="col-md-8 no-padding">
87 <div class="col-md-8 no-padding">
88 <span id="cluster_list_info">IPython parallel computing clusters</span>
88 <span id="cluster_list_info">IPython parallel computing clusters</span>
89 </div>
89 </div>
90 <div class="col-md-4 no-padding tree-buttons">
90 <div class="col-md-4 no-padding tree-buttons">
91 <span id="cluster_buttons" class="pull-right">
91 <span id="cluster_buttons" class="pull-right">
92 <button id="refresh_cluster_list" title="Refresh cluster list" class="btn btn-default btn-xs"><i class="icon-refresh"></i></button>
92 <button id="refresh_cluster_list" title="Refresh cluster list" class="btn btn-default btn-xs"><i class="icon-refresh"></i></button>
93 </span>
93 </span>
94 </div>
94 </div>
95 </div>
95 </div>
96
96
97 <div id="cluster_list">
97 <div id="cluster_list">
98 <div id="cluster_list_header" class="row list_header">
98 <div id="cluster_list_header" class="row list_header">
99 <div class="profile_col col-md-4">profile</div>
99 <div class="profile_col col-md-4">profile</div>
100 <div class="status_col col-md-3">status</div>
100 <div class="status_col col-md-3">status</div>
101 <div class="engines_col col-md-3" title="Enter the number of engines to start or empty for default"># of engines</div>
101 <div class="engines_col col-md-3" title="Enter the number of engines to start or empty for default"># of engines</div>
102 <div class="action_col col-md-2">action</div>
102 <div class="action_col col-md-2">action</div>
103 </div>
103 </div>
104 </div>
104 </div>
105 </div>
105 </div>
106 </div>
106 </div>
107
107
108 </div>
108 </div>
109
109
110 {% endblock %}
110 {% endblock %}
111
111
112 {% block script %}
112 {% block script %}
113 {{super()}}
114
113 <script src="{{ static_url("tree/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
115 <script src="{{ static_url("tree/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
114 {% endblock %}
116 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now