##// END OF EJS Templates
Rename texteditor files & folders to edit
Thomas Kluyver -
Show More
1 NO CONTENT: file renamed from IPython/html/texteditor/__init__.py to IPython/html/edit/__init__.py
NO CONTENT: file renamed from IPython/html/texteditor/__init__.py to IPython/html/edit/__init__.py
@@ -1,29 +1,29 b''
1 #encoding: utf-8
1 #encoding: utf-8
2 """Tornado handlers for the terminal emulator."""
2 """Tornado handlers for the terminal emulator."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from tornado import web
7 from tornado import web
8 from ..base.handlers import IPythonHandler, path_regex
8 from ..base.handlers import IPythonHandler, path_regex
9 from ..utils import url_escape
9 from ..utils import url_escape
10
10
11 class EditorHandler(IPythonHandler):
11 class EditorHandler(IPythonHandler):
12 """Render the text editor interface."""
12 """Render the text editor interface."""
13 @web.authenticated
13 @web.authenticated
14 def get(self, path):
14 def get(self, path):
15 path = path.strip('/')
15 path = path.strip('/')
16 if not self.contents_manager.file_exists(path):
16 if not self.contents_manager.file_exists(path):
17 raise web.HTTPError(404, u'File does not exist: %s' % path)
17 raise web.HTTPError(404, u'File does not exist: %s' % path)
18
18
19 basename = path.rsplit('/', 1)[-1]
19 basename = path.rsplit('/', 1)[-1]
20 self.write(self.render_template('texteditor.html',
20 self.write(self.render_template('edit.html',
21 file_path=url_escape(path),
21 file_path=url_escape(path),
22 basename=basename,
22 basename=basename,
23 page_title=basename + " (editing)",
23 page_title=basename + " (editing)",
24 )
24 )
25 )
25 )
26
26
27 default_handlers = [
27 default_handlers = [
28 (r"/edit%s" % path_regex, EditorHandler),
28 (r"/edit%s" % path_regex, EditorHandler),
29 ] No newline at end of file
29 ]
@@ -1,995 +1,995 b''
1 # coding: utf-8
1 # coding: utf-8
2 """A tornado based IPython notebook server."""
2 """A tornado based IPython notebook server."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 import base64
9 import base64
10 import errno
10 import errno
11 import io
11 import io
12 import json
12 import json
13 import logging
13 import logging
14 import os
14 import os
15 import random
15 import random
16 import re
16 import re
17 import select
17 import select
18 import signal
18 import signal
19 import socket
19 import socket
20 import sys
20 import sys
21 import threading
21 import threading
22 import time
22 import time
23 import webbrowser
23 import webbrowser
24
24
25
25
26 # check for pyzmq 2.1.11
26 # check for pyzmq 2.1.11
27 from IPython.utils.zmqrelated import check_for_zmq
27 from IPython.utils.zmqrelated import check_for_zmq
28 check_for_zmq('2.1.11', 'IPython.html')
28 check_for_zmq('2.1.11', 'IPython.html')
29
29
30 from jinja2 import Environment, FileSystemLoader
30 from jinja2 import Environment, FileSystemLoader
31
31
32 # Install the pyzmq ioloop. This has to be done before anything else from
32 # Install the pyzmq ioloop. This has to be done before anything else from
33 # tornado is imported.
33 # tornado is imported.
34 from zmq.eventloop import ioloop
34 from zmq.eventloop import ioloop
35 ioloop.install()
35 ioloop.install()
36
36
37 # check for tornado 3.1.0
37 # check for tornado 3.1.0
38 msg = "The IPython Notebook requires tornado >= 4.0"
38 msg = "The IPython Notebook requires tornado >= 4.0"
39 try:
39 try:
40 import tornado
40 import tornado
41 except ImportError:
41 except ImportError:
42 raise ImportError(msg)
42 raise ImportError(msg)
43 try:
43 try:
44 version_info = tornado.version_info
44 version_info = tornado.version_info
45 except AttributeError:
45 except AttributeError:
46 raise ImportError(msg + ", but you have < 1.1.0")
46 raise ImportError(msg + ", but you have < 1.1.0")
47 if version_info < (4,0):
47 if version_info < (4,0):
48 raise ImportError(msg + ", but you have %s" % tornado.version)
48 raise ImportError(msg + ", but you have %s" % tornado.version)
49
49
50 from tornado import httpserver
50 from tornado import httpserver
51 from tornado import web
51 from tornado import web
52 from tornado.log import LogFormatter, app_log, access_log, gen_log
52 from tornado.log import LogFormatter, app_log, access_log, gen_log
53
53
54 from IPython.html import (
54 from IPython.html import (
55 DEFAULT_STATIC_FILES_PATH,
55 DEFAULT_STATIC_FILES_PATH,
56 DEFAULT_TEMPLATE_PATH_LIST,
56 DEFAULT_TEMPLATE_PATH_LIST,
57 )
57 )
58 from .base.handlers import Template404
58 from .base.handlers import Template404
59 from .log import log_request
59 from .log import log_request
60 from .services.kernels.kernelmanager import MappingKernelManager
60 from .services.kernels.kernelmanager import MappingKernelManager
61 from .services.contents.manager import ContentsManager
61 from .services.contents.manager import ContentsManager
62 from .services.contents.filemanager import FileContentsManager
62 from .services.contents.filemanager import FileContentsManager
63 from .services.clusters.clustermanager import ClusterManager
63 from .services.clusters.clustermanager import ClusterManager
64 from .services.sessions.sessionmanager import SessionManager
64 from .services.sessions.sessionmanager import SessionManager
65
65
66 from .base.handlers import AuthenticatedFileHandler, FileFindHandler
66 from .base.handlers import AuthenticatedFileHandler, FileFindHandler
67
67
68 from IPython.config import Config
68 from IPython.config import Config
69 from IPython.config.application import catch_config_error, boolean_flag
69 from IPython.config.application import catch_config_error, boolean_flag
70 from IPython.core.application import (
70 from IPython.core.application import (
71 BaseIPythonApplication, base_flags, base_aliases,
71 BaseIPythonApplication, base_flags, base_aliases,
72 )
72 )
73 from IPython.core.profiledir import ProfileDir
73 from IPython.core.profiledir import ProfileDir
74 from IPython.kernel import KernelManager
74 from IPython.kernel import KernelManager
75 from IPython.kernel.kernelspec import KernelSpecManager
75 from IPython.kernel.kernelspec import KernelSpecManager
76 from IPython.kernel.zmq.session import default_secure, Session
76 from IPython.kernel.zmq.session import default_secure, Session
77 from IPython.nbformat.sign import NotebookNotary
77 from IPython.nbformat.sign import NotebookNotary
78 from IPython.utils.importstring import import_item
78 from IPython.utils.importstring import import_item
79 from IPython.utils import submodule
79 from IPython.utils import submodule
80 from IPython.utils.process import check_pid
80 from IPython.utils.process import check_pid
81 from IPython.utils.traitlets import (
81 from IPython.utils.traitlets import (
82 Dict, Unicode, Integer, List, Bool, Bytes, Instance,
82 Dict, Unicode, Integer, List, Bool, Bytes, Instance,
83 DottedObjectName, TraitError,
83 DottedObjectName, TraitError,
84 )
84 )
85 from IPython.utils import py3compat
85 from IPython.utils import py3compat
86 from IPython.utils.path import filefind, get_ipython_dir
86 from IPython.utils.path import filefind, get_ipython_dir
87
87
88 from .utils import url_path_join
88 from .utils import url_path_join
89
89
90 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
91 # Module globals
91 # Module globals
92 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
93
93
94 _examples = """
94 _examples = """
95 ipython notebook # start the notebook
95 ipython notebook # start the notebook
96 ipython notebook --profile=sympy # use the sympy profile
96 ipython notebook --profile=sympy # use the sympy profile
97 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
97 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
98 """
98 """
99
99
100 #-----------------------------------------------------------------------------
100 #-----------------------------------------------------------------------------
101 # Helper functions
101 # Helper functions
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103
103
104 def random_ports(port, n):
104 def random_ports(port, n):
105 """Generate a list of n random ports near the given port.
105 """Generate a list of n random ports near the given port.
106
106
107 The first 5 ports will be sequential, and the remaining n-5 will be
107 The first 5 ports will be sequential, and the remaining n-5 will be
108 randomly selected in the range [port-2*n, port+2*n].
108 randomly selected in the range [port-2*n, port+2*n].
109 """
109 """
110 for i in range(min(5, n)):
110 for i in range(min(5, n)):
111 yield port + i
111 yield port + i
112 for i in range(n-5):
112 for i in range(n-5):
113 yield max(1, port + random.randint(-2*n, 2*n))
113 yield max(1, port + random.randint(-2*n, 2*n))
114
114
115 def load_handlers(name):
115 def load_handlers(name):
116 """Load the (URL pattern, handler) tuples for each component."""
116 """Load the (URL pattern, handler) tuples for each component."""
117 name = 'IPython.html.' + name
117 name = 'IPython.html.' + name
118 mod = __import__(name, fromlist=['default_handlers'])
118 mod = __import__(name, fromlist=['default_handlers'])
119 return mod.default_handlers
119 return mod.default_handlers
120
120
121 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
122 # The Tornado web application
122 # The Tornado web application
123 #-----------------------------------------------------------------------------
123 #-----------------------------------------------------------------------------
124
124
125 class NotebookWebApplication(web.Application):
125 class NotebookWebApplication(web.Application):
126
126
127 def __init__(self, ipython_app, kernel_manager, contents_manager,
127 def __init__(self, ipython_app, kernel_manager, contents_manager,
128 cluster_manager, session_manager, kernel_spec_manager, log,
128 cluster_manager, session_manager, kernel_spec_manager, log,
129 base_url, default_url, settings_overrides, jinja_env_options):
129 base_url, default_url, settings_overrides, jinja_env_options):
130
130
131 settings = self.init_settings(
131 settings = self.init_settings(
132 ipython_app, kernel_manager, contents_manager, cluster_manager,
132 ipython_app, kernel_manager, contents_manager, cluster_manager,
133 session_manager, kernel_spec_manager, log, base_url, default_url,
133 session_manager, kernel_spec_manager, log, base_url, default_url,
134 settings_overrides, jinja_env_options)
134 settings_overrides, jinja_env_options)
135 handlers = self.init_handlers(settings)
135 handlers = self.init_handlers(settings)
136
136
137 super(NotebookWebApplication, self).__init__(handlers, **settings)
137 super(NotebookWebApplication, self).__init__(handlers, **settings)
138
138
139 def init_settings(self, ipython_app, kernel_manager, contents_manager,
139 def init_settings(self, ipython_app, kernel_manager, contents_manager,
140 cluster_manager, session_manager, kernel_spec_manager,
140 cluster_manager, session_manager, kernel_spec_manager,
141 log, base_url, default_url, settings_overrides,
141 log, base_url, default_url, settings_overrides,
142 jinja_env_options=None):
142 jinja_env_options=None):
143
143
144 _template_path = settings_overrides.get(
144 _template_path = settings_overrides.get(
145 "template_path",
145 "template_path",
146 ipython_app.template_file_path,
146 ipython_app.template_file_path,
147 )
147 )
148 if isinstance(_template_path, str):
148 if isinstance(_template_path, str):
149 _template_path = (_template_path,)
149 _template_path = (_template_path,)
150 template_path = [os.path.expanduser(path) for path in _template_path]
150 template_path = [os.path.expanduser(path) for path in _template_path]
151
151
152 jenv_opt = jinja_env_options if jinja_env_options else {}
152 jenv_opt = jinja_env_options if jinja_env_options else {}
153 env = Environment(loader=FileSystemLoader(template_path), **jenv_opt)
153 env = Environment(loader=FileSystemLoader(template_path), **jenv_opt)
154 settings = dict(
154 settings = dict(
155 # basics
155 # basics
156 log_function=log_request,
156 log_function=log_request,
157 base_url=base_url,
157 base_url=base_url,
158 default_url=default_url,
158 default_url=default_url,
159 template_path=template_path,
159 template_path=template_path,
160 static_path=ipython_app.static_file_path,
160 static_path=ipython_app.static_file_path,
161 static_handler_class = FileFindHandler,
161 static_handler_class = FileFindHandler,
162 static_url_prefix = url_path_join(base_url,'/static/'),
162 static_url_prefix = url_path_join(base_url,'/static/'),
163
163
164 # authentication
164 # authentication
165 cookie_secret=ipython_app.cookie_secret,
165 cookie_secret=ipython_app.cookie_secret,
166 login_url=url_path_join(base_url,'/login'),
166 login_url=url_path_join(base_url,'/login'),
167 password=ipython_app.password,
167 password=ipython_app.password,
168
168
169 # managers
169 # managers
170 kernel_manager=kernel_manager,
170 kernel_manager=kernel_manager,
171 contents_manager=contents_manager,
171 contents_manager=contents_manager,
172 cluster_manager=cluster_manager,
172 cluster_manager=cluster_manager,
173 session_manager=session_manager,
173 session_manager=session_manager,
174 kernel_spec_manager=kernel_spec_manager,
174 kernel_spec_manager=kernel_spec_manager,
175
175
176 # IPython stuff
176 # IPython stuff
177 nbextensions_path = ipython_app.nbextensions_path,
177 nbextensions_path = ipython_app.nbextensions_path,
178 websocket_url=ipython_app.websocket_url,
178 websocket_url=ipython_app.websocket_url,
179 mathjax_url=ipython_app.mathjax_url,
179 mathjax_url=ipython_app.mathjax_url,
180 config=ipython_app.config,
180 config=ipython_app.config,
181 jinja2_env=env,
181 jinja2_env=env,
182 terminals_available=False, # Set later if terminals are available
182 terminals_available=False, # Set later if terminals are available
183 profile_dir = ipython_app.profile_dir.location,
183 profile_dir = ipython_app.profile_dir.location,
184 )
184 )
185
185
186 # allow custom overrides for the tornado web app.
186 # allow custom overrides for the tornado web app.
187 settings.update(settings_overrides)
187 settings.update(settings_overrides)
188 return settings
188 return settings
189
189
190 def init_handlers(self, settings):
190 def init_handlers(self, settings):
191 """Load the (URL pattern, handler) tuples for each component."""
191 """Load the (URL pattern, handler) tuples for each component."""
192
192
193 # Order matters. The first handler to match the URL will handle the request.
193 # Order matters. The first handler to match the URL will handle the request.
194 handlers = []
194 handlers = []
195 handlers.extend(load_handlers('tree.handlers'))
195 handlers.extend(load_handlers('tree.handlers'))
196 handlers.extend(load_handlers('auth.login'))
196 handlers.extend(load_handlers('auth.login'))
197 handlers.extend(load_handlers('auth.logout'))
197 handlers.extend(load_handlers('auth.logout'))
198 handlers.extend(load_handlers('files.handlers'))
198 handlers.extend(load_handlers('files.handlers'))
199 handlers.extend(load_handlers('notebook.handlers'))
199 handlers.extend(load_handlers('notebook.handlers'))
200 handlers.extend(load_handlers('nbconvert.handlers'))
200 handlers.extend(load_handlers('nbconvert.handlers'))
201 handlers.extend(load_handlers('kernelspecs.handlers'))
201 handlers.extend(load_handlers('kernelspecs.handlers'))
202 handlers.extend(load_handlers('texteditor.handlers'))
202 handlers.extend(load_handlers('edit.handlers'))
203 handlers.extend(load_handlers('services.config.handlers'))
203 handlers.extend(load_handlers('services.config.handlers'))
204 handlers.extend(load_handlers('services.kernels.handlers'))
204 handlers.extend(load_handlers('services.kernels.handlers'))
205 handlers.extend(load_handlers('services.contents.handlers'))
205 handlers.extend(load_handlers('services.contents.handlers'))
206 handlers.extend(load_handlers('services.clusters.handlers'))
206 handlers.extend(load_handlers('services.clusters.handlers'))
207 handlers.extend(load_handlers('services.sessions.handlers'))
207 handlers.extend(load_handlers('services.sessions.handlers'))
208 handlers.extend(load_handlers('services.nbconvert.handlers'))
208 handlers.extend(load_handlers('services.nbconvert.handlers'))
209 handlers.extend(load_handlers('services.kernelspecs.handlers'))
209 handlers.extend(load_handlers('services.kernelspecs.handlers'))
210 handlers.append(
210 handlers.append(
211 (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}),
211 (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}),
212 )
212 )
213 # register base handlers last
213 # register base handlers last
214 handlers.extend(load_handlers('base.handlers'))
214 handlers.extend(load_handlers('base.handlers'))
215 # set the URL that will be redirected from `/`
215 # set the URL that will be redirected from `/`
216 handlers.append(
216 handlers.append(
217 (r'/?', web.RedirectHandler, {
217 (r'/?', web.RedirectHandler, {
218 'url' : url_path_join(settings['base_url'], settings['default_url']),
218 'url' : url_path_join(settings['base_url'], settings['default_url']),
219 'permanent': False, # want 302, not 301
219 'permanent': False, # want 302, not 301
220 })
220 })
221 )
221 )
222 # prepend base_url onto the patterns that we match
222 # prepend base_url onto the patterns that we match
223 new_handlers = []
223 new_handlers = []
224 for handler in handlers:
224 for handler in handlers:
225 pattern = url_path_join(settings['base_url'], handler[0])
225 pattern = url_path_join(settings['base_url'], handler[0])
226 new_handler = tuple([pattern] + list(handler[1:]))
226 new_handler = tuple([pattern] + list(handler[1:]))
227 new_handlers.append(new_handler)
227 new_handlers.append(new_handler)
228 # add 404 on the end, which will catch everything that falls through
228 # add 404 on the end, which will catch everything that falls through
229 new_handlers.append((r'(.*)', Template404))
229 new_handlers.append((r'(.*)', Template404))
230 return new_handlers
230 return new_handlers
231
231
232
232
233 class NbserverListApp(BaseIPythonApplication):
233 class NbserverListApp(BaseIPythonApplication):
234
234
235 description="List currently running notebook servers in this profile."
235 description="List currently running notebook servers in this profile."
236
236
237 flags = dict(
237 flags = dict(
238 json=({'NbserverListApp': {'json': True}},
238 json=({'NbserverListApp': {'json': True}},
239 "Produce machine-readable JSON output."),
239 "Produce machine-readable JSON output."),
240 )
240 )
241
241
242 json = Bool(False, config=True,
242 json = Bool(False, config=True,
243 help="If True, each line of output will be a JSON object with the "
243 help="If True, each line of output will be a JSON object with the "
244 "details from the server info file.")
244 "details from the server info file.")
245
245
246 def start(self):
246 def start(self):
247 if not self.json:
247 if not self.json:
248 print("Currently running servers:")
248 print("Currently running servers:")
249 for serverinfo in list_running_servers(self.profile):
249 for serverinfo in list_running_servers(self.profile):
250 if self.json:
250 if self.json:
251 print(json.dumps(serverinfo))
251 print(json.dumps(serverinfo))
252 else:
252 else:
253 print(serverinfo['url'], "::", serverinfo['notebook_dir'])
253 print(serverinfo['url'], "::", serverinfo['notebook_dir'])
254
254
255 #-----------------------------------------------------------------------------
255 #-----------------------------------------------------------------------------
256 # Aliases and Flags
256 # Aliases and Flags
257 #-----------------------------------------------------------------------------
257 #-----------------------------------------------------------------------------
258
258
259 flags = dict(base_flags)
259 flags = dict(base_flags)
260 flags['no-browser']=(
260 flags['no-browser']=(
261 {'NotebookApp' : {'open_browser' : False}},
261 {'NotebookApp' : {'open_browser' : False}},
262 "Don't open the notebook in a browser after startup."
262 "Don't open the notebook in a browser after startup."
263 )
263 )
264 flags['pylab']=(
264 flags['pylab']=(
265 {'NotebookApp' : {'pylab' : 'warn'}},
265 {'NotebookApp' : {'pylab' : 'warn'}},
266 "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
266 "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
267 )
267 )
268 flags['no-mathjax']=(
268 flags['no-mathjax']=(
269 {'NotebookApp' : {'enable_mathjax' : False}},
269 {'NotebookApp' : {'enable_mathjax' : False}},
270 """Disable MathJax
270 """Disable MathJax
271
271
272 MathJax is the javascript library IPython uses to render math/LaTeX. It is
272 MathJax is the javascript library IPython uses to render math/LaTeX. It is
273 very large, so you may want to disable it if you have a slow internet
273 very large, so you may want to disable it if you have a slow internet
274 connection, or for offline use of the notebook.
274 connection, or for offline use of the notebook.
275
275
276 When disabled, equations etc. will appear as their untransformed TeX source.
276 When disabled, equations etc. will appear as their untransformed TeX source.
277 """
277 """
278 )
278 )
279
279
280 # Add notebook manager flags
280 # Add notebook manager flags
281 flags.update(boolean_flag('script', 'FileContentsManager.save_script',
281 flags.update(boolean_flag('script', 'FileContentsManager.save_script',
282 'DEPRECATED, IGNORED',
282 'DEPRECATED, IGNORED',
283 'DEPRECATED, IGNORED'))
283 'DEPRECATED, IGNORED'))
284
284
285 aliases = dict(base_aliases)
285 aliases = dict(base_aliases)
286
286
287 aliases.update({
287 aliases.update({
288 'ip': 'NotebookApp.ip',
288 'ip': 'NotebookApp.ip',
289 'port': 'NotebookApp.port',
289 'port': 'NotebookApp.port',
290 'port-retries': 'NotebookApp.port_retries',
290 'port-retries': 'NotebookApp.port_retries',
291 'transport': 'KernelManager.transport',
291 'transport': 'KernelManager.transport',
292 'keyfile': 'NotebookApp.keyfile',
292 'keyfile': 'NotebookApp.keyfile',
293 'certfile': 'NotebookApp.certfile',
293 'certfile': 'NotebookApp.certfile',
294 'notebook-dir': 'NotebookApp.notebook_dir',
294 'notebook-dir': 'NotebookApp.notebook_dir',
295 'browser': 'NotebookApp.browser',
295 'browser': 'NotebookApp.browser',
296 'pylab': 'NotebookApp.pylab',
296 'pylab': 'NotebookApp.pylab',
297 })
297 })
298
298
299 #-----------------------------------------------------------------------------
299 #-----------------------------------------------------------------------------
300 # NotebookApp
300 # NotebookApp
301 #-----------------------------------------------------------------------------
301 #-----------------------------------------------------------------------------
302
302
303 class NotebookApp(BaseIPythonApplication):
303 class NotebookApp(BaseIPythonApplication):
304
304
305 name = 'ipython-notebook'
305 name = 'ipython-notebook'
306
306
307 description = """
307 description = """
308 The IPython HTML Notebook.
308 The IPython HTML Notebook.
309
309
310 This launches a Tornado based HTML Notebook Server that serves up an
310 This launches a Tornado based HTML Notebook Server that serves up an
311 HTML5/Javascript Notebook client.
311 HTML5/Javascript Notebook client.
312 """
312 """
313 examples = _examples
313 examples = _examples
314 aliases = aliases
314 aliases = aliases
315 flags = flags
315 flags = flags
316
316
317 classes = [
317 classes = [
318 KernelManager, ProfileDir, Session, MappingKernelManager,
318 KernelManager, ProfileDir, Session, MappingKernelManager,
319 ContentsManager, FileContentsManager, NotebookNotary,
319 ContentsManager, FileContentsManager, NotebookNotary,
320 ]
320 ]
321 flags = Dict(flags)
321 flags = Dict(flags)
322 aliases = Dict(aliases)
322 aliases = Dict(aliases)
323
323
324 subcommands = dict(
324 subcommands = dict(
325 list=(NbserverListApp, NbserverListApp.description.splitlines()[0]),
325 list=(NbserverListApp, NbserverListApp.description.splitlines()[0]),
326 )
326 )
327
327
328 ipython_kernel_argv = List(Unicode)
328 ipython_kernel_argv = List(Unicode)
329
329
330 _log_formatter_cls = LogFormatter
330 _log_formatter_cls = LogFormatter
331
331
332 def _log_level_default(self):
332 def _log_level_default(self):
333 return logging.INFO
333 return logging.INFO
334
334
335 def _log_datefmt_default(self):
335 def _log_datefmt_default(self):
336 """Exclude date from default date format"""
336 """Exclude date from default date format"""
337 return "%H:%M:%S"
337 return "%H:%M:%S"
338
338
339 def _log_format_default(self):
339 def _log_format_default(self):
340 """override default log format to include time"""
340 """override default log format to include time"""
341 return u"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s]%(end_color)s %(message)s"
341 return u"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s]%(end_color)s %(message)s"
342
342
343 # create requested profiles by default, if they don't exist:
343 # create requested profiles by default, if they don't exist:
344 auto_create = Bool(True)
344 auto_create = Bool(True)
345
345
346 # file to be opened in the notebook server
346 # file to be opened in the notebook server
347 file_to_run = Unicode('', config=True)
347 file_to_run = Unicode('', config=True)
348
348
349 # Network related information
349 # Network related information
350
350
351 allow_origin = Unicode('', config=True,
351 allow_origin = Unicode('', config=True,
352 help="""Set the Access-Control-Allow-Origin header
352 help="""Set the Access-Control-Allow-Origin header
353
353
354 Use '*' to allow any origin to access your server.
354 Use '*' to allow any origin to access your server.
355
355
356 Takes precedence over allow_origin_pat.
356 Takes precedence over allow_origin_pat.
357 """
357 """
358 )
358 )
359
359
360 allow_origin_pat = Unicode('', config=True,
360 allow_origin_pat = Unicode('', config=True,
361 help="""Use a regular expression for the Access-Control-Allow-Origin header
361 help="""Use a regular expression for the Access-Control-Allow-Origin header
362
362
363 Requests from an origin matching the expression will get replies with:
363 Requests from an origin matching the expression will get replies with:
364
364
365 Access-Control-Allow-Origin: origin
365 Access-Control-Allow-Origin: origin
366
366
367 where `origin` is the origin of the request.
367 where `origin` is the origin of the request.
368
368
369 Ignored if allow_origin is set.
369 Ignored if allow_origin is set.
370 """
370 """
371 )
371 )
372
372
373 allow_credentials = Bool(False, config=True,
373 allow_credentials = Bool(False, config=True,
374 help="Set the Access-Control-Allow-Credentials: true header"
374 help="Set the Access-Control-Allow-Credentials: true header"
375 )
375 )
376
376
377 default_url = Unicode('/tree', config=True,
377 default_url = Unicode('/tree', config=True,
378 help="The default URL to redirect to from `/`"
378 help="The default URL to redirect to from `/`"
379 )
379 )
380
380
381 ip = Unicode('localhost', config=True,
381 ip = Unicode('localhost', config=True,
382 help="The IP address the notebook server will listen on."
382 help="The IP address the notebook server will listen on."
383 )
383 )
384
384
385 def _ip_changed(self, name, old, new):
385 def _ip_changed(self, name, old, new):
386 if new == u'*': self.ip = u''
386 if new == u'*': self.ip = u''
387
387
388 port = Integer(8888, config=True,
388 port = Integer(8888, config=True,
389 help="The port the notebook server will listen on."
389 help="The port the notebook server will listen on."
390 )
390 )
391 port_retries = Integer(50, config=True,
391 port_retries = Integer(50, config=True,
392 help="The number of additional ports to try if the specified port is not available."
392 help="The number of additional ports to try if the specified port is not available."
393 )
393 )
394
394
395 certfile = Unicode(u'', config=True,
395 certfile = Unicode(u'', config=True,
396 help="""The full path to an SSL/TLS certificate file."""
396 help="""The full path to an SSL/TLS certificate file."""
397 )
397 )
398
398
399 keyfile = Unicode(u'', config=True,
399 keyfile = Unicode(u'', config=True,
400 help="""The full path to a private key file for usage with SSL/TLS."""
400 help="""The full path to a private key file for usage with SSL/TLS."""
401 )
401 )
402
402
403 cookie_secret_file = Unicode(config=True,
403 cookie_secret_file = Unicode(config=True,
404 help="""The file where the cookie secret is stored."""
404 help="""The file where the cookie secret is stored."""
405 )
405 )
406 def _cookie_secret_file_default(self):
406 def _cookie_secret_file_default(self):
407 if self.profile_dir is None:
407 if self.profile_dir is None:
408 return ''
408 return ''
409 return os.path.join(self.profile_dir.security_dir, 'notebook_cookie_secret')
409 return os.path.join(self.profile_dir.security_dir, 'notebook_cookie_secret')
410
410
411 cookie_secret = Bytes(b'', config=True,
411 cookie_secret = Bytes(b'', config=True,
412 help="""The random bytes used to secure cookies.
412 help="""The random bytes used to secure cookies.
413 By default this is a new random number every time you start the Notebook.
413 By default this is a new random number every time you start the Notebook.
414 Set it to a value in a config file to enable logins to persist across server sessions.
414 Set it to a value in a config file to enable logins to persist across server sessions.
415
415
416 Note: Cookie secrets should be kept private, do not share config files with
416 Note: Cookie secrets should be kept private, do not share config files with
417 cookie_secret stored in plaintext (you can read the value from a file).
417 cookie_secret stored in plaintext (you can read the value from a file).
418 """
418 """
419 )
419 )
420 def _cookie_secret_default(self):
420 def _cookie_secret_default(self):
421 if os.path.exists(self.cookie_secret_file):
421 if os.path.exists(self.cookie_secret_file):
422 with io.open(self.cookie_secret_file, 'rb') as f:
422 with io.open(self.cookie_secret_file, 'rb') as f:
423 return f.read()
423 return f.read()
424 else:
424 else:
425 secret = base64.encodestring(os.urandom(1024))
425 secret = base64.encodestring(os.urandom(1024))
426 self._write_cookie_secret_file(secret)
426 self._write_cookie_secret_file(secret)
427 return secret
427 return secret
428
428
429 def _write_cookie_secret_file(self, secret):
429 def _write_cookie_secret_file(self, secret):
430 """write my secret to my secret_file"""
430 """write my secret to my secret_file"""
431 self.log.info("Writing notebook server cookie secret to %s", self.cookie_secret_file)
431 self.log.info("Writing notebook server cookie secret to %s", self.cookie_secret_file)
432 with io.open(self.cookie_secret_file, 'wb') as f:
432 with io.open(self.cookie_secret_file, 'wb') as f:
433 f.write(secret)
433 f.write(secret)
434 try:
434 try:
435 os.chmod(self.cookie_secret_file, 0o600)
435 os.chmod(self.cookie_secret_file, 0o600)
436 except OSError:
436 except OSError:
437 self.log.warn(
437 self.log.warn(
438 "Could not set permissions on %s",
438 "Could not set permissions on %s",
439 self.cookie_secret_file
439 self.cookie_secret_file
440 )
440 )
441
441
442 password = Unicode(u'', config=True,
442 password = Unicode(u'', config=True,
443 help="""Hashed password to use for web authentication.
443 help="""Hashed password to use for web authentication.
444
444
445 To generate, type in a python/IPython shell:
445 To generate, type in a python/IPython shell:
446
446
447 from IPython.lib import passwd; passwd()
447 from IPython.lib import passwd; passwd()
448
448
449 The string should be of the form type:salt:hashed-password.
449 The string should be of the form type:salt:hashed-password.
450 """
450 """
451 )
451 )
452
452
453 open_browser = Bool(True, config=True,
453 open_browser = Bool(True, config=True,
454 help="""Whether to open in a browser after starting.
454 help="""Whether to open in a browser after starting.
455 The specific browser used is platform dependent and
455 The specific browser used is platform dependent and
456 determined by the python standard library `webbrowser`
456 determined by the python standard library `webbrowser`
457 module, unless it is overridden using the --browser
457 module, unless it is overridden using the --browser
458 (NotebookApp.browser) configuration option.
458 (NotebookApp.browser) configuration option.
459 """)
459 """)
460
460
461 browser = Unicode(u'', config=True,
461 browser = Unicode(u'', config=True,
462 help="""Specify what command to use to invoke a web
462 help="""Specify what command to use to invoke a web
463 browser when opening the notebook. If not specified, the
463 browser when opening the notebook. If not specified, the
464 default browser will be determined by the `webbrowser`
464 default browser will be determined by the `webbrowser`
465 standard library module, which allows setting of the
465 standard library module, which allows setting of the
466 BROWSER environment variable to override it.
466 BROWSER environment variable to override it.
467 """)
467 """)
468
468
469 webapp_settings = Dict(config=True,
469 webapp_settings = Dict(config=True,
470 help="DEPRECATED, use tornado_settings"
470 help="DEPRECATED, use tornado_settings"
471 )
471 )
472 def _webapp_settings_changed(self, name, old, new):
472 def _webapp_settings_changed(self, name, old, new):
473 self.log.warn("\n webapp_settings is deprecated, use tornado_settings.\n")
473 self.log.warn("\n webapp_settings is deprecated, use tornado_settings.\n")
474 self.tornado_settings = new
474 self.tornado_settings = new
475
475
476 tornado_settings = Dict(config=True,
476 tornado_settings = Dict(config=True,
477 help="Supply overrides for the tornado.web.Application that the "
477 help="Supply overrides for the tornado.web.Application that the "
478 "IPython notebook uses.")
478 "IPython notebook uses.")
479
479
480 jinja_environment_options = Dict(config=True,
480 jinja_environment_options = Dict(config=True,
481 help="Supply extra arguments that will be passed to Jinja environment.")
481 help="Supply extra arguments that will be passed to Jinja environment.")
482
482
483
483
484 enable_mathjax = Bool(True, config=True,
484 enable_mathjax = Bool(True, config=True,
485 help="""Whether to enable MathJax for typesetting math/TeX
485 help="""Whether to enable MathJax for typesetting math/TeX
486
486
487 MathJax is the javascript library IPython uses to render math/LaTeX. It is
487 MathJax is the javascript library IPython uses to render math/LaTeX. It is
488 very large, so you may want to disable it if you have a slow internet
488 very large, so you may want to disable it if you have a slow internet
489 connection, or for offline use of the notebook.
489 connection, or for offline use of the notebook.
490
490
491 When disabled, equations etc. will appear as their untransformed TeX source.
491 When disabled, equations etc. will appear as their untransformed TeX source.
492 """
492 """
493 )
493 )
494 def _enable_mathjax_changed(self, name, old, new):
494 def _enable_mathjax_changed(self, name, old, new):
495 """set mathjax url to empty if mathjax is disabled"""
495 """set mathjax url to empty if mathjax is disabled"""
496 if not new:
496 if not new:
497 self.mathjax_url = u''
497 self.mathjax_url = u''
498
498
499 base_url = Unicode('/', config=True,
499 base_url = Unicode('/', config=True,
500 help='''The base URL for the notebook server.
500 help='''The base URL for the notebook server.
501
501
502 Leading and trailing slashes can be omitted,
502 Leading and trailing slashes can be omitted,
503 and will automatically be added.
503 and will automatically be added.
504 ''')
504 ''')
505 def _base_url_changed(self, name, old, new):
505 def _base_url_changed(self, name, old, new):
506 if not new.startswith('/'):
506 if not new.startswith('/'):
507 self.base_url = '/'+new
507 self.base_url = '/'+new
508 elif not new.endswith('/'):
508 elif not new.endswith('/'):
509 self.base_url = new+'/'
509 self.base_url = new+'/'
510
510
511 base_project_url = Unicode('/', config=True, help="""DEPRECATED use base_url""")
511 base_project_url = Unicode('/', config=True, help="""DEPRECATED use base_url""")
512 def _base_project_url_changed(self, name, old, new):
512 def _base_project_url_changed(self, name, old, new):
513 self.log.warn("base_project_url is deprecated, use base_url")
513 self.log.warn("base_project_url is deprecated, use base_url")
514 self.base_url = new
514 self.base_url = new
515
515
516 extra_static_paths = List(Unicode, config=True,
516 extra_static_paths = List(Unicode, config=True,
517 help="""Extra paths to search for serving static files.
517 help="""Extra paths to search for serving static files.
518
518
519 This allows adding javascript/css to be available from the notebook server machine,
519 This allows adding javascript/css to be available from the notebook server machine,
520 or overriding individual files in the IPython"""
520 or overriding individual files in the IPython"""
521 )
521 )
522 def _extra_static_paths_default(self):
522 def _extra_static_paths_default(self):
523 return [os.path.join(self.profile_dir.location, 'static')]
523 return [os.path.join(self.profile_dir.location, 'static')]
524
524
525 @property
525 @property
526 def static_file_path(self):
526 def static_file_path(self):
527 """return extra paths + the default location"""
527 """return extra paths + the default location"""
528 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
528 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
529
529
530 extra_template_paths = List(Unicode, config=True,
530 extra_template_paths = List(Unicode, config=True,
531 help="""Extra paths to search for serving jinja templates.
531 help="""Extra paths to search for serving jinja templates.
532
532
533 Can be used to override templates from IPython.html.templates."""
533 Can be used to override templates from IPython.html.templates."""
534 )
534 )
535 def _extra_template_paths_default(self):
535 def _extra_template_paths_default(self):
536 return []
536 return []
537
537
538 @property
538 @property
539 def template_file_path(self):
539 def template_file_path(self):
540 """return extra paths + the default locations"""
540 """return extra paths + the default locations"""
541 return self.extra_template_paths + DEFAULT_TEMPLATE_PATH_LIST
541 return self.extra_template_paths + DEFAULT_TEMPLATE_PATH_LIST
542
542
543 nbextensions_path = List(Unicode, config=True,
543 nbextensions_path = List(Unicode, config=True,
544 help="""paths for Javascript extensions. By default, this is just IPYTHONDIR/nbextensions"""
544 help="""paths for Javascript extensions. By default, this is just IPYTHONDIR/nbextensions"""
545 )
545 )
546 def _nbextensions_path_default(self):
546 def _nbextensions_path_default(self):
547 return [os.path.join(get_ipython_dir(), 'nbextensions')]
547 return [os.path.join(get_ipython_dir(), 'nbextensions')]
548
548
549 websocket_url = Unicode("", config=True,
549 websocket_url = Unicode("", config=True,
550 help="""The base URL for websockets,
550 help="""The base URL for websockets,
551 if it differs from the HTTP server (hint: it almost certainly doesn't).
551 if it differs from the HTTP server (hint: it almost certainly doesn't).
552
552
553 Should be in the form of an HTTP origin: ws[s]://hostname[:port]
553 Should be in the form of an HTTP origin: ws[s]://hostname[:port]
554 """
554 """
555 )
555 )
556 mathjax_url = Unicode("", config=True,
556 mathjax_url = Unicode("", config=True,
557 help="""The url for MathJax.js."""
557 help="""The url for MathJax.js."""
558 )
558 )
559 def _mathjax_url_default(self):
559 def _mathjax_url_default(self):
560 if not self.enable_mathjax:
560 if not self.enable_mathjax:
561 return u''
561 return u''
562 static_url_prefix = self.tornado_settings.get("static_url_prefix",
562 static_url_prefix = self.tornado_settings.get("static_url_prefix",
563 url_path_join(self.base_url, "static")
563 url_path_join(self.base_url, "static")
564 )
564 )
565
565
566 # try local mathjax, either in nbextensions/mathjax or static/mathjax
566 # try local mathjax, either in nbextensions/mathjax or static/mathjax
567 for (url_prefix, search_path) in [
567 for (url_prefix, search_path) in [
568 (url_path_join(self.base_url, "nbextensions"), self.nbextensions_path),
568 (url_path_join(self.base_url, "nbextensions"), self.nbextensions_path),
569 (static_url_prefix, self.static_file_path),
569 (static_url_prefix, self.static_file_path),
570 ]:
570 ]:
571 self.log.debug("searching for local mathjax in %s", search_path)
571 self.log.debug("searching for local mathjax in %s", search_path)
572 try:
572 try:
573 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), search_path)
573 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), search_path)
574 except IOError:
574 except IOError:
575 continue
575 continue
576 else:
576 else:
577 url = url_path_join(url_prefix, u"mathjax/MathJax.js")
577 url = url_path_join(url_prefix, u"mathjax/MathJax.js")
578 self.log.info("Serving local MathJax from %s at %s", mathjax, url)
578 self.log.info("Serving local MathJax from %s at %s", mathjax, url)
579 return url
579 return url
580
580
581 # no local mathjax, serve from CDN
581 # no local mathjax, serve from CDN
582 url = u"https://cdn.mathjax.org/mathjax/latest/MathJax.js"
582 url = u"https://cdn.mathjax.org/mathjax/latest/MathJax.js"
583 self.log.info("Using MathJax from CDN: %s", url)
583 self.log.info("Using MathJax from CDN: %s", url)
584 return url
584 return url
585
585
586 def _mathjax_url_changed(self, name, old, new):
586 def _mathjax_url_changed(self, name, old, new):
587 if new and not self.enable_mathjax:
587 if new and not self.enable_mathjax:
588 # enable_mathjax=False overrides mathjax_url
588 # enable_mathjax=False overrides mathjax_url
589 self.mathjax_url = u''
589 self.mathjax_url = u''
590 else:
590 else:
591 self.log.info("Using MathJax: %s", new)
591 self.log.info("Using MathJax: %s", new)
592
592
593 contents_manager_class = DottedObjectName('IPython.html.services.contents.filemanager.FileContentsManager',
593 contents_manager_class = DottedObjectName('IPython.html.services.contents.filemanager.FileContentsManager',
594 config=True,
594 config=True,
595 help='The notebook manager class to use.'
595 help='The notebook manager class to use.'
596 )
596 )
597 kernel_manager_class = DottedObjectName('IPython.html.services.kernels.kernelmanager.MappingKernelManager',
597 kernel_manager_class = DottedObjectName('IPython.html.services.kernels.kernelmanager.MappingKernelManager',
598 config=True,
598 config=True,
599 help='The kernel manager class to use.'
599 help='The kernel manager class to use.'
600 )
600 )
601 session_manager_class = DottedObjectName('IPython.html.services.sessions.sessionmanager.SessionManager',
601 session_manager_class = DottedObjectName('IPython.html.services.sessions.sessionmanager.SessionManager',
602 config=True,
602 config=True,
603 help='The session manager class to use.'
603 help='The session manager class to use.'
604 )
604 )
605 cluster_manager_class = DottedObjectName('IPython.html.services.clusters.clustermanager.ClusterManager',
605 cluster_manager_class = DottedObjectName('IPython.html.services.clusters.clustermanager.ClusterManager',
606 config=True,
606 config=True,
607 help='The cluster manager class to use.'
607 help='The cluster manager class to use.'
608 )
608 )
609
609
610 kernel_spec_manager = Instance(KernelSpecManager)
610 kernel_spec_manager = Instance(KernelSpecManager)
611
611
612 def _kernel_spec_manager_default(self):
612 def _kernel_spec_manager_default(self):
613 return KernelSpecManager(ipython_dir=self.ipython_dir)
613 return KernelSpecManager(ipython_dir=self.ipython_dir)
614
614
615 trust_xheaders = Bool(False, config=True,
615 trust_xheaders = Bool(False, config=True,
616 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
616 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
617 "sent by the upstream reverse proxy. Necessary if the proxy handles SSL")
617 "sent by the upstream reverse proxy. Necessary if the proxy handles SSL")
618 )
618 )
619
619
620 info_file = Unicode()
620 info_file = Unicode()
621
621
622 def _info_file_default(self):
622 def _info_file_default(self):
623 info_file = "nbserver-%s.json"%os.getpid()
623 info_file = "nbserver-%s.json"%os.getpid()
624 return os.path.join(self.profile_dir.security_dir, info_file)
624 return os.path.join(self.profile_dir.security_dir, info_file)
625
625
626 pylab = Unicode('disabled', config=True,
626 pylab = Unicode('disabled', config=True,
627 help="""
627 help="""
628 DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.
628 DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.
629 """
629 """
630 )
630 )
631 def _pylab_changed(self, name, old, new):
631 def _pylab_changed(self, name, old, new):
632 """when --pylab is specified, display a warning and exit"""
632 """when --pylab is specified, display a warning and exit"""
633 if new != 'warn':
633 if new != 'warn':
634 backend = ' %s' % new
634 backend = ' %s' % new
635 else:
635 else:
636 backend = ''
636 backend = ''
637 self.log.error("Support for specifying --pylab on the command line has been removed.")
637 self.log.error("Support for specifying --pylab on the command line has been removed.")
638 self.log.error(
638 self.log.error(
639 "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself.".format(backend)
639 "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself.".format(backend)
640 )
640 )
641 self.exit(1)
641 self.exit(1)
642
642
643 notebook_dir = Unicode(config=True,
643 notebook_dir = Unicode(config=True,
644 help="The directory to use for notebooks and kernels."
644 help="The directory to use for notebooks and kernels."
645 )
645 )
646
646
647 def _notebook_dir_default(self):
647 def _notebook_dir_default(self):
648 if self.file_to_run:
648 if self.file_to_run:
649 return os.path.dirname(os.path.abspath(self.file_to_run))
649 return os.path.dirname(os.path.abspath(self.file_to_run))
650 else:
650 else:
651 return py3compat.getcwd()
651 return py3compat.getcwd()
652
652
653 def _notebook_dir_changed(self, name, old, new):
653 def _notebook_dir_changed(self, name, old, new):
654 """Do a bit of validation of the notebook dir."""
654 """Do a bit of validation of the notebook dir."""
655 if not os.path.isabs(new):
655 if not os.path.isabs(new):
656 # If we receive a non-absolute path, make it absolute.
656 # If we receive a non-absolute path, make it absolute.
657 self.notebook_dir = os.path.abspath(new)
657 self.notebook_dir = os.path.abspath(new)
658 return
658 return
659 if not os.path.isdir(new):
659 if not os.path.isdir(new):
660 raise TraitError("No such notebook dir: %r" % new)
660 raise TraitError("No such notebook dir: %r" % new)
661
661
662 # setting App.notebook_dir implies setting notebook and kernel dirs as well
662 # setting App.notebook_dir implies setting notebook and kernel dirs as well
663 self.config.FileContentsManager.root_dir = new
663 self.config.FileContentsManager.root_dir = new
664 self.config.MappingKernelManager.root_dir = new
664 self.config.MappingKernelManager.root_dir = new
665
665
666
666
667 def parse_command_line(self, argv=None):
667 def parse_command_line(self, argv=None):
668 super(NotebookApp, self).parse_command_line(argv)
668 super(NotebookApp, self).parse_command_line(argv)
669
669
670 if self.extra_args:
670 if self.extra_args:
671 arg0 = self.extra_args[0]
671 arg0 = self.extra_args[0]
672 f = os.path.abspath(arg0)
672 f = os.path.abspath(arg0)
673 self.argv.remove(arg0)
673 self.argv.remove(arg0)
674 if not os.path.exists(f):
674 if not os.path.exists(f):
675 self.log.critical("No such file or directory: %s", f)
675 self.log.critical("No such file or directory: %s", f)
676 self.exit(1)
676 self.exit(1)
677
677
678 # Use config here, to ensure that it takes higher priority than
678 # Use config here, to ensure that it takes higher priority than
679 # anything that comes from the profile.
679 # anything that comes from the profile.
680 c = Config()
680 c = Config()
681 if os.path.isdir(f):
681 if os.path.isdir(f):
682 c.NotebookApp.notebook_dir = f
682 c.NotebookApp.notebook_dir = f
683 elif os.path.isfile(f):
683 elif os.path.isfile(f):
684 c.NotebookApp.file_to_run = f
684 c.NotebookApp.file_to_run = f
685 self.update_config(c)
685 self.update_config(c)
686
686
687 def init_kernel_argv(self):
687 def init_kernel_argv(self):
688 """add the profile-dir to arguments to be passed to IPython kernels"""
688 """add the profile-dir to arguments to be passed to IPython kernels"""
689 # FIXME: remove special treatment of IPython kernels
689 # FIXME: remove special treatment of IPython kernels
690 # Kernel should get *absolute* path to profile directory
690 # Kernel should get *absolute* path to profile directory
691 self.ipython_kernel_argv = ["--profile-dir", self.profile_dir.location]
691 self.ipython_kernel_argv = ["--profile-dir", self.profile_dir.location]
692
692
693 def init_configurables(self):
693 def init_configurables(self):
694 # force Session default to be secure
694 # force Session default to be secure
695 default_secure(self.config)
695 default_secure(self.config)
696 kls = import_item(self.kernel_manager_class)
696 kls = import_item(self.kernel_manager_class)
697 self.kernel_manager = kls(
697 self.kernel_manager = kls(
698 parent=self, log=self.log, ipython_kernel_argv=self.ipython_kernel_argv,
698 parent=self, log=self.log, ipython_kernel_argv=self.ipython_kernel_argv,
699 connection_dir = self.profile_dir.security_dir,
699 connection_dir = self.profile_dir.security_dir,
700 )
700 )
701 kls = import_item(self.contents_manager_class)
701 kls = import_item(self.contents_manager_class)
702 self.contents_manager = kls(parent=self, log=self.log)
702 self.contents_manager = kls(parent=self, log=self.log)
703 kls = import_item(self.session_manager_class)
703 kls = import_item(self.session_manager_class)
704 self.session_manager = kls(parent=self, log=self.log,
704 self.session_manager = kls(parent=self, log=self.log,
705 kernel_manager=self.kernel_manager,
705 kernel_manager=self.kernel_manager,
706 contents_manager=self.contents_manager)
706 contents_manager=self.contents_manager)
707 kls = import_item(self.cluster_manager_class)
707 kls = import_item(self.cluster_manager_class)
708 self.cluster_manager = kls(parent=self, log=self.log)
708 self.cluster_manager = kls(parent=self, log=self.log)
709 self.cluster_manager.update_profiles()
709 self.cluster_manager.update_profiles()
710
710
711 def init_logging(self):
711 def init_logging(self):
712 # This prevents double log messages because tornado use a root logger that
712 # This prevents double log messages because tornado use a root logger that
713 # self.log is a child of. The logging module dipatches log messages to a log
713 # self.log is a child of. The logging module dipatches log messages to a log
714 # and all of its ancenstors until propagate is set to False.
714 # and all of its ancenstors until propagate is set to False.
715 self.log.propagate = False
715 self.log.propagate = False
716
716
717 for log in app_log, access_log, gen_log:
717 for log in app_log, access_log, gen_log:
718 # consistent log output name (NotebookApp instead of tornado.access, etc.)
718 # consistent log output name (NotebookApp instead of tornado.access, etc.)
719 log.name = self.log.name
719 log.name = self.log.name
720 # hook up tornado 3's loggers to our app handlers
720 # hook up tornado 3's loggers to our app handlers
721 logger = logging.getLogger('tornado')
721 logger = logging.getLogger('tornado')
722 logger.propagate = True
722 logger.propagate = True
723 logger.parent = self.log
723 logger.parent = self.log
724 logger.setLevel(self.log.level)
724 logger.setLevel(self.log.level)
725
725
726 def init_webapp(self):
726 def init_webapp(self):
727 """initialize tornado webapp and httpserver"""
727 """initialize tornado webapp and httpserver"""
728 self.tornado_settings['allow_origin'] = self.allow_origin
728 self.tornado_settings['allow_origin'] = self.allow_origin
729 if self.allow_origin_pat:
729 if self.allow_origin_pat:
730 self.tornado_settings['allow_origin_pat'] = re.compile(self.allow_origin_pat)
730 self.tornado_settings['allow_origin_pat'] = re.compile(self.allow_origin_pat)
731 self.tornado_settings['allow_credentials'] = self.allow_credentials
731 self.tornado_settings['allow_credentials'] = self.allow_credentials
732
732
733 self.web_app = NotebookWebApplication(
733 self.web_app = NotebookWebApplication(
734 self, self.kernel_manager, self.contents_manager,
734 self, self.kernel_manager, self.contents_manager,
735 self.cluster_manager, self.session_manager, self.kernel_spec_manager,
735 self.cluster_manager, self.session_manager, self.kernel_spec_manager,
736 self.log, self.base_url, self.default_url, self.tornado_settings,
736 self.log, self.base_url, self.default_url, self.tornado_settings,
737 self.jinja_environment_options
737 self.jinja_environment_options
738 )
738 )
739 if self.certfile:
739 if self.certfile:
740 ssl_options = dict(certfile=self.certfile)
740 ssl_options = dict(certfile=self.certfile)
741 if self.keyfile:
741 if self.keyfile:
742 ssl_options['keyfile'] = self.keyfile
742 ssl_options['keyfile'] = self.keyfile
743 else:
743 else:
744 ssl_options = None
744 ssl_options = None
745 self.web_app.password = self.password
745 self.web_app.password = self.password
746 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
746 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
747 xheaders=self.trust_xheaders)
747 xheaders=self.trust_xheaders)
748 if not self.ip:
748 if not self.ip:
749 warning = "WARNING: The notebook server is listening on all IP addresses"
749 warning = "WARNING: The notebook server is listening on all IP addresses"
750 if ssl_options is None:
750 if ssl_options is None:
751 self.log.critical(warning + " and not using encryption. This "
751 self.log.critical(warning + " and not using encryption. This "
752 "is not recommended.")
752 "is not recommended.")
753 if not self.password:
753 if not self.password:
754 self.log.critical(warning + " and not using authentication. "
754 self.log.critical(warning + " and not using authentication. "
755 "This is highly insecure and not recommended.")
755 "This is highly insecure and not recommended.")
756 success = None
756 success = None
757 for port in random_ports(self.port, self.port_retries+1):
757 for port in random_ports(self.port, self.port_retries+1):
758 try:
758 try:
759 self.http_server.listen(port, self.ip)
759 self.http_server.listen(port, self.ip)
760 except socket.error as e:
760 except socket.error as e:
761 if e.errno == errno.EADDRINUSE:
761 if e.errno == errno.EADDRINUSE:
762 self.log.info('The port %i is already in use, trying another random port.' % port)
762 self.log.info('The port %i is already in use, trying another random port.' % port)
763 continue
763 continue
764 elif e.errno in (errno.EACCES, getattr(errno, 'WSAEACCES', errno.EACCES)):
764 elif e.errno in (errno.EACCES, getattr(errno, 'WSAEACCES', errno.EACCES)):
765 self.log.warn("Permission to listen on port %i denied" % port)
765 self.log.warn("Permission to listen on port %i denied" % port)
766 continue
766 continue
767 else:
767 else:
768 raise
768 raise
769 else:
769 else:
770 self.port = port
770 self.port = port
771 success = True
771 success = True
772 break
772 break
773 if not success:
773 if not success:
774 self.log.critical('ERROR: the notebook server could not be started because '
774 self.log.critical('ERROR: the notebook server could not be started because '
775 'no available port could be found.')
775 'no available port could be found.')
776 self.exit(1)
776 self.exit(1)
777
777
778 @property
778 @property
779 def display_url(self):
779 def display_url(self):
780 ip = self.ip if self.ip else '[all ip addresses on your system]'
780 ip = self.ip if self.ip else '[all ip addresses on your system]'
781 return self._url(ip)
781 return self._url(ip)
782
782
783 @property
783 @property
784 def connection_url(self):
784 def connection_url(self):
785 ip = self.ip if self.ip else 'localhost'
785 ip = self.ip if self.ip else 'localhost'
786 return self._url(ip)
786 return self._url(ip)
787
787
788 def _url(self, ip):
788 def _url(self, ip):
789 proto = 'https' if self.certfile else 'http'
789 proto = 'https' if self.certfile else 'http'
790 return "%s://%s:%i%s" % (proto, ip, self.port, self.base_url)
790 return "%s://%s:%i%s" % (proto, ip, self.port, self.base_url)
791
791
792 def init_terminals(self):
792 def init_terminals(self):
793 try:
793 try:
794 from .terminal import initialize
794 from .terminal import initialize
795 initialize(self.web_app)
795 initialize(self.web_app)
796 self.web_app.settings['terminals_available'] = True
796 self.web_app.settings['terminals_available'] = True
797 except ImportError as e:
797 except ImportError as e:
798 self.log.info("Terminals not available (error was %s)", e)
798 self.log.info("Terminals not available (error was %s)", e)
799
799
800 def init_signal(self):
800 def init_signal(self):
801 if not sys.platform.startswith('win'):
801 if not sys.platform.startswith('win'):
802 signal.signal(signal.SIGINT, self._handle_sigint)
802 signal.signal(signal.SIGINT, self._handle_sigint)
803 signal.signal(signal.SIGTERM, self._signal_stop)
803 signal.signal(signal.SIGTERM, self._signal_stop)
804 if hasattr(signal, 'SIGUSR1'):
804 if hasattr(signal, 'SIGUSR1'):
805 # Windows doesn't support SIGUSR1
805 # Windows doesn't support SIGUSR1
806 signal.signal(signal.SIGUSR1, self._signal_info)
806 signal.signal(signal.SIGUSR1, self._signal_info)
807 if hasattr(signal, 'SIGINFO'):
807 if hasattr(signal, 'SIGINFO'):
808 # only on BSD-based systems
808 # only on BSD-based systems
809 signal.signal(signal.SIGINFO, self._signal_info)
809 signal.signal(signal.SIGINFO, self._signal_info)
810
810
811 def _handle_sigint(self, sig, frame):
811 def _handle_sigint(self, sig, frame):
812 """SIGINT handler spawns confirmation dialog"""
812 """SIGINT handler spawns confirmation dialog"""
813 # register more forceful signal handler for ^C^C case
813 # register more forceful signal handler for ^C^C case
814 signal.signal(signal.SIGINT, self._signal_stop)
814 signal.signal(signal.SIGINT, self._signal_stop)
815 # request confirmation dialog in bg thread, to avoid
815 # request confirmation dialog in bg thread, to avoid
816 # blocking the App
816 # blocking the App
817 thread = threading.Thread(target=self._confirm_exit)
817 thread = threading.Thread(target=self._confirm_exit)
818 thread.daemon = True
818 thread.daemon = True
819 thread.start()
819 thread.start()
820
820
821 def _restore_sigint_handler(self):
821 def _restore_sigint_handler(self):
822 """callback for restoring original SIGINT handler"""
822 """callback for restoring original SIGINT handler"""
823 signal.signal(signal.SIGINT, self._handle_sigint)
823 signal.signal(signal.SIGINT, self._handle_sigint)
824
824
825 def _confirm_exit(self):
825 def _confirm_exit(self):
826 """confirm shutdown on ^C
826 """confirm shutdown on ^C
827
827
828 A second ^C, or answering 'y' within 5s will cause shutdown,
828 A second ^C, or answering 'y' within 5s will cause shutdown,
829 otherwise original SIGINT handler will be restored.
829 otherwise original SIGINT handler will be restored.
830
830
831 This doesn't work on Windows.
831 This doesn't work on Windows.
832 """
832 """
833 info = self.log.info
833 info = self.log.info
834 info('interrupted')
834 info('interrupted')
835 print(self.notebook_info())
835 print(self.notebook_info())
836 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
836 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
837 sys.stdout.flush()
837 sys.stdout.flush()
838 r,w,x = select.select([sys.stdin], [], [], 5)
838 r,w,x = select.select([sys.stdin], [], [], 5)
839 if r:
839 if r:
840 line = sys.stdin.readline()
840 line = sys.stdin.readline()
841 if line.lower().startswith('y') and 'n' not in line.lower():
841 if line.lower().startswith('y') and 'n' not in line.lower():
842 self.log.critical("Shutdown confirmed")
842 self.log.critical("Shutdown confirmed")
843 ioloop.IOLoop.instance().stop()
843 ioloop.IOLoop.instance().stop()
844 return
844 return
845 else:
845 else:
846 print("No answer for 5s:", end=' ')
846 print("No answer for 5s:", end=' ')
847 print("resuming operation...")
847 print("resuming operation...")
848 # no answer, or answer is no:
848 # no answer, or answer is no:
849 # set it back to original SIGINT handler
849 # set it back to original SIGINT handler
850 # use IOLoop.add_callback because signal.signal must be called
850 # use IOLoop.add_callback because signal.signal must be called
851 # from main thread
851 # from main thread
852 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
852 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
853
853
854 def _signal_stop(self, sig, frame):
854 def _signal_stop(self, sig, frame):
855 self.log.critical("received signal %s, stopping", sig)
855 self.log.critical("received signal %s, stopping", sig)
856 ioloop.IOLoop.instance().stop()
856 ioloop.IOLoop.instance().stop()
857
857
858 def _signal_info(self, sig, frame):
858 def _signal_info(self, sig, frame):
859 print(self.notebook_info())
859 print(self.notebook_info())
860
860
861 def init_components(self):
861 def init_components(self):
862 """Check the components submodule, and warn if it's unclean"""
862 """Check the components submodule, and warn if it's unclean"""
863 status = submodule.check_submodule_status()
863 status = submodule.check_submodule_status()
864 if status == 'missing':
864 if status == 'missing':
865 self.log.warn("components submodule missing, running `git submodule update`")
865 self.log.warn("components submodule missing, running `git submodule update`")
866 submodule.update_submodules(submodule.ipython_parent())
866 submodule.update_submodules(submodule.ipython_parent())
867 elif status == 'unclean':
867 elif status == 'unclean':
868 self.log.warn("components submodule unclean, you may see 404s on static/components")
868 self.log.warn("components submodule unclean, you may see 404s on static/components")
869 self.log.warn("run `setup.py submodule` or `git submodule update` to update")
869 self.log.warn("run `setup.py submodule` or `git submodule update` to update")
870
870
871 @catch_config_error
871 @catch_config_error
872 def initialize(self, argv=None):
872 def initialize(self, argv=None):
873 super(NotebookApp, self).initialize(argv)
873 super(NotebookApp, self).initialize(argv)
874 self.init_logging()
874 self.init_logging()
875 self.init_kernel_argv()
875 self.init_kernel_argv()
876 self.init_configurables()
876 self.init_configurables()
877 self.init_components()
877 self.init_components()
878 self.init_webapp()
878 self.init_webapp()
879 self.init_terminals()
879 self.init_terminals()
880 self.init_signal()
880 self.init_signal()
881
881
882 def cleanup_kernels(self):
882 def cleanup_kernels(self):
883 """Shutdown all kernels.
883 """Shutdown all kernels.
884
884
885 The kernels will shutdown themselves when this process no longer exists,
885 The kernels will shutdown themselves when this process no longer exists,
886 but explicit shutdown allows the KernelManagers to cleanup the connection files.
886 but explicit shutdown allows the KernelManagers to cleanup the connection files.
887 """
887 """
888 self.log.info('Shutting down kernels')
888 self.log.info('Shutting down kernels')
889 self.kernel_manager.shutdown_all()
889 self.kernel_manager.shutdown_all()
890
890
891 def notebook_info(self):
891 def notebook_info(self):
892 "Return the current working directory and the server url information"
892 "Return the current working directory and the server url information"
893 info = self.contents_manager.info_string() + "\n"
893 info = self.contents_manager.info_string() + "\n"
894 info += "%d active kernels \n" % len(self.kernel_manager._kernels)
894 info += "%d active kernels \n" % len(self.kernel_manager._kernels)
895 return info + "The IPython Notebook is running at: %s" % self.display_url
895 return info + "The IPython Notebook is running at: %s" % self.display_url
896
896
897 def server_info(self):
897 def server_info(self):
898 """Return a JSONable dict of information about this server."""
898 """Return a JSONable dict of information about this server."""
899 return {'url': self.connection_url,
899 return {'url': self.connection_url,
900 'hostname': self.ip if self.ip else 'localhost',
900 'hostname': self.ip if self.ip else 'localhost',
901 'port': self.port,
901 'port': self.port,
902 'secure': bool(self.certfile),
902 'secure': bool(self.certfile),
903 'base_url': self.base_url,
903 'base_url': self.base_url,
904 'notebook_dir': os.path.abspath(self.notebook_dir),
904 'notebook_dir': os.path.abspath(self.notebook_dir),
905 'pid': os.getpid()
905 'pid': os.getpid()
906 }
906 }
907
907
908 def write_server_info_file(self):
908 def write_server_info_file(self):
909 """Write the result of server_info() to the JSON file info_file."""
909 """Write the result of server_info() to the JSON file info_file."""
910 with open(self.info_file, 'w') as f:
910 with open(self.info_file, 'w') as f:
911 json.dump(self.server_info(), f, indent=2)
911 json.dump(self.server_info(), f, indent=2)
912
912
913 def remove_server_info_file(self):
913 def remove_server_info_file(self):
914 """Remove the nbserver-<pid>.json file created for this server.
914 """Remove the nbserver-<pid>.json file created for this server.
915
915
916 Ignores the error raised when the file has already been removed.
916 Ignores the error raised when the file has already been removed.
917 """
917 """
918 try:
918 try:
919 os.unlink(self.info_file)
919 os.unlink(self.info_file)
920 except OSError as e:
920 except OSError as e:
921 if e.errno != errno.ENOENT:
921 if e.errno != errno.ENOENT:
922 raise
922 raise
923
923
924 def start(self):
924 def start(self):
925 """ Start the IPython Notebook server app, after initialization
925 """ Start the IPython Notebook server app, after initialization
926
926
927 This method takes no arguments so all configuration and initialization
927 This method takes no arguments so all configuration and initialization
928 must be done prior to calling this method."""
928 must be done prior to calling this method."""
929 if self.subapp is not None:
929 if self.subapp is not None:
930 return self.subapp.start()
930 return self.subapp.start()
931
931
932 info = self.log.info
932 info = self.log.info
933 for line in self.notebook_info().split("\n"):
933 for line in self.notebook_info().split("\n"):
934 info(line)
934 info(line)
935 info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).")
935 info("Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).")
936
936
937 self.write_server_info_file()
937 self.write_server_info_file()
938
938
939 if self.open_browser or self.file_to_run:
939 if self.open_browser or self.file_to_run:
940 try:
940 try:
941 browser = webbrowser.get(self.browser or None)
941 browser = webbrowser.get(self.browser or None)
942 except webbrowser.Error as e:
942 except webbrowser.Error as e:
943 self.log.warn('No web browser found: %s.' % e)
943 self.log.warn('No web browser found: %s.' % e)
944 browser = None
944 browser = None
945
945
946 if self.file_to_run:
946 if self.file_to_run:
947 if not os.path.exists(self.file_to_run):
947 if not os.path.exists(self.file_to_run):
948 self.log.critical("%s does not exist" % self.file_to_run)
948 self.log.critical("%s does not exist" % self.file_to_run)
949 self.exit(1)
949 self.exit(1)
950
950
951 relpath = os.path.relpath(self.file_to_run, self.notebook_dir)
951 relpath = os.path.relpath(self.file_to_run, self.notebook_dir)
952 uri = url_path_join('notebooks', *relpath.split(os.sep))
952 uri = url_path_join('notebooks', *relpath.split(os.sep))
953 else:
953 else:
954 uri = 'tree'
954 uri = 'tree'
955 if browser:
955 if browser:
956 b = lambda : browser.open(url_path_join(self.connection_url, uri),
956 b = lambda : browser.open(url_path_join(self.connection_url, uri),
957 new=2)
957 new=2)
958 threading.Thread(target=b).start()
958 threading.Thread(target=b).start()
959 try:
959 try:
960 ioloop.IOLoop.instance().start()
960 ioloop.IOLoop.instance().start()
961 except KeyboardInterrupt:
961 except KeyboardInterrupt:
962 info("Interrupted...")
962 info("Interrupted...")
963 finally:
963 finally:
964 self.cleanup_kernels()
964 self.cleanup_kernels()
965 self.remove_server_info_file()
965 self.remove_server_info_file()
966
966
967
967
968 def list_running_servers(profile='default'):
968 def list_running_servers(profile='default'):
969 """Iterate over the server info files of running notebook servers.
969 """Iterate over the server info files of running notebook servers.
970
970
971 Given a profile name, find nbserver-* files in the security directory of
971 Given a profile name, find nbserver-* files in the security directory of
972 that profile, and yield dicts of their information, each one pertaining to
972 that profile, and yield dicts of their information, each one pertaining to
973 a currently running notebook server instance.
973 a currently running notebook server instance.
974 """
974 """
975 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), name=profile)
975 pd = ProfileDir.find_profile_dir_by_name(get_ipython_dir(), name=profile)
976 for file in os.listdir(pd.security_dir):
976 for file in os.listdir(pd.security_dir):
977 if file.startswith('nbserver-'):
977 if file.startswith('nbserver-'):
978 with io.open(os.path.join(pd.security_dir, file), encoding='utf-8') as f:
978 with io.open(os.path.join(pd.security_dir, file), encoding='utf-8') as f:
979 info = json.load(f)
979 info = json.load(f)
980
980
981 # Simple check whether that process is really still running
981 # Simple check whether that process is really still running
982 if check_pid(info['pid']):
982 if check_pid(info['pid']):
983 yield info
983 yield info
984 else:
984 else:
985 # If the process has died, try to delete its info file
985 # If the process has died, try to delete its info file
986 try:
986 try:
987 os.unlink(file)
987 os.unlink(file)
988 except OSError:
988 except OSError:
989 pass # TODO: This should warn or log or something
989 pass # TODO: This should warn or log or something
990 #-----------------------------------------------------------------------------
990 #-----------------------------------------------------------------------------
991 # Main entry point
991 # Main entry point
992 #-----------------------------------------------------------------------------
992 #-----------------------------------------------------------------------------
993
993
994 launch_new_instance = NotebookApp.launch_instance
994 launch_new_instance = NotebookApp.launch_instance
995
995
1 NO CONTENT: file renamed from IPython/html/static/texteditor/js/editor.js to IPython/html/static/edit/js/editor.js
NO CONTENT: file renamed from IPython/html/static/texteditor/js/editor.js to IPython/html/static/edit/js/editor.js
@@ -1,53 +1,53 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 require([
4 require([
5 'base/js/namespace',
5 'base/js/namespace',
6 'base/js/utils',
6 'base/js/utils',
7 'base/js/page',
7 'base/js/page',
8 'base/js/events',
8 'base/js/events',
9 'contents',
9 'contents',
10 'texteditor/js/editor',
10 'edit/js/editor',
11 'texteditor/js/menubar',
11 'edit/js/menubar',
12 'texteditor/js/notificationarea',
12 'edit/js/notificationarea',
13 'custom/custom',
13 'custom/custom',
14 ], function(
14 ], function(
15 IPython,
15 IPython,
16 utils,
16 utils,
17 page,
17 page,
18 events,
18 events,
19 contents,
19 contents,
20 editor,
20 editor,
21 menubar,
21 menubar,
22 notificationarea
22 notificationarea
23 ){
23 ){
24 page = new page.Page();
24 page = new page.Page();
25
25
26 var base_url = utils.get_body_data('baseUrl');
26 var base_url = utils.get_body_data('baseUrl');
27 var file_path = utils.get_body_data('filePath');
27 var file_path = utils.get_body_data('filePath');
28 contents = new contents.Contents({base_url: base_url});
28 contents = new contents.Contents({base_url: base_url});
29
29
30 var editor = new editor.Editor('#texteditor-container', {
30 var editor = new editor.Editor('#texteditor-container', {
31 base_url: base_url,
31 base_url: base_url,
32 events: events,
32 events: events,
33 contents: contents,
33 contents: contents,
34 file_path: file_path,
34 file_path: file_path,
35 });
35 });
36
36
37 // Make it available for debugging
37 // Make it available for debugging
38 IPython.editor = editor;
38 IPython.editor = editor;
39
39
40 var menus = new menubar.MenuBar('#menubar', {
40 var menus = new menubar.MenuBar('#menubar', {
41 base_url: base_url,
41 base_url: base_url,
42 editor: editor,
42 editor: editor,
43 });
43 });
44
44
45 var notification_area = new notificationarea.EditorNotificationArea(
45 var notification_area = new notificationarea.EditorNotificationArea(
46 '#notification_area', {
46 '#notification_area', {
47 events: events,
47 events: events,
48 });
48 });
49 notification_area.init_notification_widgets();
49 notification_area.init_notification_widgets();
50
50
51 editor.load();
51 editor.load();
52 page.show();
52 page.show();
53 });
53 });
1 NO CONTENT: file renamed from IPython/html/static/texteditor/js/menubar.js to IPython/html/static/edit/js/menubar.js
NO CONTENT: file renamed from IPython/html/static/texteditor/js/menubar.js to IPython/html/static/edit/js/menubar.js
1 NO CONTENT: file renamed from IPython/html/static/texteditor/js/notificationarea.js to IPython/html/static/edit/js/notificationarea.js
NO CONTENT: file renamed from IPython/html/static/texteditor/js/notificationarea.js to IPython/html/static/edit/js/notificationarea.js
@@ -1,72 +1,72 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block title %}{{page_title}}{% endblock %}
3 {% block title %}{{page_title}}{% endblock %}
4
4
5 {% block stylesheet %}
5 {% block stylesheet %}
6 <link rel="stylesheet" href="{{ static_url('components/codemirror/lib/codemirror.css') }}">
6 <link rel="stylesheet" href="{{ static_url('components/codemirror/lib/codemirror.css') }}">
7 <link rel="stylesheet" href="{{ static_url('components/codemirror/addon/dialog/dialog.css') }}">
7 <link rel="stylesheet" href="{{ static_url('components/codemirror/addon/dialog/dialog.css') }}">
8 <style>
8 <style>
9 #texteditor-container {
9 #texteditor-container {
10 border-bottom: 1px solid #ccc;
10 border-bottom: 1px solid #ccc;
11 }
11 }
12
12
13 #filename {
13 #filename {
14 font-size: 16pt;
14 font-size: 16pt;
15 display: table;
15 display: table;
16 padding: 0px 5px;
16 padding: 0px 5px;
17 }
17 }
18 </style>
18 </style>
19
19
20 {{super()}}
20 {{super()}}
21 {% endblock %}
21 {% endblock %}
22
22
23 {% block params %}
23 {% block params %}
24
24
25 data-base-url="{{base_url}}"
25 data-base-url="{{base_url}}"
26 data-file-path="{{file_path}}"
26 data-file-path="{{file_path}}"
27
27
28 {% endblock %}
28 {% endblock %}
29
29
30 {% block header %}
30 {% block header %}
31
31
32 <span id="filename">{{ basename }}</span>
32 <span id="filename">{{ basename }}</span>
33
33
34 {% endblock %}
34 {% endblock %}
35
35
36 {% block site %}
36 {% block site %}
37
37
38 <div id="menubar-container" class="container">
38 <div id="menubar-container" class="container">
39 <div id="menubar">
39 <div id="menubar">
40 <div id="menus" class="navbar navbar-default" role="navigation">
40 <div id="menus" class="navbar navbar-default" role="navigation">
41 <div class="container-fluid">
41 <div class="container-fluid">
42 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
42 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
43 <i class="fa fa-bars"></i>
43 <i class="fa fa-bars"></i>
44 <span class="navbar-text">Menu</span>
44 <span class="navbar-text">Menu</span>
45 </button>
45 </button>
46 <ul class="nav navbar-nav navbar-right">
46 <ul class="nav navbar-nav navbar-right">
47 <li id="notification_area"></li>
47 <li id="notification_area"></li>
48 </ul>
48 </ul>
49 <div class="navbar-collapse collapse">
49 <div class="navbar-collapse collapse">
50 <ul class="nav navbar-nav">
50 <ul class="nav navbar-nav">
51 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
51 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
52 <ul id="file_menu" class="dropdown-menu">
52 <ul id="file_menu" class="dropdown-menu">
53 <li id="save_file"><a href="#">Save</a></li>
53 <li id="save_file"><a href="#">Save</a></li>
54 </ul>
54 </ul>
55 </li>
55 </li>
56 </ul>
56 </ul>
57 </div>
57 </div>
58 </div>
58 </div>
59 </div>
59 </div>
60 </div>
60 </div>
61 </div>
61 </div>
62
62
63 <div id="texteditor-container" class="container"></div>
63 <div id="texteditor-container" class="container"></div>
64
64
65 {% endblock %}
65 {% endblock %}
66
66
67 {% block script %}
67 {% block script %}
68
68
69 {{super()}}
69 {{super()}}
70
70
71 <script src="{{ static_url("texteditor/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
71 <script src="{{ static_url("edit/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
72 {% endblock %}
72 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now