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