##// END OF EJS Templates
Finishing work on "Make a Copy" functionality.
Brian Granger -
Show More
@@ -1,263 +1,269 b''
1 1 """A notebook manager that uses the local file system for storage.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import datetime
20 20 import os
21 21 import uuid
22 22 import glob
23 23
24 24 from tornado import web
25 25
26 26 from IPython.config.configurable import LoggingConfigurable
27 27 from IPython.nbformat import current
28 28 from IPython.utils.traitlets import Unicode, List, Dict, Bool
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Classes
32 32 #-----------------------------------------------------------------------------
33 33
34 34 class NotebookManager(LoggingConfigurable):
35 35
36 36 notebook_dir = Unicode(os.getcwd(), config=True, help="""
37 37 The directory to use for notebooks.
38 38 """)
39 39
40 40 save_script = Bool(False, config=True,
41 41 help="""Automatically create a Python script when saving the notebook.
42 42
43 43 For easier use of import, %run and %loadpy across notebooks, a
44 44 <notebook-name>.py script will be created next to any
45 45 <notebook-name>.ipynb on each save. This can also be set with the
46 46 short `--script` flag.
47 47 """
48 48 )
49 49
50 50 filename_ext = Unicode(u'.ipynb')
51 51 allowed_formats = List([u'json',u'py'])
52 52
53 53 # Map notebook_ids to notebook names
54 54 mapping = Dict()
55 55 # Map notebook names to notebook_ids
56 56 rev_mapping = Dict()
57 57
58 58 def list_notebooks(self):
59 59 """List all notebooks in the notebook dir.
60 60
61 61 This returns a list of dicts of the form::
62 62
63 63 dict(notebook_id=notebook,name=name)
64 64 """
65 65 names = glob.glob(os.path.join(self.notebook_dir,
66 66 '*' + self.filename_ext))
67 67 names = [os.path.splitext(os.path.basename(name))[0]
68 68 for name in names]
69 69
70 70 data = []
71 71 for name in names:
72 72 if name not in self.rev_mapping:
73 73 notebook_id = self.new_notebook_id(name)
74 74 else:
75 75 notebook_id = self.rev_mapping[name]
76 76 data.append(dict(notebook_id=notebook_id,name=name))
77 77 data = sorted(data, key=lambda item: item['name'])
78 78 return data
79 79
80 80 def new_notebook_id(self, name):
81 81 """Generate a new notebook_id for a name and store its mappings."""
82 82 # TODO: the following will give stable urls for notebooks, but unless
83 83 # the notebooks are immediately redirected to their new urls when their
84 84 # filemname changes, nasty inconsistencies result. So for now it's
85 85 # disabled and instead we use a random uuid4() call. But we leave the
86 86 # logic here so that we can later reactivate it, whhen the necessary
87 87 # url redirection code is written.
88 88 #notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
89 89 # 'file://'+self.get_path_by_name(name).encode('utf-8')))
90 90
91 91 notebook_id = unicode(uuid.uuid4())
92 92
93 93 self.mapping[notebook_id] = name
94 94 self.rev_mapping[name] = notebook_id
95 95 return notebook_id
96 96
97 97 def delete_notebook_id(self, notebook_id):
98 98 """Delete a notebook's id only. This doesn't delete the actual notebook."""
99 99 name = self.mapping[notebook_id]
100 100 del self.mapping[notebook_id]
101 101 del self.rev_mapping[name]
102 102
103 103 def notebook_exists(self, notebook_id):
104 104 """Does a notebook exist?"""
105 105 if notebook_id not in self.mapping:
106 106 return False
107 107 path = self.get_path_by_name(self.mapping[notebook_id])
108 108 return os.path.isfile(path)
109 109
110 110 def find_path(self, notebook_id):
111 111 """Return a full path to a notebook given its notebook_id."""
112 112 try:
113 113 name = self.mapping[notebook_id]
114 114 except KeyError:
115 115 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
116 116 return self.get_path_by_name(name)
117 117
118 118 def get_path_by_name(self, name):
119 119 """Return a full path to a notebook given its name."""
120 120 filename = name + self.filename_ext
121 121 path = os.path.join(self.notebook_dir, filename)
122 122 return path
123 123
124 124 def get_notebook(self, notebook_id, format=u'json'):
125 125 """Get the representation of a notebook in format by notebook_id."""
126 126 format = unicode(format)
127 127 if format not in self.allowed_formats:
128 128 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
129 129 last_modified, nb = self.get_notebook_object(notebook_id)
130 130 kwargs = {}
131 131 if format == 'json':
132 132 # don't split lines for sending over the wire, because it
133 133 # should match the Python in-memory format.
134 134 kwargs['split_lines'] = False
135 135 data = current.writes(nb, format, **kwargs)
136 136 name = nb.get('name','notebook')
137 137 return last_modified, name, data
138 138
139 139 def get_notebook_object(self, notebook_id):
140 140 """Get the NotebookNode representation of a notebook by notebook_id."""
141 141 path = self.find_path(notebook_id)
142 142 if not os.path.isfile(path):
143 143 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
144 144 info = os.stat(path)
145 145 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
146 146 with open(path,'r') as f:
147 147 s = f.read()
148 148 try:
149 149 # v1 and v2 and json in the .ipynb files.
150 150 nb = current.reads(s, u'json')
151 151 except:
152 152 raise web.HTTPError(500, u'Unreadable JSON notebook.')
153 153 if 'name' not in nb:
154 154 nb.name = os.path.split(path)[-1].split(u'.')[0]
155 155 return last_modified, nb
156 156
157 157 def save_new_notebook(self, data, name=None, format=u'json'):
158 158 """Save a new notebook and return its notebook_id.
159 159
160 160 If a name is passed in, it overrides any values in the notebook data
161 161 and the value in the data is updated to use that value.
162 162 """
163 163 if format not in self.allowed_formats:
164 164 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
165 165
166 166 try:
167 167 nb = current.reads(data.decode('utf-8'), format)
168 168 except:
169 169 raise web.HTTPError(400, u'Invalid JSON data')
170 170
171 171 if name is None:
172 172 try:
173 173 name = nb.metadata.name
174 174 except AttributeError:
175 175 raise web.HTTPError(400, u'Missing notebook name')
176 176 nb.metadata.name = name
177 177
178 178 notebook_id = self.new_notebook_id(name)
179 179 self.save_notebook_object(notebook_id, nb)
180 180 return notebook_id
181 181
182 182 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
183 183 """Save an existing notebook by notebook_id."""
184 184 if format not in self.allowed_formats:
185 185 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
186 186
187 187 try:
188 188 nb = current.reads(data.decode('utf-8'), format)
189 189 except:
190 190 raise web.HTTPError(400, u'Invalid JSON data')
191 191
192 192 if name is not None:
193 193 nb.metadata.name = name
194 194 self.save_notebook_object(notebook_id, nb)
195 195
196 196 def save_notebook_object(self, notebook_id, nb):
197 197 """Save an existing notebook object by notebook_id."""
198 198 if notebook_id not in self.mapping:
199 199 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
200 200 old_name = self.mapping[notebook_id]
201 201 try:
202 202 new_name = nb.metadata.name
203 203 except AttributeError:
204 204 raise web.HTTPError(400, u'Missing notebook name')
205 205 path = self.get_path_by_name(new_name)
206 206 try:
207 207 with open(path,'w') as f:
208 208 current.write(nb, f, u'json')
209 209 except Exception as e:
210 210 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
211 211 # save .py script as well
212 212 if self.save_script:
213 213 pypath = os.path.splitext(path)[0] + '.py'
214 214 try:
215 215 with open(pypath,'w') as f:
216 216 current.write(nb, f, u'py')
217 217 except Exception as e:
218 218 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
219 219
220 220 if old_name != new_name:
221 221 old_path = self.get_path_by_name(old_name)
222 222 if os.path.isfile(old_path):
223 223 os.unlink(old_path)
224 224 if self.save_script:
225 225 old_pypath = os.path.splitext(old_path)[0] + '.py'
226 226 if os.path.isfile(old_pypath):
227 227 os.unlink(old_pypath)
228 228 self.mapping[notebook_id] = new_name
229 229 self.rev_mapping[new_name] = notebook_id
230 230
231 231 def delete_notebook(self, notebook_id):
232 232 """Delete notebook by notebook_id."""
233 233 path = self.find_path(notebook_id)
234 234 if not os.path.isfile(path):
235 235 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
236 236 os.unlink(path)
237 237 self.delete_notebook_id(notebook_id)
238 238
239 def new_notebook(self):
240 """Create a new notebook and returns its notebook_id."""
239 def increment_filename(self, basename):
240 """Return a non-used filename of the form basename0."""
241 241 i = 0
242 242 while True:
243 name = u'Untitled%i' % i
243 name = u'%s%i' % (basename,i)
244 244 path = self.get_path_by_name(name)
245 245 if not os.path.isfile(path):
246 246 break
247 247 else:
248 248 i = i+1
249 return path, name
250
251 def new_notebook(self):
252 """Create a new notebook and return its notebook_id."""
253 path, name = self.increment_filename('Untitled')
249 254 notebook_id = self.new_notebook_id(name)
250 255 metadata = current.new_metadata(name=name)
251 256 nb = current.new_notebook(metadata=metadata)
252 257 with open(path,'w') as f:
253 258 current.write(nb, f, u'json')
254 259 return notebook_id
255 260
256 261 def copy_notebook(self, notebook_id):
257 """Create a new notebook and returns its notebook_id."""
262 """Copy an existing notebook and return its notebook_id."""
258 263 last_mod, nb = self.get_notebook_object(notebook_id)
259 264 name = nb.metadata.name + '-Copy'
265 path, name = self.increment_filename(name)
260 266 nb.metadata.name = name
261 267 notebook_id = self.new_notebook_id(name)
262 268 self.save_notebook_object(notebook_id, nb)
263 269 return notebook_id
@@ -1,839 +1,832 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // CodeCell
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var CodeCell = function (notebook) {
17 17 this.code_mirror = null;
18 18 this.input_prompt_number = '&nbsp;';
19 19 this.is_completing = false;
20 20 this.completion_cursor = null;
21 21 this.outputs = [];
22 22 this.collapsed = false;
23 23 this.tooltip_timeout = null;
24 24 IPython.Cell.apply(this, arguments);
25 25 };
26 26
27 27
28 28 CodeCell.prototype = new IPython.Cell();
29 29
30 30
31 31 CodeCell.prototype.create_element = function () {
32 32 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
33 33 cell.attr('tabindex','2');
34 34 var input = $('<div></div>').addClass('input hbox');
35 35 input.append($('<div/>').addClass('prompt input_prompt'));
36 36 var input_area = $('<div/>').addClass('input_area box-flex1');
37 37 this.code_mirror = CodeMirror(input_area.get(0), {
38 38 indentUnit : 4,
39 39 mode: 'python',
40 40 theme: 'ipython',
41 41 readOnly: this.read_only,
42 42 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
43 43 });
44 44 input.append(input_area);
45 45 var output = $('<div></div>').addClass('output vbox');
46 46 cell.append(input).append(output);
47 47 this.element = cell;
48 48 this.collapse();
49 49 };
50 50
51 51 //TODO, try to diminish the number of parameters.
52 52 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
53 53 var that = this;
54 54 if (pre_cursor === "" || pre_cursor === "(" ) {
55 55 // don't do anything if line beggin with '(' or is empty
56 56 } else {
57 57 // Will set a timer to request tooltip in `time`
58 58 that.tooltip_timeout = setTimeout(function(){
59 59 IPython.notebook.request_tool_tip(that, pre_cursor)
60 60 },time);
61 61 }
62 62 };
63 63
64 64 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
65 65 // This method gets called in CodeMirror's onKeyDown/onKeyPress
66 66 // handlers and is used to provide custom key handling. Its return
67 67 // value is used to determine if CodeMirror should ignore the event:
68 68 // true = ignore, false = don't ignore.
69 69
70 70 if (this.read_only){
71 71 return false;
72 72 }
73 73
74 74 // note that we are comparing and setting the time to wait at each key press.
75 75 // a better wqy might be to generate a new function on each time change and
76 76 // assign it to CodeCell.prototype.request_tooltip_after_time
77 77 tooltip_wait_time = this.notebook.time_before_tooltip;
78 78 tooltip_on_tab = this.notebook.tooltip_on_tab;
79 79 var that = this;
80 80 // whatever key is pressed, first, cancel the tooltip request before
81 81 // they are sent, and remove tooltip if any
82 82 if(event.type === 'keydown' ){
83 83 that.remove_and_cancel_tooltip();
84 84 }
85 85
86 86 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
87 87 // Always ignore shift-enter in CodeMirror as we handle it.
88 88 return true;
89 89 }else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
90 90 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
91 91 // browser and keyboard layout !
92 92 // Pressing '(' , request tooltip, don't forget to reappend it
93 93 var cursor = editor.getCursor();
94 94 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
95 95 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
96 96 } else if (event.keyCode === 9 && event.type == 'keydown') {
97 97 // Tab completion.
98 98 var cur = editor.getCursor();
99 99 //Do not trim here because of tooltip
100 100 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
101 101 if (pre_cursor.trim() === "") {
102 102 // Don't autocomplete if the part of the line before the cursor
103 103 // is empty. In this case, let CodeMirror handle indentation.
104 104 return false;
105 105 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
106 106 that.request_tooltip_after_time(pre_cursor,0);
107 107 } else {
108 108 pre_cursor.trim();
109 109 // Autocomplete the current line.
110 110 event.stop();
111 111 var line = editor.getLine(cur.line);
112 112 this.is_completing = true;
113 113 this.completion_cursor = cur;
114 114 IPython.notebook.complete_cell(this, line, cur.ch);
115 115 return true;
116 116 }
117 117 } else if (event.keyCode === 8 && event.type == 'keydown') {
118 118 // If backspace and the line ends with 4 spaces, remove them.
119 119 var cur = editor.getCursor();
120 120 var line = editor.getLine(cur.line);
121 121 var ending = line.slice(-4);
122 122 if (ending === ' ') {
123 123 editor.replaceRange('',
124 124 {line: cur.line, ch: cur.ch-4},
125 125 {line: cur.line, ch: cur.ch}
126 126 );
127 127 event.stop();
128 128 return true;
129 129 } else {
130 130 return false;
131 131 }
132 132 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
133 133 && event.type == 'keydown') {
134 134 // toggle line numbers with Ctrl-Shift-L
135 135 this.toggle_line_numbers();
136 136 }
137 137 else {
138 138 // keypress/keyup also trigger on TAB press, and we don't want to
139 139 // use those to disable tab completion.
140 140 if (this.is_completing && event.keyCode !== 9) {
141 141 var ed_cur = editor.getCursor();
142 142 var cc_cur = this.completion_cursor;
143 143 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
144 144 this.is_completing = false;
145 145 this.completion_cursor = null;
146 146 }
147 147 }
148 148 return false;
149 149 };
150 150 return false;
151 151 };
152 152
153 153 CodeCell.prototype.remove_and_cancel_tooltip = function() {
154 154 // note that we don't handle closing directly inside the calltip
155 155 // as in the completer, because it is not focusable, so won't
156 156 // get the event.
157 157 if (this.tooltip_timeout != null){
158 158 clearTimeout(this.tooltip_timeout);
159 159 $('#tooltip').remove();
160 160 this.tooltip_timeout = null;
161 161 }
162 162 }
163 163
164 164 CodeCell.prototype.finish_tooltip = function (reply) {
165 165 // Extract call tip data; the priority is call, init, main.
166 166 defstring = reply.call_def;
167 167 if (defstring == null) { defstring = reply.init_definition; }
168 168 if (defstring == null) { defstring = reply.definition; }
169 169
170 170 docstring = reply.call_docstring;
171 171 if (docstring == null) { docstring = reply.init_docstring; }
172 172 if (docstring == null) { docstring = reply.docstring; }
173 173 if (docstring == null) { docstring = "<empty docstring>"; }
174 174
175 175 name=reply.name;
176 176
177 177 var that = this;
178 178 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
179 179 // remove to have the tooltip not Limited in X and Y
180 180 tooltip.addClass('smalltooltip');
181 181 var pre=$('<pre/>').html(utils.fixConsole(docstring));
182 182 var expandlink=$('<a/>').attr('href',"#");
183 183 expandlink.addClass("ui-corner-all"); //rounded corner
184 184 expandlink.attr('role',"button");
185 185 //expandlink.addClass('ui-button');
186 186 //expandlink.addClass('ui-state-default');
187 187 var expandspan=$('<span/>').text('Expand');
188 188 expandspan.addClass('ui-icon');
189 189 expandspan.addClass('ui-icon-plus');
190 190 expandlink.append(expandspan);
191 191 expandlink.attr('id','expanbutton');
192 192 expandlink.click(function(){
193 193 tooltip.removeClass('smalltooltip');
194 194 tooltip.addClass('bigtooltip');
195 195 $('#expanbutton').remove();
196 196 setTimeout(function(){that.code_mirror.focus();}, 50);
197 197 });
198 198 var morelink=$('<a/>').attr('href',"#");
199 199 morelink.attr('role',"button");
200 200 morelink.addClass('ui-button');
201 201 //morelink.addClass("ui-corner-all"); //rounded corner
202 202 //morelink.addClass('ui-state-default');
203 203 var morespan=$('<span/>').text('Open in Pager');
204 204 morespan.addClass('ui-icon');
205 205 morespan.addClass('ui-icon-arrowstop-l-n');
206 206 morelink.append(morespan);
207 207 morelink.click(function(){
208 208 var msg_id = IPython.notebook.kernel.execute(name+"?");
209 209 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
210 210 that.remove_and_cancel_tooltip();
211 211 setTimeout(function(){that.code_mirror.focus();}, 50);
212 212 });
213 213
214 214 var closelink=$('<a/>').attr('href',"#");
215 215 closelink.attr('role',"button");
216 216 closelink.addClass('ui-button');
217 217 //closelink.addClass("ui-corner-all"); //rounded corner
218 218 //closelink.adClass('ui-state-default'); // grey background and blue cross
219 219 var closespan=$('<span/>').text('Close');
220 220 closespan.addClass('ui-icon');
221 221 closespan.addClass('ui-icon-close');
222 222 closelink.append(closespan);
223 223 closelink.click(function(){
224 224 that.remove_and_cancel_tooltip();
225 225 setTimeout(function(){that.code_mirror.focus();}, 50);
226 226 });
227 227 //construct the tooltip
228 228 tooltip.append(closelink);
229 229 tooltip.append(expandlink);
230 230 tooltip.append(morelink);
231 231 if(defstring){
232 232 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
233 233 tooltip.append(defstring_html);
234 234 }
235 235 tooltip.append(pre);
236 236 var pos = this.code_mirror.cursorCoords();
237 237 tooltip.css('left',pos.x+'px');
238 238 tooltip.css('top',pos.yBot+'px');
239 239 $('body').append(tooltip);
240 240
241 241 // issues with cross-closing if multiple tooltip in less than 5sec
242 242 // keep it comented for now
243 243 // setTimeout(that.remove_and_cancel_tooltip, 5000);
244 244 };
245 245
246 246 // As you type completer
247 247 CodeCell.prototype.finish_completing = function (matched_text, matches) {
248 248 //return if not completing or nothing to complete
249 249 if (!this.is_completing || matches.length === 0) {return;}
250 250
251 251 // for later readability
252 252 var key = { tab:9,
253 253 esc:27,
254 254 backspace:8,
255 255 space:32,
256 256 shift:16,
257 257 enter:13,
258 258 // _ is 95
259 259 isCompSymbol : function (code)
260 260 {
261 261 return (code > 64 && code <= 90)
262 262 || (code >= 97 && code <= 122)
263 263 || (code == 95)
264 264 },
265 265 dismissAndAppend : function (code)
266 266 {
267 267 chararr = '()[]+-/\\. ,=*'.split("");
268 268 codearr = chararr.map(function(x){return x.charCodeAt(0)});
269 269 return jQuery.inArray(code, codearr) != -1;
270 270 }
271 271
272 272 }
273 273
274 274 // smart completion, sort kwarg ending with '='
275 275 var newm = new Array();
276 276 if(this.notebook.smart_completer)
277 277 {
278 278 kwargs = new Array();
279 279 other = new Array();
280 280 for(var i = 0 ; i<matches.length ; ++i){
281 281 if(matches[i].substr(-1) === '='){
282 282 kwargs.push(matches[i]);
283 283 }else{other.push(matches[i]);}
284 284 }
285 285 newm = kwargs.concat(other);
286 286 matches = newm;
287 287 }
288 288 // end sort kwargs
289 289
290 290 // give common prefix of a array of string
291 291 function sharedStart(A){
292 292 if(A.length == 1){return A[0]}
293 293 if(A.length > 1 ){
294 294 var tem1, tem2, s, A = A.slice(0).sort();
295 295 tem1 = A[0];
296 296 s = tem1.length;
297 297 tem2 = A.pop();
298 298 while(s && tem2.indexOf(tem1) == -1){
299 299 tem1 = tem1.substring(0, --s);
300 300 }
301 301 return tem1;
302 302 }
303 303 return "";
304 304 }
305 305
306 306
307 307 //try to check if the user is typing tab at least twice after a word
308 308 // and completion is "done"
309 309 fallback_on_tooltip_after = 2
310 310 if(matches.length == 1 && matched_text === matches[0])
311 311 {
312 312 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
313 313 {
314 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
315 console.log('You should understand that there is no (more) completion for that !');
316 console.log("I'll show you the tooltip, will you stop bothering me ?");
317 314 this.request_tooltip_after_time(matched_text+'(',0);
318 315 return;
319 316 }
320 317 this.prevmatch = matched_text
321 318 this.npressed = this.npressed+1;
322 319 }
323 320 else
324 321 {
325 322 this.prevmatch = "";
326 323 this.npressed = 0;
327 324 }
328 325 // end fallback on tooltip
329 326 //==================================
330 327 // Real completion logic start here
331 328 var that = this;
332 329 var cur = this.completion_cursor;
333 330 var done = false;
334 331
335 332 // call to dismmiss the completer
336 333 var close = function () {
337 334 if (done) return;
338 335 done = true;
339 336 if (complete != undefined)
340 337 {complete.remove();}
341 338 that.is_completing = false;
342 339 that.completion_cursor = null;
343 340 };
344 341
345 342 // update codemirror with the typed text
346 343 prev = matched_text
347 344 var update = function (inserted_text, event) {
348 345 that.code_mirror.replaceRange(
349 346 inserted_text,
350 347 {line: cur.line, ch: (cur.ch-matched_text.length)},
351 348 {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
352 349 );
353 350 prev = inserted_text
354 351 if(event != null){
355 352 event.stopPropagation();
356 353 event.preventDefault();
357 354 }
358 355 };
359 356 // insert the given text and exit the completer
360 357 var insert = function (selected_text, event) {
361 358 update(selected_text)
362 359 close();
363 360 setTimeout(function(){that.code_mirror.focus();}, 50);
364 361 };
365 362
366 363 // insert the curent highlited selection and exit
367 364 var pick = function () {
368 365 insert(select.val()[0],null);
369 366 };
370 367
371 368
372 369 // Define function to clear the completer, refill it with the new
373 370 // matches, update the pseuso typing field. autopick insert match if
374 371 // only one left, in no matches (anymore) dismiss itself by pasting
375 372 // what the user have typed until then
376 373 var complete_with = function(matches,typed_text,autopick,event)
377 374 {
378 375 // If autopick an only one match, past.
379 376 // Used to 'pick' when pressing tab
380 377 if (matches.length < 1) {
381 378 insert(typed_text,event);
382 379 if(event != null){
383 380 event.stopPropagation();
384 381 event.preventDefault();
385 382 }
386 383 } else if (autopick && matches.length == 1) {
387 384 insert(matches[0],event);
388 385 if(event != null){
389 386 event.stopPropagation();
390 387 event.preventDefault();
391 388 }
392 389 }
393 390 //clear the previous completion if any
394 391 update(typed_text,event);
395 392 complete.children().children().remove();
396 393 $('#asyoutype').html("<b>"+matched_text+"</b>"+typed_text.substr(matched_text.length));
397 394 select = $('#asyoutypeselect');
398 395 for (var i = 0; i<matches.length; ++i) {
399 396 select.append($('<option/>').html(matches[i]));
400 397 }
401 398 select.children().first().attr('selected','true');
402 399 }
403 400
404 401 // create html for completer
405 402 var complete = $('<div/>').addClass('completions');
406 403 complete.attr('id','complete');
407 404 complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
408 405
409 406 var select = $('<select/>').attr('multiple','true');
410 407 select.attr('id', 'asyoutypeselect')
411 408 select.attr('size',Math.min(10,matches.length));
412 409 var pos = this.code_mirror.cursorCoords();
413 410
414 411 // TODO: I propose to remove enough horizontal pixel
415 412 // to align the text later
416 413 complete.css('left',pos.x+'px');
417 414 complete.css('top',pos.yBot+'px');
418 415 complete.append(select);
419 416
420 417 $('body').append(complete);
421 418
422 419 // So a first actual completion. see if all the completion start wit
423 420 // the same letter and complete if necessary
424 421 fastForward = sharedStart(matches)
425 422 typed_characters = fastForward.substr(matched_text.length);
426 423 complete_with(matches,matched_text+typed_characters,true,null);
427 424 filterd = matches;
428 425 // Give focus to select, and make it filter the match as the user type
429 426 // by filtering the previous matches. Called by .keypress and .keydown
430 427 var downandpress = function (event,press_or_down) {
431 428 var code = event.which;
432 429 var autopick = false; // auto 'pick' if only one match
433 430 if (press_or_down === 0){
434 431 press = true; down = false; //Are we called from keypress or keydown
435 432 } else if (press_or_down == 1){
436 433 press = false; down = true;
437 434 }
438 435 if (code === key.shift) {
439 436 // nothing on Shift
440 437 return;
441 438 }
442 439 if (key.dismissAndAppend(code) && press) {
443 440 var newchar = String.fromCharCode(code);
444 441 typed_characters = typed_characters+newchar;
445 442 insert(matched_text+typed_characters,event);
446 443 return
447 444 }
448 445 if (code === key.enter) {
449 446 // Pressing ENTER will cause a pick
450 447 event.stopPropagation();
451 448 event.preventDefault();
452 449 pick();
453 450 } else if (code === 38 || code === 40) {
454 451 // We don't want the document keydown handler to handle UP/DOWN,
455 452 // but we want the default action.
456 453 event.stopPropagation();
457 454 } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
458 455 if( key.isCompSymbol(code) && press)
459 456 {
460 457 var newchar = String.fromCharCode(code);
461 458 typed_characters = typed_characters+newchar;
462 459 } else if (code == key.tab) {
463 460 fastForward = sharedStart(filterd)
464 461 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
465 462 typed_characters = typed_characters+ffsub;
466 463 autopick = true;
467 464 } else if (code == key.backspace && down) {
468 465 // cancel if user have erase everything, otherwise decrease
469 466 // what we filter with
470 467 event.preventDefault();
471 468 if (typed_characters.length <= 0)
472 469 {
473 470 insert(matched_text,event)
474 471 return
475 472 }
476 473 typed_characters = typed_characters.substr(0,typed_characters.length-1);
477 474 } else if (press && code != key.backspace && code != key.tab && code != 0){
478 475 insert(matched_text+typed_characters,event);
479 476 return
480 477 } else {
481 478 return
482 479 }
483 480 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
484 481 filterd = matches.filter(function(x){return re.test(x)});
485 482 complete_with(filterd,matched_text+typed_characters,autopick,event);
486 483 } else if( code == key.esc) {
487 484 // dismiss the completer and go back to before invoking it
488 485 insert(matched_text,event);
489 486 } else if( press ){ // abort only on .keypress or esc
490 // abort with what the user have pressed until now
491 console.log('aborting with keycode : '+code+' is down :'+down);
492 487 }
493 488 }
494 489 select.keydown(function (event) {
495 490 downandpress(event,1)
496 491 });
497 492 select.keypress(function (event) {
498 493 downandpress(event,0)
499 494 });
500 495 // Double click also causes a pick.
501 496 // and bind the last actions.
502 497 select.dblclick(pick);
503 498 select.blur(close);
504 499 select.focus();
505 500 };
506 501
507 502 CodeCell.prototype.toggle_line_numbers = function () {
508 503 if (this.code_mirror.getOption('lineNumbers') == false) {
509 504 this.code_mirror.setOption('lineNumbers', true);
510 505 } else {
511 506 this.code_mirror.setOption('lineNumbers', false);
512 507 }
513 508 this.code_mirror.refresh();
514 509 };
515 510
516 511 CodeCell.prototype.select = function () {
517 512 IPython.Cell.prototype.select.apply(this);
518 513 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
519 514 // not causing the cursor to blink if the editor is empty initially.
520 515 // While this seems to fix the issue, this should be fixed
521 516 // in CodeMirror proper.
522 517 var s = this.code_mirror.getValue();
523 518 this.code_mirror.focus();
524 519 if (s === '') this.code_mirror.setValue('');
525 520 };
526 521
527 522
528 523 CodeCell.prototype.select_all = function () {
529 524 var start = {line: 0, ch: 0};
530 525 var nlines = this.code_mirror.lineCount();
531 526 var last_line = this.code_mirror.getLine(nlines-1);
532 527 var end = {line: nlines-1, ch: last_line.length};
533 528 this.code_mirror.setSelection(start, end);
534 529 };
535 530
536 531
537 532 CodeCell.prototype.append_output = function (json) {
538 533 this.expand();
539 534 if (json.output_type === 'pyout') {
540 535 this.append_pyout(json);
541 536 } else if (json.output_type === 'pyerr') {
542 537 this.append_pyerr(json);
543 538 } else if (json.output_type === 'display_data') {
544 539 this.append_display_data(json);
545 540 } else if (json.output_type === 'stream') {
546 541 this.append_stream(json);
547 542 };
548 543 this.outputs.push(json);
549 544 };
550 545
551 546
552 547 CodeCell.prototype.create_output_area = function () {
553 548 var oa = $("<div/>").addClass("hbox output_area");
554 549 oa.append($('<div/>').addClass('prompt'));
555 550 return oa;
556 551 };
557 552
558 553
559 554 CodeCell.prototype.append_pyout = function (json) {
560 555 n = json.prompt_number || ' ';
561 556 var toinsert = this.create_output_area();
562 557 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
563 558 this.append_mime_type(json, toinsert);
564 559 this.element.find('div.output').append(toinsert);
565 560 // If we just output latex, typeset it.
566 561 if ((json.latex !== undefined) || (json.html !== undefined)) {
567 562 this.typeset();
568 563 };
569 564 };
570 565
571 566
572 567 CodeCell.prototype.append_pyerr = function (json) {
573 568 var tb = json.traceback;
574 569 if (tb !== undefined && tb.length > 0) {
575 570 var s = '';
576 571 var len = tb.length;
577 572 for (var i=0; i<len; i++) {
578 573 s = s + tb[i] + '\n';
579 574 }
580 575 s = s + '\n';
581 576 var toinsert = this.create_output_area();
582 577 this.append_text(s, toinsert);
583 578 this.element.find('div.output').append(toinsert);
584 579 };
585 580 };
586 581
587 582
588 583 CodeCell.prototype.append_stream = function (json) {
589 584 // temporary fix: if stream undefined (json file written prior to this patch),
590 585 // default to most likely stdout:
591 586 if (json.stream == undefined){
592 587 json.stream = 'stdout';
593 588 }
594 589 var subclass = "output_"+json.stream;
595 590 if (this.outputs.length > 0){
596 591 // have at least one output to consider
597 592 var last = this.outputs[this.outputs.length-1];
598 593 if (last.output_type == 'stream' && json.stream == last.stream){
599 594 // latest output was in the same stream,
600 595 // so append directly into its pre tag
601 596 this.element.find('div.'+subclass).last().find('pre').append(json.text);
602 597 return;
603 598 }
604 599 }
605 600
606 601 // If we got here, attach a new div
607 602 var toinsert = this.create_output_area();
608 603 this.append_text(json.text, toinsert, "output_stream "+subclass);
609 604 this.element.find('div.output').append(toinsert);
610 605 };
611 606
612 607
613 608 CodeCell.prototype.append_display_data = function (json) {
614 609 var toinsert = this.create_output_area();
615 610 this.append_mime_type(json, toinsert);
616 611 this.element.find('div.output').append(toinsert);
617 612 // If we just output latex, typeset it.
618 613 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
619 614 this.typeset();
620 615 };
621 616 };
622 617
623 618
624 619 CodeCell.prototype.append_mime_type = function (json, element) {
625 620 if (json.html !== undefined) {
626 621 this.append_html(json.html, element);
627 622 } else if (json.latex !== undefined) {
628 623 this.append_latex(json.latex, element);
629 624 } else if (json.svg !== undefined) {
630 625 this.append_svg(json.svg, element);
631 626 } else if (json.png !== undefined) {
632 627 this.append_png(json.png, element);
633 628 } else if (json.jpeg !== undefined) {
634 629 this.append_jpeg(json.jpeg, element);
635 630 } else if (json.text !== undefined) {
636 631 this.append_text(json.text, element);
637 632 };
638 633 };
639 634
640 635
641 636 CodeCell.prototype.append_html = function (html, element) {
642 637 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
643 638 toinsert.append(html);
644 639 element.append(toinsert);
645 640 };
646 641
647 642
648 643 CodeCell.prototype.append_text = function (data, element, extra_class) {
649 644 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
650 645 if (extra_class){
651 646 toinsert.addClass(extra_class);
652 647 }
653 648 toinsert.append($("<pre/>").html(data));
654 649 element.append(toinsert);
655 650 };
656 651
657 652
658 653 CodeCell.prototype.append_svg = function (svg, element) {
659 654 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
660 655 toinsert.append(svg);
661 656 element.append(toinsert);
662 657 };
663 658
664 659
665 660 CodeCell.prototype.append_png = function (png, element) {
666 661 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
667 662 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
668 663 element.append(toinsert);
669 664 };
670 665
671 666
672 667 CodeCell.prototype.append_jpeg = function (jpeg, element) {
673 668 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
674 669 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
675 670 element.append(toinsert);
676 671 };
677 672
678 673
679 674 CodeCell.prototype.append_latex = function (latex, element) {
680 675 // This method cannot do the typesetting because the latex first has to
681 676 // be on the page.
682 677 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
683 678 toinsert.append(latex);
684 679 element.append(toinsert);
685 680 };
686 681
687 682
688 683 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
689 684 var output_div = this.element.find("div.output");
690 685 if (stdout && stderr && other){
691 686 // clear all, no need for logic
692 687 output_div.html("");
693 688 this.outputs = [];
694 689 return;
695 690 }
696 691 // remove html output
697 692 // each output_subarea that has an identifying class is in an output_area
698 693 // which is the element to be removed.
699 694 if (stdout){
700 695 output_div.find("div.output_stdout").parent().remove();
701 696 }
702 697 if (stderr){
703 698 output_div.find("div.output_stderr").parent().remove();
704 699 }
705 700 if (other){
706 701 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
707 702 }
708 703
709 704 // remove cleared outputs from JSON list:
710 705 for (var i = this.outputs.length - 1; i >= 0; i--){
711 706 var out = this.outputs[i];
712 707 var output_type = out.output_type;
713 708 if (output_type == "display_data" && other){
714 709 this.outputs.splice(i,1);
715 710 }else if (output_type == "stream"){
716 711 if (stdout && out.stream == "stdout"){
717 712 this.outputs.splice(i,1);
718 713 }else if (stderr && out.stream == "stderr"){
719 714 this.outputs.splice(i,1);
720 715 }
721 716 }
722 717 }
723 718 };
724 719
725 720
726 721 CodeCell.prototype.clear_input = function () {
727 722 this.code_mirror.setValue('');
728 723 };
729 724
730 725
731 726 CodeCell.prototype.collapse = function () {
732 727 if (!this.collapsed) {
733 728 this.element.find('div.output').hide();
734 729 this.collapsed = true;
735 730 };
736 731 };
737 732
738 733
739 734 CodeCell.prototype.expand = function () {
740 735 if (this.collapsed) {
741 736 this.element.find('div.output').show();
742 737 this.collapsed = false;
743 738 };
744 739 };
745 740
746 741
747 742 CodeCell.prototype.toggle_output = function () {
748 743 if (this.collapsed) {
749 744 this.expand();
750 745 } else {
751 746 this.collapse();
752 747 };
753 748 };
754 749
755 750 CodeCell.prototype.set_input_prompt = function (number) {
756 751 var n = number || '&nbsp;';
757 752 this.input_prompt_number = n;
758 753 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
759 754 };
760 755
761 756
762 757 CodeCell.prototype.get_code = function () {
763 758 return this.code_mirror.getValue();
764 759 };
765 760
766 761
767 762 CodeCell.prototype.set_code = function (code) {
768 763 return this.code_mirror.setValue(code);
769 764 };
770 765
771 766
772 767 CodeCell.prototype.at_top = function () {
773 768 var cursor = this.code_mirror.getCursor();
774 769 if (cursor.line === 0) {
775 770 return true;
776 771 } else {
777 772 return false;
778 773 }
779 774 };
780 775
781 776
782 777 CodeCell.prototype.at_bottom = function () {
783 778 var cursor = this.code_mirror.getCursor();
784 779 if (cursor.line === (this.code_mirror.lineCount()-1)) {
785 780 return true;
786 781 } else {
787 782 return false;
788 783 }
789 784 };
790 785
791 786
792 787 CodeCell.prototype.fromJSON = function (data) {
793 console.log('Import from JSON:', data);
794 788 if (data.cell_type === 'code') {
795 789 if (data.input !== undefined) {
796 790 this.set_code(data.input);
797 791 }
798 792 if (data.prompt_number !== undefined) {
799 793 this.set_input_prompt(data.prompt_number);
800 794 } else {
801 795 this.set_input_prompt();
802 796 };
803 797 var len = data.outputs.length;
804 798 for (var i=0; i<len; i++) {
805 799 this.append_output(data.outputs[i]);
806 800 };
807 801 if (data.collapsed !== undefined) {
808 802 if (data.collapsed) {
809 803 this.collapse();
810 804 };
811 805 };
812 806 };
813 807 };
814 808
815 809
816 810 CodeCell.prototype.toJSON = function () {
817 811 var data = {};
818 812 data.input = this.get_code();
819 813 data.cell_type = 'code';
820 814 if (this.input_prompt_number !== ' ') {
821 815 data.prompt_number = this.input_prompt_number;
822 816 };
823 817 var outputs = [];
824 818 var len = this.outputs.length;
825 819 for (var i=0; i<len; i++) {
826 820 outputs[i] = this.outputs[i];
827 821 };
828 822 data.outputs = outputs;
829 823 data.language = 'python';
830 824 data.collapsed = this.collapsed;
831 // console.log('Export to JSON:',data);
832 825 return data;
833 826 };
834 827
835 828
836 829 IPython.CodeCell = CodeCell;
837 830
838 831 return IPython;
839 832 }(IPython));
@@ -1,144 +1,143 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // MenuBar
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var MenuBar = function (selector) {
15 15 this.selector = selector;
16 16 if (this.selector !== undefined) {
17 17 this.element = $(selector);
18 18 this.style();
19 19 this.bind_events();
20 20 }
21 21 };
22 22
23 23
24 24 MenuBar.prototype.style = function () {
25 25 $('ul#menus').wijmenu();
26 26 $('ul#menus').wijmenu("option", "showDelay", 200);
27 27 $('ul#menus').wijmenu("option", "hideDelay", 200);
28 28 $(".selector").wijmenu("option", "animation", {animated:"fade", duration: 200, easing: null})
29 29 // Close all menus when a menu item is clicked. This is needed when
30 30 // menu shortcuts are used as they have a slightly different structure
31 31 // in the DOM.
32 32 $(".wijmo-wijmenu-text").parent().bind("click", function () {
33 33 $('ul#menus').wijmenu("hideAllMenus");
34 console.log('I am closing you!');
35 34 });
36 35 // Make sure we hover over menu items correctly. This is needed when
37 36 // menu shortcuts are used as they have a slightly different structure
38 37 // in the DOM.
39 38 $(".wijmo-wijmenu-link").hover(function () {
40 39 $(this).addClass("ui-state-hover");
41 40 }, function () {
42 41 $(this).removeClass("ui-state-hover");
43 42 });
44 43 };
45 44
46 45
47 46 MenuBar.prototype.bind_events = function () {
48 47 // File
49 48 this.element.find('#new_notebook').click(function () {
50 49 window.open($('body').data('baseProjectUrl')+'new');
51 50 });
52 51 this.element.find('#open_notebook').click(function () {
53 52 window.open($('body').data('baseProjectUrl'));
54 53 });
55 54 this.element.find('#rename_notebook').click(function () {
56 55 IPython.save_widget.rename_notebook();
57 56 });
58 57 this.element.find('#copy_notebook').click(function () {
59 58 var notebook_id = IPython.save_widget.get_notebook_id();
60 59 var url = $('body').data('baseProjectUrl') + notebook_id + '/copy';
61 60 window.open(url,'_newtab');
62 61 });
63 62 this.element.find('#save_notebook').click(function () {
64 63 IPython.save_widget.save_notebook();
65 64 });
66 65 this.element.find('#download_ipynb').click(function () {
67 66 var notebook_id = IPython.save_widget.get_notebook_id();
68 67 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
69 68 notebook_id + '?format=json';
70 69 window.open(url,'_newtab');
71 70 });
72 71 this.element.find('#download_py').click(function () {
73 72 var notebook_id = IPython.save_widget.get_notebook_id();
74 73 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
75 74 notebook_id + '?format=py';
76 75 window.open(url,'_newtab');
77 76 });
78 77 this.element.find('button#print_notebook').click(function () {
79 78 IPython.print_widget.print_notebook();
80 79 });
81 80 // Edit
82 81 this.element.find('#delete_cell').click(function () {
83 82 IPython.notebook.delete_cell();
84 83 });
85 84 this.element.find('#move_cell_up').click(function () {
86 85 IPython.notebook.move_cell_up();
87 86 });
88 87 this.element.find('#move_cell_down').click(function () {
89 88 IPython.notebook.move_cell_down();
90 89 });
91 90 this.element.find('#select_previous').click(function () {
92 91 IPython.notebook.select_prev();
93 92 });
94 93 this.element.find('#select_next').click(function () {
95 94 IPython.notebook.select_next();
96 95 });
97 96 // Insert
98 97 this.element.find('#insert_cell_above').click(function () {
99 98 IPython.notebook.insert_code_cell_above();
100 99 });
101 100 this.element.find('#insert_cell_below').click(function () {
102 101 IPython.notebook.insert_code_cell_below();
103 102 });
104 103 // Cell
105 104 this.element.find('#run_cell').click(function () {
106 105 IPython.notebook.execute_selected_cell();
107 106 });
108 107 this.element.find('#run_cell_in_place').click(function () {
109 108 IPython.notebook.execute_selected_cell({terminal:true});
110 109 });
111 110 this.element.find('#run_all_cells').click(function () {
112 111 IPython.notebook.execute_all_cells();
113 112 });
114 113 this.element.find('#to_code').click(function () {
115 114 IPython.notebook.to_code();
116 115 });
117 116 this.element.find('#to_markdown').click(function () {
118 117 IPython.notebook.to_markdown();
119 118 });
120 119 this.element.find('#toggle_output').click(function () {
121 120 IPython.notebook.toggle_output();
122 121 });
123 122 this.element.find('#clear_all_output').click(function () {
124 123 IPython.notebook.clear_all_output();
125 124 });
126 125 // Kernel
127 126 this.element.find('#int_kernel').click(function () {
128 127 IPython.notebook.kernel.interrupt();
129 128 });
130 129 this.element.find('#restart_kernel').click(function () {
131 130 IPython.notebook.restart_kernel();
132 131 });
133 132 // Help
134 133 this.element.find('#keyboard_shortcuts').click(function () {
135 134 IPython.quick_help.show_keyboard_shortcuts();
136 135 });
137 136 };
138 137
139 138
140 139 IPython.MenuBar = MenuBar;
141 140
142 141 return IPython;
143 142
144 143 }(IPython));
@@ -1,1039 +1,1036 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notebook
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var Notebook = function (selector) {
17 17 this.read_only = IPython.read_only;
18 18 this.element = $(selector);
19 19 this.element.scroll();
20 20 this.element.data("notebook", this);
21 21 this.next_prompt_number = 1;
22 22 this.kernel = null;
23 23 this.dirty = false;
24 24 this.msg_cell_map = {};
25 25 this.metadata = {};
26 26 this.control_key_active = false;
27 27 this.style();
28 28 this.create_elements();
29 29 this.bind_events();
30 30 this.set_tooltipontab(true);
31 31 this.set_smartcompleter(true);
32 32 this.set_timebeforetooltip(1200);
33 33 this.set_autoindent(true);
34 34 };
35 35
36 36
37 37 Notebook.prototype.style = function () {
38 38 $('div#notebook').addClass('border-box-sizing');
39 39 };
40 40
41 41
42 42 Notebook.prototype.create_elements = function () {
43 43 // We add this end_space div to the end of the notebook div to:
44 44 // i) provide a margin between the last cell and the end of the notebook
45 45 // ii) to prevent the div from scrolling up when the last cell is being
46 46 // edited, but is too low on the page, which browsers will do automatically.
47 47 var that = this;
48 48 var end_space = $('<div class="end_space"></div>').height("30%");
49 49 end_space.dblclick(function (e) {
50 50 if (that.read_only) return;
51 51 var ncells = that.ncells();
52 52 that.insert_code_cell_below(ncells-1);
53 53 });
54 54 this.element.append(end_space);
55 55 $('div#notebook').addClass('border-box-sizing');
56 56 };
57 57
58 58
59 59 Notebook.prototype.bind_events = function () {
60 60 var that = this;
61 61 $(document).keydown(function (event) {
62 62 // console.log(event);
63 63 if (that.read_only) return true;
64 64 if (event.which === 27) {
65 65 // Intercept escape at highest level to avoid closing
66 66 // websocket connection with firefox
67 67 event.preventDefault();
68 68 }
69 69 if (event.which === 38 && !event.shiftKey) {
70 70 var cell = that.selected_cell();
71 71 if (cell.at_top()) {
72 72 event.preventDefault();
73 73 that.select_prev();
74 74 };
75 75 } else if (event.which === 40 && !event.shiftKey) {
76 76 var cell = that.selected_cell();
77 77 if (cell.at_bottom()) {
78 78 event.preventDefault();
79 79 that.select_next();
80 80 };
81 81 } else if (event.which === 13 && event.shiftKey) {
82 82 that.execute_selected_cell();
83 83 return false;
84 84 } else if (event.which === 13 && event.ctrlKey) {
85 85 that.execute_selected_cell({terminal:true});
86 86 return false;
87 87 } else if (event.which === 77 && event.ctrlKey) {
88 88 that.control_key_active = true;
89 89 return false;
90 90 } else if (event.which === 68 && that.control_key_active) {
91 91 // Delete selected cell = d
92 92 that.delete_cell();
93 93 that.control_key_active = false;
94 94 return false;
95 95 } else if (event.which === 65 && that.control_key_active) {
96 96 // Insert code cell above selected = a
97 97 that.insert_code_cell_above();
98 98 that.control_key_active = false;
99 99 return false;
100 100 } else if (event.which === 66 && that.control_key_active) {
101 101 // Insert code cell below selected = b
102 102 that.insert_code_cell_below();
103 103 that.control_key_active = false;
104 104 return false;
105 105 } else if (event.which === 67 && that.control_key_active) {
106 106 // To code = c
107 107 that.to_code();
108 108 that.control_key_active = false;
109 109 return false;
110 110 } else if (event.which === 77 && that.control_key_active) {
111 111 // To markdown = m
112 112 that.to_markdown();
113 113 that.control_key_active = false;
114 114 return false;
115 115 } else if (event.which === 84 && that.control_key_active) {
116 116 // Toggle output = t
117 117 that.toggle_output();
118 118 that.control_key_active = false;
119 119 return false;
120 120 } else if (event.which === 83 && that.control_key_active) {
121 121 // Save notebook = s
122 122 IPython.save_widget.save_notebook();
123 123 that.control_key_active = false;
124 124 return false;
125 125 } else if (event.which === 74 && that.control_key_active) {
126 126 // Move cell down = j
127 127 that.move_cell_down();
128 128 that.control_key_active = false;
129 129 return false;
130 130 } else if (event.which === 75 && that.control_key_active) {
131 131 // Move cell up = k
132 132 that.move_cell_up();
133 133 that.control_key_active = false;
134 134 return false;
135 135 } else if (event.which === 80 && that.control_key_active) {
136 136 // Select previous = p
137 137 that.select_prev();
138 138 that.control_key_active = false;
139 139 return false;
140 140 } else if (event.which === 78 && that.control_key_active) {
141 141 // Select next = n
142 142 that.select_next();
143 143 that.control_key_active = false;
144 144 return false;
145 145 } else if (event.which === 76 && that.control_key_active) {
146 146 // Toggle line numbers = l
147 147 that.cell_toggle_line_numbers();
148 148 that.control_key_active = false;
149 149 return false;
150 150 } else if (event.which === 73 && that.control_key_active) {
151 151 // Interrupt kernel = i
152 152 IPython.notebook.kernel.interrupt();
153 153 that.control_key_active = false;
154 154 return false;
155 155 } else if (event.which === 190 && that.control_key_active) {
156 156 // Restart kernel = . # matches qt console
157 157 IPython.notebook.restart_kernel();
158 158 that.control_key_active = false;
159 159 return false;
160 160 } else if (event.which === 72 && that.control_key_active) {
161 161 // Show keyboard shortcuts = h
162 162 that.toggle_keyboard_shortcuts();
163 163 that.control_key_active = false;
164 164 return false;
165 165 } else if (that.control_key_active) {
166 166 that.control_key_active = false;
167 167 return true;
168 168 };
169 169 return true;
170 170 });
171 171
172 172 this.element.bind('collapse_pager', function () {
173 173 var app_height = $('div#main_app').height(); // content height
174 174 var splitter_height = $('div#pager_splitter').outerHeight(true);
175 175 var new_height = app_height - splitter_height;
176 176 that.element.animate({height : new_height + 'px'}, 'fast');
177 177 });
178 178
179 179 this.element.bind('expand_pager', function () {
180 180 var app_height = $('div#main_app').height(); // content height
181 181 var splitter_height = $('div#pager_splitter').outerHeight(true);
182 182 var pager_height = $('div#pager').outerHeight(true);
183 183 var new_height = app_height - pager_height - splitter_height;
184 184 that.element.animate({height : new_height + 'px'}, 'fast');
185 185 });
186 186
187 187 this.element.bind('collapse_left_panel', function () {
188 188 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
189 189 var new_margin = splitter_width;
190 190 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
191 191 });
192 192
193 193 this.element.bind('expand_left_panel', function () {
194 194 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
195 195 var left_panel_width = IPython.left_panel.width;
196 196 var new_margin = splitter_width + left_panel_width;
197 197 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
198 198 });
199 199
200 200 $(window).bind('beforeunload', function () {
201 201 // TODO: Make killing the kernel configurable.
202 202 var kill_kernel = false;
203 203 if (kill_kernel) {
204 204 that.kernel.kill();
205 205 }
206 206 if (that.dirty && ! that.read_only) {
207 207 return "You have unsaved changes that will be lost if you leave this page.";
208 208 };
209 209 // Null is the *only* return value that will make the browser not
210 210 // pop up the "don't leave" dialog.
211 211 return null;
212 212 });
213 213 };
214 214
215 215
216 216 Notebook.prototype.scroll_to_bottom = function () {
217 217 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
218 218 };
219 219
220 220
221 221 Notebook.prototype.scroll_to_top = function () {
222 222 this.element.animate({scrollTop:0}, 0);
223 223 };
224 224
225 225
226 226 // Cell indexing, retrieval, etc.
227 227
228 228
229 229 Notebook.prototype.cell_elements = function () {
230 230 return this.element.children("div.cell");
231 231 };
232 232
233 233
234 234 Notebook.prototype.ncells = function (cell) {
235 235 return this.cell_elements().length;
236 236 };
237 237
238 238
239 239 // TODO: we are often calling cells as cells()[i], which we should optimize
240 240 // to cells(i) or a new method.
241 241 Notebook.prototype.cells = function () {
242 242 return this.cell_elements().toArray().map(function (e) {
243 243 return $(e).data("cell");
244 244 });
245 245 };
246 246
247 247
248 248 Notebook.prototype.find_cell_index = function (cell) {
249 249 var result = null;
250 250 this.cell_elements().filter(function (index) {
251 251 if ($(this).data("cell") === cell) {
252 252 result = index;
253 253 };
254 254 });
255 255 return result;
256 256 };
257 257
258 258
259 259 Notebook.prototype.index_or_selected = function (index) {
260 260 return index || this.selected_index() || 0;
261 261 };
262 262
263 263
264 264 Notebook.prototype.select = function (index) {
265 265 if (index !== undefined && index >= 0 && index < this.ncells()) {
266 266 if (this.selected_index() !== null) {
267 267 this.selected_cell().unselect();
268 268 };
269 269 this.cells()[index].select();
270 270 };
271 271 return this;
272 272 };
273 273
274 274
275 275 Notebook.prototype.select_next = function () {
276 276 var index = this.selected_index();
277 277 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
278 278 this.select(index+1);
279 279 };
280 280 return this;
281 281 };
282 282
283 283
284 284 Notebook.prototype.select_prev = function () {
285 285 var index = this.selected_index();
286 286 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
287 287 this.select(index-1);
288 288 };
289 289 return this;
290 290 };
291 291
292 292
293 293 Notebook.prototype.selected_index = function () {
294 294 var result = null;
295 295 this.cell_elements().filter(function (index) {
296 296 if ($(this).data("cell").selected === true) {
297 297 result = index;
298 298 };
299 299 });
300 300 return result;
301 301 };
302 302
303 303
304 304 Notebook.prototype.cell_for_msg = function (msg_id) {
305 305 var cell_id = this.msg_cell_map[msg_id];
306 306 var result = null;
307 307 this.cell_elements().filter(function (index) {
308 308 cell = $(this).data("cell");
309 309 if (cell.cell_id === cell_id) {
310 310 result = cell;
311 311 };
312 312 });
313 313 return result;
314 314 };
315 315
316 316
317 317 Notebook.prototype.selected_cell = function () {
318 318 return this.cell_elements().eq(this.selected_index()).data("cell");
319 319 };
320 320
321 321
322 322 // Cell insertion, deletion and moving.
323 323
324 324
325 325 Notebook.prototype.delete_cell = function (index) {
326 326 var i = index || this.selected_index();
327 327 if (i !== null && i >= 0 && i < this.ncells()) {
328 328 this.cell_elements().eq(i).remove();
329 329 if (i === (this.ncells())) {
330 330 this.select(i-1);
331 331 } else {
332 332 this.select(i);
333 333 };
334 334 };
335 335 this.dirty = true;
336 336 return this;
337 337 };
338 338
339 339
340 340 Notebook.prototype.append_cell = function (cell) {
341 341 this.element.find('div.end_space').before(cell.element);
342 342 this.dirty = true;
343 343 return this;
344 344 };
345 345
346 346
347 347 Notebook.prototype.insert_cell_below = function (cell, index) {
348 348 var ncells = this.ncells();
349 349 if (ncells === 0) {
350 350 this.append_cell(cell);
351 351 return this;
352 352 };
353 353 if (index >= 0 && index < ncells) {
354 354 this.cell_elements().eq(index).after(cell.element);
355 355 };
356 356 this.dirty = true;
357 357 return this;
358 358 };
359 359
360 360
361 361 Notebook.prototype.insert_cell_above = function (cell, index) {
362 362 var ncells = this.ncells();
363 363 if (ncells === 0) {
364 364 this.append_cell(cell);
365 365 return this;
366 366 };
367 367 if (index >= 0 && index < ncells) {
368 368 this.cell_elements().eq(index).before(cell.element);
369 369 };
370 370 this.dirty = true;
371 371 return this;
372 372 };
373 373
374 374
375 375 Notebook.prototype.move_cell_up = function (index) {
376 376 var i = index || this.selected_index();
377 377 if (i !== null && i < this.ncells() && i > 0) {
378 378 var pivot = this.cell_elements().eq(i-1);
379 379 var tomove = this.cell_elements().eq(i);
380 380 if (pivot !== null && tomove !== null) {
381 381 tomove.detach();
382 382 pivot.before(tomove);
383 383 this.select(i-1);
384 384 };
385 385 };
386 386 this.dirty = true;
387 387 return this;
388 388 };
389 389
390 390
391 391 Notebook.prototype.move_cell_down = function (index) {
392 392 var i = index || this.selected_index();
393 393 if (i !== null && i < (this.ncells()-1) && i >= 0) {
394 394 var pivot = this.cell_elements().eq(i+1);
395 395 var tomove = this.cell_elements().eq(i);
396 396 if (pivot !== null && tomove !== null) {
397 397 tomove.detach();
398 398 pivot.after(tomove);
399 399 this.select(i+1);
400 400 };
401 401 };
402 402 this.dirty = true;
403 403 return this;
404 404 };
405 405
406 406
407 407 Notebook.prototype.sort_cells = function () {
408 408 var ncells = this.ncells();
409 409 var sindex = this.selected_index();
410 410 var swapped;
411 411 do {
412 412 swapped = false;
413 413 for (var i=1; i<ncells; i++) {
414 414 current = this.cell_elements().eq(i).data("cell");
415 415 previous = this.cell_elements().eq(i-1).data("cell");
416 416 if (previous.input_prompt_number > current.input_prompt_number) {
417 417 this.move_cell_up(i);
418 418 swapped = true;
419 419 };
420 420 };
421 421 } while (swapped);
422 422 this.select(sindex);
423 423 return this;
424 424 };
425 425
426 426
427 427 Notebook.prototype.insert_code_cell_above = function (index) {
428 428 // TODO: Bounds check for i
429 429 var i = this.index_or_selected(index);
430 430 var cell = new IPython.CodeCell(this);
431 431 cell.set_input_prompt();
432 432 this.insert_cell_above(cell, i);
433 433 this.select(this.find_cell_index(cell));
434 434 return cell;
435 435 };
436 436
437 437
438 438 Notebook.prototype.insert_code_cell_below = function (index) {
439 439 // TODO: Bounds check for i
440 440 var i = this.index_or_selected(index);
441 441 var cell = new IPython.CodeCell(this);
442 442 cell.set_input_prompt();
443 443 this.insert_cell_below(cell, i);
444 444 this.select(this.find_cell_index(cell));
445 445 return cell;
446 446 };
447 447
448 448
449 449 Notebook.prototype.insert_html_cell_above = function (index) {
450 450 // TODO: Bounds check for i
451 451 var i = this.index_or_selected(index);
452 452 var cell = new IPython.HTMLCell(this);
453 453 cell.config_mathjax();
454 454 this.insert_cell_above(cell, i);
455 455 this.select(this.find_cell_index(cell));
456 456 return cell;
457 457 };
458 458
459 459
460 460 Notebook.prototype.insert_html_cell_below = function (index) {
461 461 // TODO: Bounds check for i
462 462 var i = this.index_or_selected(index);
463 463 var cell = new IPython.HTMLCell(this);
464 464 cell.config_mathjax();
465 465 this.insert_cell_below(cell, i);
466 466 this.select(this.find_cell_index(cell));
467 467 return cell;
468 468 };
469 469
470 470
471 471 Notebook.prototype.insert_markdown_cell_above = function (index) {
472 472 // TODO: Bounds check for i
473 473 var i = this.index_or_selected(index);
474 474 var cell = new IPython.MarkdownCell(this);
475 475 cell.config_mathjax();
476 476 this.insert_cell_above(cell, i);
477 477 this.select(this.find_cell_index(cell));
478 478 return cell;
479 479 };
480 480
481 481
482 482 Notebook.prototype.insert_markdown_cell_below = function (index) {
483 483 // TODO: Bounds check for i
484 484 var i = this.index_or_selected(index);
485 485 var cell = new IPython.MarkdownCell(this);
486 486 cell.config_mathjax();
487 487 this.insert_cell_below(cell, i);
488 488 this.select(this.find_cell_index(cell));
489 489 return cell;
490 490 };
491 491
492 492
493 493 Notebook.prototype.to_code = function (index) {
494 494 // TODO: Bounds check for i
495 495 var i = this.index_or_selected(index);
496 496 var source_element = this.cell_elements().eq(i);
497 497 var source_cell = source_element.data("cell");
498 498 if (source_cell instanceof IPython.HTMLCell ||
499 499 source_cell instanceof IPython.MarkdownCell) {
500 500 this.insert_code_cell_below(i);
501 501 var target_cell = this.cells()[i+1];
502 502 target_cell.set_code(source_cell.get_source());
503 503 source_element.remove();
504 504 target_cell.select();
505 505 };
506 506 this.dirty = true;
507 507 };
508 508
509 509
510 510 Notebook.prototype.to_markdown = function (index) {
511 511 // TODO: Bounds check for i
512 512 var i = this.index_or_selected(index);
513 513 var source_element = this.cell_elements().eq(i);
514 514 var source_cell = source_element.data("cell");
515 515 var target_cell = null;
516 516 if (source_cell instanceof IPython.CodeCell) {
517 517 this.insert_markdown_cell_below(i);
518 518 target_cell = this.cells()[i+1];
519 519 var text = source_cell.get_code();
520 520 } else if (source_cell instanceof IPython.HTMLCell) {
521 521 this.insert_markdown_cell_below(i);
522 522 target_cell = this.cells()[i+1];
523 523 var text = source_cell.get_source();
524 524 if (text === source_cell.placeholder) {
525 525 text = target_cell.placeholder;
526 526 }
527 527 }
528 528 if (target_cell !== null) {
529 529 if (text === "") {text = target_cell.placeholder;};
530 530 target_cell.set_source(text);
531 531 source_element.remove();
532 532 target_cell.edit();
533 533 }
534 534 this.dirty = true;
535 535 };
536 536
537 537
538 538 Notebook.prototype.to_html = function (index) {
539 539 // TODO: Bounds check for i
540 540 var i = this.index_or_selected(index);
541 541 var source_element = this.cell_elements().eq(i);
542 542 var source_cell = source_element.data("cell");
543 543 var target_cell = null;
544 544 if (source_cell instanceof IPython.CodeCell) {
545 545 this.insert_html_cell_below(i);
546 546 target_cell = this.cells()[i+1];
547 547 var text = source_cell.get_code();
548 548 } else if (source_cell instanceof IPython.MarkdownCell) {
549 549 this.insert_html_cell_below(i);
550 550 target_cell = this.cells()[i+1];
551 551 var text = source_cell.get_source();
552 552 if (text === source_cell.placeholder) {
553 553 text = target_cell.placeholder;
554 554 }
555 555 }
556 556 if (target_cell !== null) {
557 557 if (text === "") {text = target_cell.placeholder;};
558 558 target_cell.set_source(text);
559 559 source_element.remove();
560 560 target_cell.edit();
561 561 }
562 562 this.dirty = true;
563 563 };
564 564
565 565
566 566 // Cell collapsing and output clearing
567 567
568 568 Notebook.prototype.collapse = function (index) {
569 569 var i = this.index_or_selected(index);
570 570 this.cells()[i].collapse();
571 571 this.dirty = true;
572 572 };
573 573
574 574
575 575 Notebook.prototype.expand = function (index) {
576 576 var i = this.index_or_selected(index);
577 577 this.cells()[i].expand();
578 578 this.dirty = true;
579 579 };
580 580
581 581
582 582 Notebook.prototype.toggle_output = function (index) {
583 583 var i = this.index_or_selected(index);
584 584 this.cells()[i].toggle_output();
585 585 this.dirty = true;
586 586 };
587 587
588 588
589 589 Notebook.prototype.set_timebeforetooltip = function (time) {
590 console.log("change time before tooltip to : "+time);
591 590 this.time_before_tooltip = time;
592 591 };
593 592
594 593 Notebook.prototype.set_tooltipontab = function (state) {
595 console.log("change tooltip on tab to : "+state);
596 594 this.tooltip_on_tab = state;
597 595 };
598 596
599 597 Notebook.prototype.set_smartcompleter = function (state) {
600 console.log("Smart completion (kwargs first) changed to to : "+state);
601 598 this.smart_completer = state;
602 599 };
603 600
604 601 Notebook.prototype.set_autoindent = function (state) {
605 602 var cells = this.cells();
606 603 len = cells.length;
607 604 for (var i=0; i<len; i++) {
608 605 cells[i].set_autoindent(state);
609 606 };
610 607 };
611 608
612 609
613 610 Notebook.prototype.clear_all_output = function () {
614 611 var ncells = this.ncells();
615 612 var cells = this.cells();
616 613 for (var i=0; i<ncells; i++) {
617 614 if (cells[i] instanceof IPython.CodeCell) {
618 615 cells[i].clear_output(true,true,true);
619 616 }
620 617 };
621 618 this.dirty = true;
622 619 };
623 620
624 621 // Other cell functions: line numbers, ...
625 622
626 623 Notebook.prototype.cell_toggle_line_numbers = function() {
627 624 this.selected_cell().toggle_line_numbers();
628 625 };
629 626
630 627 // Kernel related things
631 628
632 629 Notebook.prototype.start_kernel = function () {
633 630 this.kernel = new IPython.Kernel();
634 631 var notebook_id = IPython.save_widget.get_notebook_id();
635 632 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
636 633 };
637 634
638 635
639 636 Notebook.prototype.restart_kernel = function () {
640 637 var that = this;
641 638 var notebook_id = IPython.save_widget.get_notebook_id();
642 639
643 640 var dialog = $('<div/>');
644 641 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
645 642 $(document).append(dialog);
646 643 dialog.dialog({
647 644 resizable: false,
648 645 modal: true,
649 646 title: "Restart kernel or continue running?",
650 647 closeText: '',
651 648 buttons : {
652 649 "Restart": function () {
653 650 that.kernel.restart($.proxy(that.kernel_started, that));
654 651 $(this).dialog('close');
655 652 },
656 653 "Continue running": function () {
657 654 $(this).dialog('close');
658 655 }
659 656 }
660 657 });
661 658 };
662 659
663 660
664 661 Notebook.prototype.kernel_started = function () {
665 662 console.log("Kernel started: ", this.kernel.kernel_id);
666 663 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
667 664 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
668 665 };
669 666
670 667
671 668 Notebook.prototype.handle_shell_reply = function (e) {
672 669 reply = $.parseJSON(e.data);
673 670 var header = reply.header;
674 671 var content = reply.content;
675 672 var msg_type = header.msg_type;
676 673 // console.log(reply);
677 674 var cell = this.cell_for_msg(reply.parent_header.msg_id);
678 675 if (msg_type === "execute_reply") {
679 676 cell.set_input_prompt(content.execution_count);
680 677 cell.element.removeClass("running");
681 678 this.dirty = true;
682 679 } else if (msg_type === "complete_reply") {
683 680 cell.finish_completing(content.matched_text, content.matches);
684 681 } else if (msg_type === "object_info_reply"){
685 682 //console.log('back from object_info_request : ')
686 683 rep = reply.content;
687 684 if(rep.found)
688 685 {
689 686 cell.finish_tooltip(rep);
690 687 }
691 688 } else {
692 689 //console.log("unknown reply:"+msg_type);
693 690 }
694 691 // when having a rely from object_info_reply,
695 692 // no payload so no nned to handle it
696 693 if(typeof(content.payload)!='undefined') {
697 694 var payload = content.payload || [];
698 695 this.handle_payload(cell, payload);
699 696 }
700 697 };
701 698
702 699
703 700 Notebook.prototype.handle_payload = function (cell, payload) {
704 701 var l = payload.length;
705 702 for (var i=0; i<l; i++) {
706 703 if (payload[i].source === 'IPython.zmq.page.page') {
707 704 if (payload[i].text.trim() !== '') {
708 705 IPython.pager.clear();
709 706 IPython.pager.expand();
710 707 IPython.pager.append_text(payload[i].text);
711 708 }
712 709 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
713 710 var index = this.find_cell_index(cell);
714 711 var new_cell = this.insert_code_cell_below(index);
715 712 new_cell.set_code(payload[i].text);
716 713 this.dirty = true;
717 714 }
718 715 };
719 716 };
720 717
721 718
722 719 Notebook.prototype.handle_iopub_reply = function (e) {
723 720 reply = $.parseJSON(e.data);
724 721 var content = reply.content;
725 722 // console.log(reply);
726 723 var msg_type = reply.header.msg_type;
727 724 var cell = this.cell_for_msg(reply.parent_header.msg_id);
728 725 if (msg_type !== 'status' && !cell){
729 726 // message not from this notebook, but should be attached to a cell
730 727 console.log("Received IOPub message not caused by one of my cells");
731 728 console.log(reply);
732 729 return;
733 730 }
734 731 var output_types = ['stream','display_data','pyout','pyerr'];
735 732 if (output_types.indexOf(msg_type) >= 0) {
736 733 this.handle_output(cell, msg_type, content);
737 734 } else if (msg_type === 'status') {
738 735 if (content.execution_state === 'busy') {
739 736 IPython.kernel_status_widget.status_busy();
740 737 } else if (content.execution_state === 'idle') {
741 738 IPython.kernel_status_widget.status_idle();
742 739 } else if (content.execution_state === 'dead') {
743 740 this.handle_status_dead();
744 741 };
745 742 } else if (msg_type === 'clear_output') {
746 743 cell.clear_output(content.stdout, content.stderr, content.other);
747 744 };
748 745 };
749 746
750 747
751 748 Notebook.prototype.handle_status_dead = function () {
752 749 var that = this;
753 750 this.kernel.stop_channels();
754 751 var dialog = $('<div/>');
755 752 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
756 753 $(document).append(dialog);
757 754 dialog.dialog({
758 755 resizable: false,
759 756 modal: true,
760 757 title: "Dead kernel",
761 758 buttons : {
762 759 "Restart": function () {
763 760 that.start_kernel();
764 761 $(this).dialog('close');
765 762 },
766 763 "Continue running": function () {
767 764 $(this).dialog('close');
768 765 }
769 766 }
770 767 });
771 768 };
772 769
773 770
774 771 Notebook.prototype.handle_output = function (cell, msg_type, content) {
775 772 var json = {};
776 773 json.output_type = msg_type;
777 774 if (msg_type === "stream") {
778 775 json.text = utils.fixConsole(content.data);
779 776 json.stream = content.name;
780 777 } else if (msg_type === "display_data") {
781 778 json = this.convert_mime_types(json, content.data);
782 779 } else if (msg_type === "pyout") {
783 780 json.prompt_number = content.execution_count;
784 781 json = this.convert_mime_types(json, content.data);
785 782 } else if (msg_type === "pyerr") {
786 783 json.ename = content.ename;
787 784 json.evalue = content.evalue;
788 785 var traceback = [];
789 786 for (var i=0; i<content.traceback.length; i++) {
790 787 traceback.push(utils.fixConsole(content.traceback[i]));
791 788 }
792 789 json.traceback = traceback;
793 790 };
794 791 cell.append_output(json);
795 792 this.dirty = true;
796 793 };
797 794
798 795
799 796 Notebook.prototype.convert_mime_types = function (json, data) {
800 797 if (data['text/plain'] !== undefined) {
801 798 json.text = utils.fixConsole(data['text/plain']);
802 799 };
803 800 if (data['text/html'] !== undefined) {
804 801 json.html = data['text/html'];
805 802 };
806 803 if (data['image/svg+xml'] !== undefined) {
807 804 json.svg = data['image/svg+xml'];
808 805 };
809 806 if (data['image/png'] !== undefined) {
810 807 json.png = data['image/png'];
811 808 };
812 809 if (data['image/jpeg'] !== undefined) {
813 810 json.jpeg = data['image/jpeg'];
814 811 };
815 812 if (data['text/latex'] !== undefined) {
816 813 json.latex = data['text/latex'];
817 814 };
818 815 if (data['application/json'] !== undefined) {
819 816 json.json = data['application/json'];
820 817 };
821 818 if (data['application/javascript'] !== undefined) {
822 819 json.javascript = data['application/javascript'];
823 820 }
824 821 return json;
825 822 };
826 823
827 824
828 825 Notebook.prototype.execute_selected_cell = function (options) {
829 826 // add_new: should a new cell be added if we are at the end of the nb
830 827 // terminal: execute in terminal mode, which stays in the current cell
831 828 default_options = {terminal: false, add_new: true};
832 829 $.extend(default_options, options);
833 830 var that = this;
834 831 var cell = that.selected_cell();
835 832 var cell_index = that.find_cell_index(cell);
836 833 if (cell instanceof IPython.CodeCell) {
837 834 cell.clear_output(true, true, true);
838 835 cell.set_input_prompt('*');
839 836 cell.element.addClass("running");
840 837 var code = cell.get_code();
841 838 var msg_id = that.kernel.execute(cell.get_code());
842 839 that.msg_cell_map[msg_id] = cell.cell_id;
843 840 } else if (cell instanceof IPython.HTMLCell) {
844 841 cell.render();
845 842 }
846 843 if (default_options.terminal) {
847 844 cell.select_all();
848 845 } else {
849 846 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
850 847 that.insert_code_cell_below();
851 848 // If we are adding a new cell at the end, scroll down to show it.
852 849 that.scroll_to_bottom();
853 850 } else {
854 851 that.select(cell_index+1);
855 852 };
856 853 };
857 854 this.dirty = true;
858 855 };
859 856
860 857
861 858 Notebook.prototype.execute_all_cells = function () {
862 859 var ncells = this.ncells();
863 860 for (var i=0; i<ncells; i++) {
864 861 this.select(i);
865 862 this.execute_selected_cell({add_new:false});
866 863 };
867 864 this.scroll_to_bottom();
868 865 };
869 866
870 867
871 868 Notebook.prototype.request_tool_tip = function (cell,func) {
872 869 // Feel free to shorten this logic if you are better
873 870 // than me in regEx
874 871 // basicaly you shoul be able to get xxx.xxx.xxx from
875 872 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
876 873 // remove everything between matchin bracket (need to iterate)
877 874 matchBracket = /\([^\(\)]+\)/g;
878 875 oldfunc = func;
879 876 func = func.replace(matchBracket,"");
880 877 while( oldfunc != func )
881 878 {
882 879 oldfunc = func;
883 880 func = func.replace(matchBracket,"");
884 881 }
885 882 // remove everythin after last open bracket
886 883 endBracket = /\([^\(]*$/g;
887 884 func = func.replace(endBracket,"");
888 885 var re = /[a-zA-Z._]+$/g;
889 886 var msg_id = this.kernel.object_info_request(re.exec(func));
890 887 if(typeof(msg_id)!='undefined'){
891 888 this.msg_cell_map[msg_id] = cell.cell_id;
892 889 }
893 890 };
894 891
895 892 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
896 893 var msg_id = this.kernel.complete(line, cursor_pos);
897 894 this.msg_cell_map[msg_id] = cell.cell_id;
898 895 };
899 896
900 897 // Persistance and loading
901 898
902 899
903 900 Notebook.prototype.fromJSON = function (data) {
904 901 var ncells = this.ncells();
905 902 var i;
906 903 for (i=0; i<ncells; i++) {
907 904 // Always delete cell 0 as they get renumbered as they are deleted.
908 905 this.delete_cell(0);
909 906 };
910 907 // Save the metadata
911 908 this.metadata = data.metadata;
912 909 // Only handle 1 worksheet for now.
913 910 var worksheet = data.worksheets[0];
914 911 if (worksheet !== undefined) {
915 912 var new_cells = worksheet.cells;
916 913 ncells = new_cells.length;
917 914 var cell_data = null;
918 915 var new_cell = null;
919 916 for (i=0; i<ncells; i++) {
920 917 cell_data = new_cells[i];
921 918 if (cell_data.cell_type == 'code') {
922 919 new_cell = this.insert_code_cell_below();
923 920 new_cell.fromJSON(cell_data);
924 921 } else if (cell_data.cell_type === 'html') {
925 922 new_cell = this.insert_html_cell_below();
926 923 new_cell.fromJSON(cell_data);
927 924 } else if (cell_data.cell_type === 'markdown') {
928 925 new_cell = this.insert_markdown_cell_below();
929 926 new_cell.fromJSON(cell_data);
930 927 };
931 928 };
932 929 };
933 930 };
934 931
935 932
936 933 Notebook.prototype.toJSON = function () {
937 934 var cells = this.cells();
938 935 var ncells = cells.length;
939 936 cell_array = new Array(ncells);
940 937 for (var i=0; i<ncells; i++) {
941 938 cell_array[i] = cells[i].toJSON();
942 939 };
943 940 data = {
944 941 // Only handle 1 worksheet for now.
945 942 worksheets : [{cells:cell_array}],
946 943 metadata : this.metadata
947 944 };
948 945 return data;
949 946 };
950 947
951 948 Notebook.prototype.save_notebook = function () {
952 949 if (IPython.save_widget.test_notebook_name()) {
953 950 var notebook_id = IPython.save_widget.get_notebook_id();
954 951 var nbname = IPython.save_widget.get_notebook_name();
955 952 // We may want to move the name/id/nbformat logic inside toJSON?
956 953 var data = this.toJSON();
957 954 data.metadata.name = nbname;
958 955 data.nbformat = 2;
959 956 // We do the call with settings so we can set cache to false.
960 957 var settings = {
961 958 processData : false,
962 959 cache : false,
963 960 type : "PUT",
964 961 data : JSON.stringify(data),
965 962 headers : {'Content-Type': 'application/json'},
966 963 success : $.proxy(this.notebook_saved,this),
967 964 error : $.proxy(this.notebook_save_failed,this)
968 965 };
969 966 IPython.save_widget.status_saving();
970 967 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
971 968 $.ajax(url, settings);
972 969 };
973 970 };
974 971
975 972
976 973 Notebook.prototype.notebook_saved = function (data, status, xhr) {
977 974 this.dirty = false;
978 975 IPython.save_widget.notebook_saved();
979 976 IPython.save_widget.status_save();
980 977 };
981 978
982 979
983 980 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
984 981 // Notify the user and reset the save button
985 982 // TODO: Handle different types of errors (timeout etc.)
986 983 alert('An unexpected error occured while saving the notebook.');
987 984 IPython.save_widget.reset_status();
988 985 };
989 986
990 987
991 988 Notebook.prototype.load_notebook = function (callback) {
992 989 var that = this;
993 990 var notebook_id = IPython.save_widget.get_notebook_id();
994 991 // We do the call with settings so we can set cache to false.
995 992 var settings = {
996 993 processData : false,
997 994 cache : false,
998 995 type : "GET",
999 996 dataType : "json",
1000 997 success : function (data, status, xhr) {
1001 998 that.notebook_loaded(data, status, xhr);
1002 999 if (callback !== undefined) {
1003 1000 callback();
1004 1001 };
1005 1002 }
1006 1003 };
1007 1004 IPython.save_widget.status_loading();
1008 1005 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1009 1006 $.ajax(url, settings);
1010 1007 };
1011 1008
1012 1009
1013 1010 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
1014 1011 var allowed = xhr.getResponseHeader('Allow');
1015 1012 this.fromJSON(data);
1016 1013 if (this.ncells() === 0) {
1017 1014 this.insert_code_cell_below();
1018 1015 };
1019 1016 IPython.save_widget.status_save();
1020 1017 IPython.save_widget.set_notebook_name(data.metadata.name);
1021 1018 this.dirty = false;
1022 1019 if (! this.read_only) {
1023 1020 this.start_kernel();
1024 1021 }
1025 1022 // fromJSON always selects the last cell inserted. We need to wait
1026 1023 // until that is done before scrolling to the top.
1027 1024 setTimeout(function () {
1028 1025 IPython.notebook.select(0);
1029 1026 IPython.notebook.scroll_to_top();
1030 1027 }, 50);
1031 1028 };
1032 1029
1033 1030 IPython.Notebook = Notebook;
1034 1031
1035 1032
1036 1033 return IPython;
1037 1034
1038 1035 }(IPython));
1039 1036
@@ -1,163 +1,164 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SaveWidget
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var SaveWidget = function (selector) {
17 17 this.selector = selector;
18 18 this.notebook_name_blacklist_re = /[\/\\]/;
19 19 this.last_saved_name = '';
20 20 if (this.selector !== undefined) {
21 21 this.element = $(selector);
22 22 this.style();
23 23 this.bind_events();
24 24 }
25 25 };
26 26
27 27
28 28 SaveWidget.prototype.style = function () {
29 29 this.element.find('span#save_widget').addClass('ui-widget');
30 30 this.element.find('span#notebook_name').addClass('ui-widget ui-widget-content');
31 31 };
32 32
33 33
34 34 SaveWidget.prototype.bind_events = function () {
35 35 var that = this;
36 36 this.element.find('span#notebook_name').click(function () {
37 37 that.rename_notebook();
38 38 });
39 39 this.element.find('span#notebook_name').hover(function () {
40 40 $(this).addClass("ui-state-hover");
41 41 }, function () {
42 42 $(this).removeClass("ui-state-hover");
43 43 });
44 44 };
45 45
46 46
47 47 SaveWidget.prototype.save_notebook = function () {
48 48 IPython.notebook.save_notebook();
49 49 };
50 50
51 51
52 52 SaveWidget.prototype.rename_notebook = function () {
53 53 var that = this;
54 54 var dialog = $('<div/>');
55 55 dialog.append(
56 56 $('<h3/>').html('Enter a new notebook name:')
57 57 .css({'margin-bottom': '10px'})
58 58 );
59 59 dialog.append(
60 60 $('<input/>').attr('type','text')
61 61 .attr('value',this.get_notebook_name()).wijtextbox()
62 62 );
63 63 $(document).append(dialog);
64 64 dialog.dialog({
65 65 resizable: false,
66 66 modal: true,
67 67 title: "Rename Notebook",
68 68 closeText: "",
69 69 buttons : {
70 70 "OK": function () {
71 71 var new_name = $(this).find('input').attr('value');
72 72 if (!that.test_notebook_name(new_name)) {
73 73 $(this).find('h3').html(
74 74 "Invalid notebook name. " +
75 75 "Notebook names can contain any characters " +
76 76 "except / and \\. Please enter a new notebook name:"
77 77 );
78 78 } else {
79 79 that.set_notebook_name(new_name);
80 80 that.save_notebook();
81 81 $(this).dialog('close');
82 82 }
83 83 },
84 84 "Cancel": function () {
85 85 $(this).dialog('close');
86 86 }
87 87 }
88 88 });
89 89 }
90 90
91 91 SaveWidget.prototype.notebook_saved = function () {
92 92 this.set_document_title();
93 93 this.last_saved_name = this.get_notebook_name();
94 94 };
95 95
96 96
97 97 SaveWidget.prototype.get_notebook_name = function () {
98 98 return this.element.find('span#notebook_name').html();
99 99 };
100 100
101 101
102 102 SaveWidget.prototype.set_notebook_name = function (nbname) {
103 103 this.element.find('span#notebook_name').html(nbname);
104 104 this.set_document_title();
105 105 this.last_saved_name = nbname;
106 106 };
107 107
108 108
109 109 SaveWidget.prototype.set_document_title = function () {
110 110 nbname = this.get_notebook_name();
111 111 document.title = nbname;
112 112 };
113 113
114 114
115 115 SaveWidget.prototype.get_notebook_id = function () {
116 116 return $('body').data('notebookId');
117 117 };
118 118
119 119
120 120 SaveWidget.prototype.update_url = function () {
121 121 var notebook_id = this.get_notebook_id();
122 122 if (notebook_id !== '') {
123 window.history.replaceState({}, '', notebook_id);
123 var new_url = '/'+notebook_id;
124 window.history.replaceState({}, '', new_url);
124 125 };
125 126 };
126 127
127 128
128 129 SaveWidget.prototype.test_notebook_name = function (nbname) {
129 130 if (this.notebook_name_blacklist_re.test(nbname) == false) {
130 131 return true;
131 132 } else {
132 133 return false;
133 134 };
134 135 };
135 136
136 137
137 138 SaveWidget.prototype.reset_status = function () {
138 139 this.is_renaming();
139 140 };
140 141
141 142
142 143 SaveWidget.prototype.status_save = function () {
143 144 };
144 145
145 146
146 147 SaveWidget.prototype.status_saving = function () {
147 148 };
148 149
149 150
150 151 SaveWidget.prototype.status_loading = function () {
151 152 };
152 153
153 154
154 155 SaveWidget.prototype.status_rename = function () {
155 156 };
156 157
157 158
158 159 IPython.SaveWidget = SaveWidget;
159 160
160 161 return IPython;
161 162
162 163 }(IPython));
163 164
@@ -1,266 +1,266 b''
1 1 <!DOCTYPE HTML>
2 2 <html>
3 3
4 4 <head>
5 5 <meta charset="utf-8">
6 6
7 7 <title>IPython Notebook</title>
8 8
9 9 {% if mathjax_url %}
10 10 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
11 11 {% end %}
12 12 <script type="text/javascript">
13 13 // MathJax disabled, set as null to distingish from *missing* MathJax,
14 14 // where it will be undefined, and should prompt a dialog later.
15 15 window.mathjax_url = "{{mathjax_url}}";
16 16 </script>
17 17
18 18 <link rel="stylesheet" href="/static/jquery/css/jquery.wijmo-open.2.0.0b1.css" type="text/css" />
19 19 <link rel="stylesheet" href="/static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
20 20 <link rel="stylesheet" href="/static/codemirror/lib/codemirror.css">
21 21 <link rel="stylesheet" href="/static/codemirror/mode/markdown/markdown.css">
22 22 <link rel="stylesheet" href="/static/codemirror/mode/rst/rst.css">
23 23 <link rel="stylesheet" href="/static/codemirror/theme/ipython.css">
24 24 <link rel="stylesheet" href="/static/codemirror/theme/default.css">
25 25
26 26 <link rel="stylesheet" href="/static/prettify/prettify.css"/>
27 27
28 28 <link rel="stylesheet" href="/static/css/boilerplate.css" type="text/css" />
29 29 <link rel="stylesheet" href="/static/css/layout.css" type="text/css" />
30 30 <link rel="stylesheet" href="/static/css/base.css" type="text/css" />
31 31 <link rel="stylesheet" href="/static/css/notebook.css" type="text/css" />
32 32 <link rel="stylesheet" href="/static/css/renderedhtml.css" type="text/css" />
33 33
34 34 {% comment In the notebook, the read-only flag is used to determine %}
35 35 {% comment whether to hide the side panels and switch off input %}
36 36 <meta name="read_only" content="{{read_only and not logged_in}}"/>
37 37
38 38 </head>
39 39
40 40 <body
41 41 data-project={{project}} data-notebook-id={{notebook_id}}
42 42 data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
43 43 >
44 44
45 45 <div id="header">
46 46 <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='/static/ipynblogo.png' alt='IPython Notebook'/></a></h1></span>
47 47 <span id="save_widget">
48 48 <span id="notebook_name"></span>
49 49 </span>
50 50
51 51 <span id="login_widget">
52 52 {% comment This is a temporary workaround to hide the logout button %}
53 53 {% comment when appropriate until notebook.html is templated %}
54 54 {% if logged_in %}
55 55 <button id="logout">Logout</button>
56 56 {% elif not logged_in and login_available %}
57 57 <button id="login">Login</button>
58 58 {% end %}
59 59 </span>
60 60
61 61 <span id="kernel_status">Idle</span>
62 62 </div>
63 63
64 64 <div id="menubar">
65 65 <ul id="menus">
66 66 <li><a href="#">File</a>
67 67 <ul>
68 68 <li id="new_notebook"><span class="wijmo-wijmenu-text">New</span></li>
69 69 <li id="open_notebook"><span class="wijmo-wijmenu-text">Open...</span></li>
70 70 <li></li>
71 <li id="rename_notebook"><span class="wijmo-wijmenu-text">Rename...</span></li>
72 71 <li id="copy_notebook"><span class="wijmo-wijmenu-text">Make a Copy...</span></li>
72 <li id="rename_notebook"><span class="wijmo-wijmenu-text">Rename...</span></li>
73 73 <li id="save_notebook">
74 74 <div>
75 75 <span class="wijmo-wijmenu-text">Save</span>
76 76 <span class="wijmo-wijmenu-icon-right">Ctrl+m s</span>
77 77 </div>
78 78 </li>
79 79 <li></li>
80 80 <li><a href="#">Download as</a>
81 81 <ul>
82 82 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
83 83 <li id="download_py"><a href="#">Python (.py)</a></li>
84 84 </ul>
85 85 </li>
86 86 <li></li>
87 87 <li id="print_notebook"><a href="#">Print</a></li>
88 88 </ul>
89 89 </li>
90 90 <li><a href="#">Edit</a>
91 91 <ul>
92 92 <li id="delete_cell">
93 93 <div>
94 94 <span class="wijmo-wijmenu-text">Delete</span>
95 95 <span class="wijmo-wijmenu-icon-right">Ctrl+m d</span>
96 96 </div>
97 97 </li>
98 98 <li></li>
99 99 <li id="move_cell_up">
100 100 <div>
101 101 <span class="wijmo-wijmenu-text">Move Cell Up</span>
102 102 <span class="wijmo-wijmenu-icon-right">Ctrl+m k</span>
103 103 </div>
104 104 </li>
105 105 <li id="move_cell_down">
106 106 <div>
107 107 <span class="wijmo-wijmenu-text">Move Cell Down</span>
108 108 <span class="wijmo-wijmenu-icon-right">Ctrl+m j</span>
109 109 </div>
110 110 </li>
111 111 <li></li>
112 112 <li id="select_previous">
113 113 <div>
114 114 <span class="wijmo-wijmenu-text">Select Previous</span>
115 115 <span class="wijmo-wijmenu-icon-right">Ctrl+m p</span>
116 116 </div>
117 117 </li>
118 118 <li id="select_next">
119 119 <div>
120 120 <span class="wijmo-wijmenu-text">Select Next</span>
121 121 <span class="wijmo-wijmenu-icon-right">Ctrl+m n</span>
122 122 </div>
123 123 </li>
124 124 </ul>
125 125 </li>
126 126 <li><a href="#">Insert</a>
127 127 <ul>
128 128 <li id="insert_cell_above">
129 129 <div>
130 130 <span class="wijmo-wijmenu-text">Insert Cell Above</span>
131 131 <span class="wijmo-wijmenu-icon-right">Ctrl+m a</span>
132 132 </div>
133 133 </li>
134 134 <li id="insert_cell_below">
135 135 <div>
136 136 <span class="wijmo-wijmenu-text">Insert Cell Below</span>
137 137 <span class="wijmo-wijmenu-icon-right">Ctrl+m b</span>
138 138 </div>
139 139 </li>
140 140 </ul>
141 141 </li>
142 142 <li><a href="#">Cell</a>
143 143 <ul>
144 144 <li id="run_cell">
145 145 <div>
146 146 <span class="wijmo-wijmenu-text">Run</span>
147 147 <span class="wijmo-wijmenu-icon-right">Shift+Enter</span>
148 148 </div>
149 149 </li>
150 150 <li id="run_cell_in_place">
151 151 <div>
152 152 <span class="wijmo-wijmenu-text">Run in Place</span>
153 153 <span class="wijmo-wijmenu-icon-right">Ctrl+Enter</span>
154 154 </div>
155 155 </li>
156 156 <li id="run_all_cells"><a href="#">Run All</a></li>
157 157 <li></li>
158 158 <li id="to_code">
159 159 <div>
160 160 <span class="wijmo-wijmenu-text">Code Cell</span>
161 161 <span class="wijmo-wijmenu-icon-right">Ctrl+m c</span>
162 162 </div>
163 163 </li>
164 164 <li id="to_markdown">
165 165 <div>
166 166 <span class="wijmo-wijmenu-text">Markdown Cell</span>
167 167 <span class="wijmo-wijmenu-icon-right">Ctrl+m s</span>
168 168 </div>
169 169 </li>
170 170 <li></li>
171 171 <li id="toggle_output">
172 172 <div>
173 173 <span class="wijmo-wijmenu-text">Toggle Output</span>
174 174 <span class="wijmo-wijmenu-icon-right">Ctrl+m t</span>
175 175 </div>
176 176 </li>
177 177 <li id="clear_all_output"><a href="#">Clear All Output</a></li>
178 178 </ul>
179 179 </li>
180 180 <li><a href="#">Kernel</a>
181 181 <ul>
182 182 <li id="int_kernel">
183 183 <div>
184 184 <span class="wijmo-wijmenu-text">Interrupt</span>
185 185 <span class="wijmo-wijmenu-icon-right">Ctrl+m i</span>
186 186 </div>
187 187 </li>
188 188 <li id="restart_kernel">
189 189 <div>
190 190 <span class="wijmo-wijmenu-text">Restart</span>
191 191 <span class="wijmo-wijmenu-icon-right">Ctrl+m s</span>
192 192 </div>
193 193 </li>
194 194 </ul>
195 195 </li>
196 196 <li><a href="#">Help</a>
197 197 <ul>
198 198 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
199 199 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
200 200 <li id="keyboard_shortcuts">
201 201 <div>
202 202 <span class="wijmo-wijmenu-text">Keyboard Shortcuts</span>
203 203 <span class="wijmo-wijmenu-icon-right">Ctrl+m h</span>
204 204 </div>
205 205 </li>
206 206 <li><h2>External Docs</h2></li>
207 207 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
208 208 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
209 209 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
210 210 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
211 211 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
212 212 </ul>
213 213 </li>
214 214 </ul>
215 215
216 216 </div>
217 217
218 218 <div id="main_app">
219 219
220 220 <div id="notebook_panel">
221 221 <div id="notebook"></div>
222 222 <div id="pager_splitter"></div>
223 223 <div id="pager"></div>
224 224 </div>
225 225
226 226 </div>
227 227
228 228 <script src="/static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
229 229 <script src="/static/jquery/js/jquery-ui-1.8.16.custom.min.js" type="text/javascript" charset="utf-8"></script>
230 230 <script src="/static/jquery/js/jquery.wijmo-open.all.2.0.0b1.min.js" type="text/javascript" charset="utf-8"></script>
231 231 <script src="/static/jquery/js/jquery.bgiframe-2.1.3-pre.js" type="text/javascript" charset="utf-8"></script>
232 232 <script src="/static/jquery/js/jquery.mousewheel.min.js" type="text/javascript" charset="utf-8"></script>
233 233
234 234 <script src="/static/codemirror/lib/codemirror.js" charset="utf-8"></script>
235 235 <script src="/static/codemirror/mode/python/python.js" charset="utf-8"></script>
236 236 <script src="/static/codemirror/mode/htmlmixed/htmlmixed.js" charset="utf-8"></script>
237 237 <script src="/static/codemirror/mode/xml/xml.js" charset="utf-8"></script>
238 238 <script src="/static/codemirror/mode/javascript/javascript.js" charset="utf-8"></script>
239 239 <script src="/static/codemirror/mode/css/css.js" charset="utf-8"></script>
240 240 <script src="/static/codemirror/mode/rst/rst.js" charset="utf-8"></script>
241 241 <script src="/static/codemirror/mode/markdown/markdown.js" charset="utf-8"></script>
242 242
243 243 <script src="/static/pagedown/Markdown.Converter.js" charset="utf-8"></script>
244 244
245 245 <script src="/static/prettify/prettify.js" charset="utf-8"></script>
246 246
247 247 <script src="/static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
248 248 <script src="/static/js/utils.js" type="text/javascript" charset="utf-8"></script>
249 249 <script src="/static/js/cell.js" type="text/javascript" charset="utf-8"></script>
250 250 <script src="/static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
251 251 <script src="/static/js/textcell.js" type="text/javascript" charset="utf-8"></script>
252 252 <script src="/static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
253 253 <script src="/static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
254 254 <script src="/static/js/layout.js" type="text/javascript" charset="utf-8"></script>
255 255 <script src="/static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
256 256 <script src="/static/js/quickhelp.js" type="text/javascript" charset="utf-8"></script>
257 257 <script src="/static/js/loginwidget.js" type="text/javascript" charset="utf-8"></script>
258 258 <script src="/static/js/pager.js" type="text/javascript" charset="utf-8"></script>
259 259 <script src="/static/js/printwidget.js" type="text/javascript" charset="utf-8"></script>
260 260 <script src="/static/js/menubar.js" type="text/javascript" charset="utf-8"></script>
261 261 <script src="/static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
262 262 <script src="/static/js/notebookmain.js" type="text/javascript" charset="utf-8"></script>
263 263
264 264 </body>
265 265
266 266 </html>
General Comments 0
You need to be logged in to leave comments. Login now