##// END OF EJS Templates
Merge pull request #3089 from minrk/stdin...
Brian E. Granger -
r10380:c3a90443 merge
parent child Browse files
Show More
@@ -1,927 +1,924 b''
1 1 """Tornado handlers for the notebook.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import Cookie
20 20 import datetime
21 21 import email.utils
22 22 import hashlib
23 23 import logging
24 24 import mimetypes
25 25 import os
26 26 import stat
27 27 import threading
28 28 import time
29 29 import uuid
30 30
31 31 from tornado.escape import url_escape
32 32 from tornado import web
33 33 from tornado import websocket
34 34
35 35 try:
36 36 from tornado.log import app_log
37 37 except ImportError:
38 38 app_log = logging.getLogger()
39 39
40 40 from zmq.eventloop import ioloop
41 41 from zmq.utils import jsonapi
42 42
43 43 from IPython.config import Application
44 44 from IPython.external.decorator import decorator
45 45 from IPython.kernel.zmq.session import Session
46 46 from IPython.lib.security import passwd_check
47 47 from IPython.utils.jsonutil import date_default
48 48 from IPython.utils.path import filefind
49 49 from IPython.utils.py3compat import PY3
50 50
51 51 try:
52 52 from docutils.core import publish_string
53 53 except ImportError:
54 54 publish_string = None
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Monkeypatch for Tornado <= 2.1.1 - Remove when no longer necessary!
58 58 #-----------------------------------------------------------------------------
59 59
60 60 # Google Chrome, as of release 16, changed its websocket protocol number. The
61 61 # parts tornado cares about haven't really changed, so it's OK to continue
62 62 # accepting Chrome connections, but as of Tornado 2.1.1 (the currently released
63 63 # version as of Oct 30/2011) the version check fails, see the issue report:
64 64
65 65 # https://github.com/facebook/tornado/issues/385
66 66
67 67 # This issue has been fixed in Tornado post 2.1.1:
68 68
69 69 # https://github.com/facebook/tornado/commit/84d7b458f956727c3b0d6710
70 70
71 71 # Here we manually apply the same patch as above so that users of IPython can
72 72 # continue to work with an officially released Tornado. We make the
73 73 # monkeypatch version check as narrow as possible to limit its effects; once
74 74 # Tornado 2.1.1 is no longer found in the wild we'll delete this code.
75 75
76 76 import tornado
77 77
78 78 if tornado.version_info <= (2,1,1):
79 79
80 80 def _execute(self, transforms, *args, **kwargs):
81 81 from tornado.websocket import WebSocketProtocol8, WebSocketProtocol76
82 82
83 83 self.open_args = args
84 84 self.open_kwargs = kwargs
85 85
86 86 # The difference between version 8 and 13 is that in 8 the
87 87 # client sends a "Sec-Websocket-Origin" header and in 13 it's
88 88 # simply "Origin".
89 89 if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8", "13"):
90 90 self.ws_connection = WebSocketProtocol8(self)
91 91 self.ws_connection.accept_connection()
92 92
93 93 elif self.request.headers.get("Sec-WebSocket-Version"):
94 94 self.stream.write(tornado.escape.utf8(
95 95 "HTTP/1.1 426 Upgrade Required\r\n"
96 96 "Sec-WebSocket-Version: 8\r\n\r\n"))
97 97 self.stream.close()
98 98
99 99 else:
100 100 self.ws_connection = WebSocketProtocol76(self)
101 101 self.ws_connection.accept_connection()
102 102
103 103 websocket.WebSocketHandler._execute = _execute
104 104 del _execute
105 105
106 106 #-----------------------------------------------------------------------------
107 107 # Decorator for disabling read-only handlers
108 108 #-----------------------------------------------------------------------------
109 109
110 110 @decorator
111 111 def not_if_readonly(f, self, *args, **kwargs):
112 112 if self.settings.get('read_only', False):
113 113 raise web.HTTPError(403, "Notebook server is read-only")
114 114 else:
115 115 return f(self, *args, **kwargs)
116 116
117 117 @decorator
118 118 def authenticate_unless_readonly(f, self, *args, **kwargs):
119 119 """authenticate this page *unless* readonly view is active.
120 120
121 121 In read-only mode, the notebook list and print view should
122 122 be accessible without authentication.
123 123 """
124 124
125 125 @web.authenticated
126 126 def auth_f(self, *args, **kwargs):
127 127 return f(self, *args, **kwargs)
128 128
129 129 if self.settings.get('read_only', False):
130 130 return f(self, *args, **kwargs)
131 131 else:
132 132 return auth_f(self, *args, **kwargs)
133 133
134 134 def urljoin(*pieces):
135 135 """Join components of url into a relative url
136 136
137 137 Use to prevent double slash when joining subpath
138 138 """
139 139 striped = [s.strip('/') for s in pieces]
140 140 return '/'.join(s for s in striped if s)
141 141
142 142 #-----------------------------------------------------------------------------
143 143 # Top-level handlers
144 144 #-----------------------------------------------------------------------------
145 145
146 146 class RequestHandler(web.RequestHandler):
147 147 """RequestHandler with default variable setting."""
148 148
149 149 def render(*args, **kwargs):
150 150 kwargs.setdefault('message', '')
151 151 return web.RequestHandler.render(*args, **kwargs)
152 152
153 153 class AuthenticatedHandler(RequestHandler):
154 154 """A RequestHandler with an authenticated user."""
155 155
156 156 def clear_login_cookie(self):
157 157 self.clear_cookie(self.cookie_name)
158 158
159 159 def get_current_user(self):
160 160 user_id = self.get_secure_cookie(self.cookie_name)
161 161 # For now the user_id should not return empty, but it could eventually
162 162 if user_id == '':
163 163 user_id = 'anonymous'
164 164 if user_id is None:
165 165 # prevent extra Invalid cookie sig warnings:
166 166 self.clear_login_cookie()
167 167 if not self.read_only and not self.login_available:
168 168 user_id = 'anonymous'
169 169 return user_id
170 170
171 171 @property
172 172 def cookie_name(self):
173 173 return self.settings.get('cookie_name', '')
174 174
175 175 @property
176 176 def password(self):
177 177 """our password"""
178 178 return self.settings.get('password', '')
179 179
180 180 @property
181 181 def logged_in(self):
182 182 """Is a user currently logged in?
183 183
184 184 """
185 185 user = self.get_current_user()
186 186 return (user and not user == 'anonymous')
187 187
188 188 @property
189 189 def login_available(self):
190 190 """May a user proceed to log in?
191 191
192 192 This returns True if login capability is available, irrespective of
193 193 whether the user is already logged in or not.
194 194
195 195 """
196 196 return bool(self.settings.get('password', ''))
197 197
198 198 @property
199 199 def read_only(self):
200 200 """Is the notebook read-only?
201 201
202 202 """
203 203 return self.settings.get('read_only', False)
204 204
205 205
206 206 class IPythonHandler(AuthenticatedHandler):
207 207 """IPython-specific extensions to authenticated handling
208 208
209 209 Mostly property shortcuts to IPython-specific settings.
210 210 """
211 211
212 212 @property
213 213 def config(self):
214 214 return self.settings.get('config', None)
215 215
216 216 @property
217 217 def log(self):
218 218 """use the IPython log by default, falling back on tornado's logger"""
219 219 if Application.initialized():
220 220 return Application.instance().log
221 221 else:
222 222 return app_log
223 223
224 224 @property
225 225 def use_less(self):
226 226 """Use less instead of css in templates"""
227 227 return self.settings.get('use_less', False)
228 228
229 229 #---------------------------------------------------------------
230 230 # URLs
231 231 #---------------------------------------------------------------
232 232
233 233 @property
234 234 def ws_url(self):
235 235 """websocket url matching the current request
236 236
237 237 turns http[s]://host[:port] into
238 238 ws[s]://host[:port]
239 239 """
240 240 proto = self.request.protocol.replace('http', 'ws')
241 241 host = self.settings.get('websocket_host', '')
242 242 # default to config value
243 243 if host == '':
244 244 host = self.request.host # get from request
245 245 return "%s://%s" % (proto, host)
246 246
247 247 @property
248 248 def mathjax_url(self):
249 249 return self.settings.get('mathjax_url', '')
250 250
251 251 @property
252 252 def base_project_url(self):
253 253 return self.settings.get('base_project_url', '/')
254 254
255 255 @property
256 256 def base_kernel_url(self):
257 257 return self.settings.get('base_kernel_url', '/')
258 258
259 259 #---------------------------------------------------------------
260 260 # Manager objects
261 261 #---------------------------------------------------------------
262 262
263 263 @property
264 264 def kernel_manager(self):
265 265 return self.settings['kernel_manager']
266 266
267 267 @property
268 268 def notebook_manager(self):
269 269 return self.settings['notebook_manager']
270 270
271 271 @property
272 272 def cluster_manager(self):
273 273 return self.settings['cluster_manager']
274 274
275 275 @property
276 276 def project(self):
277 277 return self.notebook_manager.notebook_dir
278 278
279 279 #---------------------------------------------------------------
280 280 # template rendering
281 281 #---------------------------------------------------------------
282 282
283 283 def get_template(self, name):
284 284 """Return the jinja template object for a given name"""
285 285 return self.settings['jinja2_env'].get_template(name)
286 286
287 287 def render_template(self, name, **ns):
288 288 ns.update(self.template_namespace)
289 289 template = self.get_template(name)
290 290 return template.render(**ns)
291 291
292 292 @property
293 293 def template_namespace(self):
294 294 return dict(
295 295 base_project_url=self.base_project_url,
296 296 base_kernel_url=self.base_kernel_url,
297 297 read_only=self.read_only,
298 298 logged_in=self.logged_in,
299 299 login_available=self.login_available,
300 300 use_less=self.use_less,
301 301 )
302 302
303 303 class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
304 304 """static files should only be accessible when logged in"""
305 305
306 306 @authenticate_unless_readonly
307 307 def get(self, path):
308 308 return web.StaticFileHandler.get(self, path)
309 309
310 310
311 311 class ProjectDashboardHandler(IPythonHandler):
312 312
313 313 @authenticate_unless_readonly
314 314 def get(self):
315 315 self.write(self.render_template('projectdashboard.html',
316 316 project=self.project,
317 317 project_component=self.project.split('/'),
318 318 ))
319 319
320 320
321 321 class LoginHandler(IPythonHandler):
322 322
323 323 def _render(self, message=None):
324 324 self.write(self.render_template('login.html',
325 325 next=url_escape(self.get_argument('next', default=self.base_project_url)),
326 326 message=message,
327 327 ))
328 328
329 329 def get(self):
330 330 if self.current_user:
331 331 self.redirect(self.get_argument('next', default=self.base_project_url))
332 332 else:
333 333 self._render()
334 334
335 335 def post(self):
336 336 pwd = self.get_argument('password', default=u'')
337 337 if self.login_available:
338 338 if passwd_check(self.password, pwd):
339 339 self.set_secure_cookie(self.cookie_name, str(uuid.uuid4()))
340 340 else:
341 341 self._render(message={'error': 'Invalid password'})
342 342 return
343 343
344 344 self.redirect(self.get_argument('next', default=self.base_project_url))
345 345
346 346
347 347 class LogoutHandler(IPythonHandler):
348 348
349 349 def get(self):
350 350 self.clear_login_cookie()
351 351 if self.login_available:
352 352 message = {'info': 'Successfully logged out.'}
353 353 else:
354 354 message = {'warning': 'Cannot log out. Notebook authentication '
355 355 'is disabled.'}
356 356 self.write(self.render_template('logout.html',
357 357 message=message))
358 358
359 359
360 360 class NewHandler(IPythonHandler):
361 361
362 362 @web.authenticated
363 363 def get(self):
364 364 notebook_id = self.notebook_manager.new_notebook()
365 365 self.redirect('/' + urljoin(self.base_project_url, notebook_id))
366 366
367 367 class NamedNotebookHandler(IPythonHandler):
368 368
369 369 @authenticate_unless_readonly
370 370 def get(self, notebook_id):
371 371 nbm = self.notebook_manager
372 372 if not nbm.notebook_exists(notebook_id):
373 373 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
374 374 self.write(self.render_template('notebook.html',
375 375 project=self.project,
376 376 notebook_id=notebook_id,
377 377 kill_kernel=False,
378 378 mathjax_url=self.mathjax_url,
379 379 )
380 380 )
381 381
382 382
383 383 class PrintNotebookHandler(IPythonHandler):
384 384
385 385 @authenticate_unless_readonly
386 386 def get(self, notebook_id):
387 387 if not self.notebook_manager.notebook_exists(notebook_id):
388 388 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
389 389 self.write( self.render_template('printnotebook.html',
390 390 project=self.project,
391 391 notebook_id=notebook_id,
392 392 kill_kernel=False,
393 393 mathjax_url=self.mathjax_url,
394 394 ))
395 395
396 396 #-----------------------------------------------------------------------------
397 397 # Kernel handlers
398 398 #-----------------------------------------------------------------------------
399 399
400 400
401 401 class MainKernelHandler(IPythonHandler):
402 402
403 403 @web.authenticated
404 404 def get(self):
405 405 km = self.kernel_manager
406 406 self.finish(jsonapi.dumps(km.list_kernel_ids()))
407 407
408 408 @web.authenticated
409 409 def post(self):
410 410 km = self.kernel_manager
411 411 nbm = self.notebook_manager
412 412 notebook_id = self.get_argument('notebook', default=None)
413 413 kernel_id = km.start_kernel(notebook_id, cwd=nbm.notebook_dir)
414 414 data = {'ws_url':self.ws_url,'kernel_id':kernel_id}
415 415 self.set_header('Location', '/'+kernel_id)
416 416 self.finish(jsonapi.dumps(data))
417 417
418 418
419 419 class KernelHandler(IPythonHandler):
420 420
421 421 SUPPORTED_METHODS = ('DELETE')
422 422
423 423 @web.authenticated
424 424 def delete(self, kernel_id):
425 425 km = self.kernel_manager
426 426 km.shutdown_kernel(kernel_id)
427 427 self.set_status(204)
428 428 self.finish()
429 429
430 430
431 431 class KernelActionHandler(IPythonHandler):
432 432
433 433 @web.authenticated
434 434 def post(self, kernel_id, action):
435 435 km = self.kernel_manager
436 436 if action == 'interrupt':
437 437 km.interrupt_kernel(kernel_id)
438 438 self.set_status(204)
439 439 if action == 'restart':
440 440 km.restart_kernel(kernel_id)
441 441 data = {'ws_url':self.ws_url, 'kernel_id':kernel_id}
442 442 self.set_header('Location', '/'+kernel_id)
443 443 self.write(jsonapi.dumps(data))
444 444 self.finish()
445 445
446 446
447 447 class ZMQStreamHandler(websocket.WebSocketHandler):
448 448
449 449 def clear_cookie(self, *args, **kwargs):
450 450 """meaningless for websockets"""
451 451 pass
452 452
453 453 def _reserialize_reply(self, msg_list):
454 454 """Reserialize a reply message using JSON.
455 455
456 456 This takes the msg list from the ZMQ socket, unserializes it using
457 457 self.session and then serializes the result using JSON. This method
458 458 should be used by self._on_zmq_reply to build messages that can
459 459 be sent back to the browser.
460 460 """
461 461 idents, msg_list = self.session.feed_identities(msg_list)
462 462 msg = self.session.unserialize(msg_list)
463 463 try:
464 464 msg['header'].pop('date')
465 465 except KeyError:
466 466 pass
467 467 try:
468 468 msg['parent_header'].pop('date')
469 469 except KeyError:
470 470 pass
471 471 msg.pop('buffers')
472 472 return jsonapi.dumps(msg, default=date_default)
473 473
474 474 def _on_zmq_reply(self, msg_list):
475 475 # Sometimes this gets triggered when the on_close method is scheduled in the
476 476 # eventloop but hasn't been called.
477 477 if self.stream.closed(): return
478 478 try:
479 479 msg = self._reserialize_reply(msg_list)
480 480 except Exception:
481 481 self.log.critical("Malformed message: %r" % msg_list, exc_info=True)
482 482 else:
483 483 self.write_message(msg)
484 484
485 485 def allow_draft76(self):
486 486 """Allow draft 76, until browsers such as Safari update to RFC 6455.
487 487
488 488 This has been disabled by default in tornado in release 2.2.0, and
489 489 support will be removed in later versions.
490 490 """
491 491 return True
492 492
493 493
494 494 class AuthenticatedZMQStreamHandler(ZMQStreamHandler, IPythonHandler):
495 495
496 496 def open(self, kernel_id):
497 497 self.kernel_id = kernel_id.decode('ascii')
498 498 self.session = Session(config=self.config)
499 499 self.save_on_message = self.on_message
500 500 self.on_message = self.on_first_message
501 501
502 502 def _inject_cookie_message(self, msg):
503 503 """Inject the first message, which is the document cookie,
504 504 for authentication."""
505 505 if not PY3 and isinstance(msg, unicode):
506 506 # Cookie constructor doesn't accept unicode strings
507 507 # under Python 2.x for some reason
508 508 msg = msg.encode('utf8', 'replace')
509 509 try:
510 identity, msg = msg.split(':', 1)
511 self.session.session = identity.decode('ascii')
512 except Exception:
513 logging.error("First ws message didn't have the form 'identity:[cookie]' - %r", msg)
514
515 try:
510 516 self.request._cookies = Cookie.SimpleCookie(msg)
511 517 except:
512 518 self.log.warn("couldn't parse cookie string: %s",msg, exc_info=True)
513 519
514 520 def on_first_message(self, msg):
515 521 self._inject_cookie_message(msg)
516 522 if self.get_current_user() is None:
517 523 self.log.warn("Couldn't authenticate WebSocket connection")
518 524 raise web.HTTPError(403)
519 525 self.on_message = self.save_on_message
520 526
521 527
522 class IOPubHandler(AuthenticatedZMQStreamHandler):
523
528 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
529
530 @property
531 def max_msg_size(self):
532 return self.settings.get('max_msg_size', 65535)
533
534 def create_stream(self):
535 km = self.kernel_manager
536 meth = getattr(km, 'connect_%s' % self.channel)
537 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
538
524 539 def initialize(self, *args, **kwargs):
525 self.iopub_stream = None
526
540 self.zmq_stream = None
541
527 542 def on_first_message(self, msg):
528 543 try:
529 super(IOPubHandler, self).on_first_message(msg)
544 super(ZMQChannelHandler, self).on_first_message(msg)
530 545 except web.HTTPError:
531 546 self.close()
532 547 return
533 km = self.kernel_manager
534 kernel_id = self.kernel_id
535 km.add_restart_callback(kernel_id, self.on_kernel_restarted)
536 km.add_restart_callback(kernel_id, self.on_restart_failed, 'dead')
537 548 try:
538 self.iopub_stream = km.connect_iopub(kernel_id)
549 self.create_stream()
539 550 except web.HTTPError:
540 551 # WebSockets don't response to traditional error codes so we
541 552 # close the connection.
542 553 if not self.stream.closed():
543 554 self.stream.close()
544 555 self.close()
545 556 else:
546 self.iopub_stream.on_recv(self._on_zmq_reply)
557 self.zmq_stream.on_recv(self._on_zmq_reply)
547 558
548 559 def on_message(self, msg):
549 pass
550
551 def _send_status_message(self, status):
552 msg = self.session.msg("status",
553 {'execution_state': status}
554 )
555 self.write_message(jsonapi.dumps(msg, default=date_default))
556
557 def on_kernel_restarted(self):
558 self.log.warn("kernel %s restarted", self.kernel_id)
559 self._send_status_message('restarting')
560
561 def on_restart_failed(self):
562 self.log.error("kernel %s restarted failed!", self.kernel_id)
563 self._send_status_message('dead')
560 if len(msg) < self.max_msg_size:
561 msg = jsonapi.loads(msg)
562 self.session.send(self.zmq_stream, msg)
564 563
565 564 def on_close(self):
566 565 # This method can be called twice, once by self.kernel_died and once
567 566 # from the WebSocket close event. If the WebSocket connection is
568 567 # closed before the ZMQ streams are setup, they could be None.
568 if self.zmq_stream is not None and not self.zmq_stream.closed():
569 self.zmq_stream.on_recv(None)
570 self.zmq_stream.close()
571
572
573 class IOPubHandler(ZMQChannelHandler):
574 channel = 'iopub'
575
576 def create_stream(self):
577 super(IOPubHandler, self).create_stream()
578 km = self.kernel_manager
579 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
580 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
581
582 def on_close(self):
569 583 km = self.kernel_manager
570 584 if self.kernel_id in km:
571 585 km.remove_restart_callback(
572 586 self.kernel_id, self.on_kernel_restarted,
573 587 )
574 588 km.remove_restart_callback(
575 589 self.kernel_id, self.on_restart_failed, 'dead',
576 590 )
577 if self.iopub_stream is not None and not self.iopub_stream.closed():
578 self.iopub_stream.on_recv(None)
579 self.iopub_stream.close()
580
581
582 class ShellHandler(AuthenticatedZMQStreamHandler):
591 super(IOPubHandler, self).on_close()
583 592
584 @property
585 def max_msg_size(self):
586 return self.settings.get('max_msg_size', 65535)
587
588 def initialize(self, *args, **kwargs):
589 self.shell_stream = None
593 def _send_status_message(self, status):
594 msg = self.session.msg("status",
595 {'execution_state': status}
596 )
597 self.write_message(jsonapi.dumps(msg, default=date_default))
590 598
591 def on_first_message(self, msg):
592 try:
593 super(ShellHandler, self).on_first_message(msg)
594 except web.HTTPError:
595 self.close()
596 return
597 km = self.kernel_manager
598 kernel_id = self.kernel_id
599 try:
600 self.shell_stream = km.connect_shell(kernel_id)
601 except web.HTTPError:
602 # WebSockets don't response to traditional error codes so we
603 # close the connection.
604 if not self.stream.closed():
605 self.stream.close()
606 self.close()
607 else:
608 self.shell_stream.on_recv(self._on_zmq_reply)
599 def on_kernel_restarted(self):
600 logging.warn("kernel %s restarted", self.kernel_id)
601 self._send_status_message('restarting')
609 602
603 def on_restart_failed(self):
604 logging.error("kernel %s restarted failed!", self.kernel_id)
605 self._send_status_message('dead')
606
610 607 def on_message(self, msg):
611 if len(msg) < self.max_msg_size:
612 msg = jsonapi.loads(msg)
613 self.session.send(self.shell_stream, msg)
608 """IOPub messages make no sense"""
609 pass
614 610
615 def on_close(self):
616 # Make sure the stream exists and is not already closed.
617 if self.shell_stream is not None and not self.shell_stream.closed():
618 self.shell_stream.close()
611 class ShellHandler(ZMQChannelHandler):
612 channel = 'shell'
613
614 class StdinHandler(ZMQChannelHandler):
615 channel = 'stdin'
619 616
620 617
621 618 #-----------------------------------------------------------------------------
622 619 # Notebook web service handlers
623 620 #-----------------------------------------------------------------------------
624 621
625 622 class NotebookRedirectHandler(IPythonHandler):
626 623
627 624 @authenticate_unless_readonly
628 625 def get(self, notebook_name):
629 626 # strip trailing .ipynb:
630 627 notebook_name = os.path.splitext(notebook_name)[0]
631 628 notebook_id = self.notebook_manager.rev_mapping.get(notebook_name, '')
632 629 if notebook_id:
633 630 url = self.settings.get('base_project_url', '/') + notebook_id
634 631 return self.redirect(url)
635 632 else:
636 633 raise HTTPError(404)
637 634
638 635
639 636 class NotebookRootHandler(IPythonHandler):
640 637
641 638 @authenticate_unless_readonly
642 639 def get(self):
643 640 nbm = self.notebook_manager
644 641 km = self.kernel_manager
645 642 files = nbm.list_notebooks()
646 643 for f in files :
647 644 f['kernel_id'] = km.kernel_for_notebook(f['notebook_id'])
648 645 self.finish(jsonapi.dumps(files))
649 646
650 647 @web.authenticated
651 648 def post(self):
652 649 nbm = self.notebook_manager
653 650 body = self.request.body.strip()
654 651 format = self.get_argument('format', default='json')
655 652 name = self.get_argument('name', default=None)
656 653 if body:
657 654 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
658 655 else:
659 656 notebook_id = nbm.new_notebook()
660 657 self.set_header('Location', '/'+notebook_id)
661 658 self.finish(jsonapi.dumps(notebook_id))
662 659
663 660
664 661 class NotebookHandler(IPythonHandler):
665 662
666 663 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
667 664
668 665 @authenticate_unless_readonly
669 666 def get(self, notebook_id):
670 667 nbm = self.notebook_manager
671 668 format = self.get_argument('format', default='json')
672 669 last_mod, name, data = nbm.get_notebook(notebook_id, format)
673 670
674 671 if format == u'json':
675 672 self.set_header('Content-Type', 'application/json')
676 673 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
677 674 elif format == u'py':
678 675 self.set_header('Content-Type', 'application/x-python')
679 676 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
680 677 self.set_header('Last-Modified', last_mod)
681 678 self.finish(data)
682 679
683 680 @web.authenticated
684 681 def put(self, notebook_id):
685 682 nbm = self.notebook_manager
686 683 format = self.get_argument('format', default='json')
687 684 name = self.get_argument('name', default=None)
688 685 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
689 686 self.set_status(204)
690 687 self.finish()
691 688
692 689 @web.authenticated
693 690 def delete(self, notebook_id):
694 691 self.notebook_manager.delete_notebook(notebook_id)
695 692 self.set_status(204)
696 693 self.finish()
697 694
698 695
699 696 class NotebookCopyHandler(IPythonHandler):
700 697
701 698 @web.authenticated
702 699 def get(self, notebook_id):
703 700 notebook_id = self.notebook_manager.copy_notebook(notebook_id)
704 701 self.redirect('/'+urljoin(self.base_project_url, notebook_id))
705 702
706 703
707 704 #-----------------------------------------------------------------------------
708 705 # Cluster handlers
709 706 #-----------------------------------------------------------------------------
710 707
711 708
712 709 class MainClusterHandler(IPythonHandler):
713 710
714 711 @web.authenticated
715 712 def get(self):
716 713 self.finish(jsonapi.dumps(self.cluster_manager.list_profiles()))
717 714
718 715
719 716 class ClusterProfileHandler(IPythonHandler):
720 717
721 718 @web.authenticated
722 719 def get(self, profile):
723 720 self.finish(jsonapi.dumps(self.cluster_manager.profile_info(profile)))
724 721
725 722
726 723 class ClusterActionHandler(IPythonHandler):
727 724
728 725 @web.authenticated
729 726 def post(self, profile, action):
730 727 cm = self.cluster_manager
731 728 if action == 'start':
732 729 n = self.get_argument('n',default=None)
733 730 if n is None:
734 731 data = cm.start_cluster(profile)
735 732 else:
736 733 data = cm.start_cluster(profile, int(n))
737 734 if action == 'stop':
738 735 data = cm.stop_cluster(profile)
739 736 self.finish(jsonapi.dumps(data))
740 737
741 738
742 739 #-----------------------------------------------------------------------------
743 740 # RST web service handlers
744 741 #-----------------------------------------------------------------------------
745 742
746 743
747 744 class RSTHandler(IPythonHandler):
748 745
749 746 @web.authenticated
750 747 def post(self):
751 748 if publish_string is None:
752 749 raise web.HTTPError(503, u'docutils not available')
753 750 body = self.request.body.strip()
754 751 source = body
755 752 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
756 753 defaults = {'file_insertion_enabled': 0,
757 754 'raw_enabled': 0,
758 755 '_disable_config': 1,
759 756 'stylesheet_path': 0
760 757 # 'template': template_path
761 758 }
762 759 try:
763 760 html = publish_string(source, writer_name='html',
764 761 settings_overrides=defaults
765 762 )
766 763 except:
767 764 raise web.HTTPError(400, u'Invalid RST')
768 765 print html
769 766 self.set_header('Content-Type', 'text/html')
770 767 self.finish(html)
771 768
772 769 # to minimize subclass changes:
773 770 HTTPError = web.HTTPError
774 771
775 772 class FileFindHandler(web.StaticFileHandler):
776 773 """subclass of StaticFileHandler for serving files from a search path"""
777 774
778 775 _static_paths = {}
779 776 # _lock is needed for tornado < 2.2.0 compat
780 777 _lock = threading.Lock() # protects _static_hashes
781 778
782 779 def initialize(self, path, default_filename=None):
783 780 if isinstance(path, basestring):
784 781 path = [path]
785 782 self.roots = tuple(
786 783 os.path.abspath(os.path.expanduser(p)) + os.path.sep for p in path
787 784 )
788 785 self.default_filename = default_filename
789 786
790 787 @classmethod
791 788 def locate_file(cls, path, roots):
792 789 """locate a file to serve on our static file search path"""
793 790 with cls._lock:
794 791 if path in cls._static_paths:
795 792 return cls._static_paths[path]
796 793 try:
797 794 abspath = os.path.abspath(filefind(path, roots))
798 795 except IOError:
799 796 # empty string should always give exists=False
800 797 return ''
801 798
802 799 # os.path.abspath strips a trailing /
803 800 # it needs to be temporarily added back for requests to root/
804 801 if not (abspath + os.path.sep).startswith(roots):
805 802 raise HTTPError(403, "%s is not in root static directory", path)
806 803
807 804 cls._static_paths[path] = abspath
808 805 return abspath
809 806
810 807 def get(self, path, include_body=True):
811 808 path = self.parse_url_path(path)
812 809
813 810 # begin subclass override
814 811 abspath = self.locate_file(path, self.roots)
815 812 # end subclass override
816 813
817 814 if os.path.isdir(abspath) and self.default_filename is not None:
818 815 # need to look at the request.path here for when path is empty
819 816 # but there is some prefix to the path that was already
820 817 # trimmed by the routing
821 818 if not self.request.path.endswith("/"):
822 819 self.redirect(self.request.path + "/")
823 820 return
824 821 abspath = os.path.join(abspath, self.default_filename)
825 822 if not os.path.exists(abspath):
826 823 raise HTTPError(404)
827 824 if not os.path.isfile(abspath):
828 825 raise HTTPError(403, "%s is not a file", path)
829 826
830 827 stat_result = os.stat(abspath)
831 828 modified = datetime.datetime.utcfromtimestamp(stat_result[stat.ST_MTIME])
832 829
833 830 self.set_header("Last-Modified", modified)
834 831
835 832 mime_type, encoding = mimetypes.guess_type(abspath)
836 833 if mime_type:
837 834 self.set_header("Content-Type", mime_type)
838 835
839 836 cache_time = self.get_cache_time(path, modified, mime_type)
840 837
841 838 if cache_time > 0:
842 839 self.set_header("Expires", datetime.datetime.utcnow() + \
843 840 datetime.timedelta(seconds=cache_time))
844 841 self.set_header("Cache-Control", "max-age=" + str(cache_time))
845 842 else:
846 843 self.set_header("Cache-Control", "public")
847 844
848 845 self.set_extra_headers(path)
849 846
850 847 # Check the If-Modified-Since, and don't send the result if the
851 848 # content has not been modified
852 849 ims_value = self.request.headers.get("If-Modified-Since")
853 850 if ims_value is not None:
854 851 date_tuple = email.utils.parsedate(ims_value)
855 852 if_since = datetime.datetime(*date_tuple[:6])
856 853 if if_since >= modified:
857 854 self.set_status(304)
858 855 return
859 856
860 857 with open(abspath, "rb") as file:
861 858 data = file.read()
862 859 hasher = hashlib.sha1()
863 860 hasher.update(data)
864 861 self.set_header("Etag", '"%s"' % hasher.hexdigest())
865 862 if include_body:
866 863 self.write(data)
867 864 else:
868 865 assert self.request.method == "HEAD"
869 866 self.set_header("Content-Length", len(data))
870 867
871 868 @classmethod
872 869 def get_version(cls, settings, path):
873 870 """Generate the version string to be used in static URLs.
874 871
875 872 This method may be overridden in subclasses (but note that it
876 873 is a class method rather than a static method). The default
877 874 implementation uses a hash of the file's contents.
878 875
879 876 ``settings`` is the `Application.settings` dictionary and ``path``
880 877 is the relative location of the requested asset on the filesystem.
881 878 The returned value should be a string, or ``None`` if no version
882 879 could be determined.
883 880 """
884 881 # begin subclass override:
885 882 static_paths = settings['static_path']
886 883 if isinstance(static_paths, basestring):
887 884 static_paths = [static_paths]
888 885 roots = tuple(
889 886 os.path.abspath(os.path.expanduser(p)) + os.path.sep for p in static_paths
890 887 )
891 888
892 889 try:
893 890 abs_path = filefind(path, roots)
894 891 except IOError:
895 892 app_log.error("Could not find static file %r", path)
896 893 return None
897 894
898 895 # end subclass override
899 896
900 897 with cls._lock:
901 898 hashes = cls._static_hashes
902 899 if abs_path not in hashes:
903 900 try:
904 901 f = open(abs_path, "rb")
905 902 hashes[abs_path] = hashlib.md5(f.read()).hexdigest()
906 903 f.close()
907 904 except Exception:
908 905 app_log.error("Could not open static file %r", path)
909 906 hashes[abs_path] = None
910 907 hsh = hashes.get(abs_path)
911 908 if hsh:
912 909 return hsh[:5]
913 910 return None
914 911
915 912
916 913 def parse_url_path(self, url_path):
917 914 """Converts a static URL path into a filesystem path.
918 915
919 916 ``url_path`` is the path component of the URL with
920 917 ``static_url_prefix`` removed. The return value should be
921 918 filesystem path relative to ``static_path``.
922 919 """
923 920 if os.path.sep != "/":
924 921 url_path = url_path.replace("/", os.path.sep)
925 922 return url_path
926 923
927 924
@@ -1,740 +1,741 b''
1 1 # coding: utf-8
2 2 """A tornado based IPython notebook server.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 # stdlib
20 20 import errno
21 21 import logging
22 22 import os
23 23 import random
24 24 import re
25 25 import select
26 26 import signal
27 27 import socket
28 28 import sys
29 29 import threading
30 30 import time
31 31 import uuid
32 32 import webbrowser
33 33
34 34
35 35 # Third party
36 36 # check for pyzmq 2.1.11
37 37 from IPython.utils.zmqrelated import check_for_zmq
38 38 check_for_zmq('2.1.11', 'IPython.frontend.html.notebook')
39 39
40 40 import zmq
41 41 from jinja2 import Environment, FileSystemLoader
42 42
43 43 # Install the pyzmq ioloop. This has to be done before anything else from
44 44 # tornado is imported.
45 45 from zmq.eventloop import ioloop
46 46 ioloop.install()
47 47
48 48 # check for tornado 2.1.0
49 49 msg = "The IPython Notebook requires tornado >= 2.1.0"
50 50 try:
51 51 import tornado
52 52 except ImportError:
53 53 raise ImportError(msg)
54 54 try:
55 55 version_info = tornado.version_info
56 56 except AttributeError:
57 57 raise ImportError(msg + ", but you have < 1.1.0")
58 58 if version_info < (2,1,0):
59 59 raise ImportError(msg + ", but you have %s" % tornado.version)
60 60
61 61 from tornado import httpserver
62 62 from tornado import web
63 63
64 64 # Our own libraries
65 65 from IPython.frontend.html.notebook import DEFAULT_STATIC_FILES_PATH
66 66 from .kernelmanager import MappingKernelManager
67 67 from .handlers import (LoginHandler, LogoutHandler,
68 68 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
69 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
69 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler, StdinHandler,
70 70 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
71 71 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
72 72 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
73 73 FileFindHandler, NotebookRedirectHandler,
74 74 )
75 75 from .nbmanager import NotebookManager
76 76 from .filenbmanager import FileNotebookManager
77 77 from .clustermanager import ClusterManager
78 78
79 79 from IPython.config.application import catch_config_error, boolean_flag
80 80 from IPython.core.application import BaseIPythonApplication
81 81 from IPython.core.profiledir import ProfileDir
82 82 from IPython.frontend.consoleapp import IPythonConsoleApp
83 83 from IPython.kernel import swallow_argv
84 84 from IPython.kernel.zmq.session import Session, default_secure
85 85 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
86 86 from IPython.kernel.zmq.kernelapp import (
87 87 kernel_flags,
88 88 kernel_aliases,
89 89 IPKernelApp
90 90 )
91 91 from IPython.utils.importstring import import_item
92 92 from IPython.utils.localinterfaces import LOCALHOST
93 93 from IPython.utils.traitlets import (
94 94 Dict, Unicode, Integer, List, Enum, Bool,
95 95 DottedObjectName
96 96 )
97 97 from IPython.utils import py3compat
98 98 from IPython.utils.path import filefind
99 99
100 100 #-----------------------------------------------------------------------------
101 101 # Module globals
102 102 #-----------------------------------------------------------------------------
103 103
104 104 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
105 105 _kernel_action_regex = r"(?P<action>restart|interrupt)"
106 106 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
107 107 _notebook_name_regex = r"(?P<notebook_name>.+\.ipynb)"
108 108 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
109 109 _cluster_action_regex = r"(?P<action>start|stop)"
110 110
111 111 _examples = """
112 112 ipython notebook # start the notebook
113 113 ipython notebook --profile=sympy # use the sympy profile
114 114 ipython notebook --pylab=inline # pylab in inline plotting mode
115 115 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
116 116 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
117 117 """
118 118
119 119 #-----------------------------------------------------------------------------
120 120 # Helper functions
121 121 #-----------------------------------------------------------------------------
122 122
123 123 def url_path_join(a,b):
124 124 if a.endswith('/') and b.startswith('/'):
125 125 return a[:-1]+b
126 126 else:
127 127 return a+b
128 128
129 129 def random_ports(port, n):
130 130 """Generate a list of n random ports near the given port.
131 131
132 132 The first 5 ports will be sequential, and the remaining n-5 will be
133 133 randomly selected in the range [port-2*n, port+2*n].
134 134 """
135 135 for i in range(min(5, n)):
136 136 yield port + i
137 137 for i in range(n-5):
138 138 yield port + random.randint(-2*n, 2*n)
139 139
140 140 #-----------------------------------------------------------------------------
141 141 # The Tornado web application
142 142 #-----------------------------------------------------------------------------
143 143
144 144 class NotebookWebApplication(web.Application):
145 145
146 146 def __init__(self, ipython_app, kernel_manager, notebook_manager,
147 147 cluster_manager, log,
148 148 base_project_url, settings_overrides):
149 149 handlers = [
150 150 (r"/", ProjectDashboardHandler),
151 151 (r"/login", LoginHandler),
152 152 (r"/logout", LogoutHandler),
153 153 (r"/new", NewHandler),
154 154 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
155 155 (r"/%s" % _notebook_name_regex, NotebookRedirectHandler),
156 156 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
157 157 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
158 158 (r"/kernels", MainKernelHandler),
159 159 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
160 160 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
161 161 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
162 162 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
163 (r"/kernels/%s/stdin" % _kernel_id_regex, StdinHandler),
163 164 (r"/notebooks", NotebookRootHandler),
164 165 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
165 166 (r"/rstservice/render", RSTHandler),
166 167 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
167 168 (r"/clusters", MainClusterHandler),
168 169 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
169 170 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
170 171 ]
171 172
172 173 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
173 174 # base_project_url will always be unicode, which will in turn
174 175 # make the patterns unicode, and ultimately result in unicode
175 176 # keys in kwargs to handler._execute(**kwargs) in tornado.
176 177 # This enforces that base_project_url be ascii in that situation.
177 178 #
178 179 # Note that the URLs these patterns check against are escaped,
179 180 # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'.
180 181 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
181 182 template_path = os.path.join(os.path.dirname(__file__), "templates")
182 183 settings = dict(
183 184 # basics
184 185 base_project_url=base_project_url,
185 186 base_kernel_url=ipython_app.base_kernel_url,
186 187 template_path=template_path,
187 188 static_path=ipython_app.static_file_path,
188 189 static_handler_class = FileFindHandler,
189 190 static_url_prefix = url_path_join(base_project_url,'/static/'),
190 191
191 192 # authentication
192 193 cookie_secret=os.urandom(1024),
193 194 login_url=url_path_join(base_project_url,'/login'),
194 195 cookie_name='username-%s' % uuid.uuid4(),
195 196 read_only=ipython_app.read_only,
196 197 password=ipython_app.password,
197 198
198 199 # managers
199 200 kernel_manager=kernel_manager,
200 201 notebook_manager=notebook_manager,
201 202 cluster_manager=cluster_manager,
202 203
203 204 # IPython stuff
204 205 max_msg_size=ipython_app.max_msg_size,
205 206 config=ipython_app.config,
206 207 use_less=ipython_app.use_less,
207 208 jinja2_env=Environment(loader=FileSystemLoader(template_path)),
208 209 )
209 210
210 211 # allow custom overrides for the tornado web app.
211 212 settings.update(settings_overrides)
212 213
213 214 # prepend base_project_url onto the patterns that we match
214 215 new_handlers = []
215 216 for handler in handlers:
216 217 pattern = url_path_join(base_project_url, handler[0])
217 218 new_handler = tuple([pattern] + list(handler[1:]))
218 219 new_handlers.append(new_handler)
219 220
220 221 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
221 222
222 223
223 224
224 225 #-----------------------------------------------------------------------------
225 226 # Aliases and Flags
226 227 #-----------------------------------------------------------------------------
227 228
228 229 flags = dict(kernel_flags)
229 230 flags['no-browser']=(
230 231 {'NotebookApp' : {'open_browser' : False}},
231 232 "Don't open the notebook in a browser after startup."
232 233 )
233 234 flags['no-mathjax']=(
234 235 {'NotebookApp' : {'enable_mathjax' : False}},
235 236 """Disable MathJax
236 237
237 238 MathJax is the javascript library IPython uses to render math/LaTeX. It is
238 239 very large, so you may want to disable it if you have a slow internet
239 240 connection, or for offline use of the notebook.
240 241
241 242 When disabled, equations etc. will appear as their untransformed TeX source.
242 243 """
243 244 )
244 245 flags['read-only'] = (
245 246 {'NotebookApp' : {'read_only' : True}},
246 247 """Allow read-only access to notebooks.
247 248
248 249 When using a password to protect the notebook server, this flag
249 250 allows unauthenticated clients to view the notebook list, and
250 251 individual notebooks, but not edit them, start kernels, or run
251 252 code.
252 253
253 254 If no password is set, the server will be entirely read-only.
254 255 """
255 256 )
256 257
257 258 # Add notebook manager flags
258 259 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
259 260 'Auto-save a .py script everytime the .ipynb notebook is saved',
260 261 'Do not auto-save .py scripts for every notebook'))
261 262
262 263 # the flags that are specific to the frontend
263 264 # these must be scrubbed before being passed to the kernel,
264 265 # or it will raise an error on unrecognized flags
265 266 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
266 267
267 268 aliases = dict(kernel_aliases)
268 269
269 270 aliases.update({
270 271 'ip': 'NotebookApp.ip',
271 272 'port': 'NotebookApp.port',
272 273 'port-retries': 'NotebookApp.port_retries',
273 274 'transport': 'KernelManager.transport',
274 275 'keyfile': 'NotebookApp.keyfile',
275 276 'certfile': 'NotebookApp.certfile',
276 277 'notebook-dir': 'NotebookManager.notebook_dir',
277 278 'browser': 'NotebookApp.browser',
278 279 })
279 280
280 281 # remove ipkernel flags that are singletons, and don't make sense in
281 282 # multi-kernel evironment:
282 283 aliases.pop('f', None)
283 284
284 285 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
285 286 u'notebook-dir']
286 287
287 288 #-----------------------------------------------------------------------------
288 289 # NotebookApp
289 290 #-----------------------------------------------------------------------------
290 291
291 292 class NotebookApp(BaseIPythonApplication):
292 293
293 294 name = 'ipython-notebook'
294 295 default_config_file_name='ipython_notebook_config.py'
295 296
296 297 description = """
297 298 The IPython HTML Notebook.
298 299
299 300 This launches a Tornado based HTML Notebook Server that serves up an
300 301 HTML5/Javascript Notebook client.
301 302 """
302 303 examples = _examples
303 304
304 305 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
305 306 FileNotebookManager]
306 307 flags = Dict(flags)
307 308 aliases = Dict(aliases)
308 309
309 310 kernel_argv = List(Unicode)
310 311
311 312 max_msg_size = Integer(65536, config=True, help="""
312 313 The max raw message size accepted from the browser
313 314 over a WebSocket connection.
314 315 """)
315 316
316 317 def _log_level_default(self):
317 318 return logging.INFO
318 319
319 320 def _log_format_default(self):
320 321 """override default log format to include time"""
321 322 return u"%(asctime)s.%(msecs).03d [%(name)s] %(message)s"
322 323
323 324 # create requested profiles by default, if they don't exist:
324 325 auto_create = Bool(True)
325 326
326 327 # file to be opened in the notebook server
327 328 file_to_run = Unicode('')
328 329
329 330 # Network related information.
330 331
331 332 ip = Unicode(LOCALHOST, config=True,
332 333 help="The IP address the notebook server will listen on."
333 334 )
334 335
335 336 def _ip_changed(self, name, old, new):
336 337 if new == u'*': self.ip = u''
337 338
338 339 port = Integer(8888, config=True,
339 340 help="The port the notebook server will listen on."
340 341 )
341 342 port_retries = Integer(50, config=True,
342 343 help="The number of additional ports to try if the specified port is not available."
343 344 )
344 345
345 346 certfile = Unicode(u'', config=True,
346 347 help="""The full path to an SSL/TLS certificate file."""
347 348 )
348 349
349 350 keyfile = Unicode(u'', config=True,
350 351 help="""The full path to a private key file for usage with SSL/TLS."""
351 352 )
352 353
353 354 password = Unicode(u'', config=True,
354 355 help="""Hashed password to use for web authentication.
355 356
356 357 To generate, type in a python/IPython shell:
357 358
358 359 from IPython.lib import passwd; passwd()
359 360
360 361 The string should be of the form type:salt:hashed-password.
361 362 """
362 363 )
363 364
364 365 open_browser = Bool(True, config=True,
365 366 help="""Whether to open in a browser after starting.
366 367 The specific browser used is platform dependent and
367 368 determined by the python standard library `webbrowser`
368 369 module, unless it is overridden using the --browser
369 370 (NotebookApp.browser) configuration option.
370 371 """)
371 372
372 373 browser = Unicode(u'', config=True,
373 374 help="""Specify what command to use to invoke a web
374 375 browser when opening the notebook. If not specified, the
375 376 default browser will be determined by the `webbrowser`
376 377 standard library module, which allows setting of the
377 378 BROWSER environment variable to override it.
378 379 """)
379 380
380 381 read_only = Bool(False, config=True,
381 382 help="Whether to prevent editing/execution of notebooks."
382 383 )
383 384
384 385 use_less = Bool(False, config=True,
385 386 help="""Wether to use Browser Side less-css parsing
386 387 instead of compiled css version in templates that allows
387 388 it. This is mainly convenient when working on the less
388 389 file to avoid a build step, or if user want to overwrite
389 390 some of the less variables without having to recompile
390 391 everything.
391 392
392 393 You will need to install the less.js component in the static directory
393 394 either in the source tree or in your profile folder.
394 395 """)
395 396
396 397 webapp_settings = Dict(config=True,
397 398 help="Supply overrides for the tornado.web.Application that the "
398 399 "IPython notebook uses.")
399 400
400 401 enable_mathjax = Bool(True, config=True,
401 402 help="""Whether to enable MathJax for typesetting math/TeX
402 403
403 404 MathJax is the javascript library IPython uses to render math/LaTeX. It is
404 405 very large, so you may want to disable it if you have a slow internet
405 406 connection, or for offline use of the notebook.
406 407
407 408 When disabled, equations etc. will appear as their untransformed TeX source.
408 409 """
409 410 )
410 411 def _enable_mathjax_changed(self, name, old, new):
411 412 """set mathjax url to empty if mathjax is disabled"""
412 413 if not new:
413 414 self.mathjax_url = u''
414 415
415 416 base_project_url = Unicode('/', config=True,
416 417 help='''The base URL for the notebook server.
417 418
418 419 Leading and trailing slashes can be omitted,
419 420 and will automatically be added.
420 421 ''')
421 422 def _base_project_url_changed(self, name, old, new):
422 423 if not new.startswith('/'):
423 424 self.base_project_url = '/'+new
424 425 elif not new.endswith('/'):
425 426 self.base_project_url = new+'/'
426 427
427 428 base_kernel_url = Unicode('/', config=True,
428 429 help='''The base URL for the kernel server
429 430
430 431 Leading and trailing slashes can be omitted,
431 432 and will automatically be added.
432 433 ''')
433 434 def _base_kernel_url_changed(self, name, old, new):
434 435 if not new.startswith('/'):
435 436 self.base_kernel_url = '/'+new
436 437 elif not new.endswith('/'):
437 438 self.base_kernel_url = new+'/'
438 439
439 440 websocket_host = Unicode("", config=True,
440 441 help="""The hostname for the websocket server."""
441 442 )
442 443
443 444 extra_static_paths = List(Unicode, config=True,
444 445 help="""Extra paths to search for serving static files.
445 446
446 447 This allows adding javascript/css to be available from the notebook server machine,
447 448 or overriding individual files in the IPython"""
448 449 )
449 450 def _extra_static_paths_default(self):
450 451 return [os.path.join(self.profile_dir.location, 'static')]
451 452
452 453 @property
453 454 def static_file_path(self):
454 455 """return extra paths + the default location"""
455 456 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
456 457
457 458 mathjax_url = Unicode("", config=True,
458 459 help="""The url for MathJax.js."""
459 460 )
460 461 def _mathjax_url_default(self):
461 462 if not self.enable_mathjax:
462 463 return u''
463 464 static_url_prefix = self.webapp_settings.get("static_url_prefix",
464 465 "/static/")
465 466 try:
466 467 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
467 468 except IOError:
468 469 if self.certfile:
469 470 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
470 471 base = u"https://c328740.ssl.cf1.rackcdn.com"
471 472 else:
472 473 base = u"http://cdn.mathjax.org"
473 474
474 475 url = base + u"/mathjax/latest/MathJax.js"
475 476 self.log.info("Using MathJax from CDN: %s", url)
476 477 return url
477 478 else:
478 479 self.log.info("Using local MathJax from %s" % mathjax)
479 480 return static_url_prefix+u"mathjax/MathJax.js"
480 481
481 482 def _mathjax_url_changed(self, name, old, new):
482 483 if new and not self.enable_mathjax:
483 484 # enable_mathjax=False overrides mathjax_url
484 485 self.mathjax_url = u''
485 486 else:
486 487 self.log.info("Using MathJax: %s", new)
487 488
488 489 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
489 490 config=True,
490 491 help='The notebook manager class to use.')
491 492
492 493 trust_xheaders = Bool(False, config=True,
493 494 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
494 495 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
495 496 )
496 497
497 498 def parse_command_line(self, argv=None):
498 499 super(NotebookApp, self).parse_command_line(argv)
499 500 if argv is None:
500 501 argv = sys.argv[1:]
501 502
502 503 # Scrub frontend-specific flags
503 504 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
504 505 # Kernel should inherit default config file from frontend
505 506 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
506 507
507 508 if self.extra_args:
508 509 f = os.path.abspath(self.extra_args[0])
509 510 if os.path.isdir(f):
510 511 nbdir = f
511 512 else:
512 513 self.file_to_run = f
513 514 nbdir = os.path.dirname(f)
514 515 self.config.NotebookManager.notebook_dir = nbdir
515 516
516 517 def init_configurables(self):
517 518 # force Session default to be secure
518 519 default_secure(self.config)
519 520 self.kernel_manager = MappingKernelManager(
520 521 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
521 522 connection_dir = self.profile_dir.security_dir,
522 523 )
523 524 kls = import_item(self.notebook_manager_class)
524 525 self.notebook_manager = kls(config=self.config, log=self.log)
525 526 self.notebook_manager.load_notebook_names()
526 527 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
527 528 self.cluster_manager.update_profiles()
528 529
529 530 def init_logging(self):
530 531 # This prevents double log messages because tornado use a root logger that
531 532 # self.log is a child of. The logging module dipatches log messages to a log
532 533 # and all of its ancenstors until propagate is set to False.
533 534 self.log.propagate = False
534 535
535 536 # set the date format
536 537 formatter = logging.Formatter(self.log_format, datefmt="%Y-%m-%d %H:%M:%S")
537 538 self.log.handlers[0].setFormatter(formatter)
538 539
539 540 # hook up tornado 3's loggers to our app handlers
540 541 for name in ('access', 'application', 'general'):
541 542 logging.getLogger('tornado.%s' % name).handlers = self.log.handlers
542 543
543 544 def init_webapp(self):
544 545 """initialize tornado webapp and httpserver"""
545 546 self.web_app = NotebookWebApplication(
546 547 self, self.kernel_manager, self.notebook_manager,
547 548 self.cluster_manager, self.log,
548 549 self.base_project_url, self.webapp_settings
549 550 )
550 551 if self.certfile:
551 552 ssl_options = dict(certfile=self.certfile)
552 553 if self.keyfile:
553 554 ssl_options['keyfile'] = self.keyfile
554 555 else:
555 556 ssl_options = None
556 557 self.web_app.password = self.password
557 558 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
558 559 xheaders=self.trust_xheaders)
559 560 if not self.ip:
560 561 warning = "WARNING: The notebook server is listening on all IP addresses"
561 562 if ssl_options is None:
562 563 self.log.critical(warning + " and not using encryption. This"
563 564 "is not recommended.")
564 565 if not self.password and not self.read_only:
565 566 self.log.critical(warning + "and not using authentication."
566 567 "This is highly insecure and not recommended.")
567 568 success = None
568 569 for port in random_ports(self.port, self.port_retries+1):
569 570 try:
570 571 self.http_server.listen(port, self.ip)
571 572 except socket.error as e:
572 573 # XXX: remove the e.errno == -9 block when we require
573 574 # tornado >= 3.0
574 575 if e.errno == -9 and tornado.version_info[0] < 3:
575 576 # The flags passed to socket.getaddrinfo from
576 577 # tornado.netutils.bind_sockets can cause "gaierror:
577 578 # [Errno -9] Address family for hostname not supported"
578 579 # when the interface is not associated, for example.
579 580 # Changing the flags to exclude socket.AI_ADDRCONFIG does
580 581 # not cause this error, but the only way to do this is to
581 582 # monkeypatch socket to remove the AI_ADDRCONFIG attribute
582 583 saved_AI_ADDRCONFIG = socket.AI_ADDRCONFIG
583 584 self.log.warn('Monkeypatching socket to fix tornado bug')
584 585 del(socket.AI_ADDRCONFIG)
585 586 try:
586 587 # retry the tornado call without AI_ADDRCONFIG flags
587 588 self.http_server.listen(port, self.ip)
588 589 except socket.error as e2:
589 590 e = e2
590 591 else:
591 592 self.port = port
592 593 success = True
593 594 break
594 595 # restore the monekypatch
595 596 socket.AI_ADDRCONFIG = saved_AI_ADDRCONFIG
596 597 if e.errno != errno.EADDRINUSE:
597 598 raise
598 599 self.log.info('The port %i is already in use, trying another random port.' % port)
599 600 else:
600 601 self.port = port
601 602 success = True
602 603 break
603 604 if not success:
604 605 self.log.critical('ERROR: the notebook server could not be started because '
605 606 'no available port could be found.')
606 607 self.exit(1)
607 608
608 609 def init_signal(self):
609 610 if not sys.platform.startswith('win'):
610 611 signal.signal(signal.SIGINT, self._handle_sigint)
611 612 signal.signal(signal.SIGTERM, self._signal_stop)
612 613 if hasattr(signal, 'SIGUSR1'):
613 614 # Windows doesn't support SIGUSR1
614 615 signal.signal(signal.SIGUSR1, self._signal_info)
615 616 if hasattr(signal, 'SIGINFO'):
616 617 # only on BSD-based systems
617 618 signal.signal(signal.SIGINFO, self._signal_info)
618 619
619 620 def _handle_sigint(self, sig, frame):
620 621 """SIGINT handler spawns confirmation dialog"""
621 622 # register more forceful signal handler for ^C^C case
622 623 signal.signal(signal.SIGINT, self._signal_stop)
623 624 # request confirmation dialog in bg thread, to avoid
624 625 # blocking the App
625 626 thread = threading.Thread(target=self._confirm_exit)
626 627 thread.daemon = True
627 628 thread.start()
628 629
629 630 def _restore_sigint_handler(self):
630 631 """callback for restoring original SIGINT handler"""
631 632 signal.signal(signal.SIGINT, self._handle_sigint)
632 633
633 634 def _confirm_exit(self):
634 635 """confirm shutdown on ^C
635 636
636 637 A second ^C, or answering 'y' within 5s will cause shutdown,
637 638 otherwise original SIGINT handler will be restored.
638 639
639 640 This doesn't work on Windows.
640 641 """
641 642 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
642 643 time.sleep(0.1)
643 644 info = self.log.info
644 645 info('interrupted')
645 646 print self.notebook_info()
646 647 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
647 648 sys.stdout.flush()
648 649 r,w,x = select.select([sys.stdin], [], [], 5)
649 650 if r:
650 651 line = sys.stdin.readline()
651 652 if line.lower().startswith('y'):
652 653 self.log.critical("Shutdown confirmed")
653 654 ioloop.IOLoop.instance().stop()
654 655 return
655 656 else:
656 657 print "No answer for 5s:",
657 658 print "resuming operation..."
658 659 # no answer, or answer is no:
659 660 # set it back to original SIGINT handler
660 661 # use IOLoop.add_callback because signal.signal must be called
661 662 # from main thread
662 663 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
663 664
664 665 def _signal_stop(self, sig, frame):
665 666 self.log.critical("received signal %s, stopping", sig)
666 667 ioloop.IOLoop.instance().stop()
667 668
668 669 def _signal_info(self, sig, frame):
669 670 print self.notebook_info()
670 671
671 672 @catch_config_error
672 673 def initialize(self, argv=None):
673 674 self.init_logging()
674 675 super(NotebookApp, self).initialize(argv)
675 676 self.init_configurables()
676 677 self.init_webapp()
677 678 self.init_signal()
678 679
679 680 def cleanup_kernels(self):
680 681 """Shutdown all kernels.
681 682
682 683 The kernels will shutdown themselves when this process no longer exists,
683 684 but explicit shutdown allows the KernelManagers to cleanup the connection files.
684 685 """
685 686 self.log.info('Shutting down kernels')
686 687 self.kernel_manager.shutdown_all()
687 688
688 689 def notebook_info(self):
689 690 "Return the current working directory and the server url information"
690 691 mgr_info = self.notebook_manager.info_string() + "\n"
691 692 return mgr_info +"The IPython Notebook is running at: %s" % self._url
692 693
693 694 def start(self):
694 695 """ Start the IPython Notebook server app, after initialization
695 696
696 697 This method takes no arguments so all configuration and initialization
697 698 must be done prior to calling this method."""
698 699 ip = self.ip if self.ip else '[all ip addresses on your system]'
699 700 proto = 'https' if self.certfile else 'http'
700 701 info = self.log.info
701 702 self._url = "%s://%s:%i%s" % (proto, ip, self.port,
702 703 self.base_project_url)
703 704 for line in self.notebook_info().split("\n"):
704 705 info(line)
705 706 info("Use Control-C to stop this server and shut down all kernels.")
706 707
707 708 if self.open_browser or self.file_to_run:
708 709 ip = self.ip or LOCALHOST
709 710 try:
710 711 browser = webbrowser.get(self.browser or None)
711 712 except webbrowser.Error as e:
712 713 self.log.warn('No web browser found: %s.' % e)
713 714 browser = None
714 715
715 716 if self.file_to_run:
716 717 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
717 718 url = self.notebook_manager.rev_mapping.get(name, '')
718 719 else:
719 720 url = ''
720 721 if browser:
721 722 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
722 723 self.port, self.base_project_url, url), new=2)
723 724 threading.Thread(target=b).start()
724 725 try:
725 726 ioloop.IOLoop.instance().start()
726 727 except KeyboardInterrupt:
727 728 info("Interrupted...")
728 729 finally:
729 730 self.cleanup_kernels()
730 731
731 732
732 733 #-----------------------------------------------------------------------------
733 734 # Main entry point
734 735 #-----------------------------------------------------------------------------
735 736
736 737 def launch_new_instance():
737 738 app = NotebookApp.instance()
738 739 app.initialize()
739 740 app.start()
740 741
@@ -1,973 +1,976 b''
1 1 article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
2 2 audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
3 3 audio:not([controls]){display:none;}
4 4 html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
5 5 a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
6 6 a:hover,a:active{outline:0;}
7 7 sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
8 8 sup{top:-0.5em;}
9 9 sub{bottom:-0.25em;}
10 10 img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}
11 11 #map_canvas img,.google-maps img{max-width:none;}
12 12 button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
13 13 button,input{*overflow:visible;line-height:normal;}
14 14 button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
15 15 button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}
16 16 label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer;}
17 17 input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield;}
18 18 input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
19 19 textarea{overflow:auto;vertical-align:top;}
20 20 @media print{*{text-shadow:none !important;color:#000 !important;background:transparent !important;box-shadow:none !important;} a,a:visited{text-decoration:underline;} a[href]:after{content:" (" attr(href) ")";} abbr[title]:after{content:" (" attr(title) ")";} .ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:"";} pre,blockquote{border:1px solid #999;page-break-inside:avoid;} thead{display:table-header-group;} tr,img{page-break-inside:avoid;} img{max-width:100% !important;} @page {margin:0.5cm;}p,h2,h3{orphans:3;widows:3;} h2,h3{page-break-after:avoid;}}.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0;}
21 21 .clearfix:after{clear:both;}
22 22 .hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;}
23 23 .input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
24 24 body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:20px;color:#000000;background-color:#ffffff;}
25 25 a{color:#0088cc;text-decoration:none;}
26 26 a:hover{color:#005580;text-decoration:underline;}
27 27 .img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
28 28 .img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.1);}
29 29 .img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px;}
30 30 .row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;}
31 31 .row:after{clear:both;}
32 32 [class*="span"]{float:left;min-height:1px;margin-left:20px;}
33 33 .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
34 34 .span12{width:940px;}
35 35 .span11{width:860px;}
36 36 .span10{width:780px;}
37 37 .span9{width:700px;}
38 38 .span8{width:620px;}
39 39 .span7{width:540px;}
40 40 .span6{width:460px;}
41 41 .span5{width:380px;}
42 42 .span4{width:300px;}
43 43 .span3{width:220px;}
44 44 .span2{width:140px;}
45 45 .span1{width:60px;}
46 46 .offset12{margin-left:980px;}
47 47 .offset11{margin-left:900px;}
48 48 .offset10{margin-left:820px;}
49 49 .offset9{margin-left:740px;}
50 50 .offset8{margin-left:660px;}
51 51 .offset7{margin-left:580px;}
52 52 .offset6{margin-left:500px;}
53 53 .offset5{margin-left:420px;}
54 54 .offset4{margin-left:340px;}
55 55 .offset3{margin-left:260px;}
56 56 .offset2{margin-left:180px;}
57 57 .offset1{margin-left:100px;}
58 58 .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;}
59 59 .row-fluid:after{clear:both;}
60 60 .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;}
61 61 .row-fluid [class*="span"]:first-child{margin-left:0;}
62 62 .row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%;}
63 63 .row-fluid .span12{width:100%;*width:99.94680851063829%;}
64 64 .row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%;}
65 65 .row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%;}
66 66 .row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%;}
67 67 .row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%;}
68 68 .row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%;}
69 69 .row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%;}
70 70 .row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%;}
71 71 .row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%;}
72 72 .row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%;}
73 73 .row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%;}
74 74 .row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%;}
75 75 .row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%;}
76 76 .row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%;}
77 77 .row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%;}
78 78 .row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%;}
79 79 .row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%;}
80 80 .row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%;}
81 81 .row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%;}
82 82 .row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%;}
83 83 .row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%;}
84 84 .row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%;}
85 85 .row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%;}
86 86 .row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%;}
87 87 .row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%;}
88 88 .row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%;}
89 89 .row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%;}
90 90 .row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%;}
91 91 .row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%;}
92 92 .row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%;}
93 93 .row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%;}
94 94 .row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%;}
95 95 .row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%;}
96 96 .row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%;}
97 97 .row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%;}
98 98 .row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%;}
99 99 [class*="span"].hide,.row-fluid [class*="span"].hide{display:none;}
100 100 [class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right;}
101 101 .container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";line-height:0;}
102 102 .container:after{clear:both;}
103 103 .container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";line-height:0;}
104 104 .container-fluid:after{clear:both;}
105 105 p{margin:0 0 10px;}
106 106 .lead{margin-bottom:20px;font-size:19.5px;font-weight:200;line-height:30px;}
107 107 small{font-size:85%;}
108 108 strong{font-weight:bold;}
109 109 em{font-style:italic;}
110 110 cite{font-style:normal;}
111 111 .muted{color:#999999;}
112 112 a.muted:hover{color:#808080;}
113 113 .text-warning{color:#c09853;}
114 114 a.text-warning:hover{color:#a47e3c;}
115 115 .text-error{color:#b94a48;}
116 116 a.text-error:hover{color:#953b39;}
117 117 .text-info{color:#3a87ad;}
118 118 a.text-info:hover{color:#2d6987;}
119 119 .text-success{color:#468847;}
120 120 a.text-success:hover{color:#356635;}
121 121 h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999999;}
122 122 h1,h2,h3{line-height:40px;}
123 123 h1{font-size:35.75px;}
124 124 h2{font-size:29.25px;}
125 125 h3{font-size:22.75px;}
126 126 h4{font-size:16.25px;}
127 127 h5{font-size:13px;}
128 128 h6{font-size:11.049999999999999px;}
129 129 h1 small{font-size:22.75px;}
130 130 h2 small{font-size:16.25px;}
131 131 h3 small{font-size:13px;}
132 132 h4 small{font-size:13px;}
133 133 .page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eeeeee;}
134 134 ul,ol{padding:0;margin:0 0 10px 25px;}
135 135 ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
136 136 li{line-height:20px;}
137 137 ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
138 138 ul.inline,ol.inline{margin-left:0;list-style:none;}ul.inline>li,ol.inline>li{display:inline-block;padding-left:5px;padding-right:5px;}
139 139 dl{margin-bottom:20px;}
140 140 dt,dd{line-height:20px;}
141 141 dt{font-weight:bold;}
142 142 dd{margin-left:10px;}
143 143 .dl-horizontal{*zoom:1;}.dl-horizontal:before,.dl-horizontal:after{display:table;content:"";line-height:0;}
144 144 .dl-horizontal:after{clear:both;}
145 145 .dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
146 146 .dl-horizontal dd{margin-left:180px;}
147 147 hr{margin:20px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
148 148 abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999999;}
149 149 abbr.initialism{font-size:90%;text-transform:uppercase;}
150 150 blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px;}
151 151 blockquote small{display:block;line-height:20px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
152 152 blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eeeeee;border-left:0;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
153 153 blockquote.pull-right small:before{content:'';}
154 154 blockquote.pull-right small:after{content:'\00A0 \2014';}
155 155 q:before,q:after,blockquote:before,blockquote:after{content:"";}
156 156 address{display:block;margin-bottom:20px;font-style:normal;line-height:20px;}
157 157 form{margin:0 0 20px;}
158 158 fieldset{padding:0;margin:0;border:0;}
159 159 legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:19.5px;line-height:40px;color:#333333;border:0;border-bottom:1px solid #e5e5e5;}legend small{font-size:15px;color:#999999;}
160 160 label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:20px;}
161 161 input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
162 162 label{display:block;margin-bottom:5px;}
163 163 select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:13px;line-height:20px;color:#555555;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;vertical-align:middle;}
164 164 input,textarea,.uneditable-input{width:206px;}
165 165 textarea{height:auto;}
166 166 textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear .2s, box-shadow linear .2s;-moz-transition:border linear .2s, box-shadow linear .2s;-o-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82, 168, 236, 0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);}
167 167 input[type="radio"],input[type="checkbox"]{margin:4px 0 0;*margin-top:0;margin-top:1px \9;line-height:normal;}
168 168 input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto;}
169 169 select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px;}
170 170 select{width:220px;border:1px solid #cccccc;background-color:#ffffff;}
171 171 select[multiple],select[size]{height:auto;}
172 172 select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
173 173 .uneditable-input,.uneditable-textarea{color:#999999;background-color:#fcfcfc;border-color:#cccccc;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
174 174 .uneditable-input{overflow:hidden;white-space:nowrap;}
175 175 .uneditable-textarea{width:auto;height:auto;}
176 176 input:-moz-placeholder,textarea:-moz-placeholder{color:#999999;}
177 177 input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999999;}
178 178 input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999999;}
179 179 .radio,.checkbox{min-height:20px;padding-left:20px;}
180 180 .radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px;}
181 181 .controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
182 182 .radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
183 183 .radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
184 184 .input-mini{width:60px;}
185 185 .input-small{width:90px;}
186 186 .input-medium{width:150px;}
187 187 .input-large{width:210px;}
188 188 .input-xlarge{width:270px;}
189 189 .input-xxlarge{width:530px;}
190 190 input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0;}
191 191 .input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block;}
192 192 input,textarea,.uneditable-input{margin-left:0;}
193 193 .controls-row [class*="span"]+[class*="span"]{margin-left:20px;}
194 194 input.span12, textarea.span12, .uneditable-input.span12{width:926px;}
195 195 input.span11, textarea.span11, .uneditable-input.span11{width:846px;}
196 196 input.span10, textarea.span10, .uneditable-input.span10{width:766px;}
197 197 input.span9, textarea.span9, .uneditable-input.span9{width:686px;}
198 198 input.span8, textarea.span8, .uneditable-input.span8{width:606px;}
199 199 input.span7, textarea.span7, .uneditable-input.span7{width:526px;}
200 200 input.span6, textarea.span6, .uneditable-input.span6{width:446px;}
201 201 input.span5, textarea.span5, .uneditable-input.span5{width:366px;}
202 202 input.span4, textarea.span4, .uneditable-input.span4{width:286px;}
203 203 input.span3, textarea.span3, .uneditable-input.span3{width:206px;}
204 204 input.span2, textarea.span2, .uneditable-input.span2{width:126px;}
205 205 input.span1, textarea.span1, .uneditable-input.span1{width:46px;}
206 206 .controls-row{*zoom:1;}.controls-row:before,.controls-row:after{display:table;content:"";line-height:0;}
207 207 .controls-row:after{clear:both;}
208 208 .controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left;}
209 209 .controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px;}
210 210 input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eeeeee;}
211 211 input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent;}
212 212 .control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
213 213 .control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;}
214 214 .control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #dbc59e;}
215 215 .control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
216 216 .control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
217 217 .control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;}
218 218 .control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #d59392;}
219 219 .control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
220 220 .control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
221 221 .control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;}
222 222 .control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7aba7b;}
223 223 .control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
224 224 .control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad;}
225 225 .control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad;}
226 226 .control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 6px #7ab5d3;}
227 227 .control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad;}
228 228 input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
229 229 .form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";line-height:0;}
230 230 .form-actions:after{clear:both;}
231 231 .help-block,.help-inline{color:#262626;}
232 232 .help-block{display:block;margin-bottom:10px;}
233 233 .help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
234 234 .input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap;}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu{font-size:13px;}
235 235 .input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2;}
236 236 .input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:13px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#eeeeee;border:1px solid #ccc;}
237 237 .input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
238 238 .input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546;}
239 239 .input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
240 240 .input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
241 241 .input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
242 242 .input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px;}
243 243 .input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
244 244 .input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
245 245 .input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
246 246 .input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
247 247 .input-prepend.input-append .btn-group:first-child{margin-left:0;}
248 248 input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
249 249 .form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
250 250 .form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
251 251 .form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
252 252 .form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0;}
253 253 .form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px;}
254 254 .form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;vertical-align:middle;}
255 255 .form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
256 256 .form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block;}
257 257 .form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
258 258 .form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
259 259 .form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0;}
260 260 .control-group{margin-bottom:10px;}
261 261 legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate;}
262 262 .form-horizontal .control-group{margin-bottom:20px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";line-height:0;}
263 263 .form-horizontal .control-group:after{clear:both;}
264 264 .form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right;}
265 265 .form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0;}.form-horizontal .controls:first-child{*padding-left:180px;}
266 266 .form-horizontal .help-block{margin-bottom:0;}
267 267 .form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px;}
268 268 .form-horizontal .form-actions{padding-left:180px;}
269 269 table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}
270 270 .table{width:100%;margin-bottom:20px;}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
271 271 .table th{font-weight:bold;}
272 272 .table thead th{vertical-align:bottom;}
273 273 .table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
274 274 .table tbody+tbody{border-top:2px solid #dddddd;}
275 275 .table .table{background-color:#ffffff;}
276 276 .table-condensed th,.table-condensed td{padding:4px 5px;}
277 277 .table-bordered{border:1px solid #dddddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
278 278 .table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
279 279 .table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child{-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
280 280 .table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;}
281 281 .table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child{-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
282 282 .table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
283 283 .table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;-moz-border-radius-bottomleft:0;border-bottom-left-radius:0;}
284 284 .table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;-moz-border-radius-bottomright:0;border-bottom-right-radius:0;}
285 285 .table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
286 286 .table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;}
287 287 .table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9;}
288 288 .table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5;}
289 289 table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0;}
290 290 .table td.span1,.table th.span1{float:none;width:44px;margin-left:0;}
291 291 .table td.span2,.table th.span2{float:none;width:124px;margin-left:0;}
292 292 .table td.span3,.table th.span3{float:none;width:204px;margin-left:0;}
293 293 .table td.span4,.table th.span4{float:none;width:284px;margin-left:0;}
294 294 .table td.span5,.table th.span5{float:none;width:364px;margin-left:0;}
295 295 .table td.span6,.table th.span6{float:none;width:444px;margin-left:0;}
296 296 .table td.span7,.table th.span7{float:none;width:524px;margin-left:0;}
297 297 .table td.span8,.table th.span8{float:none;width:604px;margin-left:0;}
298 298 .table td.span9,.table th.span9{float:none;width:684px;margin-left:0;}
299 299 .table td.span10,.table th.span10{float:none;width:764px;margin-left:0;}
300 300 .table td.span11,.table th.span11{float:none;width:844px;margin-left:0;}
301 301 .table td.span12,.table th.span12{float:none;width:924px;margin-left:0;}
302 302 .table tbody tr.success td{background-color:#dff0d8;}
303 303 .table tbody tr.error td{background-color:#f2dede;}
304 304 .table tbody tr.warning td{background-color:#fcf8e3;}
305 305 .table tbody tr.info td{background-color:#d9edf7;}
306 306 .table-hover tbody tr.success:hover td{background-color:#d0e9c6;}
307 307 .table-hover tbody tr.error:hover td{background-color:#ebcccc;}
308 308 .table-hover tbody tr.warning:hover td{background-color:#faf2cc;}
309 309 .table-hover tbody tr.info:hover td{background-color:#c4e3f3;}
310 310 [class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;margin-top:1px;}
311 311 .icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png");}
312 312 .icon-glass{background-position:0 0;}
313 313 .icon-music{background-position:-24px 0;}
314 314 .icon-search{background-position:-48px 0;}
315 315 .icon-envelope{background-position:-72px 0;}
316 316 .icon-heart{background-position:-96px 0;}
317 317 .icon-star{background-position:-120px 0;}
318 318 .icon-star-empty{background-position:-144px 0;}
319 319 .icon-user{background-position:-168px 0;}
320 320 .icon-film{background-position:-192px 0;}
321 321 .icon-th-large{background-position:-216px 0;}
322 322 .icon-th{background-position:-240px 0;}
323 323 .icon-th-list{background-position:-264px 0;}
324 324 .icon-ok{background-position:-288px 0;}
325 325 .icon-remove{background-position:-312px 0;}
326 326 .icon-zoom-in{background-position:-336px 0;}
327 327 .icon-zoom-out{background-position:-360px 0;}
328 328 .icon-off{background-position:-384px 0;}
329 329 .icon-signal{background-position:-408px 0;}
330 330 .icon-cog{background-position:-432px 0;}
331 331 .icon-trash{background-position:-456px 0;}
332 332 .icon-home{background-position:0 -24px;}
333 333 .icon-file{background-position:-24px -24px;}
334 334 .icon-time{background-position:-48px -24px;}
335 335 .icon-road{background-position:-72px -24px;}
336 336 .icon-download-alt{background-position:-96px -24px;}
337 337 .icon-download{background-position:-120px -24px;}
338 338 .icon-upload{background-position:-144px -24px;}
339 339 .icon-inbox{background-position:-168px -24px;}
340 340 .icon-play-circle{background-position:-192px -24px;}
341 341 .icon-repeat{background-position:-216px -24px;}
342 342 .icon-refresh{background-position:-240px -24px;}
343 343 .icon-list-alt{background-position:-264px -24px;}
344 344 .icon-lock{background-position:-287px -24px;}
345 345 .icon-flag{background-position:-312px -24px;}
346 346 .icon-headphones{background-position:-336px -24px;}
347 347 .icon-volume-off{background-position:-360px -24px;}
348 348 .icon-volume-down{background-position:-384px -24px;}
349 349 .icon-volume-up{background-position:-408px -24px;}
350 350 .icon-qrcode{background-position:-432px -24px;}
351 351 .icon-barcode{background-position:-456px -24px;}
352 352 .icon-tag{background-position:0 -48px;}
353 353 .icon-tags{background-position:-25px -48px;}
354 354 .icon-book{background-position:-48px -48px;}
355 355 .icon-bookmark{background-position:-72px -48px;}
356 356 .icon-print{background-position:-96px -48px;}
357 357 .icon-camera{background-position:-120px -48px;}
358 358 .icon-font{background-position:-144px -48px;}
359 359 .icon-bold{background-position:-167px -48px;}
360 360 .icon-italic{background-position:-192px -48px;}
361 361 .icon-text-height{background-position:-216px -48px;}
362 362 .icon-text-width{background-position:-240px -48px;}
363 363 .icon-align-left{background-position:-264px -48px;}
364 364 .icon-align-center{background-position:-288px -48px;}
365 365 .icon-align-right{background-position:-312px -48px;}
366 366 .icon-align-justify{background-position:-336px -48px;}
367 367 .icon-list{background-position:-360px -48px;}
368 368 .icon-indent-left{background-position:-384px -48px;}
369 369 .icon-indent-right{background-position:-408px -48px;}
370 370 .icon-facetime-video{background-position:-432px -48px;}
371 371 .icon-picture{background-position:-456px -48px;}
372 372 .icon-pencil{background-position:0 -72px;}
373 373 .icon-map-marker{background-position:-24px -72px;}
374 374 .icon-adjust{background-position:-48px -72px;}
375 375 .icon-tint{background-position:-72px -72px;}
376 376 .icon-edit{background-position:-96px -72px;}
377 377 .icon-share{background-position:-120px -72px;}
378 378 .icon-check{background-position:-144px -72px;}
379 379 .icon-move{background-position:-168px -72px;}
380 380 .icon-step-backward{background-position:-192px -72px;}
381 381 .icon-fast-backward{background-position:-216px -72px;}
382 382 .icon-backward{background-position:-240px -72px;}
383 383 .icon-play{background-position:-264px -72px;}
384 384 .icon-pause{background-position:-288px -72px;}
385 385 .icon-stop{background-position:-312px -72px;}
386 386 .icon-forward{background-position:-336px -72px;}
387 387 .icon-fast-forward{background-position:-360px -72px;}
388 388 .icon-step-forward{background-position:-384px -72px;}
389 389 .icon-eject{background-position:-408px -72px;}
390 390 .icon-chevron-left{background-position:-432px -72px;}
391 391 .icon-chevron-right{background-position:-456px -72px;}
392 392 .icon-plus-sign{background-position:0 -96px;}
393 393 .icon-minus-sign{background-position:-24px -96px;}
394 394 .icon-remove-sign{background-position:-48px -96px;}
395 395 .icon-ok-sign{background-position:-72px -96px;}
396 396 .icon-question-sign{background-position:-96px -96px;}
397 397 .icon-info-sign{background-position:-120px -96px;}
398 398 .icon-screenshot{background-position:-144px -96px;}
399 399 .icon-remove-circle{background-position:-168px -96px;}
400 400 .icon-ok-circle{background-position:-192px -96px;}
401 401 .icon-ban-circle{background-position:-216px -96px;}
402 402 .icon-arrow-left{background-position:-240px -96px;}
403 403 .icon-arrow-right{background-position:-264px -96px;}
404 404 .icon-arrow-up{background-position:-289px -96px;}
405 405 .icon-arrow-down{background-position:-312px -96px;}
406 406 .icon-share-alt{background-position:-336px -96px;}
407 407 .icon-resize-full{background-position:-360px -96px;}
408 408 .icon-resize-small{background-position:-384px -96px;}
409 409 .icon-plus{background-position:-408px -96px;}
410 410 .icon-minus{background-position:-433px -96px;}
411 411 .icon-asterisk{background-position:-456px -96px;}
412 412 .icon-exclamation-sign{background-position:0 -120px;}
413 413 .icon-gift{background-position:-24px -120px;}
414 414 .icon-leaf{background-position:-48px -120px;}
415 415 .icon-fire{background-position:-72px -120px;}
416 416 .icon-eye-open{background-position:-96px -120px;}
417 417 .icon-eye-close{background-position:-120px -120px;}
418 418 .icon-warning-sign{background-position:-144px -120px;}
419 419 .icon-plane{background-position:-168px -120px;}
420 420 .icon-calendar{background-position:-192px -120px;}
421 421 .icon-random{background-position:-216px -120px;width:16px;}
422 422 .icon-comment{background-position:-240px -120px;}
423 423 .icon-magnet{background-position:-264px -120px;}
424 424 .icon-chevron-up{background-position:-288px -120px;}
425 425 .icon-chevron-down{background-position:-313px -119px;}
426 426 .icon-retweet{background-position:-336px -120px;}
427 427 .icon-shopping-cart{background-position:-360px -120px;}
428 428 .icon-folder-close{background-position:-384px -120px;}
429 429 .icon-folder-open{background-position:-408px -120px;width:16px;}
430 430 .icon-resize-vertical{background-position:-432px -119px;}
431 431 .icon-resize-horizontal{background-position:-456px -118px;}
432 432 .icon-hdd{background-position:0 -144px;}
433 433 .icon-bullhorn{background-position:-24px -144px;}
434 434 .icon-bell{background-position:-48px -144px;}
435 435 .icon-certificate{background-position:-72px -144px;}
436 436 .icon-thumbs-up{background-position:-96px -144px;}
437 437 .icon-thumbs-down{background-position:-120px -144px;}
438 438 .icon-hand-right{background-position:-144px -144px;}
439 439 .icon-hand-left{background-position:-168px -144px;}
440 440 .icon-hand-up{background-position:-192px -144px;}
441 441 .icon-hand-down{background-position:-216px -144px;}
442 442 .icon-circle-arrow-right{background-position:-240px -144px;}
443 443 .icon-circle-arrow-left{background-position:-264px -144px;}
444 444 .icon-circle-arrow-up{background-position:-288px -144px;}
445 445 .icon-circle-arrow-down{background-position:-312px -144px;}
446 446 .icon-globe{background-position:-336px -144px;}
447 447 .icon-wrench{background-position:-360px -144px;}
448 448 .icon-tasks{background-position:-384px -144px;}
449 449 .icon-filter{background-position:-408px -144px;}
450 450 .icon-briefcase{background-position:-432px -144px;}
451 451 .icon-fullscreen{background-position:-456px -144px;}
452 452 .dropup,.dropdown{position:relative;}
453 453 .dropdown-toggle{*margin-bottom:-3px;}
454 454 .dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
455 455 .caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";}
456 456 .dropdown .caret{margin-top:8px;margin-left:2px;}
457 457 .dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#ffffff;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.dropdown-menu.pull-right{right:0;left:auto;}
458 458 .dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
459 459 .dropdown-menu li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333333;white-space:nowrap;}
460 460 .dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{text-decoration:none;color:#ffffff;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);}
461 461 .dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;outline:0;background-color:#0081c2;background-image:-moz-linear-gradient(top, #0088cc, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));background-image:-webkit-linear-gradient(top, #0088cc, #0077b3);background-image:-o-linear-gradient(top, #0088cc, #0077b3);background-image:linear-gradient(to bottom, #0088cc, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);}
462 462 .dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999999;}
463 463 .dropdown-menu .disabled>a:hover{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:default;}
464 464 .open{*z-index:1000;}.open>.dropdown-menu{display:block;}
465 465 .pull-right>.dropdown-menu{right:0;left:auto;}
466 466 .dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"";}
467 467 .dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
468 468 .dropdown-submenu{position:relative;}
469 469 .dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
470 470 .dropdown-submenu:hover>.dropdown-menu{display:block;}
471 471 .dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0;}
472 472 .dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
473 473 .dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
474 474 .dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
475 475 .dropdown .dropdown-menu .nav-header{padding-left:20px;padding-right:20px;}
476 476 .typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
477 477 .well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
478 478 .well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
479 479 .well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
480 480 .fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;}.fade.in{opacity:1;}
481 481 .collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;}.collapse.in{height:auto;}
482 482 .close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40);}
483 483 button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;}
484 484 .btn{display:inline-block;*display:inline;*zoom:1;padding:4px 12px;margin-bottom:0;font-size:13px;line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333333;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(to bottom, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #bbbbbb;*border:0;border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333333;background-color:#e6e6e6;*background-color:#d9d9d9;}
485 485 .btn:active,.btn.active{background-color:#cccccc \9;}
486 486 .btn:first-child{*margin-left:0;}
487 487 .btn:hover{color:#333333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
488 488 .btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
489 489 .btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);}
490 490 .btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
491 491 .btn-large{padding:11px 19px;font-size:16.25px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
492 492 .btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px;}
493 493 .btn-small{padding:2px 10px;font-size:11.049999999999999px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
494 494 .btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0;}
495 495 .btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px;}
496 496 .btn-mini{padding:0 6px;font-size:9.75px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
497 497 .btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}
498 498 .btn-block+.btn-block{margin-top:5px;}
499 499 input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%;}
500 500 .btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
501 501 .btn{border-color:#c5c5c5;border-color:rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);}
502 502 .btn-primary{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(to bottom, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#0044cc;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#ffffff;background-color:#0044cc;*background-color:#003bb3;}
503 503 .btn-primary:active,.btn-primary.active{background-color:#003399 \9;}
504 504 .btn-warning{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#ffffff;background-color:#f89406;*background-color:#df8505;}
505 505 .btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
506 506 .btn-danger{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(to bottom, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#ffffff;background-color:#bd362f;*background-color:#a9302a;}
507 507 .btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
508 508 .btn-success{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(to bottom, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#ffffff;background-color:#51a351;*background-color:#499249;}
509 509 .btn-success:active,.btn-success.active{background-color:#408140 \9;}
510 510 .btn-info{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(to bottom, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#ffffff;background-color:#2f96b4;*background-color:#2a85a0;}
511 511 .btn-info:active,.btn-info.active{background-color:#24748c \9;}
512 512 .btn-inverse{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#363636;background-image:-moz-linear-gradient(top, #444444, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));background-image:-webkit-linear-gradient(top, #444444, #222222);background-image:-o-linear-gradient(top, #444444, #222222);background-image:linear-gradient(to bottom, #444444, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#222222;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#ffffff;background-color:#222222;*background-color:#151515;}
513 513 .btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;}
514 514 button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
515 515 button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
516 516 button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
517 517 button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
518 518 .btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
519 519 .btn-link{border-color:transparent;cursor:pointer;color:#0088cc;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
520 520 .btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent;}
521 521 .btn-link[disabled]:hover{color:#333333;text-decoration:none;}
522 522 .btn-group{position:relative;display:inline-block;*display:inline;*zoom:1;font-size:0;vertical-align:middle;white-space:nowrap;*margin-left:.3em;}.btn-group:first-child{*margin-left:0;}
523 523 .btn-group+.btn-group{margin-left:5px;}
524 524 .btn-toolbar{font-size:0;margin-top:10px;margin-bottom:10px;}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px;}
525 525 .btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
526 526 .btn-group>.btn+.btn{margin-left:-1px;}
527 527 .btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:13px;}
528 528 .btn-group>.btn-mini{font-size:9.75px;}
529 529 .btn-group>.btn-small{font-size:11.049999999999999px;}
530 530 .btn-group>.btn-large{font-size:16.25px;}
531 531 .btn-group>.btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
532 532 .btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
533 533 .btn-group>.btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
534 534 .btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
535 535 .btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2;}
536 536 .btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
537 537 .btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);*padding-top:5px;*padding-bottom:5px;}
538 538 .btn-group>.btn-mini+.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:2px;*padding-bottom:2px;}
539 539 .btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px;}
540 540 .btn-group>.btn-large+.dropdown-toggle{padding-left:12px;padding-right:12px;*padding-top:7px;*padding-bottom:7px;}
541 541 .btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);}
542 542 .btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6;}
543 543 .btn-group.open .btn-primary.dropdown-toggle{background-color:#0044cc;}
544 544 .btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406;}
545 545 .btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f;}
546 546 .btn-group.open .btn-success.dropdown-toggle{background-color:#51a351;}
547 547 .btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4;}
548 548 .btn-group.open .btn-inverse.dropdown-toggle{background-color:#222222;}
549 549 .btn .caret{margin-top:8px;margin-left:0;}
550 550 .btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px;}
551 551 .btn-large .caret{border-left-width:5px;border-right-width:5px;border-top-width:5px;}
552 552 .dropup .btn-large .caret{border-bottom-width:5px;}
553 553 .btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
554 554 .btn-group-vertical{display:inline-block;*display:inline;*zoom:1;}
555 555 .btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
556 556 .btn-group-vertical>.btn+.btn{margin-left:0;margin-top:-1px;}
557 557 .btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
558 558 .btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
559 559 .btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0;}
560 560 .btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
561 561 .alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
562 562 .alert,.alert h4{color:#c09853;}
563 563 .alert h4{margin:0;}
564 564 .alert .close{position:relative;top:-2px;right:-21px;line-height:20px;}
565 565 .alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
566 566 .alert-success h4{color:#468847;}
567 567 .alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
568 568 .alert-danger h4,.alert-error h4{color:#b94a48;}
569 569 .alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
570 570 .alert-info h4{color:#3a87ad;}
571 571 .alert-block{padding-top:14px;padding-bottom:14px;}
572 572 .alert-block>p,.alert-block>ul{margin-bottom:0;}
573 573 .alert-block p+p{margin-top:5px;}
574 574 .nav{margin-left:0;margin-bottom:20px;list-style:none;}
575 575 .nav>li>a{display:block;}
576 576 .nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
577 577 .nav>li>a>img{max-width:none;}
578 578 .nav>.pull-right{float:right;}
579 579 .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
580 580 .nav li+.nav-header{margin-top:9px;}
581 581 .nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
582 582 .nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
583 583 .nav-list>li>a{padding:3px 15px;}
584 584 .nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
585 585 .nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px;}
586 586 .nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;}
587 587 .nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";line-height:0;}
588 588 .nav-tabs:after,.nav-pills:after{clear:both;}
589 589 .nav-tabs>li,.nav-pills>li{float:left;}
590 590 .nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
591 591 .nav-tabs{border-bottom:1px solid #ddd;}
592 592 .nav-tabs>li{margin-bottom:-1px;}
593 593 .nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
594 594 .nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
595 595 .nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
596 596 .nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
597 597 .nav-stacked>li{float:none;}
598 598 .nav-stacked>li>a{margin-right:0;}
599 599 .nav-tabs.nav-stacked{border-bottom:0;}
600 600 .nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
601 601 .nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;}
602 602 .nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
603 603 .nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
604 604 .nav-pills.nav-stacked>li>a{margin-bottom:3px;}
605 605 .nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
606 606 .nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;}
607 607 .nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
608 608 .nav .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
609 609 .nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
610 610 .nav-tabs .dropdown-toggle .caret{margin-top:8px;}
611 611 .nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff;}
612 612 .nav-tabs .active .dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;}
613 613 .nav>.dropdown.active>a:hover{cursor:pointer;}
614 614 .nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
615 615 .nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
616 616 .tabs-stacked .open>a:hover{border-color:#999999;}
617 617 .tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";line-height:0;}
618 618 .tabbable:after{clear:both;}
619 619 .tab-content{overflow:auto;}
620 620 .tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0;}
621 621 .tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
622 622 .tab-content>.active,.pill-content>.active{display:block;}
623 623 .tabs-below>.nav-tabs{border-top:1px solid #ddd;}
624 624 .tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0;}
625 625 .tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below>.nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
626 626 .tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd;}
627 627 .tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none;}
628 628 .tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
629 629 .tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
630 630 .tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
631 631 .tabs-left>.nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
632 632 .tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
633 633 .tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
634 634 .tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
635 635 .tabs-right>.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
636 636 .tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
637 637 .nav>.disabled>a{color:#999999;}
638 638 .nav>.disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default;}
639 639 .navbar{overflow:visible;margin-bottom:20px;*position:relative;*z-index:2;}
640 640 .navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top, #ffffff, #f2f2f2);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));background-image:-webkit-linear-gradient(top, #ffffff, #f2f2f2);background-image:-o-linear-gradient(top, #ffffff, #f2f2f2);background-image:linear-gradient(to bottom, #ffffff, #f2f2f2);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);box-shadow:0 1px 4px rgba(0, 0, 0, 0.065);*zoom:1;}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0;}
641 641 .navbar-inner:after{clear:both;}
642 642 .navbar .container{width:auto;}
643 643 .nav-collapse.collapse{height:auto;overflow:visible;}
644 644 .navbar .brand{float:left;display:block;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777777;text-shadow:0 1px 0 #ffffff;}.navbar .brand:hover{text-decoration:none;}
645 645 .navbar-text{margin-bottom:0;line-height:40px;color:#777777;}
646 646 .navbar-link{color:#777777;}.navbar-link:hover{color:#333333;}
647 647 .navbar .divider-vertical{height:40px;margin:0 9px;border-left:1px solid #f2f2f2;border-right:1px solid #ffffff;}
648 648 .navbar .btn,.navbar .btn-group{margin-top:5px;}
649 649 .navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0;}
650 650 .navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";line-height:0;}
651 651 .navbar-form:after{clear:both;}
652 652 .navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
653 653 .navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0;}
654 654 .navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
655 655 .navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
656 656 .navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0;}.navbar-search .search-query{margin-bottom:0;padding:4px 14px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
657 657 .navbar-static-top{position:static;margin-bottom:0;}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
658 658 .navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
659 659 .navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px;}
660 660 .navbar-fixed-bottom .navbar-inner{border-width:1px 0 0;}
661 661 .navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
662 662 .navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
663 663 .navbar-fixed-top{top:0;}
664 664 .navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,.1);box-shadow:0 1px 10px rgba(0,0,0,.1);}
665 665 .navbar-fixed-bottom{bottom:0;}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,.1);box-shadow:0 -1px 10px rgba(0,0,0,.1);}
666 666 .navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
667 667 .navbar .nav.pull-right{float:right;margin-right:0;}
668 668 .navbar .nav>li{float:left;}
669 669 .navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777777;text-decoration:none;text-shadow:0 1px 0 #ffffff;}
670 670 .navbar .nav .dropdown-toggle .caret{margin-top:8px;}
671 671 .navbar .nav>li>a:focus,.navbar .nav>li>a:hover{background-color:transparent;color:#333333;text-decoration:none;}
672 672 .navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);-moz-box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);box-shadow:inset 0 3px 8px rgba(0, 0, 0, 0.125);}
673 673 .navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#ededed;background-image:-moz-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));background-image:-webkit-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:-o-linear-gradient(top, #f2f2f2, #e5e5e5);background-image:linear-gradient(to bottom, #f2f2f2, #e5e5e5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#e5e5e5;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075);}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#ffffff;background-color:#e5e5e5;*background-color:#d9d9d9;}
674 674 .navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#cccccc \9;}
675 675 .navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
676 676 .btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
677 677 .navbar .nav>li>.dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
678 678 .navbar .nav>li>.dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
679 679 .navbar-fixed-bottom .nav>li>.dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
680 680 .navbar-fixed-bottom .nav>li>.dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
681 681 .navbar .nav li.dropdown>a:hover .caret{border-top-color:#555555;border-bottom-color:#555555;}
682 682 .navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:#e5e5e5;color:#555555;}
683 683 .navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777777;border-bottom-color:#777777;}
684 684 .navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555555;border-bottom-color:#555555;}
685 685 .navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{left:auto;right:0;}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{left:auto;right:12px;}
686 686 .navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{left:auto;right:13px;}
687 687 .navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{left:auto;right:100%;margin-left:0;margin-right:-1px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
688 688 .navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top, #222222, #111111);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));background-image:-webkit-linear-gradient(top, #222222, #111111);background-image:-o-linear-gradient(top, #222222, #111111);background-image:linear-gradient(to bottom, #222222, #111111);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);border-color:#252525;}
689 689 .navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999999;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#ffffff;}
690 690 .navbar-inverse .brand{color:#999999;}
691 691 .navbar-inverse .navbar-text{color:#999999;}
692 692 .navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{background-color:transparent;color:#ffffff;}
693 693 .navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#ffffff;background-color:#111111;}
694 694 .navbar-inverse .navbar-link{color:#999999;}.navbar-inverse .navbar-link:hover{color:#ffffff;}
695 695 .navbar-inverse .divider-vertical{border-left-color:#111111;border-right-color:#222222;}
696 696 .navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{background-color:#111111;color:#ffffff;}
697 697 .navbar-inverse .nav li.dropdown>a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
698 698 .navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999999;border-bottom-color:#999999;}
699 699 .navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
700 700 .navbar-inverse .navbar-search .search-query{color:#ffffff;background-color:#515151;border-color:#111111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);box-shadow:inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none;}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#cccccc;}
701 701 .navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#cccccc;}
702 702 .navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
703 703 .navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
704 704 .navbar-inverse .btn-navbar{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e0e0e;background-image:-moz-linear-gradient(top, #151515, #040404);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));background-image:-webkit-linear-gradient(top, #151515, #040404);background-image:-o-linear-gradient(top, #151515, #040404);background-image:linear-gradient(to bottom, #151515, #040404);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);border-color:#040404 #040404 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);*background-color:#040404;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#ffffff;background-color:#040404;*background-color:#000000;}
705 705 .navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000000 \9;}
706 706 .breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.breadcrumb>li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}.breadcrumb>li>.divider{padding:0 5px;color:#ccc;}
707 707 .breadcrumb>.active{color:#999999;}
708 708 .pagination{margin:20px 0;}
709 709 .pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
710 710 .pagination ul>li{display:inline;}
711 711 .pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd;border-left-width:0;}
712 712 .pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5;}
713 713 .pagination ul>.active>a,.pagination ul>.active>span{color:#999999;cursor:default;}
714 714 .pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999999;background-color:transparent;cursor:default;}
715 715 .pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
716 716 .pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
717 717 .pagination-centered{text-align:center;}
718 718 .pagination-right{text-align:right;}
719 719 .pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:16.25px;}
720 720 .pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
721 721 .pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
722 722 .pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-top-left-radius:3px;-moz-border-radius-topleft:3px;border-top-left-radius:3px;-webkit-border-bottom-left-radius:3px;-moz-border-radius-bottomleft:3px;border-bottom-left-radius:3px;}
723 723 .pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;-moz-border-radius-topright:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;-moz-border-radius-bottomright:3px;border-bottom-right-radius:3px;}
724 724 .pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.049999999999999px;}
725 725 .pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:9.75px;}
726 726 .pager{margin:20px 0;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";line-height:0;}
727 727 .pager:after{clear:both;}
728 728 .pager li{display:inline;}
729 729 .pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
730 730 .pager li>a:hover{text-decoration:none;background-color:#f5f5f5;}
731 731 .pager .next>a,.pager .next>span{float:right;}
732 732 .pager .previous>a,.pager .previous>span{float:left;}
733 733 .pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>span{color:#999999;background-color:#fff;cursor:default;}
734 734 .modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
735 735 .modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
736 736 .modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;outline:none;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
737 737 .modal.fade.in{top:10%;}
738 738 .modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
739 739 .modal-header h3{margin:0;line-height:30px;}
740 740 .modal-body{position:relative;overflow-y:auto;max-height:400px;padding:15px;}
741 741 .modal-form{margin-bottom:0;}
742 742 .modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";line-height:0;}
743 743 .modal-footer:after{clear:both;}
744 744 .modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
745 745 .modal-footer .btn-group .btn+.btn{margin-left:-1px;}
746 746 .modal-footer .btn-block+.btn-block{margin-left:0;}
747 747 .tooltip{position:absolute;z-index:1030;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
748 748 .tooltip.top{margin-top:-3px;}
749 749 .tooltip.right{margin-left:3px;}
750 750 .tooltip.bottom{margin-top:3px;}
751 751 .tooltip.left{margin-left:-3px;}
752 752 .tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
753 753 .tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid;}
754 754 .tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000;}
755 755 .tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000;}
756 756 .tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000;}
757 757 .tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000;}
758 758 .popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;text-align:left;background-color:#ffffff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);white-space:normal;}.popover.top{margin-top:-10px;}
759 759 .popover.right{margin-left:10px;}
760 760 .popover.bottom{margin-top:10px;}
761 761 .popover.left{margin-left:-10px;}
762 762 .popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;}
763 763 .popover-content{padding:9px 14px;}
764 764 .popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid;}
765 765 .popover .arrow{border-width:11px;}
766 766 .popover .arrow:after{border-width:10px;content:"";}
767 767 .popover.top .arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0, 0, 0, 0.25);bottom:-11px;}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#ffffff;}
768 768 .popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0, 0, 0, 0.25);}.popover.right .arrow:after{left:1px;bottom:-10px;border-left-width:0;border-right-color:#ffffff;}
769 769 .popover.bottom .arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0, 0, 0, 0.25);top:-11px;}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#ffffff;}
770 770 .popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0, 0, 0, 0.25);}.popover.left .arrow:after{right:1px;border-right-width:0;border-left-color:#ffffff;bottom:-10px;}
771 771 .thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";line-height:0;}
772 772 .thumbnails:after{clear:both;}
773 773 .row-fluid .thumbnails{margin-left:0;}
774 774 .thumbnails>li{float:left;margin-bottom:20px;margin-left:20px;}
775 775 .thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);box-shadow:0 1px 3px rgba(0, 0, 0, 0.055);-webkit-transition:all 0.2s ease-in-out;-moz-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;}
776 776 a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
777 777 .thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
778 778 .thumbnail .caption{padding:9px;color:#555555;}
779 779 .media,.media-body{overflow:hidden;*overflow:visible;zoom:1;}
780 780 .media,.media .media{margin-top:15px;}
781 781 .media:first-child{margin-top:0;}
782 782 .media-object{display:block;}
783 783 .media-heading{margin:0 0 5px;}
784 784 .media .pull-left{margin-right:10px;}
785 785 .media .pull-right{margin-left:10px;}
786 786 .media-list{margin-left:0;list-style:none;}
787 787 .label,.badge{display:inline-block;padding:2px 4px;font-size:10.998px;font-weight:bold;line-height:14px;color:#ffffff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;}
788 788 .label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
789 789 .badge{padding-left:9px;padding-right:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
790 790 .label:empty,.badge:empty{display:none;}
791 791 a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
792 792 .label-important,.badge-important{background-color:#b94a48;}
793 793 .label-important[href],.badge-important[href]{background-color:#953b39;}
794 794 .label-warning,.badge-warning{background-color:#f89406;}
795 795 .label-warning[href],.badge-warning[href]{background-color:#c67605;}
796 796 .label-success,.badge-success{background-color:#468847;}
797 797 .label-success[href],.badge-success[href]{background-color:#356635;}
798 798 .label-info,.badge-info{background-color:#3a87ad;}
799 799 .label-info[href],.badge-info[href]{background-color:#2d6987;}
800 800 .label-inverse,.badge-inverse{background-color:#333333;}
801 801 .label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}
802 802 .btn .label,.btn .badge{position:relative;top:-1px;}
803 803 .btn-mini .label,.btn-mini .badge{top:0;}
804 804 @-webkit-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}@-o-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:40px 0;} to{background-position:0 0;}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(to bottom, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
805 805 .progress .bar{width:0%;height:100%;color:#ffffff;float:left;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(to bottom, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
806 806 .progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15);}
807 807 .progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
808 808 .progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
809 809 .progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(to bottom, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);}
810 810 .progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
811 811 .progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(to bottom, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);}
812 812 .progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
813 813 .progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(to bottom, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);}
814 814 .progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
815 815 .progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(to bottom, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);}
816 816 .progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
817 817 .accordion{margin-bottom:20px;}
818 818 .accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
819 819 .accordion-heading{border-bottom:0;}
820 820 .accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
821 821 .accordion-toggle{cursor:pointer;}
822 822 .accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
823 823 .carousel{position:relative;margin-bottom:20px;line-height:1;}
824 824 .carousel-inner{overflow:hidden;width:100%;position:relative;}
825 825 .carousel-inner>.item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
826 826 .carousel-inner>.item>img{display:block;line-height:1;}
827 827 .carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block;}
828 828 .carousel-inner>.active{left:0;}
829 829 .carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%;}
830 830 .carousel-inner>.next{left:100%;}
831 831 .carousel-inner>.prev{left:-100%;}
832 832 .carousel-inner>.next.left,.carousel-inner>.prev.right{left:0;}
833 833 .carousel-inner>.active.left{left:-100%;}
834 834 .carousel-inner>.active.right{left:100%;}
835 835 .carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
836 836 .carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
837 837 .carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:15px;background:#333333;background:rgba(0, 0, 0, 0.75);}
838 838 .carousel-caption h4,.carousel-caption p{color:#ffffff;line-height:20px;}
839 839 .carousel-caption h4{margin:0 0 5px;}
840 840 .carousel-caption p{margin-bottom:0;}
841 841 .hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
842 842 .hero-unit li{line-height:30px;}
843 843 .pull-right{float:right;}
844 844 .pull-left{float:left;}
845 845 .hide{display:none;}
846 846 .show{display:block;}
847 847 .invisible{visibility:hidden;}
848 848 .affix{position:fixed;}
849 849 .corner-all{border-radius:4px;}
850 850 .hbox{display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;}
851 851 .hbox>*{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;}
852 852 .vbox{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}
853 853 .vbox>*{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;}
854 854 .reverse{-webkit-box-direction:reverse;-moz-box-direction:reverse;box-direction:reverse;}
855 855 .box-flex0{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;}
856 856 .box-flex1{-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
857 857 .box-flex{-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
858 858 .box-flex2{-webkit-box-flex:2;-moz-box-flex:2;box-flex:2;}
859 859 .box-group1{-webkit-box-flex-group:1;-moz-box-flex-group:1;box-flex-group:1;}
860 860 .box-group2{-webkit-box-flex-group:2;-moz-box-flex-group:2;box-flex-group:2;}
861 861 .start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;}
862 862 .end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;}
863 863 .center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;}
864 864 .corner-all{border-radius:4px;}
865 865 body{background-color:#ffffff;}
866 866 body.notebook_app{overflow:hidden;}
867 867 blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777;}
868 868 span#save_widget{padding:5px;margin:0px 0px 0px 300px;display:inline-block;}
869 869 span#notebook_name{height:1em;line-height:1em;padding:3px;border:none;font-size:146.5%;}
870 870 .ui-menubar-item .ui-button .ui-button-text{padding:0.4em 1.0em;font-size:100%;}
871 871 .ui-menu{-moz-box-shadow:0px 6px 10px -1px #adadad;-webkit-box-shadow:0px 6px 10px -1px #adadad;box-shadow:0px 6px 10px -1px #adadad;}
872 872 .ui-menu .ui-menu-item a{border:1px solid transparent;padding:2px 1.6em;}
873 873 .ui-menu .ui-menu-item a.ui-state-focus{margin:0;}
874 874 .ui-menu hr{margin:0.3em 0;}
875 875 #menubar_container{position:relative;}
876 876 #notification_area{position:absolute;right:0px;top:0px;height:25px;padding:3px 0px;padding-right:3px;z-index:10;}
877 877 .notification_widget{float:right;right:0px;top:1px;height:25px;padding:3px 6px;z-index:10;}
878 878 .toolbar{padding:3px 15px;border-bottom:1px #ababab solid;}.toolbar button{margin-top:2px;margin-bottom:2px;}
879 879 .toolbar select,.toolbar label{height:19px;vertical-align:middle;margin-right:2px;margin-bottom:0;display:inline;font-size:92%;margin-left:0.3em;margin-right:0.3em;padding:0px;}
880 880 .toolbar select{width:auto;}
881 881 #ipython-main-app{width:100%;position:relative;font-size:110%;}
882 882 span#quick_help_area{position:static;padding:5px 0px;margin:0px 0px 0px 0px;}
883 883 .help_string{float:right;width:170px;padding:0px 5px;text-align:left;font-size:85%;}
884 884 .help_string_label{float:right;font-size:85%;}
885 885 div#notebook_panel{margin:0px 0px 0px 0px;padding:0px;}
886 886 div#notebook{overflow-y:scroll;overflow-x:auto;width:100%;padding:5px 5px 15px 5px;margin:0px;}
887 887 div#pager_splitter{height:8px;}
888 888 #pager_container{position:relative;}
889 889 div#pager{padding:15px;overflow:auto;display:none;}
890 890 div.ui-widget-content{border:1px solid #ababab;outline:none;}
891 891 .cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}.cell.selected{border-radius:4px;border:thin #ababab solid;}
892 892 div.cell{width:100%;padding:5px 5px 5px 0px;margin:2px 0px 2px 0px;outline:none;}
893 893 div.prompt{width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231;}
894 894 div.input{page-break-inside:avoid;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;}
895 895 div.input_area{border:1px solid #cfcfcf;border-radius:4px;background:#f7f7f7;}
896 896 div.input_prompt{color:navy;border-top:1px solid transparent;}
897 897 div.output_wrapper{margin-top:5px;margin-left:5px;width:100%;position:relative;}
898 898 div.output_scroll{height:24em;width:100%;overflow:auto;border-radius:4px;box-shadow:inset 0 2px 8px rgba(0, 0, 0, 0.8);}
899 899 div.output_collapsed{margin-right:5px;}
900 900 div.out_prompt_overlay{height:100%;padding:0px;position:absolute;border-radius:4px;}
901 901 div.out_prompt_overlay:hover{box-shadow:inset 0 0 1px #000;background:rgba(240, 240, 240, 0.5);}
902 902 div.output_prompt{color:darkred;margin:0 5px 0 -5px;}
903 903 div.output_area{padding:0px;page-break-inside:avoid;display:-webkit-box;-webkit-box-orient:horizontal;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:horizontal;-moz-box-align:stretch;display:box;box-orient:horizontal;box-align:stretch;}
904 904 div.output_area pre{font-family:monospace;margin:0;padding:0;border:0;font-size:100%;vertical-align:baseline;color:black;}
905 905 div.output_subarea{padding:0.44em 0.4em 0.4em 1px;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
906 906 div.output_text{text-align:left;color:#000000;font-family:monospace;line-height:1.231;}
907 907 div.output_stream{padding-top:0.0em;padding-bottom:0.0em;}
908 908 div.output_stderr{background:#fdd;}
909 909 div.output_latex{text-align:left;}
910 910 div.text_cell{padding:5px 5px 5px 5px;}
911 911 div.text_cell_input{color:#000000;border:1px solid #cfcfcf;border-radius:4px;background:#f7f7f7;}
912 912 div.text_cell_render{outline:none;resize:none;width:inherit;border-style:none;padding:5px;color:#000000;}
913 913 .CodeMirror{line-height:1.231;}
914 914 .CodeMirror-scroll{height:auto;overflow-y:hidden;overflow-x:auto;}
915 915 .ansiblack{color:#000000;}
916 916 .ansired{color:darkred;}
917 917 .ansigreen{color:darkgreen;}
918 918 .ansiyellow{color:brown;}
919 919 .ansiblue{color:darkblue;}
920 920 .ansipurple{color:darkviolet;}
921 921 .ansicyan{color:steelblue;}
922 922 .ansigrey{color:grey;}
923 923 .ansibold{font-weight:bold;}
924 924 .completions{position:absolute;z-index:10;overflow:hidden;border:1px solid #ababab;}
925 925 .completions select{background:white;outline:none;border:none;padding:0px;margin:0px;overflow:auto;font-family:monospace;}
926 926 option.context{background-color:#DEF7FF;}
927 927 option.introspection{background-color:#EBF4EB;}
928 928 .completions p b{font-weight:bold;}
929 929 .completions p{background:#DDF;border-bottom:black solid 1px;padding:1px;font-family:monospace;}
930 930 pre.dialog{background-color:#f7f7f7;border:1px solid #ddd;border-radius:4px;padding:0.4em;padding-left:2em;}
931 931 p.dialog{padding:0.2em;}
932 932 .shortcut_key{display:inline-block;width:15ex;text-align:right;font-family:monospace;}
933 933 pre,code,kbd,samp{white-space:pre-wrap;}
934 934 #fonttest{font-family:monospace;}
935 935 .js-error{color:darkred;}
936 936 a{text-decoration:underline;}
937 937 p{margin-bottom:0;}
938 938 a.heading-anchor:link,a.heading-anchor:visited{text-decoration:none;color:inherit;}
939 div.raw_input{padding-top:0px;padding-bottom:0px;height:1em;line-height:1em;font-family:monospace;}
940 span.input_prompt{font-family:inherit;}
941 input.raw_input{font-family:inherit;font-size:inherit;color:inherit;width:auto;margin:-2px 0px 0px 1px;padding-left:1px;padding-top:2px;height:1em;}
939 942 @media print{body{overflow:visible !important;} div#notebook{overflow:visible !important;} .ui-widget-content{border:0px;} #save_widget{margin:0px !important;} #header,#pager,#pager_splitter,#menubar,#toolbar{display:none !important;} .cell{border:none !important;} .toolbar{display:none;}}.rendered_html{color:black;}.rendered_html em{font-style:italic;}
940 943 .rendered_html strong{font-weight:bold;}
941 944 .rendered_html u{text-decoration:underline;}
942 945 .rendered_html :link{text-decoration:underline;}
943 946 .rendered_html :visited{text-decoration:underline;}
944 947 .rendered_html h1{font-size:197%;margin:.65em 0;font-weight:bold;}
945 948 .rendered_html h2{font-size:153.9%;margin:.75em 0;font-weight:bold;}
946 949 .rendered_html h3{font-size:123.1%;margin:.85em 0;font-weight:bold;}
947 950 .rendered_html h4{font-size:100%;margin:0.95em 0;font-weight:bold;}
948 951 .rendered_html h5{font-size:85%;margin:1.5em 0;font-weight:bold;}
949 952 .rendered_html h6{font-size:77%;margin:1.65em 0;font-weight:bold;}
950 953 .rendered_html ul{list-style:disc;margin:1em 2em;}
951 954 .rendered_html ul ul{list-style:square;margin:0em 2em;}
952 955 .rendered_html ul ul ul{list-style:circle;margin:0em 2em;}
953 956 .rendered_html ol{list-style:decimal;margin:1em 2em;}
954 957 .rendered_html ol ol{list-style:upper-alpha;margin:0em 2em;}
955 958 .rendered_html ol ol ol{list-style:lower-alpha;margin:0em 2em;}
956 959 .rendered_html ol ol ol ol{list-style:lower-roman;margin:0em 2em;}
957 960 .rendered_html ol ol ol ol ol{list-style:decimal;margin:0em 2em;}
958 961 .rendered_html hr{color:black;background-color:black;}
959 962 .rendered_html pre{margin:1em 2em;}
960 963 .rendered_html blockquote{margin:1em 2em;}
961 964 .rendered_html table,.rendered_html tr,.rendered_html th,.rendered_html td{border:1px solid black;border-collapse:collapse;margin:1em 2em;}
962 965 .rendered_html td,.rendered_html th{text-align:left;vertical-align:middle;padding:4px;}
963 966 .rendered_html th{font-weight:bold;}
964 967 .rendered_html p{text-align:justify;}
965 968 .rendered_html p+p{margin-top:1em;}
966 969 .corner-all{border-radius:4px;}
967 970 @-moz-keyframes fadeOut{from{opacity:1;} to{opacity:0;}}@-webkit-keyframes fadeOut{from{opacity:1;} to{opacity:0;}}@-moz-keyframes fadeIn{from{opacity:0;} to{opacity:1;}}@-webkit-keyframes fadeIn{from{opacity:0;} to{opacity:1;}}.bigtooltip{overflow:auto;height:200px;-webkit-transition-property:height;-webkit-transition-duration:500ms;-moz-transition-property:height;-moz-transition-duration:500ms;transition-property:height;transition-duration:500ms;}
968 971 .smalltooltip{-webkit-transition-property:height;-webkit-transition-duration:500ms;-moz-transition-property:height;-moz-transition-duration:500ms;transition-property:height;transition-duration:500ms;text-overflow:ellipsis;overflow:hidden;height:80px;}
969 972 .tooltipbuttons{position:absolute;padding-right:15px;top:0px;right:0px;}
970 973 .tooltiptext{padding-right:30px;}
971 974 .ipython_tooltip{max-width:700px;-webkit-animation:fadeOut 400ms;-moz-animation:fadeOut 400ms;animation:fadeOut 400ms;-webkit-animation:fadeIn 400ms;-moz-animation:fadeIn 400ms;animation:fadeIn 400ms;vertical-align:middle;background-color:#f7f7f7;overflow:visible;border:#ababab 1px solid;outline:none;padding:3px;margin:0px;padding-left:7px;font-family:monospace;min-height:50px;-moz-box-shadow:0px 6px 10px -1px #adadad;-webkit-box-shadow:0px 6px 10px -1px #adadad;box-shadow:0px 6px 10px -1px #adadad;border-radius:4px;position:absolute;}.ipython_tooltip a{float:right;}
972 975 .pretooltiparrow{left:0px;margin:0px;top:-16px;width:40px;height:16px;overflow:hidden;position:absolute;}
973 976 .pretooltiparrow:before{background-color:#f7f7f7;border:1px #ababab solid;z-index:11;content:"";position:absolute;left:15px;top:10px;width:25px;height:25px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);}
@@ -1,425 +1,439 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // CodeCell
10 10 //============================================================================
11 11 /**
12 12 * An extendable module that provide base functionnality to create cell for notebook.
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule CodeCell
16 16 */
17 17
18 18
19 19 /* local util for codemirror */
20 20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}
21 21
22 22 /**
23 23 *
24 24 * function to delete until previous non blanking space character
25 25 * or first multiple of 4 tabstop.
26 26 * @private
27 27 */
28 28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
29 29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
30 30 if (!posEq(from, to)) {cm.replaceRange("", from, to); return}
31 31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
32 32 var tabsize = cm.getOption('tabSize');
33 33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
34 34 var from = {ch:cur.ch-chToPrevTabStop,line:cur.line}
35 35 var select = cm.getRange(from,cur)
36 36 if( select.match(/^\ +$/) != null){
37 37 cm.replaceRange("",from,cur)
38 38 } else {
39 39 cm.deleteH(-1,"char")
40 40 }
41 41 };
42 42
43 43
44 44 var IPython = (function (IPython) {
45 45 "use strict";
46 46
47 47 var utils = IPython.utils;
48 48 var key = IPython.utils.keycodes;
49 49 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
50 50
51 51 /**
52 52 * A Cell conceived to write code.
53 53 *
54 54 * The kernel doesn't have to be set at creation time, in that case
55 55 * it will be null and set_kernel has to be called later.
56 56 * @class CodeCell
57 57 * @extends IPython.Cell
58 58 *
59 59 * @constructor
60 60 * @param {Object|null} kernel
61 61 * @param {object|undefined} [options]
62 62 * @param [options.cm_config] {object} config to pass to CodeMirror
63 63 */
64 64 var CodeCell = function (kernel, options) {
65 65 this.kernel = kernel || null;
66 66 this.code_mirror = null;
67 67 this.input_prompt_number = null;
68 68 this.collapsed = false;
69 69 this.default_mode = 'python';
70 70
71 71
72 72 var cm_overwrite_options = {
73 73 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
74 74 };
75 75
76 76 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
77 77
78 78 IPython.Cell.apply(this,[options]);
79 79
80 80 var that = this;
81 81 this.element.focusout(
82 82 function() { that.auto_highlight(); }
83 83 );
84 84 };
85 85
86 86 CodeCell.options_default = {
87 87 cm_config : {
88 88 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
89 89 mode: 'python',
90 90 theme: 'ipython',
91 91 matchBrackets: true
92 92 }
93 93 };
94 94
95 95
96 96 CodeCell.prototype = new IPython.Cell();
97 97
98 98 /**
99 99 * @method auto_highlight
100 100 */
101 101 CodeCell.prototype.auto_highlight = function () {
102 102 this._auto_highlight(IPython.config.cell_magic_highlight)
103 103 };
104 104
105 105 /** @method create_element */
106 106 CodeCell.prototype.create_element = function () {
107 107 IPython.Cell.prototype.create_element.apply(this, arguments);
108 108
109 109 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
110 110 cell.attr('tabindex','2');
111 111
112 112 this.celltoolbar = new IPython.CellToolbar(this);
113 113
114 114 var input = $('<div></div>').addClass('input');
115 115 var vbox = $('<div/>').addClass('vbox box-flex1')
116 116 input.append($('<div/>').addClass('prompt input_prompt'));
117 117 vbox.append(this.celltoolbar.element);
118 118 var input_area = $('<div/>').addClass('input_area');
119 119 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
120 120 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
121 121 vbox.append(input_area);
122 122 input.append(vbox);
123 123 var output = $('<div></div>');
124 124 cell.append(input).append(output);
125 125 this.element = cell;
126 126 this.output_area = new IPython.OutputArea(output, true);
127 127
128 128 // construct a completer only if class exist
129 129 // otherwise no print view
130 130 if (IPython.Completer !== undefined)
131 131 {
132 132 this.completer = new IPython.Completer(this);
133 133 }
134 134 };
135 135
136 136 /**
137 137 * This method gets called in CodeMirror's onKeyDown/onKeyPress
138 138 * handlers and is used to provide custom key handling. Its return
139 139 * value is used to determine if CodeMirror should ignore the event:
140 140 * true = ignore, false = don't ignore.
141 141 * @method handle_codemirror_keyevent
142 142 */
143 143 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
144 144
145 145 if (this.read_only){
146 146 return false;
147 147 }
148 148
149 149 var that = this;
150 150 // whatever key is pressed, first, cancel the tooltip request before
151 151 // they are sent, and remove tooltip if any, except for tab again
152 152 if (event.type === 'keydown' && event.which != key.TAB ) {
153 153 IPython.tooltip.remove_and_cancel_tooltip();
154 154 };
155 155
156 156 var cur = editor.getCursor();
157 157 if (event.keyCode === key.ENTER){
158 158 this.auto_highlight();
159 159 }
160 160
161 161 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
162 162 // Always ignore shift-enter in CodeMirror as we handle it.
163 163 return true;
164 164 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
165 165 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
166 166 // browser and keyboard layout !
167 167 // Pressing '(' , request tooltip, don't forget to reappend it
168 168 IPython.tooltip.pending(that);
169 169 } else if (event.which === key.UPARROW && event.type === 'keydown') {
170 170 // If we are not at the top, let CM handle the up arrow and
171 171 // prevent the global keydown handler from handling it.
172 172 if (!that.at_top()) {
173 173 event.stop();
174 174 return false;
175 175 } else {
176 176 return true;
177 177 };
178 178 } else if (event.which === key.ESC) {
179 179 IPython.tooltip.remove_and_cancel_tooltip(true);
180 180 return true;
181 181 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
182 182 // If we are not at the bottom, let CM handle the down arrow and
183 183 // prevent the global keydown handler from handling it.
184 184 if (!that.at_bottom()) {
185 185 event.stop();
186 186 return false;
187 187 } else {
188 188 return true;
189 189 };
190 190 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
191 191 if (editor.somethingSelected()){
192 192 var anchor = editor.getCursor("anchor");
193 193 var head = editor.getCursor("head");
194 194 if( anchor.line != head.line){
195 195 return false;
196 196 }
197 197 }
198 198 IPython.tooltip.request(that);
199 199 event.stop();
200 200 return true;
201 201 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
202 202 // Tab completion.
203 203 //Do not trim here because of tooltip
204 204 if (editor.somethingSelected()){return false}
205 205 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
206 206 if (pre_cursor.trim() === "") {
207 207 // Don't autocomplete if the part of the line before the cursor
208 208 // is empty. In this case, let CodeMirror handle indentation.
209 209 return false;
210 210 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
211 211 IPython.tooltip.request(that);
212 212 // Prevent the event from bubbling up.
213 213 event.stop();
214 214 // Prevent CodeMirror from handling the tab.
215 215 return true;
216 216 } else {
217 217 event.stop();
218 218 this.completer.startCompletion();
219 219 return true;
220 220 };
221 221 } else {
222 222 // keypress/keyup also trigger on TAB press, and we don't want to
223 223 // use those to disable tab completion.
224 224 return false;
225 225 };
226 226 return false;
227 227 };
228 228
229 229
230 230 // Kernel related calls.
231 231
232 232 CodeCell.prototype.set_kernel = function (kernel) {
233 233 this.kernel = kernel;
234 234 }
235 235
236 236 /**
237 237 * Execute current code cell to the kernel
238 238 * @method execute
239 239 */
240 240 CodeCell.prototype.execute = function () {
241 241 this.output_area.clear_output(true, true, true);
242 242 this.set_input_prompt('*');
243 243 this.element.addClass("running");
244 244 var callbacks = {
245 245 'execute_reply': $.proxy(this._handle_execute_reply, this),
246 246 'output': $.proxy(this.output_area.handle_output, this.output_area),
247 247 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
248 'set_next_input': $.proxy(this._handle_set_next_input, this)
248 'set_next_input': $.proxy(this._handle_set_next_input, this),
249 'input_request': $.proxy(this._handle_input_request, this)
249 250 };
250 251 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
251 252 };
252 253
253 254 /**
254 255 * @method _handle_execute_reply
255 256 * @private
256 257 */
257 258 CodeCell.prototype._handle_execute_reply = function (content) {
258 259 this.set_input_prompt(content.execution_count);
259 260 this.element.removeClass("running");
260 261 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
261 262 }
262 263
264 /**
265 * @method _handle_set_next_input
266 * @private
267 */
263 268 CodeCell.prototype._handle_set_next_input = function (text) {
264 269 var data = {'cell': this, 'text': text}
265 270 $([IPython.events]).trigger('set_next_input.Notebook', data);
266 271 }
272
273 /**
274 * @method _handle_input_request
275 * @private
276 */
277 CodeCell.prototype._handle_input_request = function (content) {
278 this.output_area.append_raw_input(content);
279 }
280
267 281
268 282 // Basic cell manipulation.
269 283
270 284 CodeCell.prototype.select = function () {
271 285 IPython.Cell.prototype.select.apply(this);
272 286 this.code_mirror.refresh();
273 287 this.code_mirror.focus();
274 288 this.auto_highlight();
275 289 // We used to need an additional refresh() after the focus, but
276 290 // it appears that this has been fixed in CM. This bug would show
277 291 // up on FF when a newly loaded markdown cell was edited.
278 292 };
279 293
280 294
281 295 CodeCell.prototype.select_all = function () {
282 296 var start = {line: 0, ch: 0};
283 297 var nlines = this.code_mirror.lineCount();
284 298 var last_line = this.code_mirror.getLine(nlines-1);
285 299 var end = {line: nlines-1, ch: last_line.length};
286 300 this.code_mirror.setSelection(start, end);
287 301 };
288 302
289 303
290 304 CodeCell.prototype.collapse = function () {
291 305 this.collapsed = true;
292 306 this.output_area.collapse();
293 307 };
294 308
295 309
296 310 CodeCell.prototype.expand = function () {
297 311 this.collapsed = false;
298 312 this.output_area.expand();
299 313 };
300 314
301 315
302 316 CodeCell.prototype.toggle_output = function () {
303 317 this.collapsed = Boolean(1 - this.collapsed);
304 318 this.output_area.toggle_output();
305 319 };
306 320
307 321
308 322 CodeCell.prototype.toggle_output_scroll = function () {
309 323 this.output_area.toggle_scroll();
310 324 };
311 325
312 326
313 327 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
314 328 var ns = prompt_value || "&nbsp;";
315 329 return 'In&nbsp;[' + ns + ']:'
316 330 };
317 331
318 332 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
319 333 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
320 334 for(var i=1; i < lines_number; i++){html.push(['...:'])};
321 335 return html.join('</br>')
322 336 };
323 337
324 338 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
325 339
326 340
327 341 CodeCell.prototype.set_input_prompt = function (number) {
328 342 var nline = 1
329 343 if( this.code_mirror != undefined) {
330 344 nline = this.code_mirror.lineCount();
331 345 }
332 346 this.input_prompt_number = number;
333 347 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
334 348 this.element.find('div.input_prompt').html(prompt_html);
335 349 };
336 350
337 351
338 352 CodeCell.prototype.clear_input = function () {
339 353 this.code_mirror.setValue('');
340 354 };
341 355
342 356
343 357 CodeCell.prototype.get_text = function () {
344 358 return this.code_mirror.getValue();
345 359 };
346 360
347 361
348 362 CodeCell.prototype.set_text = function (code) {
349 363 return this.code_mirror.setValue(code);
350 364 };
351 365
352 366
353 367 CodeCell.prototype.at_top = function () {
354 368 var cursor = this.code_mirror.getCursor();
355 369 if (cursor.line === 0 && cursor.ch === 0) {
356 370 return true;
357 371 } else {
358 372 return false;
359 373 }
360 374 };
361 375
362 376
363 377 CodeCell.prototype.at_bottom = function () {
364 378 var cursor = this.code_mirror.getCursor();
365 379 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
366 380 return true;
367 381 } else {
368 382 return false;
369 383 }
370 384 };
371 385
372 386
373 387 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
374 388 this.output_area.clear_output(stdout, stderr, other);
375 389 };
376 390
377 391
378 392 // JSON serialization
379 393
380 394 CodeCell.prototype.fromJSON = function (data) {
381 395 IPython.Cell.prototype.fromJSON.apply(this, arguments);
382 396 if (data.cell_type === 'code') {
383 397 if (data.input !== undefined) {
384 398 this.set_text(data.input);
385 399 // make this value the starting point, so that we can only undo
386 400 // to this state, instead of a blank cell
387 401 this.code_mirror.clearHistory();
388 402 this.auto_highlight();
389 403 }
390 404 if (data.prompt_number !== undefined) {
391 405 this.set_input_prompt(data.prompt_number);
392 406 } else {
393 407 this.set_input_prompt();
394 408 };
395 409 this.output_area.fromJSON(data.outputs);
396 410 if (data.collapsed !== undefined) {
397 411 if (data.collapsed) {
398 412 this.collapse();
399 413 } else {
400 414 this.expand();
401 415 };
402 416 };
403 417 };
404 418 };
405 419
406 420
407 421 CodeCell.prototype.toJSON = function () {
408 422 var data = IPython.Cell.prototype.toJSON.apply(this);
409 423 data.input = this.get_text();
410 424 data.cell_type = 'code';
411 425 if (this.input_prompt_number) {
412 426 data.prompt_number = this.input_prompt_number;
413 427 };
414 428 var outputs = this.output_area.toJSON();
415 429 data.outputs = outputs;
416 430 data.language = 'python';
417 431 data.collapsed = this.collapsed;
418 432 return data;
419 433 };
420 434
421 435
422 436 IPython.CodeCell = CodeCell;
423 437
424 438 return IPython;
425 439 }(IPython));
@@ -1,441 +1,481 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Kernel
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule Kernel
16 16 */
17 17
18 18 var IPython = (function (IPython) {
19 19
20 20 var utils = IPython.utils;
21 21
22 22 // Initialization and connection.
23 23 /**
24 24 * A Kernel Class to communicate with the Python kernel
25 25 * @Class Kernel
26 26 */
27 27 var Kernel = function (base_url) {
28 28 this.kernel_id = null;
29 29 this.shell_channel = null;
30 30 this.iopub_channel = null;
31 this.stdin_channel = null;
31 32 this.base_url = base_url;
32 33 this.running = false;
33 34 this.username = "username";
34 35 this.session_id = utils.uuid();
35 36 this._msg_callbacks = {};
36 37
37 38 if (typeof(WebSocket) !== 'undefined') {
38 39 this.WebSocket = WebSocket;
39 40 } else if (typeof(MozWebSocket) !== 'undefined') {
40 41 this.WebSocket = MozWebSocket;
41 42 } else {
42 43 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox β‰₯ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
43 44 };
44 45 };
45 46
46 47
47 48 Kernel.prototype._get_msg = function (msg_type, content) {
48 49 var msg = {
49 50 header : {
50 51 msg_id : utils.uuid(),
51 52 username : this.username,
52 53 session : this.session_id,
53 54 msg_type : msg_type
54 55 },
55 56 metadata : {},
56 57 content : content,
57 58 parent_header : {}
58 59 };
59 60 return msg;
60 61 };
61 62
62 63 /**
63 64 * Start the Python kernel
64 65 * @method start
65 66 */
66 67 Kernel.prototype.start = function (notebook_id) {
67 68 var that = this;
68 69 if (!this.running) {
69 70 var qs = $.param({notebook:notebook_id});
70 71 var url = this.base_url + '?' + qs;
71 72 $.post(url,
72 73 $.proxy(that._kernel_started,that),
73 74 'json'
74 75 );
75 76 };
76 77 };
77 78
78 79 /**
79 80 * Restart the python kernel.
80 81 *
81 82 * Emit a 'status_restarting.Kernel' event with
82 83 * the current object as parameter
83 84 *
84 85 * @method restart
85 86 */
86 87 Kernel.prototype.restart = function () {
87 88 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
88 89 var that = this;
89 90 if (this.running) {
90 91 this.stop_channels();
91 92 var url = this.kernel_url + "/restart";
92 93 $.post(url,
93 94 $.proxy(that._kernel_started, that),
94 95 'json'
95 96 );
96 97 };
97 98 };
98 99
99 100
100 101 Kernel.prototype._kernel_started = function (json) {
101 102 console.log("Kernel started: ", json.kernel_id);
102 103 this.running = true;
103 104 this.kernel_id = json.kernel_id;
104 105 this.ws_url = json.ws_url;
105 106 this.kernel_url = this.base_url + "/" + this.kernel_id;
106 107 this.start_channels();
107 108 $([IPython.events]).trigger('status_started.Kernel', {kernel: this});
108 109 };
109 110
110 111
111 112 Kernel.prototype._websocket_closed = function(ws_url, early) {
112 113 this.stop_channels();
113 114 $([IPython.events]).trigger('websocket_closed.Kernel',
114 115 {ws_url: ws_url, kernel: this, early: early}
115 116 );
116 117 };
117 118
118 119 /**
119 120 * Start the `shell`and `iopub` channels.
120 121 * Will stop and restart them if they already exist.
121 122 *
122 123 * @method start_channels
123 124 */
124 125 Kernel.prototype.start_channels = function () {
125 126 var that = this;
126 127 this.stop_channels();
127 128 var ws_url = this.ws_url + this.kernel_url;
128 129 console.log("Starting WebSockets:", ws_url);
129 130 this.shell_channel = new this.WebSocket(ws_url + "/shell");
131 this.stdin_channel = new this.WebSocket(ws_url + "/stdin");
130 132 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
131 133 send_cookie = function(){
132 this.send(document.cookie);
134 // send the session id so the Session object Python-side
135 // has the same identity
136 this.send(that.session_id + ':' + document.cookie);
133 137 };
134 138 var already_called_onclose = false; // only alert once
135 139 var ws_closed_early = function(evt){
136 140 if (already_called_onclose){
137 141 return;
138 142 }
139 143 already_called_onclose = true;
140 144 if ( ! evt.wasClean ){
141 145 that._websocket_closed(ws_url, true);
142 146 }
143 147 };
144 148 var ws_closed_late = function(evt){
145 149 if (already_called_onclose){
146 150 return;
147 151 }
148 152 already_called_onclose = true;
149 153 if ( ! evt.wasClean ){
150 154 that._websocket_closed(ws_url, false);
151 155 }
152 156 };
153 this.shell_channel.onopen = send_cookie;
154 this.shell_channel.onclose = ws_closed_early;
155 this.iopub_channel.onopen = send_cookie;
156 this.iopub_channel.onclose = ws_closed_early;
157 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
158 for (var i=0; i < channels.length; i++) {
159 channels[i].onopen = send_cookie;
160 channels[i].onclose = ws_closed_early;
161 }
157 162 // switch from early-close to late-close message after 1s
158 163 setTimeout(function() {
159 if (that.shell_channel !== null) {
160 that.shell_channel.onclose = ws_closed_late;
161 }
162 if (that.iopub_channel !== null) {
163 that.iopub_channel.onclose = ws_closed_late;
164 for (var i=0; i < channels.length; i++) {
165 if (channels[i] !== null) {
166 channels[i].onclose = ws_closed_late;
167 }
164 168 }
165 169 }, 1000);
166 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
167 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
170 this.shell_channel.onmessage = $.proxy(this._handle_shell_reply, this);
171 this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply, this);
172 this.stdin_channel.onmessage = $.proxy(this._handle_input_request, this);
173
174 $([IPython.events]).on('send_input_reply.Kernel', function(evt, data) {
175 that.send_input_reply(data);
176 });
168 177 };
169 178
170 179 /**
171 180 * Start the `shell`and `iopub` channels.
172 181 * @method stop_channels
173 182 */
174 183 Kernel.prototype.stop_channels = function () {
175 if (this.shell_channel !== null) {
176 this.shell_channel.onclose = function (evt) {};
177 this.shell_channel.close();
178 this.shell_channel = null;
179 };
180 if (this.iopub_channel !== null) {
181 this.iopub_channel.onclose = function (evt) {};
182 this.iopub_channel.close();
183 this.iopub_channel = null;
184 var channels = [this.shell_channel, this.iopub_channel, this.stdin_channel];
185 for (var i=0; i < channels.length; i++) {
186 if ( channels[i] !== null ) {
187 channels[i].onclose = function (evt) {};
188 channels[i].close();
189 }
184 190 };
191 this.shell_channel = this.iopub_channel = this.stdin_channel = null;
185 192 };
186 193
187 194 // Main public methods.
188 195
189 196 /**
190 197 * Get info on object asynchronoulsy
191 198 *
192 199 * @async
193 200 * @param objname {string}
194 201 * @param callback {dict}
195 202 * @method object_info_request
196 203 *
197 204 * @example
198 205 *
199 206 * When calling this method pass a callbacks structure of the form:
200 207 *
201 208 * callbacks = {
202 209 * 'object_info_reply': object_info_reply_callback
203 210 * }
204 211 *
205 212 * The `object_info_reply_callback` will be passed the content object of the
206 213 *
207 214 * `object_into_reply` message documented in
208 215 * [IPython dev documentation](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information)
209 216 */
210 217 Kernel.prototype.object_info_request = function (objname, callbacks) {
211 218 if(typeof(objname)!=null && objname!=null)
212 219 {
213 220 var content = {
214 221 oname : objname.toString(),
215 222 };
216 223 var msg = this._get_msg("object_info_request", content);
217 224 this.shell_channel.send(JSON.stringify(msg));
218 225 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
219 226 return msg.header.msg_id;
220 227 }
221 228 return;
222 229 }
223 230
224 231 /**
225 232 * Execute given code into kernel, and pass result to callback.
226 233 *
227 234 * @async
228 235 * @method execute
229 236 * @param {string} code
230 237 * @param callback {Object} With the following keys
231 238 * @param callback.'execute_reply' {function}
232 239 * @param callback.'output' {function}
233 240 * @param callback.'clear_output' {function}
234 241 * @param callback.'set_next_input' {function}
235 242 * @param {object} [options]
236 243 * @param [options.silent=false] {Boolean}
237 244 * @param [options.user_expressions=empty_dict] {Dict}
238 245 * @param [options.user_variables=empty_list] {List od Strings}
239 246 * @param [options.allow_stdin=false] {Boolean} true|false
240 247 *
241 248 * @example
242 249 *
243 250 * The options object should contain the options for the execute call. Its default
244 251 * values are:
245 252 *
246 253 * options = {
247 254 * silent : true,
248 255 * user_variables : [],
249 256 * user_expressions : {},
250 257 * allow_stdin : false
251 258 * }
252 259 *
253 260 * When calling this method pass a callbacks structure of the form:
254 261 *
255 262 * callbacks = {
256 263 * 'execute_reply': execute_reply_callback,
257 264 * 'output': output_callback,
258 265 * 'clear_output': clear_output_callback,
259 266 * 'set_next_input': set_next_input_callback
260 267 * }
261 268 *
262 269 * The `execute_reply_callback` will be passed the content and metadata
263 270 * objects of the `execute_reply` message documented
264 271 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#execute)
265 272 *
266 273 * The `output_callback` will be passed `msg_type` ('stream','display_data','pyout','pyerr')
267 274 * of the output and the content and metadata objects of the PUB/SUB channel that contains the
268 275 * output:
269 276 *
270 277 * http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
271 278 *
272 279 * The `clear_output_callback` will be passed a content object that contains
273 280 * stdout, stderr and other fields that are booleans, as well as the metadata object.
274 281 *
275 282 * The `set_next_input_callback` will be passed the text that should become the next
276 283 * input cell.
277 284 */
278 285 Kernel.prototype.execute = function (code, callbacks, options) {
279 286
280 287 var content = {
281 288 code : code,
282 289 silent : true,
283 290 user_variables : [],
284 291 user_expressions : {},
285 292 allow_stdin : false
286 293 };
294 if (callbacks.input_request !== undefined) {
295 content.allow_stdin = true;
296 }
287 297 $.extend(true, content, options)
288 298 $([IPython.events]).trigger('execution_request.Kernel', {kernel: this, content:content});
289 299 var msg = this._get_msg("execute_request", content);
290 300 this.shell_channel.send(JSON.stringify(msg));
291 301 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
292 302 return msg.header.msg_id;
293 303 };
294 304
295 305 /**
296 306 * When calling this method pass a callbacks structure of the form:
297 307 *
298 308 * callbacks = {
299 309 * 'complete_reply': complete_reply_callback
300 310 * }
301 311 *
302 312 * The `complete_reply_callback` will be passed the content object of the
303 313 * `complete_reply` message documented
304 314 * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete)
305 315 *
306 316 * @method complete
307 317 * @param line {integer}
308 318 * @param cursor_pos {integer}
309 319 * @param {dict} callbacks
310 320 * @param callbacks.complete_reply {function} `complete_reply_callback`
311 321 *
312 322 */
313 323 Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
314 324 callbacks = callbacks || {};
315 325 var content = {
316 326 text : '',
317 327 line : line,
318 328 cursor_pos : cursor_pos
319 329 };
320 330 var msg = this._get_msg("complete_request", content);
321 331 this.shell_channel.send(JSON.stringify(msg));
322 332 this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
323 333 return msg.header.msg_id;
324 334 };
325 335
326 336
327 337 Kernel.prototype.interrupt = function () {
328 338 if (this.running) {
329 339 $([IPython.events]).trigger('status_interrupting.Kernel', {kernel: this});
330 340 $.post(this.kernel_url + "/interrupt");
331 341 };
332 342 };
333 343
334 344
335 345 Kernel.prototype.kill = function () {
336 346 if (this.running) {
337 347 this.running = false;
338 348 var settings = {
339 349 cache : false,
340 350 type : "DELETE"
341 351 };
342 352 $.ajax(this.kernel_url, settings);
343 353 };
344 354 };
345 355
356 Kernel.prototype.send_input_reply = function (input) {
357 var content = {
358 value : input,
359 };
360 $([IPython.events]).trigger('input_reply.Kernel', {kernel: this, content:content});
361 var msg = this._get_msg("input_reply", content);
362 this.stdin_channel.send(JSON.stringify(msg));
363 return msg.header.msg_id;
364 };
365
346 366
347 // Reply handlers.
367 // Reply handlers
348 368
349 369 Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
350 370 var callbacks = this._msg_callbacks[msg_id];
351 371 return callbacks;
352 372 };
353 373
354 374
355 375 Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
356 376 this._msg_callbacks[msg_id] = callbacks || {};
357 377 }
358 378
359 379
360 380 Kernel.prototype._handle_shell_reply = function (e) {
361 381 var reply = $.parseJSON(e.data);
362 382 $([IPython.events]).trigger('shell_reply.Kernel', {kernel: this, reply:reply});
363 383 var header = reply.header;
364 384 var content = reply.content;
365 385 var metadata = reply.metadata;
366 386 var msg_type = header.msg_type;
367 387 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
368 388 if (callbacks !== undefined) {
369 389 var cb = callbacks[msg_type];
370 390 if (cb !== undefined) {
371 391 cb(content, metadata);
372 392 }
373 393 };
374 394
375 395 if (content.payload !== undefined) {
376 396 var payload = content.payload || [];
377 397 this._handle_payload(callbacks, payload);
378 398 }
379 399 };
380 400
381 401
382 402 Kernel.prototype._handle_payload = function (callbacks, payload) {
383 403 var l = payload.length;
384 404 // Payloads are handled by triggering events because we don't want the Kernel
385 405 // to depend on the Notebook or Pager classes.
386 406 for (var i=0; i<l; i++) {
387 407 if (payload[i].source === 'IPython.kernel.zmq.page.page') {
388 408 var data = {'text':payload[i].text}
389 409 $([IPython.events]).trigger('open_with_text.Pager', data);
390 410 } else if (payload[i].source === 'IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
391 411 if (callbacks.set_next_input !== undefined) {
392 412 callbacks.set_next_input(payload[i].text)
393 413 }
394 414 }
395 415 };
396 416 };
397 417
398 418
399 419 Kernel.prototype._handle_iopub_reply = function (e) {
400 420 var reply = $.parseJSON(e.data);
401 421 var content = reply.content;
402 422 var msg_type = reply.header.msg_type;
403 423 var metadata = reply.metadata;
404 424 var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
405 425 if (msg_type !== 'status' && callbacks === undefined) {
406 426 // Message not from one of this notebook's cells and there are no
407 427 // callbacks to handle it.
408 428 return;
409 429 }
410 430 var output_types = ['stream','display_data','pyout','pyerr'];
411 431 if (output_types.indexOf(msg_type) >= 0) {
412 432 var cb = callbacks['output'];
413 433 if (cb !== undefined) {
414 434 cb(msg_type, content, metadata);
415 435 }
416 436 } else if (msg_type === 'status') {
417 437 if (content.execution_state === 'busy') {
418 438 $([IPython.events]).trigger('status_busy.Kernel', {kernel: this});
419 439 } else if (content.execution_state === 'idle') {
420 440 $([IPython.events]).trigger('status_idle.Kernel', {kernel: this});
421 441 } else if (content.execution_state === 'restarting') {
422 442 $([IPython.events]).trigger('status_restarting.Kernel', {kernel: this});
423 443 } else if (content.execution_state === 'dead') {
424 444 this.stop_channels();
425 445 $([IPython.events]).trigger('status_dead.Kernel', {kernel: this});
426 446 };
427 447 } else if (msg_type === 'clear_output') {
428 448 var cb = callbacks['clear_output'];
429 449 if (cb !== undefined) {
430 450 cb(content, metadata);
431 451 }
432 452 };
433 453 };
434 454
435 455
456 Kernel.prototype._handle_input_request = function (e) {
457 var request = $.parseJSON(e.data);
458 var header = request.header;
459 var content = request.content;
460 var metadata = request.metadata;
461 var msg_type = header.msg_type;
462 if (msg_type !== 'input_request') {
463 console.log("Invalid input request!", request);
464 return;
465 }
466 var callbacks = this.get_callbacks_for_msg(request.parent_header.msg_id);
467 if (callbacks !== undefined) {
468 var cb = callbacks[msg_type];
469 if (cb !== undefined) {
470 cb(content, metadata);
471 }
472 };
473 };
474
475
436 476 IPython.Kernel = Kernel;
437 477
438 478 return IPython;
439 479
440 480 }(IPython));
441 481
@@ -1,557 +1,606 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // OutputArea
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14
15 15 var utils = IPython.utils;
16 16
17 17 var OutputArea = function (selector, prompt_area) {
18 18 this.selector = selector;
19 19 this.wrapper = $(selector);
20 20 this.outputs = [];
21 21 this.collapsed = false;
22 22 this.scrolled = false;
23 23 this.clear_out_timeout = null;
24 24 if (prompt_area === undefined) {
25 25 this.prompt_area = true;
26 26 } else {
27 27 this.prompt_area = prompt_area;
28 28 }
29 29 this.create_elements();
30 30 this.style();
31 31 this.bind_events();
32 32 };
33 33
34 34 OutputArea.prototype.create_elements = function () {
35 35 this.element = $("<div/>");
36 36 this.collapse_button = $("<div/>");
37 37 this.prompt_overlay = $("<div/>");
38 38 this.wrapper.append(this.prompt_overlay);
39 39 this.wrapper.append(this.element);
40 40 this.wrapper.append(this.collapse_button);
41 41 };
42 42
43 43
44 44 OutputArea.prototype.style = function () {
45 45 this.collapse_button.hide();
46 46 this.prompt_overlay.hide();
47 47
48 48 this.wrapper.addClass('output_wrapper');
49 49 this.element.addClass('output vbox');
50 50
51 51 this.collapse_button.button();
52 52 this.collapse_button.addClass('output_collapsed vbox');
53 53 this.collapse_button.attr('title', 'click to expand output');
54 54 this.collapse_button.html('. . .');
55 55
56 56 this.prompt_overlay.addClass('out_prompt_overlay prompt');
57 57 this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
58 58
59 59 this.collapse();
60 60 };
61 61
62 62
63 63 OutputArea.prototype._should_scroll = function (lines) {
64 64 if (!lines) {
65 65 lines = 100;
66 66 }
67 67 // line-height from http://stackoverflow.com/questions/1185151
68 68 var fontSize = this.element.css('font-size');
69 69 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
70 70
71 71 return (this.element.height() > lines * lineHeight);
72 72 };
73 73
74 74
75 75 OutputArea.prototype.bind_events = function () {
76 76 var that = this;
77 77 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
78 78 this.prompt_overlay.click(function () { that.toggle_scroll(); });
79 79
80 80 this.element.resize(function () {
81 81 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
82 82 if ( IPython.utils.browser[0] === "Firefox" ) {
83 83 return;
84 84 }
85 85 // maybe scroll output,
86 86 // if it's grown large enough and hasn't already been scrolled.
87 87 if ( !that.scrolled && that._should_scroll()) {
88 88 that.scroll_area();
89 89 }
90 90 });
91 91 this.collapse_button.click(function () {
92 92 that.expand();
93 93 });
94 94 this.collapse_button.hover(function () {
95 95 $(this).addClass("ui-state-hover");
96 96 }, function () {
97 97 $(this).removeClass("ui-state-hover");
98 98 });
99 99 };
100 100
101 101
102 102 OutputArea.prototype.collapse = function () {
103 103 if (!this.collapsed) {
104 104 this.element.hide();
105 105 this.prompt_overlay.hide();
106 106 if (this.element.html()){
107 107 this.collapse_button.show();
108 108 }
109 109 this.collapsed = true;
110 110 }
111 111 };
112 112
113 113
114 114 OutputArea.prototype.expand = function () {
115 115 if (this.collapsed) {
116 116 this.collapse_button.hide();
117 117 this.element.show();
118 118 this.prompt_overlay.show();
119 119 this.collapsed = false;
120 120 }
121 121 };
122 122
123 123
124 124 OutputArea.prototype.toggle_output = function () {
125 125 if (this.collapsed) {
126 126 this.expand();
127 127 } else {
128 128 this.collapse();
129 129 }
130 130 };
131 131
132 132
133 133 OutputArea.prototype.scroll_area = function () {
134 134 this.element.addClass('output_scroll');
135 135 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
136 136 this.scrolled = true;
137 137 };
138 138
139 139
140 140 OutputArea.prototype.unscroll_area = function () {
141 141 this.element.removeClass('output_scroll');
142 142 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
143 143 this.scrolled = false;
144 144 };
145 145
146 146
147 147 OutputArea.prototype.scroll_if_long = function (lines) {
148 148 if (this._should_scroll(lines)) {
149 149 // only allow scrolling long-enough output
150 150 this.scroll_area();
151 151 }
152 152 };
153 153
154 154
155 155 OutputArea.prototype.toggle_scroll = function () {
156 156 if (this.scrolled) {
157 157 this.unscroll_area();
158 158 } else {
159 159 // only allow scrolling long-enough output
160 160 this.scroll_if_long(20);
161 161 }
162 162 };
163 163
164 164
165 165 // typeset with MathJax if MathJax is available
166 166 OutputArea.prototype.typeset = function () {
167 167 if (window.MathJax){
168 168 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
169 169 }
170 170 };
171 171
172 172
173 173 OutputArea.prototype.handle_output = function (msg_type, content) {
174 174 var json = {};
175 175 json.output_type = msg_type;
176 176 if (msg_type === "stream") {
177 177 json.text = content.data;
178 178 json.stream = content.name;
179 179 } else if (msg_type === "display_data") {
180 180 json = this.convert_mime_types(json, content.data);
181 181 } else if (msg_type === "pyout") {
182 182 json.prompt_number = content.execution_count;
183 183 json = this.convert_mime_types(json, content.data);
184 184 } else if (msg_type === "pyerr") {
185 185 json.ename = content.ename;
186 186 json.evalue = content.evalue;
187 187 json.traceback = content.traceback;
188 188 }
189 189 // append with dynamic=true
190 190 this.append_output(json, true);
191 191 };
192 192
193 193
194 194 OutputArea.prototype.convert_mime_types = function (json, data) {
195 195 if (data['text/plain'] !== undefined) {
196 196 json.text = data['text/plain'];
197 197 }
198 198 if (data['text/html'] !== undefined) {
199 199 json.html = data['text/html'];
200 200 }
201 201 if (data['image/svg+xml'] !== undefined) {
202 202 json.svg = data['image/svg+xml'];
203 203 }
204 204 if (data['image/png'] !== undefined) {
205 205 json.png = data['image/png'];
206 206 }
207 207 if (data['image/jpeg'] !== undefined) {
208 208 json.jpeg = data['image/jpeg'];
209 209 }
210 210 if (data['text/latex'] !== undefined) {
211 211 json.latex = data['text/latex'];
212 212 }
213 213 if (data['application/json'] !== undefined) {
214 214 json.json = data['application/json'];
215 215 }
216 216 if (data['application/javascript'] !== undefined) {
217 217 json.javascript = data['application/javascript'];
218 218 }
219 219 return json;
220 220 };
221 221
222 222
223 223 OutputArea.prototype.append_output = function (json, dynamic) {
224 224 // If dynamic is true, javascript output will be eval'd.
225 225 this.expand();
226 226 this.flush_clear_timeout();
227 227 if (json.output_type === 'pyout') {
228 228 this.append_pyout(json, dynamic);
229 229 } else if (json.output_type === 'pyerr') {
230 230 this.append_pyerr(json);
231 231 } else if (json.output_type === 'display_data') {
232 232 this.append_display_data(json, dynamic);
233 233 } else if (json.output_type === 'stream') {
234 234 this.append_stream(json);
235 235 }
236 236 this.outputs.push(json);
237 237 var that = this;
238 238 setTimeout(function(){that.element.trigger('resize');}, 100);
239 239 };
240 240
241 241
242 242 OutputArea.prototype.create_output_area = function () {
243 243 var oa = $("<div/>").addClass("output_area");
244 244 if (this.prompt_area) {
245 245 oa.append($('<div/>').addClass('prompt'));
246 246 }
247 247 return oa;
248 248 };
249 249
250 250
251 251 OutputArea.prototype.append_pyout = function (json, dynamic) {
252 252 var n = json.prompt_number || ' ';
253 253 var toinsert = this.create_output_area();
254 254 if (this.prompt_area) {
255 255 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
256 256 }
257 257 this.append_mime_type(json, toinsert, dynamic);
258 258 this.element.append(toinsert);
259 259 // If we just output latex, typeset it.
260 260 if ((json.latex !== undefined) || (json.html !== undefined)) {
261 261 this.typeset();
262 262 }
263 263 };
264 264
265 265
266 266 OutputArea.prototype.append_pyerr = function (json) {
267 267 var tb = json.traceback;
268 268 if (tb !== undefined && tb.length > 0) {
269 269 var s = '';
270 270 var len = tb.length;
271 271 for (var i=0; i<len; i++) {
272 272 s = s + tb[i] + '\n';
273 273 }
274 274 s = s + '\n';
275 275 var toinsert = this.create_output_area();
276 276 this.append_text(s, toinsert);
277 277 this.element.append(toinsert);
278 278 }
279 279 };
280 280
281 281
282 282 OutputArea.prototype.append_stream = function (json) {
283 283 // temporary fix: if stream undefined (json file written prior to this patch),
284 284 // default to most likely stdout:
285 285 if (json.stream == undefined){
286 286 json.stream = 'stdout';
287 287 }
288 288 var text = json.text;
289 289 var subclass = "output_"+json.stream;
290 290 if (this.outputs.length > 0){
291 291 // have at least one output to consider
292 292 var last = this.outputs[this.outputs.length-1];
293 293 if (last.output_type == 'stream' && json.stream == last.stream){
294 294 // latest output was in the same stream,
295 295 // so append directly into its pre tag
296 296 // escape ANSI & HTML specials:
297 297 var pre = this.element.find('div.'+subclass).last().find('pre');
298 298 var html = utils.fixCarriageReturn(
299 299 pre.html() + utils.fixConsole(text));
300 300 pre.html(html);
301 301 return;
302 302 }
303 303 }
304 304
305 305 if (!text.replace("\r", "")) {
306 306 // text is nothing (empty string, \r, etc.)
307 307 // so don't append any elements, which might add undesirable space
308 308 return;
309 309 }
310 310
311 311 // If we got here, attach a new div
312 312 var toinsert = this.create_output_area();
313 313 this.append_text(text, toinsert, "output_stream "+subclass);
314 314 this.element.append(toinsert);
315 315 };
316 316
317 317
318 318 OutputArea.prototype.append_display_data = function (json, dynamic) {
319 319 var toinsert = this.create_output_area();
320 320 this.append_mime_type(json, toinsert, dynamic);
321 321 this.element.append(toinsert);
322 322 // If we just output latex, typeset it.
323 323 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
324 324 this.typeset();
325 325 }
326 326 };
327 327
328 328 OutputArea.display_order = ['javascript','latex','svg','png','jpeg','text'];
329 329
330 330 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
331 331 for(var type_i in OutputArea.display_order){
332 332 var type = OutputArea.display_order[type_i];
333 333 if(json[type] != undefined ){
334 334 if(type == 'javascript'){
335 335 if (dynamic) {
336 336 this.append_javascript(json.javascript, element, dynamic);
337 337 }
338 338 } else {
339 339 this['append_'+type](json[type], element);
340 340 }
341 341 return;
342 342 }
343 343 }
344 344 };
345 345
346 346
347 347 OutputArea.prototype.append_html = function (html, element) {
348 348 var toinsert = $("<div/>").addClass("output_subarea output_html rendered_html");
349 349 toinsert.append(html);
350 350 element.append(toinsert);
351 351 };
352 352
353 353
354 354 OutputArea.prototype.append_javascript = function (js, container) {
355 355 // We just eval the JS code, element appears in the local scope.
356 356 var element = $("<div/>").addClass("output_subarea");
357 357 container.append(element);
358 358 // Div for js shouldn't be drawn, as it will add empty height to the area.
359 359 container.hide();
360 360 // If the Javascript appends content to `element` that should be drawn, then
361 361 // it must also call `container.show()`.
362 362 try {
363 363 eval(js);
364 364 } catch(err) {
365 365 console.log('Error in Javascript!');
366 366 console.log(err);
367 367 container.show();
368 368 element.append($('<div/>')
369 369 .html("Error in Javascript !<br/>"+
370 370 err.toString()+
371 371 '<br/>See your browser Javascript console for more details.')
372 372 .addClass('js-error')
373 373 );
374 374 }
375 375 };
376 376
377 377
378 378 OutputArea.prototype.append_text = function (data, element, extra_class) {
379 379 var toinsert = $("<div/>").addClass("output_subarea output_text");
380 380 // escape ANSI & HTML specials in plaintext:
381 381 data = utils.fixConsole(data);
382 382 data = utils.fixCarriageReturn(data);
383 383 data = utils.autoLinkUrls(data);
384 384 if (extra_class){
385 385 toinsert.addClass(extra_class);
386 386 }
387 387 toinsert.append($("<pre/>").html(data));
388 388 element.append(toinsert);
389 389 };
390 390
391 391
392 392 OutputArea.prototype.append_svg = function (svg, element) {
393 393 var toinsert = $("<div/>").addClass("output_subarea output_svg");
394 394 toinsert.append(svg);
395 395 element.append(toinsert);
396 396 };
397 397
398 398
399 399 OutputArea.prototype._dblclick_to_reset_size = function (img) {
400 400 // schedule wrapping image in resizable after a delay,
401 401 // so we don't end up calling resize on a zero-size object
402 402 var that = this;
403 403 setTimeout(function () {
404 404 var h0 = img.height();
405 405 var w0 = img.width();
406 406 if (!(h0 && w0)) {
407 407 // zero size, schedule another timeout
408 408 that._dblclick_to_reset_size(img);
409 409 return;
410 410 }
411 411 img.resizable({
412 412 aspectRatio: true,
413 413 autoHide: true
414 414 });
415 415 img.dblclick(function () {
416 416 // resize wrapper & image together for some reason:
417 417 img.parent().height(h0);
418 418 img.height(h0);
419 419 img.parent().width(w0);
420 420 img.width(w0);
421 421 });
422 422 }, 250);
423 423 };
424 424
425 425
426 426 OutputArea.prototype.append_png = function (png, element) {
427 427 var toinsert = $("<div/>").addClass("output_subarea output_png");
428 428 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
429 429 this._dblclick_to_reset_size(img);
430 430 toinsert.append(img);
431 431 element.append(toinsert);
432 432 };
433 433
434 434
435 435 OutputArea.prototype.append_jpeg = function (jpeg, element) {
436 436 var toinsert = $("<div/>").addClass("output_subarea output_jpeg");
437 437 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
438 438 this._dblclick_to_reset_size(img);
439 439 toinsert.append(img);
440 440 element.append(toinsert);
441 441 };
442 442
443 443
444 444 OutputArea.prototype.append_latex = function (latex, element) {
445 445 // This method cannot do the typesetting because the latex first has to
446 446 // be on the page.
447 447 var toinsert = $("<div/>").addClass("output_subarea output_latex");
448 448 toinsert.append(latex);
449 449 element.append(toinsert);
450 450 };
451
452 OutputArea.prototype.append_raw_input = function (content) {
453 var that = this;
454 this.expand();
455 this.flush_clear_timeout();
456 var area = this.create_output_area();
457
458 area.append(
459 $("<div/>")
460 .addClass("box-flex1 output_subarea raw_input")
461 .append(
462 $("<span/>")
463 .addClass("input_prompt")
464 .text(content.prompt)
465 )
466 .append(
467 $("<input/>")
468 .addClass("raw_input")
469 .attr('type', 'text')
470 .attr("size", 80)
471 .keydown(function (event, ui) {
472 // make sure we submit on enter,
473 // and don't re-execute the *cell* on shift-enter
474 if (event.which === utils.keycodes.ENTER) {
475 that._submit_raw_input();
476 return false;
477 }
478 })
479 )
480 );
481 this.element.append(area);
482 area.find("input.raw_input").focus();
483 }
484 OutputArea.prototype._submit_raw_input = function (evt) {
485 var container = this.element.find("div.raw_input");
486 var theprompt = container.find("span.input_prompt");
487 var theinput = container.find("input.raw_input");
488 var value = theinput.attr("value");
489 var content = {
490 output_type : 'stream',
491 name : 'stdout',
492 text : theprompt.text() + value + '\n'
493 }
494 // remove form container
495 container.parent().remove();
496 // replace with plaintext version in stdout
497 this.append_output(content, false);
498 $([IPython.events]).trigger('send_input_reply.Kernel', value);
499 }
451 500
452 501
453 502 OutputArea.prototype.handle_clear_output = function (content) {
454 503 this.clear_output(content.stdout, content.stderr, content.other);
455 504 };
456 505
457 506
458 507 OutputArea.prototype.clear_output = function (stdout, stderr, other) {
459 508 var that = this;
460 509 if (this.clear_out_timeout != null){
461 510 // fire previous pending clear *immediately*
462 511 clearTimeout(this.clear_out_timeout);
463 512 this.clear_out_timeout = null;
464 513 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
465 514 }
466 515 // store flags for flushing the timeout
467 516 this._clear_stdout = stdout;
468 517 this._clear_stderr = stderr;
469 518 this._clear_other = other;
470 519 this.clear_out_timeout = setTimeout(function() {
471 520 // really clear timeout only after a short delay
472 521 // this reduces flicker in 'clear_output; print' cases
473 522 that.clear_out_timeout = null;
474 523 that._clear_stdout = that._clear_stderr = that._clear_other = null;
475 524 that.clear_output_callback(stdout, stderr, other);
476 525 }, 500
477 526 );
478 527 };
479 528
480 529
481 530 OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
482 531 var output_div = this.element;
483 532
484 533 if (stdout && stderr && other){
485 534 // clear all, no need for logic
486 535 output_div.html("");
487 536 this.outputs = [];
488 537 this.unscroll_area();
489 538 return;
490 539 }
491 540 // remove html output
492 541 // each output_subarea that has an identifying class is in an output_area
493 542 // which is the element to be removed.
494 543 if (stdout) {
495 544 output_div.find("div.output_stdout").parent().remove();
496 545 }
497 546 if (stderr) {
498 547 output_div.find("div.output_stderr").parent().remove();
499 548 }
500 549 if (other) {
501 550 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
502 551 }
503 552 this.unscroll_area();
504 553
505 554 // remove cleared outputs from JSON list:
506 555 for (var i = this.outputs.length - 1; i >= 0; i--) {
507 556 var out = this.outputs[i];
508 557 var output_type = out.output_type;
509 558 if (output_type == "display_data" && other) {
510 559 this.outputs.splice(i,1);
511 560 } else if (output_type == "stream") {
512 561 if (stdout && out.stream == "stdout") {
513 562 this.outputs.splice(i,1);
514 563 } else if (stderr && out.stream == "stderr") {
515 564 this.outputs.splice(i,1);
516 565 }
517 566 }
518 567 }
519 568 };
520 569
521 570
522 571 OutputArea.prototype.flush_clear_timeout = function() {
523 572 var output_div = this.element;
524 573 if (this.clear_out_timeout){
525 574 clearTimeout(this.clear_out_timeout);
526 575 this.clear_out_timeout = null;
527 576 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
528 577 }
529 578 };
530 579
531 580
532 581 // JSON serialization
533 582
534 583 OutputArea.prototype.fromJSON = function (outputs) {
535 584 var len = outputs.length;
536 585 for (var i=0; i<len; i++) {
537 586 // append with dynamic=false.
538 587 this.append_output(outputs[i], false);
539 588 }
540 589 };
541 590
542 591
543 592 OutputArea.prototype.toJSON = function () {
544 593 var outputs = [];
545 594 var len = this.outputs.length;
546 595 for (var i=0; i<len; i++) {
547 596 outputs[i] = this.outputs[i];
548 597 }
549 598 return outputs;
550 599 };
551 600
552 601
553 602 IPython.OutputArea = OutputArea;
554 603
555 604 return IPython;
556 605
557 606 }(IPython));
@@ -1,479 +1,502 b''
1 1 /**
2 2 * Primary styles
3 3 *
4 4 * Author: IPython Development Team
5 5 */
6 6
7 7 @import "variables.less";
8 8
9 9
10 10 body {
11 11 background-color:@notebook_background;
12 12 }
13 13
14 14 body.notebook_app {
15 15 overflow: hidden;
16 16 }
17 17
18 18 blockquote {
19 19 border-left: 4px solid #DDD;
20 20 padding: 0 15px;
21 21 color: #777;
22 22 }
23 23
24 24 span#save_widget {
25 25 padding: 5px;
26 26 margin: 0px 0px 0px 300px;
27 27 display:inline-block;
28 28 }
29 29
30 30 span#notebook_name {
31 31 height: 1em;
32 32 line-height: 1em;
33 33 padding: 3px;
34 34 border: none;
35 35 font-size: 146.5%;
36 36 }
37 37
38 38
39 39 .ui-menubar-item .ui-button .ui-button-text {
40 40 padding: 0.4em 1.0em;
41 41 font-size: 100%;
42 42 }
43 43
44 44 .ui-menu {
45 45 -moz-box-shadow: 0px 6px 10px -1px #adadad;
46 46 -webkit-box-shadow: 0px 6px 10px -1px #adadad;
47 47 box-shadow: 0px 6px 10px -1px #adadad;
48 48 }
49 49
50 50 .ui-menu .ui-menu-item a {
51 51 border: 1px solid transparent;
52 52 padding: 2px 1.6em;
53 53 }
54 54
55 55 .ui-menu .ui-menu-item a.ui-state-focus {
56 56 margin: 0;
57 57 }
58 58
59 59 .ui-menu hr {
60 60 margin: 0.3em 0;
61 61 }
62 62
63 63 #menubar_container {
64 64 position: relative;
65 65 }
66 66
67 67 #notification_area {
68 68 position: absolute;
69 69 right: 0px;
70 70 top: 0px;
71 71 height: 25px;
72 72 padding: 3px 0px;
73 73 padding-right: 3px;
74 74 z-index: 10;
75 75 }
76 76
77 77 .notification_widget{
78 78 float : right;
79 79 right: 0px;
80 80 top: 1px;
81 81 height: 25px;
82 82 padding: 3px 6px;
83 83 z-index: 10;
84 84 }
85 85
86 86 .toolbar {
87 87 padding: 3px 15px;
88 88 border-bottom: @borderwidth @border_color solid;
89 89
90 90 button {
91 91 margin-top:2px;
92 92 margin-bottom:2px;
93 93 }
94 94
95 95
96 96 select, label {
97 97 height : 19px;
98 98 vertical-align:middle;
99 99 margin-right:2px;
100 100 margin-bottom:0;
101 101 display: inline;
102 102 font-size: 92%;
103 103 margin-left:0.3em;
104 104 margin-right:0.3em;
105 105 padding: 0px;
106 106 }
107 107 }
108 108
109 109 .toolbar select{
110 110 width:auto;
111 111 }
112 112
113 113
114 114 #ipython-main-app {
115 115 width: 100%;
116 116 position: relative;
117 117 font-size: 110%;
118 118 }
119 119
120 120 span#quick_help_area {
121 121 position: static;
122 122 padding: 5px 0px;
123 123 margin: 0px 0px 0px 0px;
124 124 }
125 125
126 126 .help_string {
127 127 float: right;
128 128 width: 170px;
129 129 padding: 0px 5px;
130 130 text-align: left;
131 131 font-size: 85%;
132 132 }
133 133
134 134 .help_string_label {
135 135 float: right;
136 136 font-size: 85%;
137 137 }
138 138
139 139 div#notebook_panel {
140 140 margin: 0px 0px 0px 0px;
141 141 padding: 0px;
142 142 }
143 143
144 144 div#notebook {
145 145 overflow-y: scroll;
146 146 overflow-x: auto;
147 147 width: 100%;
148 148 /* This spaces the cell away from the edge of the notebook area */
149 149 padding: 5px 5px 15px 5px;
150 150 margin: 0px;
151 151 }
152 152
153 153 div#pager_splitter {
154 154 height: 8px;
155 155 }
156 156
157 157 #pager_container {
158 158 position : relative;
159 159 }
160 160
161 161 div#pager {
162 162 padding: 15px;
163 163 overflow: auto;
164 164 display: none;
165 165 }
166 166
167 167 div.ui-widget-content {
168 168 border: 1px solid @border_color;
169 169 outline: none;
170 170 }
171 171
172 172 .cell {
173 173 border: 1px solid transparent;
174 174 .vbox();
175 175
176 176 &.selected {
177 177 .corner-all;
178 178 border : thin @border_color solid;
179 179 }
180 180 }
181 181
182 182 div.cell {
183 183 width: 100%;
184 184 padding: 5px 5px 5px 0px;
185 185 /* This acts as a spacer between cells, that is outside the border */
186 186 margin: 2px 0px 2px 0px;
187 187 outline: none;
188 188 }
189 189
190 190 div.code_cell {
191 191 }
192 192
193 193 /* any special styling for code cells that are currently running goes here */
194 194 div.code_cell.running {
195 195 }
196 196
197 197 div.prompt {
198 198 /* This needs to be wide enough for 3 digit prompt numbers: In[100]: */
199 199 width: 11ex;
200 200 /* This 0.4em is tuned to match the padding on the CodeMirror editor. */
201 201 padding: 0.4em;
202 202 margin: 0px;
203 203 font-family: monospace;
204 204 text-align: right;
205 205 /* This has to match that of the the CodeMirror class line-height below */
206 206 line-height: 1.231;
207 207 }
208 208
209 209 div.input {
210 210 page-break-inside: avoid;
211 211 .hbox();
212 212 }
213 213
214 214 /* input_area and input_prompt must match in top border and margin for alignment */
215 215 div.input_area {
216 216 /*color: @fontBaseColor;*/
217 217 border: 1px solid @light_border_color;
218 218 .corner-all;
219 219 background: @cell_background;
220 220 }
221 221
222 222 div.input_prompt {
223 223 color: navy;
224 224 border-top: 1px solid transparent;
225 225 }
226 226
227 227 div.output_wrapper {
228 228 /* This is a spacer between the input and output of each cell */
229 229 margin-top: 5px;
230 230 margin-left: 5px;
231 231 /* FF needs explicit width to stretch */
232 232 width: 100%;
233 233 /* this position must be relative to enable descendents to be absolute within it */
234 234 position: relative;
235 235 }
236 236
237 237 /* class for the output area when it should be height-limited */
238 238 div.output_scroll {
239 239 /* ideally, this would be max-height, but FF barfs all over that */
240 240 height: 24em;
241 241 /* FF needs this *and the wrapper* to specify full width, or it will shrinkwrap */
242 242 width: 100%;
243 243
244 244 overflow: auto;
245 245 .corner-all;
246 246 box-shadow: inset 0 2px 8px rgba(0, 0, 0, .8);
247 247 }
248 248
249 249 /* output div while it is collapsed */
250 250 div.output_collapsed {
251 251 margin-right: 5px;
252 252 }
253 253
254 254 div.out_prompt_overlay {
255 255 height: 100%;
256 256 padding: 0px;
257 257 position: absolute;
258 258 .corner-all;
259 259 }
260 260
261 261 div.out_prompt_overlay:hover {
262 262 /* use inner shadow to get border that is computed the same on WebKit/FF */
263 263 box-shadow: inset 0 0 1px #000;
264 264 background: rgba(240, 240, 240, 0.5);
265 265 }
266 266
267 267 div.output_prompt {
268 268 color: darkred;
269 269 /* 5px right shift to account for margin in parent container */
270 270 margin: 0 5px 0 -5px;
271 271 }
272 272
273 273 /* This class is the outer container of all output sections. */
274 274 div.output_area {
275 275 padding: 0px;
276 276 page-break-inside: avoid;
277 277 .hbox();
278 278 }
279 279
280 280
281 281 /* This is needed to protect the pre formating from global settings such
282 282 as that of bootstrap */
283 283 div.output_area pre {
284 284 font-family: monospace;
285 285 margin: 0;
286 286 padding: 0;
287 287 border: 0;
288 288 font-size: 100%;
289 289 vertical-align: baseline;
290 290 color: black;
291 291 }
292 292
293 293 /* This class is for the output subarea inside the output_area and after
294 294 the prompt div. */
295 295 div.output_subarea {
296 296 padding: 0.44em 0.4em 0.4em 1px;
297 297 .box-flex1();
298 298 }
299 299
300 300 /* The rest of the output_* classes are for special styling of the different
301 301 output types */
302 302
303 303 /* all text output has this class: */
304 304 div.output_text {
305 305 text-align: left;
306 306 color: @fontBaseColor;
307 307 font-family: monospace;
308 308 /* This has to match that of the the CodeMirror class line-height below */
309 309 line-height: 1.231;
310 310 }
311 311
312 312 /* stdout/stderr are 'text' as well as 'stream', but pyout/pyerr are *not* streams */
313 313 div.output_stream {
314 314 padding-top: 0.0em;
315 315 padding-bottom: 0.0em;
316 316 }
317 317 div.output_stdout {
318 318 }
319 319 div.output_stderr {
320 320 background: #fdd; /* very light red background for stderr */
321 321 }
322 322
323 323 div.output_latex {
324 324 text-align: left;
325 325 }
326 326
327 327 div.output_html {
328 328 }
329 329
330 330 div.output_png {
331 331 }
332 332
333 333 div.output_jpeg {
334 334 }
335 335
336 336 div.text_cell {
337 337 padding: 5px 5px 5px 5px;
338 338 }
339 339
340 340 div.text_cell_input {
341 341 color: @fontBaseColor;
342 342 border: 1px solid @light_border_color;
343 343 .corner-all;
344 344 background: @cell_background;
345 345 }
346 346
347 347 div.text_cell_render {
348 348 /*font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;*/
349 349 outline: none;
350 350 resize: none;
351 351 width: inherit;
352 352 border-style: none;
353 353 padding: 5px;
354 354 color: @fontBaseColor;
355 355 }
356 356
357 357 /* The following gets added to the <head> if it is detected that the user has a
358 358 * monospace font with inconsistent normal/bold/italic height. See
359 359 * notebookmain.js. Such fonts will have keywords vertically offset with
360 360 * respect to the rest of the text. The user should select a better font.
361 361 * See: https://github.com/ipython/ipython/issues/1503
362 362 *
363 363 * .CodeMirror span {
364 364 * vertical-align: bottom;
365 365 * }
366 366 */
367 367
368 368 .CodeMirror {
369 369 line-height: 1.231; /* Changed from 1em to our global default */
370 370 }
371 371
372 372 .CodeMirror-scroll {
373 373 height: auto; /* Changed to auto to autogrow */
374 374 /* The CodeMirror docs are a bit fuzzy on if overflow-y should be hidden or visible.*/
375 375 /* We have found that if it is visible, vertical scrollbars appear with font size changes.*/
376 376 overflow-y: hidden;
377 377 overflow-x: auto; /* Changed from auto to remove scrollbar */
378 378 }
379 379
380 380 /* CSS font colors for translated ANSI colors. */
381 381
382 382
383 383 .ansiblack {color: @fontBaseColor;}
384 384 .ansired {color: darkred;}
385 385 .ansigreen {color: darkgreen;}
386 386 .ansiyellow {color: brown;}
387 387 .ansiblue {color: darkblue;}
388 388 .ansipurple {color: darkviolet;}
389 389 .ansicyan {color: steelblue;}
390 390 .ansigrey {color: grey;}
391 391 .ansibold {font-weight: bold;}
392 392
393 393 .completions {
394 394 position: absolute;
395 395 z-index: 10;
396 396 overflow: hidden;
397 397 border: 1px solid @border_color;
398 398 }
399 399
400 400 .completions select {
401 401 background: white;
402 402 outline: none;
403 403 border: none;
404 404 padding: 0px;
405 405 margin: 0px;
406 406 overflow: auto;
407 407 font-family: monospace;
408 408 }
409 409
410 410 option.context {
411 411 background-color: #DEF7FF;
412 412 }
413 413 option.introspection {
414 414 background-color: #EBF4EB;
415 415 }
416 416
417 417 /*fixed part of the completion*/
418 418 .completions p b {
419 419 font-weight:bold;
420 420 }
421 421
422 422 .completions p {
423 423 background: #DDF;
424 424 /*outline: none;
425 425 padding: 0px;*/
426 426 border-bottom: black solid 1px;
427 427 padding: 1px;
428 428 font-family: monospace;
429 429 }
430 430
431 431 pre.dialog {
432 432 background-color: @cell_background;
433 433 border: 1px solid #ddd;
434 434 .corner-all;
435 435 padding: 0.4em;
436 436 padding-left: 2em;
437 437 }
438 438
439 439 p.dialog {
440 440 padding : 0.2em;
441 441 }
442 442
443 443 .shortcut_key {
444 444 display: inline-block;
445 445 width: 15ex;
446 446 text-align: right;
447 447 font-family: monospace;
448 448 }
449 449
450 450 .shortcut_descr {
451 451 }
452 452
453 453 /* Word-wrap output correctly. This is the CSS3 spelling, though Firefox seems
454 454 to not honor it correctly. Webkit browsers (Chrome, rekonq, Safari) do.
455 455 */
456 456 pre, code, kbd, samp { white-space: pre-wrap; }
457 457
458 458 #fonttest {
459 459 font-family: monospace;
460 460 }
461 461
462 462 .js-error {
463 463 color: darkred;
464 464 }
465 465
466 466 a {
467 467 text-decoration: underline;
468 468 }
469 469
470 470 p {
471 471
472 472 margin-bottom:0;
473 473
474 474 }
475 475
476 476 a.heading-anchor:link, a.heading-anchor:visited {
477 477 text-decoration: none;
478 478 color: inherit;
479 479 }
480
481 /* raw_input styles */
482
483 div.raw_input {
484 padding-top: 0px;
485 padding-bottom: 0px;
486 height: 1em;
487 line-height: 1em;
488 font-family: monospace;
489 }
490 span.input_prompt {
491 font-family: inherit;
492 }
493 input.raw_input {
494 font-family: inherit;
495 font-size: inherit;
496 color: inherit;
497 width: auto;
498 margin: -2px 0px 0px 1px;
499 padding-left: 1px;
500 padding-top: 2px;
501 height: 1em;
502 }
@@ -1,795 +1,804 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports
19 19 import __builtin__
20 20 import sys
21 21 import time
22 22 import traceback
23 23 import logging
24 24 import uuid
25 25
26 26 from datetime import datetime
27 27 from signal import (
28 28 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
29 29 )
30 30
31 31 # System library imports
32 32 import zmq
33 33 from zmq.eventloop import ioloop
34 34 from zmq.eventloop.zmqstream import ZMQStream
35 35
36 36 # Local imports
37 37 from IPython.config.configurable import Configurable
38 38 from IPython.core.error import StdinNotImplementedError
39 39 from IPython.core import release
40 40 from IPython.utils import io
41 41 from IPython.utils import py3compat
42 42 from IPython.utils.jsonutil import json_clean
43 43 from IPython.utils.traitlets import (
44 44 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode,
45 45 Type
46 46 )
47 47
48 48 from serialize import serialize_object, unpack_apply_message
49 49 from session import Session
50 50 from zmqshell import ZMQInteractiveShell
51 51
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Main kernel class
55 55 #-----------------------------------------------------------------------------
56 56
57 57 protocol_version = list(release.kernel_protocol_version_info)
58 58 ipython_version = list(release.version_info)
59 59 language_version = list(sys.version_info[:3])
60 60
61 61
62 62 class Kernel(Configurable):
63 63
64 64 #---------------------------------------------------------------------------
65 65 # Kernel interface
66 66 #---------------------------------------------------------------------------
67 67
68 68 # attribute to override with a GUI
69 69 eventloop = Any(None)
70 70 def _eventloop_changed(self, name, old, new):
71 71 """schedule call to eventloop from IOLoop"""
72 72 loop = ioloop.IOLoop.instance()
73 73 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
74 74
75 75 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
76 76 shell_class = Type(ZMQInteractiveShell)
77 77
78 78 session = Instance(Session)
79 79 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
80 80 shell_streams = List()
81 81 control_stream = Instance(ZMQStream)
82 82 iopub_socket = Instance(zmq.Socket)
83 83 stdin_socket = Instance(zmq.Socket)
84 84 log = Instance(logging.Logger)
85 85
86 86 user_module = Any()
87 87 def _user_module_changed(self, name, old, new):
88 88 if self.shell is not None:
89 89 self.shell.user_module = new
90 90
91 91 user_ns = Dict(default_value=None)
92 92 def _user_ns_changed(self, name, old, new):
93 93 if self.shell is not None:
94 94 self.shell.user_ns = new
95 95 self.shell.init_user_ns()
96 96
97 97 # identities:
98 98 int_id = Integer(-1)
99 99 ident = Unicode()
100 100
101 101 def _ident_default(self):
102 102 return unicode(uuid.uuid4())
103 103
104 104
105 105 # Private interface
106 106
107 107 # Time to sleep after flushing the stdout/err buffers in each execute
108 108 # cycle. While this introduces a hard limit on the minimal latency of the
109 109 # execute cycle, it helps prevent output synchronization problems for
110 110 # clients.
111 111 # Units are in seconds. The minimum zmq latency on local host is probably
112 112 # ~150 microseconds, set this to 500us for now. We may need to increase it
113 113 # a little if it's not enough after more interactive testing.
114 114 _execute_sleep = Float(0.0005, config=True)
115 115
116 116 # Frequency of the kernel's event loop.
117 117 # Units are in seconds, kernel subclasses for GUI toolkits may need to
118 118 # adapt to milliseconds.
119 119 _poll_interval = Float(0.05, config=True)
120 120
121 121 # If the shutdown was requested over the network, we leave here the
122 122 # necessary reply message so it can be sent by our registered atexit
123 123 # handler. This ensures that the reply is only sent to clients truly at
124 124 # the end of our shutdown process (which happens after the underlying
125 125 # IPython shell's own shutdown).
126 126 _shutdown_message = None
127 127
128 128 # This is a dict of port number that the kernel is listening on. It is set
129 129 # by record_ports and used by connect_request.
130 130 _recorded_ports = Dict()
131 131
132 132 # A reference to the Python builtin 'raw_input' function.
133 133 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
134 134 _sys_raw_input = Any()
135 135
136 136 # set of aborted msg_ids
137 137 aborted = Set()
138 138
139 139
140 140 def __init__(self, **kwargs):
141 141 super(Kernel, self).__init__(**kwargs)
142 142
143 143 # Initialize the InteractiveShell subclass
144 144 self.shell = self.shell_class.instance(config=self.config,
145 145 profile_dir = self.profile_dir,
146 146 user_module = self.user_module,
147 147 user_ns = self.user_ns,
148 148 )
149 149 self.shell.displayhook.session = self.session
150 150 self.shell.displayhook.pub_socket = self.iopub_socket
151 151 self.shell.displayhook.topic = self._topic('pyout')
152 152 self.shell.display_pub.session = self.session
153 153 self.shell.display_pub.pub_socket = self.iopub_socket
154 154 self.shell.data_pub.session = self.session
155 155 self.shell.data_pub.pub_socket = self.iopub_socket
156 156
157 157 # TMP - hack while developing
158 158 self.shell._reply_content = None
159 159
160 160 # Build dict of handlers for message types
161 161 msg_types = [ 'execute_request', 'complete_request',
162 162 'object_info_request', 'history_request',
163 163 'kernel_info_request',
164 164 'connect_request', 'shutdown_request',
165 165 'apply_request',
166 166 ]
167 167 self.shell_handlers = {}
168 168 for msg_type in msg_types:
169 169 self.shell_handlers[msg_type] = getattr(self, msg_type)
170 170
171 171 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
172 172 self.control_handlers = {}
173 173 for msg_type in control_msg_types:
174 174 self.control_handlers[msg_type] = getattr(self, msg_type)
175 175
176 176 def dispatch_control(self, msg):
177 177 """dispatch control requests"""
178 178 idents,msg = self.session.feed_identities(msg, copy=False)
179 179 try:
180 180 msg = self.session.unserialize(msg, content=True, copy=False)
181 181 except:
182 182 self.log.error("Invalid Control Message", exc_info=True)
183 183 return
184 184
185 185 self.log.debug("Control received: %s", msg)
186 186
187 187 header = msg['header']
188 188 msg_id = header['msg_id']
189 189 msg_type = header['msg_type']
190 190
191 191 handler = self.control_handlers.get(msg_type, None)
192 192 if handler is None:
193 193 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
194 194 else:
195 195 try:
196 196 handler(self.control_stream, idents, msg)
197 197 except Exception:
198 198 self.log.error("Exception in control handler:", exc_info=True)
199 199
200 200 def dispatch_shell(self, stream, msg):
201 201 """dispatch shell requests"""
202 202 # flush control requests first
203 203 if self.control_stream:
204 204 self.control_stream.flush()
205 205
206 206 idents,msg = self.session.feed_identities(msg, copy=False)
207 207 try:
208 208 msg = self.session.unserialize(msg, content=True, copy=False)
209 209 except:
210 210 self.log.error("Invalid Message", exc_info=True)
211 211 return
212 212
213 213 header = msg['header']
214 214 msg_id = header['msg_id']
215 215 msg_type = msg['header']['msg_type']
216 216
217 217 # Print some info about this message and leave a '--->' marker, so it's
218 218 # easier to trace visually the message chain when debugging. Each
219 219 # handler prints its message at the end.
220 220 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
221 221 self.log.debug(' Content: %s\n --->\n ', msg['content'])
222 222
223 223 if msg_id in self.aborted:
224 224 self.aborted.remove(msg_id)
225 225 # is it safe to assume a msg_id will not be resubmitted?
226 226 reply_type = msg_type.split('_')[0] + '_reply'
227 227 status = {'status' : 'aborted'}
228 228 md = {'engine' : self.ident}
229 229 md.update(status)
230 230 reply_msg = self.session.send(stream, reply_type, metadata=md,
231 231 content=status, parent=msg, ident=idents)
232 232 return
233 233
234 234 handler = self.shell_handlers.get(msg_type, None)
235 235 if handler is None:
236 236 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
237 237 else:
238 238 # ensure default_int_handler during handler call
239 239 sig = signal(SIGINT, default_int_handler)
240 240 try:
241 241 handler(stream, idents, msg)
242 242 except Exception:
243 243 self.log.error("Exception in message handler:", exc_info=True)
244 244 finally:
245 245 signal(SIGINT, sig)
246 246
247 247 def enter_eventloop(self):
248 248 """enter eventloop"""
249 249 self.log.info("entering eventloop")
250 250 # restore default_int_handler
251 251 signal(SIGINT, default_int_handler)
252 252 while self.eventloop is not None:
253 253 try:
254 254 self.eventloop(self)
255 255 except KeyboardInterrupt:
256 256 # Ctrl-C shouldn't crash the kernel
257 257 self.log.error("KeyboardInterrupt caught in kernel")
258 258 continue
259 259 else:
260 260 # eventloop exited cleanly, this means we should stop (right?)
261 261 self.eventloop = None
262 262 break
263 263 self.log.info("exiting eventloop")
264 264
265 265 def start(self):
266 266 """register dispatchers for streams"""
267 267 self.shell.exit_now = False
268 268 if self.control_stream:
269 269 self.control_stream.on_recv(self.dispatch_control, copy=False)
270 270
271 271 def make_dispatcher(stream):
272 272 def dispatcher(msg):
273 273 return self.dispatch_shell(stream, msg)
274 274 return dispatcher
275 275
276 276 for s in self.shell_streams:
277 277 s.on_recv(make_dispatcher(s), copy=False)
278 278
279 279 # publish idle status
280 280 self._publish_status('starting')
281 281
282 282 def do_one_iteration(self):
283 283 """step eventloop just once"""
284 284 if self.control_stream:
285 285 self.control_stream.flush()
286 286 for stream in self.shell_streams:
287 287 # handle at most one request per iteration
288 288 stream.flush(zmq.POLLIN, 1)
289 289 stream.flush(zmq.POLLOUT)
290 290
291 291
292 292 def record_ports(self, ports):
293 293 """Record the ports that this kernel is using.
294 294
295 295 The creator of the Kernel instance must call this methods if they
296 296 want the :meth:`connect_request` method to return the port numbers.
297 297 """
298 298 self._recorded_ports = ports
299 299
300 300 #---------------------------------------------------------------------------
301 301 # Kernel request handlers
302 302 #---------------------------------------------------------------------------
303 303
304 304 def _make_metadata(self, other=None):
305 305 """init metadata dict, for execute/apply_reply"""
306 306 new_md = {
307 307 'dependencies_met' : True,
308 308 'engine' : self.ident,
309 309 'started': datetime.now(),
310 310 }
311 311 if other:
312 312 new_md.update(other)
313 313 return new_md
314 314
315 315 def _publish_pyin(self, code, parent, execution_count):
316 316 """Publish the code request on the pyin stream."""
317 317
318 318 self.session.send(self.iopub_socket, u'pyin',
319 319 {u'code':code, u'execution_count': execution_count},
320 320 parent=parent, ident=self._topic('pyin')
321 321 )
322 322
323 323 def _publish_status(self, status, parent=None):
324 324 """send status (busy/idle) on IOPub"""
325 325 self.session.send(self.iopub_socket,
326 326 u'status',
327 327 {u'execution_state': status},
328 328 parent=parent,
329 329 ident=self._topic('status'),
330 330 )
331 331
332 332
333 333 def execute_request(self, stream, ident, parent):
334 334 """handle an execute_request"""
335 335
336 336 self._publish_status(u'busy', parent)
337 337
338 338 try:
339 339 content = parent[u'content']
340 340 code = content[u'code']
341 341 silent = content[u'silent']
342 342 store_history = content.get(u'store_history', not silent)
343 343 except:
344 344 self.log.error("Got bad msg: ")
345 345 self.log.error("%s", parent)
346 346 return
347 347
348 348 md = self._make_metadata(parent['metadata'])
349 349
350 350 shell = self.shell # we'll need this a lot here
351 351
352 352 # Replace raw_input. Note that is not sufficient to replace
353 353 # raw_input in the user namespace.
354 354 if content.get('allow_stdin', False):
355 355 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
356 356 else:
357 357 raw_input = lambda prompt='' : self._no_raw_input()
358 358
359 359 if py3compat.PY3:
360 360 self._sys_raw_input = __builtin__.input
361 361 __builtin__.input = raw_input
362 362 else:
363 363 self._sys_raw_input = __builtin__.raw_input
364 364 __builtin__.raw_input = raw_input
365 365
366 366 # Set the parent message of the display hook and out streams.
367 367 shell.displayhook.set_parent(parent)
368 368 shell.display_pub.set_parent(parent)
369 369 shell.data_pub.set_parent(parent)
370 370 try:
371 371 sys.stdout.set_parent(parent)
372 372 except AttributeError:
373 373 pass
374 374 try:
375 375 sys.stderr.set_parent(parent)
376 376 except AttributeError:
377 377 pass
378 378
379 379 # Re-broadcast our input for the benefit of listening clients, and
380 380 # start computing output
381 381 if not silent:
382 382 self._publish_pyin(code, parent, shell.execution_count)
383 383
384 384 reply_content = {}
385 385 try:
386 386 # FIXME: the shell calls the exception handler itself.
387 387 shell.run_cell(code, store_history=store_history, silent=silent)
388 388 except:
389 389 status = u'error'
390 390 # FIXME: this code right now isn't being used yet by default,
391 391 # because the run_cell() call above directly fires off exception
392 392 # reporting. This code, therefore, is only active in the scenario
393 393 # where runlines itself has an unhandled exception. We need to
394 394 # uniformize this, for all exception construction to come from a
395 395 # single location in the codbase.
396 396 etype, evalue, tb = sys.exc_info()
397 397 tb_list = traceback.format_exception(etype, evalue, tb)
398 398 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
399 399 else:
400 400 status = u'ok'
401 401 finally:
402 402 # Restore raw_input.
403 403 if py3compat.PY3:
404 404 __builtin__.input = self._sys_raw_input
405 405 else:
406 406 __builtin__.raw_input = self._sys_raw_input
407 407
408 408 reply_content[u'status'] = status
409 409
410 410 # Return the execution counter so clients can display prompts
411 411 reply_content['execution_count'] = shell.execution_count - 1
412 412
413 413 # FIXME - fish exception info out of shell, possibly left there by
414 414 # runlines. We'll need to clean up this logic later.
415 415 if shell._reply_content is not None:
416 416 reply_content.update(shell._reply_content)
417 417 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
418 418 reply_content['engine_info'] = e_info
419 419 # reset after use
420 420 shell._reply_content = None
421 421
422 422 if 'traceback' in reply_content:
423 423 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
424 424
425 425
426 426 # At this point, we can tell whether the main code execution succeeded
427 427 # or not. If it did, we proceed to evaluate user_variables/expressions
428 428 if reply_content['status'] == 'ok':
429 429 reply_content[u'user_variables'] = \
430 430 shell.user_variables(content.get(u'user_variables', []))
431 431 reply_content[u'user_expressions'] = \
432 432 shell.user_expressions(content.get(u'user_expressions', {}))
433 433 else:
434 434 # If there was an error, don't even try to compute variables or
435 435 # expressions
436 436 reply_content[u'user_variables'] = {}
437 437 reply_content[u'user_expressions'] = {}
438 438
439 439 # Payloads should be retrieved regardless of outcome, so we can both
440 440 # recover partial output (that could have been generated early in a
441 441 # block, before an error) and clear the payload system always.
442 442 reply_content[u'payload'] = shell.payload_manager.read_payload()
443 443 # Be agressive about clearing the payload because we don't want
444 444 # it to sit in memory until the next execute_request comes in.
445 445 shell.payload_manager.clear_payload()
446 446
447 447 # Flush output before sending the reply.
448 448 sys.stdout.flush()
449 449 sys.stderr.flush()
450 450 # FIXME: on rare occasions, the flush doesn't seem to make it to the
451 451 # clients... This seems to mitigate the problem, but we definitely need
452 452 # to better understand what's going on.
453 453 if self._execute_sleep:
454 454 time.sleep(self._execute_sleep)
455 455
456 456 # Send the reply.
457 457 reply_content = json_clean(reply_content)
458 458
459 459 md['status'] = reply_content['status']
460 460 if reply_content['status'] == 'error' and \
461 461 reply_content['ename'] == 'UnmetDependency':
462 462 md['dependencies_met'] = False
463 463
464 464 reply_msg = self.session.send(stream, u'execute_reply',
465 465 reply_content, parent, metadata=md,
466 466 ident=ident)
467 467
468 468 self.log.debug("%s", reply_msg)
469 469
470 470 if not silent and reply_msg['content']['status'] == u'error':
471 471 self._abort_queues()
472 472
473 473 self._publish_status(u'idle', parent)
474 474
475 475 def complete_request(self, stream, ident, parent):
476 476 txt, matches = self._complete(parent)
477 477 matches = {'matches' : matches,
478 478 'matched_text' : txt,
479 479 'status' : 'ok'}
480 480 matches = json_clean(matches)
481 481 completion_msg = self.session.send(stream, 'complete_reply',
482 482 matches, parent, ident)
483 483 self.log.debug("%s", completion_msg)
484 484
485 485 def object_info_request(self, stream, ident, parent):
486 486 content = parent['content']
487 487 object_info = self.shell.object_inspect(content['oname'],
488 488 detail_level = content.get('detail_level', 0)
489 489 )
490 490 # Before we send this object over, we scrub it for JSON usage
491 491 oinfo = json_clean(object_info)
492 492 msg = self.session.send(stream, 'object_info_reply',
493 493 oinfo, parent, ident)
494 494 self.log.debug("%s", msg)
495 495
496 496 def history_request(self, stream, ident, parent):
497 497 # We need to pull these out, as passing **kwargs doesn't work with
498 498 # unicode keys before Python 2.6.5.
499 499 hist_access_type = parent['content']['hist_access_type']
500 500 raw = parent['content']['raw']
501 501 output = parent['content']['output']
502 502 if hist_access_type == 'tail':
503 503 n = parent['content']['n']
504 504 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
505 505 include_latest=True)
506 506
507 507 elif hist_access_type == 'range':
508 508 session = parent['content']['session']
509 509 start = parent['content']['start']
510 510 stop = parent['content']['stop']
511 511 hist = self.shell.history_manager.get_range(session, start, stop,
512 512 raw=raw, output=output)
513 513
514 514 elif hist_access_type == 'search':
515 515 n = parent['content'].get('n')
516 516 unique = parent['content'].get('unique', False)
517 517 pattern = parent['content']['pattern']
518 518 hist = self.shell.history_manager.search(
519 519 pattern, raw=raw, output=output, n=n, unique=unique)
520 520
521 521 else:
522 522 hist = []
523 523 hist = list(hist)
524 524 content = {'history' : hist}
525 525 content = json_clean(content)
526 526 msg = self.session.send(stream, 'history_reply',
527 527 content, parent, ident)
528 528 self.log.debug("Sending history reply with %i entries", len(hist))
529 529
530 530 def connect_request(self, stream, ident, parent):
531 531 if self._recorded_ports is not None:
532 532 content = self._recorded_ports.copy()
533 533 else:
534 534 content = {}
535 535 msg = self.session.send(stream, 'connect_reply',
536 536 content, parent, ident)
537 537 self.log.debug("%s", msg)
538 538
539 539 def kernel_info_request(self, stream, ident, parent):
540 540 vinfo = {
541 541 'protocol_version': protocol_version,
542 542 'ipython_version': ipython_version,
543 543 'language_version': language_version,
544 544 'language': 'python',
545 545 }
546 546 msg = self.session.send(stream, 'kernel_info_reply',
547 547 vinfo, parent, ident)
548 548 self.log.debug("%s", msg)
549 549
550 550 def shutdown_request(self, stream, ident, parent):
551 551 self.shell.exit_now = True
552 552 content = dict(status='ok')
553 553 content.update(parent['content'])
554 554 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
555 555 # same content, but different msg_id for broadcasting on IOPub
556 556 self._shutdown_message = self.session.msg(u'shutdown_reply',
557 557 content, parent
558 558 )
559 559
560 560 self._at_shutdown()
561 561 # call sys.exit after a short delay
562 562 loop = ioloop.IOLoop.instance()
563 563 loop.add_timeout(time.time()+0.1, loop.stop)
564 564
565 565 #---------------------------------------------------------------------------
566 566 # Engine methods
567 567 #---------------------------------------------------------------------------
568 568
569 569 def apply_request(self, stream, ident, parent):
570 570 try:
571 571 content = parent[u'content']
572 572 bufs = parent[u'buffers']
573 573 msg_id = parent['header']['msg_id']
574 574 except:
575 575 self.log.error("Got bad msg: %s", parent, exc_info=True)
576 576 return
577 577
578 578 self._publish_status(u'busy', parent)
579 579
580 580 # Set the parent message of the display hook and out streams.
581 581 shell = self.shell
582 582 shell.displayhook.set_parent(parent)
583 583 shell.display_pub.set_parent(parent)
584 584 shell.data_pub.set_parent(parent)
585 585 try:
586 586 sys.stdout.set_parent(parent)
587 587 except AttributeError:
588 588 pass
589 589 try:
590 590 sys.stderr.set_parent(parent)
591 591 except AttributeError:
592 592 pass
593 593
594 594 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
595 595 # self.iopub_socket.send(pyin_msg)
596 596 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
597 597 md = self._make_metadata(parent['metadata'])
598 598 try:
599 599 working = shell.user_ns
600 600
601 601 prefix = "_"+str(msg_id).replace("-","")+"_"
602 602
603 603 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
604 604
605 605 fname = getattr(f, '__name__', 'f')
606 606
607 607 fname = prefix+"f"
608 608 argname = prefix+"args"
609 609 kwargname = prefix+"kwargs"
610 610 resultname = prefix+"result"
611 611
612 612 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
613 613 # print ns
614 614 working.update(ns)
615 615 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
616 616 try:
617 617 exec code in shell.user_global_ns, shell.user_ns
618 618 result = working.get(resultname)
619 619 finally:
620 620 for key in ns.iterkeys():
621 621 working.pop(key)
622 622
623 623 result_buf = serialize_object(result,
624 624 buffer_threshold=self.session.buffer_threshold,
625 625 item_threshold=self.session.item_threshold,
626 626 )
627 627
628 628 except:
629 629 # invoke IPython traceback formatting
630 630 shell.showtraceback()
631 631 # FIXME - fish exception info out of shell, possibly left there by
632 632 # run_code. We'll need to clean up this logic later.
633 633 reply_content = {}
634 634 if shell._reply_content is not None:
635 635 reply_content.update(shell._reply_content)
636 636 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
637 637 reply_content['engine_info'] = e_info
638 638 # reset after use
639 639 shell._reply_content = None
640 640
641 641 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
642 642 ident=self._topic('pyerr'))
643 643 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
644 644 result_buf = []
645 645
646 646 if reply_content['ename'] == 'UnmetDependency':
647 647 md['dependencies_met'] = False
648 648 else:
649 649 reply_content = {'status' : 'ok'}
650 650
651 651 # put 'ok'/'error' status in header, for scheduler introspection:
652 652 md['status'] = reply_content['status']
653 653
654 654 # flush i/o
655 655 sys.stdout.flush()
656 656 sys.stderr.flush()
657 657
658 658 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
659 659 parent=parent, ident=ident,buffers=result_buf, metadata=md)
660 660
661 661 self._publish_status(u'idle', parent)
662 662
663 663 #---------------------------------------------------------------------------
664 664 # Control messages
665 665 #---------------------------------------------------------------------------
666 666
667 667 def abort_request(self, stream, ident, parent):
668 668 """abort a specifig msg by id"""
669 669 msg_ids = parent['content'].get('msg_ids', None)
670 670 if isinstance(msg_ids, basestring):
671 671 msg_ids = [msg_ids]
672 672 if not msg_ids:
673 673 self.abort_queues()
674 674 for mid in msg_ids:
675 675 self.aborted.add(str(mid))
676 676
677 677 content = dict(status='ok')
678 678 reply_msg = self.session.send(stream, 'abort_reply', content=content,
679 679 parent=parent, ident=ident)
680 680 self.log.debug("%s", reply_msg)
681 681
682 682 def clear_request(self, stream, idents, parent):
683 683 """Clear our namespace."""
684 684 self.shell.reset(False)
685 685 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
686 686 content = dict(status='ok'))
687 687
688 688
689 689 #---------------------------------------------------------------------------
690 690 # Protected interface
691 691 #---------------------------------------------------------------------------
692 692
693 693 def _wrap_exception(self, method=None):
694 694 # import here, because _wrap_exception is only used in parallel,
695 695 # and parallel has higher min pyzmq version
696 696 from IPython.parallel.error import wrap_exception
697 697 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
698 698 content = wrap_exception(e_info)
699 699 return content
700 700
701 701 def _topic(self, topic):
702 702 """prefixed topic for IOPub messages"""
703 703 if self.int_id >= 0:
704 704 base = "engine.%i" % self.int_id
705 705 else:
706 706 base = "kernel.%s" % self.ident
707 707
708 708 return py3compat.cast_bytes("%s.%s" % (base, topic))
709 709
710 710 def _abort_queues(self):
711 711 for stream in self.shell_streams:
712 712 if stream:
713 713 self._abort_queue(stream)
714 714
715 715 def _abort_queue(self, stream):
716 716 poller = zmq.Poller()
717 717 poller.register(stream.socket, zmq.POLLIN)
718 718 while True:
719 719 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
720 720 if msg is None:
721 721 return
722 722
723 723 self.log.info("Aborting:")
724 724 self.log.info("%s", msg)
725 725 msg_type = msg['header']['msg_type']
726 726 reply_type = msg_type.split('_')[0] + '_reply'
727 727
728 728 status = {'status' : 'aborted'}
729 729 md = {'engine' : self.ident}
730 730 md.update(status)
731 731 reply_msg = self.session.send(stream, reply_type, metadata=md,
732 732 content=status, parent=msg, ident=idents)
733 733 self.log.debug("%s", reply_msg)
734 734 # We need to wait a bit for requests to come in. This can probably
735 735 # be set shorter for true asynchronous clients.
736 736 poller.poll(50)
737 737
738 738
739 739 def _no_raw_input(self):
740 740 """Raise StdinNotImplentedError if active frontend doesn't support
741 741 stdin."""
742 742 raise StdinNotImplementedError("raw_input was called, but this "
743 743 "frontend does not support stdin.")
744 744
745 745 def _raw_input(self, prompt, ident, parent):
746 746 # Flush output before making the request.
747 747 sys.stderr.flush()
748 748 sys.stdout.flush()
749
749 # flush the stdin socket, to purge stale replies
750 while True:
751 try:
752 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
753 except zmq.ZMQError as e:
754 if e.errno == zmq.EAGAIN:
755 break
756 else:
757 raise
758
750 759 # Send the input request.
751 760 content = json_clean(dict(prompt=prompt))
752 761 self.session.send(self.stdin_socket, u'input_request', content, parent,
753 762 ident=ident)
754 763
755 764 # Await a response.
756 765 while True:
757 766 try:
758 767 ident, reply = self.session.recv(self.stdin_socket, 0)
759 768 except Exception:
760 769 self.log.warn("Invalid Message:", exc_info=True)
761 770 else:
762 771 break
763 772 try:
764 773 value = reply['content']['value']
765 774 except:
766 775 self.log.error("Got bad raw_input reply: ")
767 776 self.log.error("%s", parent)
768 777 value = ''
769 778 if value == '\x04':
770 779 # EOF
771 780 raise EOFError
772 781 return value
773 782
774 783 def _complete(self, msg):
775 784 c = msg['content']
776 785 try:
777 786 cpos = int(c['cursor_pos'])
778 787 except:
779 788 # If we don't get something that we can convert to an integer, at
780 789 # least attempt the completion guessing the cursor is at the end of
781 790 # the text, if there's any, and otherwise of the line
782 791 cpos = len(c['text'])
783 792 if cpos==0:
784 793 cpos = len(c['line'])
785 794 return self.shell.complete(c['text'], c['line'], cpos)
786 795
787 796 def _at_shutdown(self):
788 797 """Actions taken at shutdown by the kernel, called by python's atexit.
789 798 """
790 799 # io.rprint("Kernel at_shutdown") # dbg
791 800 if self._shutdown_message is not None:
792 801 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
793 802 self.log.debug("%s", self._shutdown_message)
794 803 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
795 804
General Comments 0
You need to be logged in to leave comments. Login now