##// END OF EJS Templates
Merge branch 'master' of github.com:ipython/ipython
Brian E. Granger -
r15980:8eca9149 merge
parent child Browse files
Show More
@@ -1,566 +1,552 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 // Cell
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 Cell
16 16 */
17 17
18 18 var IPython = (function (IPython) {
19 19 "use strict";
20 20
21 21 var utils = IPython.utils;
22 22 var keycodes = IPython.keyboard.keycodes;
23 23
24 24 /**
25 25 * The Base `Cell` class from which to inherit
26 26 * @class Cell
27 27 **/
28 28
29 29 /*
30 30 * @constructor
31 31 *
32 32 * @param {object|undefined} [options]
33 33 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
34 34 */
35 35 var Cell = function (options) {
36 36
37 37 options = this.mergeopt(Cell, options);
38 38 // superclass default overwrite our default
39 39
40 40 this.placeholder = options.placeholder || '';
41 41 this.read_only = options.cm_config.readOnly;
42 42 this.selected = false;
43 43 this.rendered = false;
44 44 this.mode = 'command';
45 45 this.metadata = {};
46 46 // load this from metadata later ?
47 47 this.user_highlight = 'auto';
48 48 this.cm_config = options.cm_config;
49 49 this.cell_id = utils.uuid();
50 50 this._options = options;
51 51
52 52 // For JS VM engines optimization, attributes should be all set (even
53 53 // to null) in the constructor, and if possible, if different subclass
54 54 // have new attributes with same name, they should be created in the
55 55 // same order. Easiest is to create and set to null in parent class.
56 56
57 57 this.element = null;
58 58 this.cell_type = this.cell_type || null;
59 59 this.code_mirror = null;
60 60
61 61 this.create_element();
62 62 if (this.element !== null) {
63 63 this.element.data("cell", this);
64 64 this.bind_events();
65 65 this.init_classes();
66 66 }
67 67 };
68 68
69 69 Cell.options_default = {
70 70 cm_config : {
71 71 indentUnit : 4,
72 72 readOnly: false,
73 73 theme: "default"
74 74 }
75 75 };
76 76
77 77 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
78 78 // by disabling drag/drop altogether on Safari
79 79 // https://github.com/marijnh/CodeMirror/issues/332
80 80 if (utils.browser[0] == "Safari") {
81 81 Cell.options_default.cm_config.dragDrop = false;
82 82 }
83 83
84 84 Cell.prototype.mergeopt = function(_class, options, overwrite){
85 85 options = options || {};
86 86 overwrite = overwrite || {};
87 87 return $.extend(true, {}, _class.options_default, options, overwrite);
88 88 };
89 89
90 90 /**
91 91 * Empty. Subclasses must implement create_element.
92 92 * This should contain all the code to create the DOM element in notebook
93 93 * and will be called by Base Class constructor.
94 94 * @method create_element
95 95 */
96 96 Cell.prototype.create_element = function () {
97 97 };
98 98
99 99 Cell.prototype.init_classes = function () {
100 100 // Call after this.element exists to initialize the css classes
101 101 // related to selected, rendered and mode.
102 102 if (this.selected) {
103 103 this.element.addClass('selected');
104 104 } else {
105 105 this.element.addClass('unselected');
106 106 }
107 107 if (this.rendered) {
108 108 this.element.addClass('rendered');
109 109 } else {
110 110 this.element.addClass('unrendered');
111 111 }
112 112 if (this.mode === 'edit') {
113 113 this.element.addClass('edit_mode');
114 114 } else {
115 115 this.element.addClass('command_mode');
116 116 }
117 117 };
118 118
119 119 /**
120 120 * Subclasses can implement override bind_events.
121 121 * Be carefull to call the parent method when overwriting as it fires event.
122 122 * this will be triggerd after create_element in constructor.
123 123 * @method bind_events
124 124 */
125 125 Cell.prototype.bind_events = function () {
126 126 var that = this;
127 127 // We trigger events so that Cell doesn't have to depend on Notebook.
128 128 that.element.click(function (event) {
129 129 if (!that.selected) {
130 130 $([IPython.events]).trigger('select.Cell', {'cell':that});
131 131 }
132 132 });
133 133 that.element.focusin(function (event) {
134 134 if (!that.selected) {
135 135 $([IPython.events]).trigger('select.Cell', {'cell':that});
136 136 }
137 137 });
138 138 if (this.code_mirror) {
139 139 this.code_mirror.on("change", function(cm, change) {
140 140 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
141 141 });
142 142 }
143 143 if (this.code_mirror) {
144 144 this.code_mirror.on('focus', function(cm, change) {
145 145 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
146 146 });
147 147 }
148 148 if (this.code_mirror) {
149 149 this.code_mirror.on('blur', function(cm, change) {
150 // Check if this unfocus event is legit.
151 if (!that.should_cancel_blur()) {
152 $([IPython.events]).trigger('command_mode.Cell', {cell: that});
153 }
150 $([IPython.events]).trigger('command_mode.Cell', {cell: that});
154 151 });
155 152 }
156 153 };
157 154
158 155 /**
159 156 * This method gets called in CodeMirror's onKeyDown/onKeyPress
160 157 * handlers and is used to provide custom key handling.
161 158 *
162 159 * To have custom handling, subclasses should override this method, but still call it
163 160 * in order to process the Edit mode keyboard shortcuts.
164 161 *
165 162 * @method handle_codemirror_keyevent
166 163 * @param {CodeMirror} editor - The codemirror instance bound to the cell
167 164 * @param {event} event - key press event which either should or should not be handled by CodeMirror
168 165 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
169 166 */
170 167 Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
171 168 var that = this;
172 169 var shortcuts = IPython.keyboard_manager.edit_shortcuts;
173 170
174 171 // if this is an edit_shortcuts shortcut, the global keyboard/shortcut
175 172 // manager will handle it
176 173 if (shortcuts.handles(event)) { return true; }
177 174
178 175 return false;
179 176 };
180 177
181 178
182 179 /**
183 180 * Triger typsetting of math by mathjax on current cell element
184 181 * @method typeset
185 182 */
186 183 Cell.prototype.typeset = function () {
187 184 if (window.MathJax) {
188 185 var cell_math = this.element.get(0);
189 186 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
190 187 }
191 188 };
192 189
193 190 /**
194 191 * handle cell level logic when a cell is selected
195 192 * @method select
196 193 * @return is the action being taken
197 194 */
198 195 Cell.prototype.select = function () {
199 196 if (!this.selected) {
200 197 this.element.addClass('selected');
201 198 this.element.removeClass('unselected');
202 199 this.selected = true;
203 200 return true;
204 201 } else {
205 202 return false;
206 203 }
207 204 };
208 205
209 206 /**
210 207 * handle cell level logic when a cell is unselected
211 208 * @method unselect
212 209 * @return is the action being taken
213 210 */
214 211 Cell.prototype.unselect = function () {
215 212 if (this.selected) {
216 213 this.element.addClass('unselected');
217 214 this.element.removeClass('selected');
218 215 this.selected = false;
219 216 return true;
220 217 } else {
221 218 return false;
222 219 }
223 220 };
224 221
225 222 /**
226 223 * handle cell level logic when a cell is rendered
227 224 * @method render
228 225 * @return is the action being taken
229 226 */
230 227 Cell.prototype.render = function () {
231 228 if (!this.rendered) {
232 229 this.element.addClass('rendered');
233 230 this.element.removeClass('unrendered');
234 231 this.rendered = true;
235 232 return true;
236 233 } else {
237 234 return false;
238 235 }
239 236 };
240 237
241 238 /**
242 239 * handle cell level logic when a cell is unrendered
243 240 * @method unrender
244 241 * @return is the action being taken
245 242 */
246 243 Cell.prototype.unrender = function () {
247 244 if (this.rendered) {
248 245 this.element.addClass('unrendered');
249 246 this.element.removeClass('rendered');
250 247 this.rendered = false;
251 248 return true;
252 249 } else {
253 250 return false;
254 251 }
255 252 };
256 253
257 254 /**
258 255 * Delegates keyboard shortcut handling to either IPython keyboard
259 256 * manager when in command mode, or CodeMirror when in edit mode
260 257 *
261 258 * @method handle_keyevent
262 259 * @param {CodeMirror} editor - The codemirror instance bound to the cell
263 260 * @param {event} - key event to be handled
264 261 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
265 262 */
266 263 Cell.prototype.handle_keyevent = function (editor, event) {
267 264
268 265 // console.log('CM', this.mode, event.which, event.type)
269 266
270 267 if (this.mode === 'command') {
271 268 return true;
272 269 } else if (this.mode === 'edit') {
273 270 return this.handle_codemirror_keyevent(editor, event);
274 271 }
275 272 };
276 273
277 274 /**
278 275 * @method at_top
279 276 * @return {Boolean}
280 277 */
281 278 Cell.prototype.at_top = function () {
282 279 var cm = this.code_mirror;
283 280 var cursor = cm.getCursor();
284 281 if (cursor.line === 0 && cursor.ch === 0) {
285 282 return true;
286 283 }
287 284 return false;
288 285 };
289 286
290 287 /**
291 288 * @method at_bottom
292 289 * @return {Boolean}
293 290 * */
294 291 Cell.prototype.at_bottom = function () {
295 292 var cm = this.code_mirror;
296 293 var cursor = cm.getCursor();
297 294 if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
298 295 return true;
299 296 }
300 297 return false;
301 298 };
302 299
303 300 /**
304 301 * enter the command mode for the cell
305 302 * @method command_mode
306 303 * @return is the action being taken
307 304 */
308 305 Cell.prototype.command_mode = function () {
309 306 if (this.mode !== 'command') {
310 307 this.element.addClass('command_mode');
311 308 this.element.removeClass('edit_mode');
312 309 this.mode = 'command';
313 310 return true;
314 311 } else {
315 312 return false;
316 313 }
317 314 };
318 315
319 316 /**
320 317 * enter the edit mode for the cell
321 318 * @method command_mode
322 319 * @return is the action being taken
323 320 */
324 321 Cell.prototype.edit_mode = function () {
325 322 if (this.mode !== 'edit') {
326 323 this.element.addClass('edit_mode');
327 324 this.element.removeClass('command_mode');
328 325 this.mode = 'edit';
329 326 return true;
330 327 } else {
331 328 return false;
332 329 }
333 330 };
334
335 /**
336 * Determine whether or not the unfocus event should be aknowledged.
337 *
338 * @method should_cancel_blur
339 *
340 * @return results {bool} Whether or not to ignore the cell's blur event.
341 **/
342 Cell.prototype.should_cancel_blur = function () {
343 return false;
344 };
345
331
346 332 /**
347 333 * Focus the cell in the DOM sense
348 334 * @method focus_cell
349 335 */
350 336 Cell.prototype.focus_cell = function () {
351 337 this.element.focus();
352 338 };
353 339
354 340 /**
355 341 * Focus the editor area so a user can type
356 342 *
357 343 * NOTE: If codemirror is focused via a mouse click event, you don't want to
358 344 * call this because it will cause a page jump.
359 345 * @method focus_editor
360 346 */
361 347 Cell.prototype.focus_editor = function () {
362 348 this.refresh();
363 349 this.code_mirror.focus();
364 350 };
365 351
366 352 /**
367 353 * Refresh codemirror instance
368 354 * @method refresh
369 355 */
370 356 Cell.prototype.refresh = function () {
371 357 this.code_mirror.refresh();
372 358 };
373 359
374 360 /**
375 361 * should be overritten by subclass
376 362 * @method get_text
377 363 */
378 364 Cell.prototype.get_text = function () {
379 365 };
380 366
381 367 /**
382 368 * should be overritten by subclass
383 369 * @method set_text
384 370 * @param {string} text
385 371 */
386 372 Cell.prototype.set_text = function (text) {
387 373 };
388 374
389 375 /**
390 376 * should be overritten by subclass
391 377 * serialise cell to json.
392 378 * @method toJSON
393 379 **/
394 380 Cell.prototype.toJSON = function () {
395 381 var data = {};
396 382 data.metadata = this.metadata;
397 383 data.cell_type = this.cell_type;
398 384 return data;
399 385 };
400 386
401 387
402 388 /**
403 389 * should be overritten by subclass
404 390 * @method fromJSON
405 391 **/
406 392 Cell.prototype.fromJSON = function (data) {
407 393 if (data.metadata !== undefined) {
408 394 this.metadata = data.metadata;
409 395 }
410 396 this.celltoolbar.rebuild();
411 397 };
412 398
413 399
414 400 /**
415 401 * can the cell be split into two cells
416 402 * @method is_splittable
417 403 **/
418 404 Cell.prototype.is_splittable = function () {
419 405 return true;
420 406 };
421 407
422 408
423 409 /**
424 410 * can the cell be merged with other cells
425 411 * @method is_mergeable
426 412 **/
427 413 Cell.prototype.is_mergeable = function () {
428 414 return true;
429 415 };
430 416
431 417
432 418 /**
433 419 * @return {String} - the text before the cursor
434 420 * @method get_pre_cursor
435 421 **/
436 422 Cell.prototype.get_pre_cursor = function () {
437 423 var cursor = this.code_mirror.getCursor();
438 424 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
439 425 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
440 426 return text;
441 427 };
442 428
443 429
444 430 /**
445 431 * @return {String} - the text after the cursor
446 432 * @method get_post_cursor
447 433 **/
448 434 Cell.prototype.get_post_cursor = function () {
449 435 var cursor = this.code_mirror.getCursor();
450 436 var last_line_num = this.code_mirror.lineCount()-1;
451 437 var last_line_len = this.code_mirror.getLine(last_line_num).length;
452 438 var end = {line:last_line_num, ch:last_line_len};
453 439 var text = this.code_mirror.getRange(cursor, end);
454 440 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
455 441 return text;
456 442 };
457 443
458 444 /**
459 445 * Show/Hide CodeMirror LineNumber
460 446 * @method show_line_numbers
461 447 *
462 448 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
463 449 **/
464 450 Cell.prototype.show_line_numbers = function (value) {
465 451 this.code_mirror.setOption('lineNumbers', value);
466 452 this.code_mirror.refresh();
467 453 };
468 454
469 455 /**
470 456 * Toggle CodeMirror LineNumber
471 457 * @method toggle_line_numbers
472 458 **/
473 459 Cell.prototype.toggle_line_numbers = function () {
474 460 var val = this.code_mirror.getOption('lineNumbers');
475 461 this.show_line_numbers(!val);
476 462 };
477 463
478 464 /**
479 465 * Force codemirror highlight mode
480 466 * @method force_highlight
481 467 * @param {object} - CodeMirror mode
482 468 **/
483 469 Cell.prototype.force_highlight = function(mode) {
484 470 this.user_highlight = mode;
485 471 this.auto_highlight();
486 472 };
487 473
488 474 /**
489 475 * Try to autodetect cell highlight mode, or use selected mode
490 476 * @methods _auto_highlight
491 477 * @private
492 478 * @param {String|object|undefined} - CodeMirror mode | 'auto'
493 479 **/
494 480 Cell.prototype._auto_highlight = function (modes) {
495 481 //Here we handle manually selected modes
496 482 var mode;
497 483 if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
498 484 {
499 485 mode = this.user_highlight;
500 486 CodeMirror.autoLoadMode(this.code_mirror, mode);
501 487 this.code_mirror.setOption('mode', mode);
502 488 return;
503 489 }
504 490 var current_mode = this.code_mirror.getOption('mode', mode);
505 491 var first_line = this.code_mirror.getLine(0);
506 492 // loop on every pairs
507 493 for(mode in modes) {
508 494 var regs = modes[mode].reg;
509 495 // only one key every time but regexp can't be keys...
510 496 for(var i=0; i<regs.length; i++) {
511 497 // here we handle non magic_modes
512 498 if(first_line.match(regs[i]) !== null) {
513 499 if(current_mode == mode){
514 500 return;
515 501 }
516 502 if (mode.search('magic_') !== 0) {
517 503 this.code_mirror.setOption('mode', mode);
518 504 CodeMirror.autoLoadMode(this.code_mirror, mode);
519 505 return;
520 506 }
521 507 var open = modes[mode].open || "%%";
522 508 var close = modes[mode].close || "%%end";
523 509 var mmode = mode;
524 510 mode = mmode.substr(6);
525 511 if(current_mode == mode){
526 512 return;
527 513 }
528 514 CodeMirror.autoLoadMode(this.code_mirror, mode);
529 515 // create on the fly a mode that swhitch between
530 516 // plain/text and smth else otherwise `%%` is
531 517 // source of some highlight issues.
532 518 // we use patchedGetMode to circumvent a bug in CM
533 519 CodeMirror.defineMode(mmode , function(config) {
534 520 return CodeMirror.multiplexingMode(
535 521 CodeMirror.patchedGetMode(config, 'text/plain'),
536 522 // always set someting on close
537 523 {open: open, close: close,
538 524 mode: CodeMirror.patchedGetMode(config, mode),
539 525 delimStyle: "delimit"
540 526 }
541 527 );
542 528 });
543 529 this.code_mirror.setOption('mode', mmode);
544 530 return;
545 531 }
546 532 }
547 533 }
548 534 // fallback on default
549 535 var default_mode;
550 536 try {
551 537 default_mode = this._options.cm_config.mode;
552 538 } catch(e) {
553 539 default_mode = 'text/plain';
554 540 }
555 541 if( current_mode === default_mode){
556 542 return;
557 543 }
558 544 this.code_mirror.setOption('mode', default_mode);
559 545 };
560 546
561 547 IPython.Cell = Cell;
562 548
563 549 return IPython;
564 550
565 551 }(IPython));
566 552
@@ -1,515 +1,517 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 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 keycodes = IPython.keyboard.keycodes;
49 49
50 50 /**
51 51 * A Cell conceived to write code.
52 52 *
53 53 * The kernel doesn't have to be set at creation time, in that case
54 54 * it will be null and set_kernel has to be called later.
55 55 * @class CodeCell
56 56 * @extends IPython.Cell
57 57 *
58 58 * @constructor
59 59 * @param {Object|null} kernel
60 60 * @param {object|undefined} [options]
61 61 * @param [options.cm_config] {object} config to pass to CodeMirror
62 62 */
63 63 var CodeCell = function (kernel, options) {
64 64 this.kernel = kernel || null;
65 65 this.collapsed = false;
66 66
67 67 // create all attributed in constructor function
68 68 // even if null for V8 VM optimisation
69 69 this.input_prompt_number = null;
70 70 this.celltoolbar = null;
71 71 this.output_area = null;
72 72 this.last_msg_id = null;
73 73 this.completer = null;
74 74
75 75
76 76 var cm_overwrite_options = {
77 77 onKeyEvent: $.proxy(this.handle_keyevent,this)
78 78 };
79 79
80 80 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
81 81
82 82 IPython.Cell.apply(this,[options]);
83 83
84 84 // Attributes we want to override in this subclass.
85 85 this.cell_type = "code";
86 86
87 87 var that = this;
88 88 this.element.focusout(
89 89 function() { that.auto_highlight(); }
90 90 );
91 91 };
92 92
93 93 CodeCell.options_default = {
94 94 cm_config : {
95 95 extraKeys: {
96 96 "Tab" : "indentMore",
97 97 "Shift-Tab" : "indentLess",
98 98 "Backspace" : "delSpaceToPrevTabStop",
99 99 "Cmd-/" : "toggleComment",
100 100 "Ctrl-/" : "toggleComment"
101 101 },
102 102 mode: 'ipython',
103 103 theme: 'ipython',
104 104 matchBrackets: true,
105 105 // don't auto-close strings because of CodeMirror #2385
106 106 autoCloseBrackets: "()[]{}"
107 107 }
108 108 };
109 109
110 110 CodeCell.msg_cells = {};
111 111
112 112 CodeCell.prototype = new IPython.Cell();
113 113
114 114 /**
115 115 * @method auto_highlight
116 116 */
117 117 CodeCell.prototype.auto_highlight = function () {
118 118 this._auto_highlight(IPython.config.cell_magic_highlight);
119 119 };
120 120
121 121 /** @method create_element */
122 122 CodeCell.prototype.create_element = function () {
123 123 IPython.Cell.prototype.create_element.apply(this, arguments);
124 124
125 125 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
126 126 cell.attr('tabindex','2');
127 127
128 128 var input = $('<div></div>').addClass('input');
129 129 var prompt = $('<div/>').addClass('prompt input_prompt');
130 130 var inner_cell = $('<div/>').addClass('inner_cell');
131 131 this.celltoolbar = new IPython.CellToolbar(this);
132 132 inner_cell.append(this.celltoolbar.element);
133 133 var input_area = $('<div/>').addClass('input_area');
134 134 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
135 135 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
136 136 inner_cell.append(input_area);
137 137 input.append(prompt).append(inner_cell);
138 138
139 139 var widget_area = $('<div/>')
140 140 .addClass('widget-area')
141 141 .hide();
142 142 this.widget_area = widget_area;
143 143 var widget_prompt = $('<div/>')
144 144 .addClass('prompt')
145 145 .appendTo(widget_area);
146 146 var widget_subarea = $('<div/>')
147 147 .addClass('widget-subarea')
148 148 .appendTo(widget_area);
149 149 this.widget_subarea = widget_subarea;
150 150 var widget_clear_buton = $('<button />')
151 151 .addClass('close')
152 152 .html('&times;')
153 153 .click(function() {
154 154 widget_area.slideUp('', function(){ widget_subarea.html(''); });
155 155 })
156 156 .appendTo(widget_prompt);
157 157
158 158 var output = $('<div></div>');
159 159 cell.append(input).append(widget_area).append(output);
160 160 this.element = cell;
161 161 this.output_area = new IPython.OutputArea(output, true);
162 162 this.completer = new IPython.Completer(this);
163 163 };
164 164
165 165 /** @method bind_events */
166 166 CodeCell.prototype.bind_events = function () {
167 167 IPython.Cell.prototype.bind_events.apply(this);
168 168 var that = this;
169 169
170 170 this.element.focusout(
171 171 function() { that.auto_highlight(); }
172 172 );
173 173 };
174 174
175 175
176 176 /**
177 177 * This method gets called in CodeMirror's onKeyDown/onKeyPress
178 178 * handlers and is used to provide custom key handling. Its return
179 179 * value is used to determine if CodeMirror should ignore the event:
180 180 * true = ignore, false = don't ignore.
181 181 * @method handle_codemirror_keyevent
182 182 */
183 183 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
184 184
185 185 var that = this;
186 186 // whatever key is pressed, first, cancel the tooltip request before
187 187 // they are sent, and remove tooltip if any, except for tab again
188 188 var tooltip_closed = null;
189 189 if (event.type === 'keydown' && event.which != keycodes.tab ) {
190 190 tooltip_closed = IPython.tooltip.remove_and_cancel_tooltip();
191 191 }
192 192
193 193 var cur = editor.getCursor();
194 194 if (event.keyCode === keycodes.enter){
195 195 this.auto_highlight();
196 196 }
197 197
198 198 if (event.which === keycodes.down && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
199 199 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
200 200 // browser and keyboard layout !
201 201 // Pressing '(' , request tooltip, don't forget to reappend it
202 202 // The second argument says to hide the tooltip if the docstring
203 203 // is actually empty
204 204 IPython.tooltip.pending(that, true);
205 205 } else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
206 206 // If tooltip is active, cancel it. The call to
207 207 // remove_and_cancel_tooltip above doesn't pass, force=true.
208 208 // Because of this it won't actually close the tooltip
209 209 // if it is in sticky mode. Thus, we have to check again if it is open
210 210 // and close it with force=true.
211 211 if (!IPython.tooltip._hidden) {
212 212 IPython.tooltip.remove_and_cancel_tooltip(true);
213 213 }
214 214 // If we closed the tooltip, don't let CM or the global handlers
215 215 // handle this event.
216 216 event.stop();
217 217 return true;
218 218 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
219 219 if (editor.somethingSelected()){
220 220 var anchor = editor.getCursor("anchor");
221 221 var head = editor.getCursor("head");
222 222 if( anchor.line != head.line){
223 223 return false;
224 224 }
225 225 }
226 226 IPython.tooltip.request(that);
227 227 event.stop();
228 228 return true;
229 229 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
230 230 // Tab completion.
231 231 IPython.tooltip.remove_and_cancel_tooltip();
232 232 if (editor.somethingSelected()) {
233 233 return false;
234 234 }
235 235 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
236 236 if (pre_cursor.trim() === "") {
237 237 // Don't autocomplete if the part of the line before the cursor
238 238 // is empty. In this case, let CodeMirror handle indentation.
239 239 return false;
240 240 } else {
241 241 event.stop();
242 242 this.completer.startCompletion();
243 243 return true;
244 244 }
245 245 }
246 246
247 247 // keyboard event wasn't one of those unique to code cells, let's see
248 248 // if it's one of the generic ones (i.e. check edit mode shortcuts)
249 249 return IPython.Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
250 250 };
251 251
252 252 // Kernel related calls.
253 253
254 254 CodeCell.prototype.set_kernel = function (kernel) {
255 255 this.kernel = kernel;
256 256 };
257 257
258 258 /**
259 259 * Execute current code cell to the kernel
260 260 * @method execute
261 261 */
262 262 CodeCell.prototype.execute = function () {
263 263 this.output_area.clear_output();
264 264
265 265 // Clear widget area
266 266 this.widget_subarea.html('');
267 267 this.widget_subarea.height('');
268 268 this.widget_area.height('');
269 269 this.widget_area.hide();
270 270
271 271 this.set_input_prompt('*');
272 272 this.element.addClass("running");
273 273 if (this.last_msg_id) {
274 274 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
275 275 }
276 276 var callbacks = this.get_callbacks();
277 277
278 278 var old_msg_id = this.last_msg_id;
279 279 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
280 280 if (old_msg_id) {
281 281 delete CodeCell.msg_cells[old_msg_id];
282 282 }
283 283 CodeCell.msg_cells[this.last_msg_id] = this;
284 284 };
285 285
286 286 /**
287 287 * Construct the default callbacks for
288 288 * @method get_callbacks
289 289 */
290 290 CodeCell.prototype.get_callbacks = function () {
291 291 return {
292 292 shell : {
293 293 reply : $.proxy(this._handle_execute_reply, this),
294 294 payload : {
295 295 set_next_input : $.proxy(this._handle_set_next_input, this),
296 296 page : $.proxy(this._open_with_pager, this)
297 297 }
298 298 },
299 299 iopub : {
300 300 output : $.proxy(this.output_area.handle_output, this.output_area),
301 301 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
302 302 },
303 303 input : $.proxy(this._handle_input_request, this)
304 304 };
305 305 };
306 306
307 307 CodeCell.prototype._open_with_pager = function (payload) {
308 308 $([IPython.events]).trigger('open_with_text.Pager', payload);
309 309 };
310 310
311 311 /**
312 312 * @method _handle_execute_reply
313 313 * @private
314 314 */
315 315 CodeCell.prototype._handle_execute_reply = function (msg) {
316 316 this.set_input_prompt(msg.content.execution_count);
317 317 this.element.removeClass("running");
318 318 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
319 319 };
320 320
321 321 /**
322 322 * @method _handle_set_next_input
323 323 * @private
324 324 */
325 325 CodeCell.prototype._handle_set_next_input = function (payload) {
326 326 var data = {'cell': this, 'text': payload.text};
327 327 $([IPython.events]).trigger('set_next_input.Notebook', data);
328 328 };
329 329
330 330 /**
331 331 * @method _handle_input_request
332 332 * @private
333 333 */
334 334 CodeCell.prototype._handle_input_request = function (msg) {
335 335 this.output_area.append_raw_input(msg);
336 336 };
337 337
338 338
339 339 // Basic cell manipulation.
340 340
341 341 CodeCell.prototype.select = function () {
342 342 var cont = IPython.Cell.prototype.select.apply(this);
343 343 if (cont) {
344 344 this.code_mirror.refresh();
345 345 this.auto_highlight();
346 346 }
347 347 return cont;
348 348 };
349 349
350 350 CodeCell.prototype.render = function () {
351 351 var cont = IPython.Cell.prototype.render.apply(this);
352 352 // Always execute, even if we are already in the rendered state
353 353 return cont;
354 354 };
355 355
356 356 CodeCell.prototype.unrender = function () {
357 357 // CodeCell is always rendered
358 358 return false;
359 359 };
360 360
361 /**
362 * Determine whether or not the unfocus event should be aknowledged.
363 *
364 * @method should_cancel_blur
365 *
366 * @return results {bool} Whether or not to ignore the cell's blur event.
367 **/
368 CodeCell.prototype.should_cancel_blur = function () {
369 // Cancel this unfocus event if the base wants to cancel or the cell
370 // completer is open or the tooltip is open.
371 return IPython.Cell.prototype.should_cancel_blur.apply(this) ||
372 (this.completer && this.completer.is_visible()) ||
373 (IPython.tooltip && IPython.tooltip.is_visible());
374 };
375
376 361 CodeCell.prototype.select_all = function () {
377 362 var start = {line: 0, ch: 0};
378 363 var nlines = this.code_mirror.lineCount();
379 364 var last_line = this.code_mirror.getLine(nlines-1);
380 365 var end = {line: nlines-1, ch: last_line.length};
381 366 this.code_mirror.setSelection(start, end);
382 367 };
383 368
384 369
385 370 CodeCell.prototype.collapse_output = function () {
386 371 this.collapsed = true;
387 372 this.output_area.collapse();
388 373 };
389 374
390 375
391 376 CodeCell.prototype.expand_output = function () {
392 377 this.collapsed = false;
393 378 this.output_area.expand();
394 379 this.output_area.unscroll_area();
395 380 };
396 381
397 382 CodeCell.prototype.scroll_output = function () {
398 383 this.output_area.expand();
399 384 this.output_area.scroll_if_long();
400 385 };
401 386
402 387 CodeCell.prototype.toggle_output = function () {
403 388 this.collapsed = Boolean(1 - this.collapsed);
404 389 this.output_area.toggle_output();
405 390 };
406 391
407 392 CodeCell.prototype.toggle_output_scroll = function () {
408 393 this.output_area.toggle_scroll();
409 394 };
410 395
411 396
412 397 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
413 398 var ns;
414 399 if (prompt_value === undefined) {
415 400 ns = "&nbsp;";
416 401 } else {
417 402 ns = encodeURIComponent(prompt_value);
418 403 }
419 404 return 'In&nbsp;[' + ns + ']:';
420 405 };
421 406
422 407 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
423 408 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
424 409 for(var i=1; i < lines_number; i++) {
425 410 html.push(['...:']);
426 411 }
427 412 return html.join('<br/>');
428 413 };
429 414
430 415 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
431 416
432 417
433 418 CodeCell.prototype.set_input_prompt = function (number) {
434 419 var nline = 1;
435 420 if (this.code_mirror !== undefined) {
436 421 nline = this.code_mirror.lineCount();
437 422 }
438 423 this.input_prompt_number = number;
439 424 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
440 425 // This HTML call is okay because the user contents are escaped.
441 426 this.element.find('div.input_prompt').html(prompt_html);
442 427 };
443 428
444 429
445 430 CodeCell.prototype.clear_input = function () {
446 431 this.code_mirror.setValue('');
447 432 };
448 433
449 434
450 435 CodeCell.prototype.get_text = function () {
451 436 return this.code_mirror.getValue();
452 437 };
453 438
454 439
455 440 CodeCell.prototype.set_text = function (code) {
456 441 return this.code_mirror.setValue(code);
457 442 };
458 443
459 444
460 445 CodeCell.prototype.clear_output = function (wait) {
461 446 this.output_area.clear_output(wait);
462 447 this.set_input_prompt();
463 448 };
464 449
465 450
466 451 // JSON serialization
467 452
468 453 CodeCell.prototype.fromJSON = function (data) {
469 454 IPython.Cell.prototype.fromJSON.apply(this, arguments);
470 455 if (data.cell_type === 'code') {
471 456 if (data.input !== undefined) {
472 457 this.set_text(data.input);
473 458 // make this value the starting point, so that we can only undo
474 459 // to this state, instead of a blank cell
475 460 this.code_mirror.clearHistory();
476 461 this.auto_highlight();
477 462 }
478 463 if (data.prompt_number !== undefined) {
479 464 this.set_input_prompt(data.prompt_number);
480 465 } else {
481 466 this.set_input_prompt();
482 467 }
483 468 this.output_area.trusted = data.trusted || false;
484 469 this.output_area.fromJSON(data.outputs);
485 470 if (data.collapsed !== undefined) {
486 471 if (data.collapsed) {
487 472 this.collapse_output();
488 473 } else {
489 474 this.expand_output();
490 475 }
491 476 }
492 477 }
493 478 };
494 479
495 480
496 481 CodeCell.prototype.toJSON = function () {
497 482 var data = IPython.Cell.prototype.toJSON.apply(this);
498 483 data.input = this.get_text();
499 484 // is finite protect against undefined and '*' value
500 485 if (isFinite(this.input_prompt_number)) {
501 486 data.prompt_number = this.input_prompt_number;
502 487 }
503 488 var outputs = this.output_area.toJSON();
504 489 data.outputs = outputs;
505 490 data.language = 'python';
506 491 data.trusted = this.output_area.trusted;
507 492 data.collapsed = this.collapsed;
508 493 return data;
509 494 };
510 495
496 /**
497 * handle cell level logic when a cell is unselected
498 * @method unselect
499 * @return is the action being taken
500 */
501 CodeCell.prototype.unselect = function () {
502 var cont = IPython.Cell.prototype.unselect.apply(this);
503 if (cont) {
504 // When a code cell is usnelected, make sure that the corresponding
505 // tooltip and completer to that cell is closed.
506 IPython.tooltip.remove_and_cancel_tooltip(true);
507 if (this.completer !== null) {
508 this.completer.close();
509 }
510 }
511 return cont;
512 };
511 513
512 514 IPython.CodeCell = CodeCell;
513 515
514 516 return IPython;
515 517 }(IPython));
@@ -1,384 +1,369 b''
1 1 // function completer.
2 2 //
3 3 // completer should be a class that takes an cell instance
4 4 var IPython = (function (IPython) {
5 5 // that will prevent us from misspelling
6 6 "use strict";
7 7
8 8 // easier key mapping
9 9 var keycodes = IPython.keyboard.keycodes;
10 10
11 11 function prepend_n_prc(str, n) {
12 12 for( var i =0 ; i< n ; i++){
13 13 str = '%'+str ;
14 14 }
15 15 return str;
16 16 }
17 17
18 18 function _existing_completion(item, completion_array){
19 19 for( var c in completion_array ) {
20 20 if(completion_array[c].trim().substr(-item.length) == item)
21 21 { return true; }
22 22 }
23 23 return false;
24 24 }
25 25
26 26 // what is the common start of all completions
27 27 function shared_start(B, drop_prct) {
28 28 if (B.length == 1) {
29 29 return B[0];
30 30 }
31 31 var A = [];
32 32 var common;
33 33 var min_lead_prct = 10;
34 34 for (var i = 0; i < B.length; i++) {
35 35 var str = B[i].str;
36 36 var localmin = 0;
37 37 if(drop_prct === true){
38 38 while ( str.substr(0, 1) == '%') {
39 39 localmin = localmin+1;
40 40 str = str.substring(1);
41 41 }
42 42 }
43 43 min_lead_prct = Math.min(min_lead_prct, localmin);
44 44 A.push(str);
45 45 }
46 46
47 47 if (A.length > 1) {
48 48 var tem1, tem2, s;
49 49 A = A.slice(0).sort();
50 50 tem1 = A[0];
51 51 s = tem1.length;
52 52 tem2 = A.pop();
53 53 while (s && tem2.indexOf(tem1) == -1) {
54 54 tem1 = tem1.substring(0, --s);
55 55 }
56 56 if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
57 57 return {
58 58 str:prepend_n_prc('', min_lead_prct),
59 59 type: "computed",
60 60 from: B[0].from,
61 61 to: B[0].to
62 62 };
63 63 }
64 64 return {
65 65 str: prepend_n_prc(tem1, min_lead_prct),
66 66 type: "computed",
67 67 from: B[0].from,
68 68 to: B[0].to
69 69 };
70 70 }
71 71 return null;
72 72 }
73 73
74 74
75 75 var Completer = function (cell) {
76 this._visible = false;
77 76 this.cell = cell;
78 77 this.editor = cell.code_mirror;
79 78 var that = this;
80 79 $([IPython.events]).on('status_busy.Kernel', function () {
81 80 that.skip_kernel_completion = true;
82 81 });
83 82 $([IPython.events]).on('status_idle.Kernel', function () {
84 83 that.skip_kernel_completion = false;
85 84 });
86 85 };
87 86
88 Completer.prototype.is_visible = function () {
89 // Return whether or not the completer is visible.
90 return this._visible;
91 };
92
93 87 Completer.prototype.startCompletion = function () {
94 88 // call for a 'first' completion, that will set the editor and do some
95 // special behaviour like autopicking if only one completion availlable
96 //
89 // special behavior like autopicking if only one completion available.
97 90 if (this.editor.somethingSelected()) return;
98 91 this.done = false;
99 92 // use to get focus back on opera
100 93 this.carry_on_completion(true);
101 94 };
102 95
103 96
104 97 // easy access for julia to monkeypatch
105 98 //
106 99 Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;
107 100
108 101 Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
109 102 return Completer.reinvoke_re.test(pre_cursor);
110 103 };
111 104
112 105 /**
113 106 *
114 107 * pass true as parameter if this is the first invocation of the completer
115 108 * this will prevent the completer to dissmiss itself if it is not on a
116 109 * word boundary like pressing tab after a space, and make it autopick the
117 110 * only choice if there is only one which prevent from popping the UI. as
118 111 * well as fast-forwarding the typing if all completion have a common
119 112 * shared start
120 113 **/
121 114 Completer.prototype.carry_on_completion = function (first_invocation) {
122 115 // Pass true as parameter if you want the completer to autopick when
123 116 // only one completion. This function is automatically reinvoked at
124 117 // each keystroke with first_invocation = false
125 118 var cur = this.editor.getCursor();
126 119 var line = this.editor.getLine(cur.line);
127 120 var pre_cursor = this.editor.getRange({
128 121 line: cur.line,
129 122 ch: cur.ch - 1
130 123 }, cur);
131 124
132 125 // we need to check that we are still on a word boundary
133 126 // because while typing the completer is still reinvoking itself
134 127 // so dismiss if we are on a "bad" caracter
135 128 if (!this.reinvoke(pre_cursor) && !first_invocation) {
136 129 this.close();
137 130 return;
138 131 }
139 132
140 133 this.autopick = false;
141 134 if (first_invocation) {
142 135 this.autopick = true;
143 136 }
144 137
145 138 // We want a single cursor position.
146 139 if (this.editor.somethingSelected()) {
147 140 return;
148 141 }
149 142
150 143 // one kernel completion came back, finish_completing will be called with the results
151 144 // we fork here and directly call finish completing if kernel is busy
152 145 if (this.skip_kernel_completion) {
153 146 this.finish_completing({
154 147 'matches': [],
155 148 matched_text: ""
156 149 });
157 150 } else {
158 151 this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
159 152 }
160 153 };
161 154
162 155 Completer.prototype.finish_completing = function (msg) {
163 156 // let's build a function that wrap all that stuff into what is needed
164 157 // for the new completer:
165 158 var content = msg.content;
166 159 var matched_text = content.matched_text;
167 160 var matches = content.matches;
168 161
169 162 var cur = this.editor.getCursor();
170 163 var results = CodeMirror.contextHint(this.editor);
171 164 var filtered_results = [];
172 165 //remove results from context completion
173 166 //that are already in kernel completion
174 167 for (var elm in results) {
175 168 if (!_existing_completion(results[elm].str, matches)) {
176 169 filtered_results.push(results[elm]);
177 170 }
178 171 }
179 172
180 173 // append the introspection result, in order, at at the beginning of
181 174 // the table and compute the replacement range from current cursor
182 175 // positon and matched_text length.
183 176 for (var i = matches.length - 1; i >= 0; --i) {
184 177 filtered_results.unshift({
185 178 str: matches[i],
186 179 type: "introspection",
187 180 from: {
188 181 line: cur.line,
189 182 ch: cur.ch - matched_text.length
190 183 },
191 184 to: {
192 185 line: cur.line,
193 186 ch: cur.ch
194 187 }
195 188 });
196 189 }
197 190
198 191 // one the 2 sources results have been merge, deal with it
199 192 this.raw_result = filtered_results;
200 193
201 194 // if empty result return
202 195 if (!this.raw_result || !this.raw_result.length) return;
203 196
204 197 // When there is only one completion, use it directly.
205 198 if (this.autopick && this.raw_result.length == 1) {
206 199 this.insert(this.raw_result[0]);
207 200 return;
208 201 }
209 202
210 203 if (this.raw_result.length == 1) {
211 204 // test if first and only completion totally matches
212 205 // what is typed, in this case dismiss
213 206 var str = this.raw_result[0].str;
214 207 var pre_cursor = this.editor.getRange({
215 208 line: cur.line,
216 209 ch: cur.ch - str.length
217 210 }, cur);
218 211 if (pre_cursor == str) {
219 212 this.close();
220 213 return;
221 214 }
222 215 }
223 216
224 this.complete = $('<div/>').addClass('completions');
225 this.complete.attr('id', 'complete');
226
227 // Currently webkit doesn't use the size attr correctly. See:
228 // https://code.google.com/p/chromium/issues/detail?id=4579
229 this.sel = $('<select style="width: auto"/>')
230 .attr('multiple', 'true')
231 .attr('size', Math.min(10, this.raw_result.length));
232 this.complete.append(this.sel);
233 this._visible = true;
234 $('body').append(this.complete);
217 if (!this.visible) {
218 this.complete = $('<div/>').addClass('completions');
219 this.complete.attr('id', 'complete');
220
221 // Currently webkit doesn't use the size attr correctly. See:
222 // https://code.google.com/p/chromium/issues/detail?id=4579
223 this.sel = $('<select/>')
224 .attr('tabindex', -1)
225 .attr('multiple', 'true');
226 this.complete.append(this.sel);
227 this.visible = true;
228 $('body').append(this.complete);
229
230 //build the container
231 var that = this;
232 this.sel.dblclick(function () {
233 that.pick();
234 });
235 this.sel.focus(function () {
236 that.editor.focus();
237 });
238 this._handle_keydown = function (cm, event) {
239 that.keydown(event);
240 };
241 this.editor.on('keydown', this._handle_keydown);
242 this._handle_keypress = function (cm, event) {
243 that.keypress(event);
244 };
245 this.editor.on('keypress', this._handle_keypress);
246 }
247 this.sel.attr('size', Math.min(10, this.raw_result.length));
235 248
236 249 // After everything is on the page, compute the postion.
237 250 // We put it above the code if it is too close to the bottom of the page.
238 251 cur.ch = cur.ch-matched_text.length;
239 252 var pos = this.editor.cursorCoords(cur);
240 253 var left = pos.left-3;
241 254 var top;
242 255 var cheight = this.complete.height();
243 256 var wheight = $(window).height();
244 257 if (pos.bottom+cheight+5 > wheight) {
245 258 top = pos.top-cheight-4;
246 259 } else {
247 260 top = pos.bottom+1;
248 261 }
249 262 this.complete.css('left', left + 'px');
250 263 this.complete.css('top', top + 'px');
251 264
252
253 //build the container
254 var that = this;
255 this.sel.dblclick(function () {
256 that.pick();
257 });
258 this.sel.blur(this.close);
259 this.sel.keydown(function (event) {
260 that.keydown(event);
261 });
262 this.sel.keypress(function (event) {
263 that.keypress(event);
264 });
265
265 // Clear and fill the list.
266 this.sel.text('');
266 267 this.build_gui_list(this.raw_result);
267
268 this.sel.focus();
269 IPython.keyboard_manager.disable();
270 // Opera sometimes ignores focusing a freshly created node
271 if (window.opera) setTimeout(function () {
272 if (!this.done) this.sel.focus();
273 }, 100);
274 268 return true;
275 269 };
276 270
277 271 Completer.prototype.insert = function (completion) {
278 272 this.editor.replaceRange(completion.str, completion.from, completion.to);
279 273 };
280 274
281 275 Completer.prototype.build_gui_list = function (completions) {
282 276 for (var i = 0; i < completions.length; ++i) {
283 277 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
284 278 this.sel.append(opt);
285 279 }
286 280 this.sel.children().first().attr('selected', 'true');
287 281 this.sel.scrollTop(0);
288 282 };
289 283
290 284 Completer.prototype.close = function () {
291 this._visible = false;
292 if (this.done) return;
293 285 this.done = true;
294 $('.completions').remove();
295 IPython.keyboard_manager.enable();
286 $('#complete').remove();
287 this.editor.off('keydown', this._handle_keydown);
288 this.editor.off('keypress', this._handle_keypress);
289 this.visible = false;
296 290 };
297 291
298 292 Completer.prototype.pick = function () {
299 293 this.insert(this.raw_result[this.sel[0].selectedIndex]);
300 294 this.close();
301 var that = this;
302 setTimeout(function () {
303 that.editor.focus();
304 }, 50);
305 295 };
306 296
307 297 Completer.prototype.keydown = function (event) {
308 298 var code = event.keyCode;
309 299 var that = this;
310 300
311 301 // Enter
312 302 if (code == keycodes.enter) {
313 303 CodeMirror.e_stop(event);
314 304 this.pick();
315 }
316 305 // Escape or backspace
317 else if (code == keycodes.esc) {
306 } else if (code == keycodes.esc || code == keycodes.backspace) {
318 307 CodeMirror.e_stop(event);
319 308 this.close();
320 this.editor.focus();
321
322 } else if (code == keycodes.backspace) {
323 this.close();
324 this.editor.focus();
325 309 } else if (code == keycodes.tab) {
326 310 //all the fastforwarding operation,
327 311 //Check that shared start is not null which can append with prefixed completion
328 312 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
329 313 // to erase py
330 314 var sh = shared_start(this.raw_result, true);
331 315 if (sh) {
332 316 this.insert(sh);
333 317 }
334 318 this.close();
335 CodeMirror.e_stop(event);
336 this.editor.focus();
337 319 //reinvoke self
338 320 setTimeout(function () {
339 321 that.carry_on_completion();
340 322 }, 50);
341 323 } else if (code == keycodes.up || code == keycodes.down) {
342 324 // need to do that to be able to move the arrow
343 325 // when on the first or last line ofo a code cell
344 event.stopPropagation();
326 CodeMirror.e_stop(event);
327
328 var options = this.sel.find('option');
329 var index = this.sel[0].selectedIndex;
330 if (code == keycodes.up) {
331 index--;
332 }
333 if (code == keycodes.down) {
334 index++;
335 }
336 index = Math.min(Math.max(index, 0), options.length-1);
337 this.sel[0].selectedIndex = index;
338 } else if (code == keycodes.left || code == keycodes.right) {
339 this.close();
345 340 }
346 341 };
347
342
348 343 Completer.prototype.keypress = function (event) {
349 344 // FIXME: This is a band-aid.
350 345 // on keypress, trigger insertion of a single character.
351 346 // This simulates the old behavior of completion as you type,
352 347 // before events were disconnected and CodeMirror stopped
353 348 // receiving events while the completer is focused.
354 349
355 350 var that = this;
356 351 var code = event.keyCode;
357 352
358 353 // don't handle keypress if it's not a character (arrows on FF)
359 354 // or ENTER/TAB
360 355 if (event.charCode === 0 ||
361 code == keycodes.enter ||
362 code == keycodes.tab
356 code == keycodes.tab ||
357 code == keycodes.enter
363 358 ) return;
364 359
365 var cur = this.editor.getCursor();
366 var completion = {
367 str: String.fromCharCode(event.which),
368 type: "introspection",
369 from: cur,
370 to: cur,
371 };
372 this.insert(completion);
373
374 360 this.close();
375 361 this.editor.focus();
376 362 setTimeout(function () {
377 363 that.carry_on_completion();
378 364 }, 50);
379 365 };
380
381 366 IPython.Completer = Completer;
382 367
383 368 return IPython;
384 369 }(IPython));
@@ -1,24 +1,25 b''
1 1 .completions {
2 2 position: absolute;
3 3 z-index: 10;
4 4 overflow: hidden;
5 5 border: 1px solid @border_color;
6 6 .corner-all;
7 7 .box-shadow(0px 6px 10px -1px #adadad);
8 8 }
9 9
10 10 .completions select {
11 11 background: white;
12 12 outline: none;
13 13 border: none;
14 14 padding: 0px;
15 15 margin: 0px;
16 16 overflow: auto;
17 17 font-family: @monoFontFamily;
18 18 font-size: 110%;
19 19 color: @textColor;
20 width: auto;
20 21 }
21 22
22 23 .completions select option.context {
23 24 color: @blueDark;
24 25 }
@@ -1,1536 +1,1536 b''
1 .clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0}
2 .clearfix:after{clear:both}
3 .hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}
4 .input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
5 1 article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}
6 2 audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
7 3 audio:not([controls]){display:none}
8 4 html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}
9 5 a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}
10 6 a:hover,a:active{outline:0}
11 7 sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}
12 8 sup{top:-0.5em}
13 9 sub{bottom:-0.25em}
14 10 img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}
15 11 #map_canvas img,.google-maps img{max-width:none}
16 12 button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}
17 13 button,input{*overflow:visible;line-height:normal}
18 14 button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
19 15 button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
20 16 label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}
21 17 input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}
22 18 input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}
23 19 textarea{overflow:auto;vertical-align:top}
24 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:.5cm}p,h2,h3{orphans:3;widows:3} h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:20px;color:#000;background-color:#fff}
25 21 a{color:#08c;text-decoration:none}
26 22 a:hover,a:focus{color:#005580;text-decoration:underline}
27 23 .img-rounded{border-radius:6px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}
28 24 .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 25 .img-circle{border-radius:500px;-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}
30 26 .row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:"";line-height:0}
31 27 .row:after{clear:both}
32 28 [class*="span"]{float:left;min-height:1px;margin-left:20px}
33 29 .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}
34 30 .span12{width:940px}
35 31 .span11{width:860px}
36 32 .span10{width:780px}
37 33 .span9{width:700px}
38 34 .span8{width:620px}
39 35 .span7{width:540px}
40 36 .span6{width:460px}
41 37 .span5{width:380px}
42 38 .span4{width:300px}
43 39 .span3{width:220px}
44 40 .span2{width:140px}
45 41 .span1{width:60px}
46 42 .offset12{margin-left:980px}
47 43 .offset11{margin-left:900px}
48 44 .offset10{margin-left:820px}
49 45 .offset9{margin-left:740px}
50 46 .offset8{margin-left:660px}
51 47 .offset7{margin-left:580px}
52 48 .offset6{margin-left:500px}
53 49 .offset5{margin-left:420px}
54 50 .offset4{margin-left:340px}
55 51 .offset3{margin-left:260px}
56 52 .offset2{margin-left:180px}
57 53 .offset1{margin-left:100px}
58 54 .row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0}
59 55 .row-fluid:after{clear:both}
60 56 .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 57 .row-fluid [class*="span"]:first-child{margin-left:0}
62 58 .row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}
63 59 .row-fluid .span12{width:100%;*width:99.94680851063829%}
64 60 .row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}
65 61 .row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}
66 62 .row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}
67 63 .row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}
68 64 .row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}
69 65 .row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}
70 66 .row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}
71 67 .row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}
72 68 .row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}
73 69 .row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}
74 70 .row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}
75 71 .row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}
76 72 .row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}
77 73 .row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}
78 74 .row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}
79 75 .row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}
80 76 .row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}
81 77 .row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}
82 78 .row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}
83 79 .row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}
84 80 .row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}
85 81 .row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}
86 82 .row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}
87 83 .row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}
88 84 .row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}
89 85 .row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}
90 86 .row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}
91 87 .row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}
92 88 .row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}
93 89 .row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}
94 90 .row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}
95 91 .row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}
96 92 .row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}
97 93 .row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}
98 94 .row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}
99 95 [class*="span"].hide,.row-fluid [class*="span"].hide{display:none}
100 96 [class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}
101 97 .container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;content:"";line-height:0}
102 98 .container:after{clear:both}
103 99 .container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;content:"";line-height:0}
104 100 .container-fluid:after{clear:both}
105 101 p{margin:0 0 10px}
106 102 .lead{margin-bottom:20px;font-size:19.5px;font-weight:200;line-height:30px}
107 103 small{font-size:85%}
108 104 strong{font-weight:bold}
109 105 em{font-style:italic}
110 106 cite{font-style:normal}
111 107 .muted{color:#999}
112 108 a.muted:hover,a.muted:focus{color:#808080}
113 109 .text-warning{color:#c09853}
114 110 a.text-warning:hover,a.text-warning:focus{color:#a47e3c}
115 111 .text-error{color:#b94a48}
116 112 a.text-error:hover,a.text-error:focus{color:#953b39}
117 113 .text-info{color:#3a87ad}
118 114 a.text-info:hover,a.text-info:focus{color:#2d6987}
119 115 .text-success{color:#468847}
120 116 a.text-success:hover,a.text-success:focus{color:#356635}
121 117 .text-left{text-align:left}
122 118 .text-right{text-align:right}
123 119 .text-center{text-align:center}
124 120 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:#999}
125 121 h1,h2,h3{line-height:40px}
126 122 h1{font-size:35.75px}
127 123 h2{font-size:29.25px}
128 124 h3{font-size:22.75px}
129 125 h4{font-size:16.25px}
130 126 h5{font-size:13px}
131 127 h6{font-size:11.049999999999999px}
132 128 h1 small{font-size:22.75px}
133 129 h2 small{font-size:16.25px}
134 130 h3 small{font-size:13px}
135 131 h4 small{font-size:13px}
136 132 .page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}
137 133 ul,ol{padding:0;margin:0 0 10px 25px}
138 134 ul ul,ul ol,ol ol,ol ul{margin-bottom:0}
139 135 li{line-height:20px}
140 136 ul.unstyled,ol.unstyled{margin-left:0;list-style:none}
141 137 ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;*zoom:1;padding-left:5px;padding-right:5px}
142 138 dl{margin-bottom:20px}
143 139 dt,dd{line-height:20px}
144 140 dt{font-weight:bold}
145 141 dd{margin-left:10px}
146 142 .dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;content:"";line-height:0}
147 143 .dl-horizontal:after{clear:both}
148 144 .dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
149 145 .dl-horizontal dd{margin-left:180px}
150 146 hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}
151 147 abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}
152 148 abbr.initialism{font-size:90%;text-transform:uppercase}
153 149 blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16.25px;font-weight:300;line-height:1.25}
154 150 blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}
155 151 blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}
156 152 blockquote.pull-right small:before{content:''}
157 153 blockquote.pull-right small:after{content:'\00A0 \2014'}
158 154 q:before,q:after,blockquote:before,blockquote:after{content:""}
159 155 address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}
160 156 code,pre{padding:0 3px 2px;font-family:monospace;font-size:11px;color:#333;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}
161 157 code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;white-space:nowrap}
162 158 pre{display:block;padding:9.5px;margin:0 0 10px;font-size:12px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}
163 159 pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}
164 160 .pre-scrollable{max-height:340px;overflow-y:scroll}
165 161 form{margin:0 0 20px}
166 162 fieldset{padding:0;margin:0;border:0}
167 163 legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:19.5px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}
168 164 label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:20px}
169 165 input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}
170 166 label{display:block;margin-bottom:5px}
171 167 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:#555;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;vertical-align:middle}
172 168 input,textarea,.uneditable-input{width:206px}
173 169 textarea{height:auto}
174 170 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:#fff;border:1px solid #ccc;-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)}
175 171 input[type="radio"],input[type="checkbox"]{margin:4px 0 0;*margin-top:0;margin-top:1px \9;line-height:normal}
176 172 input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}
177 173 select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}
178 174 select{width:220px;border:1px solid #ccc;background-color:#fff}
179 175 select[multiple],select[size]{height:auto}
180 176 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}
181 177 .uneditable-input,.uneditable-textarea{color:#999;background-color:#fcfcfc;border-color:#ccc;-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}
182 178 .uneditable-input{overflow:hidden;white-space:nowrap}
183 179 .uneditable-textarea{width:auto;height:auto}
184 180 input:-moz-placeholder,textarea:-moz-placeholder{color:#999}
185 181 input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}
186 182 input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}
187 183 .radio,.checkbox{min-height:20px;padding-left:20px}
188 184 .radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}
189 185 .controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}
190 186 .radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}
191 187 .radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}
192 188 .input-mini{width:60px}
193 189 .input-small{width:90px}
194 190 .input-medium{width:150px}
195 191 .input-large{width:210px}
196 192 .input-xlarge{width:270px}
197 193 .input-xxlarge{width:530px}
198 194 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}
199 195 .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}
200 196 input,textarea,.uneditable-input{margin-left:0}
201 197 .controls-row [class*="span"]+[class*="span"]{margin-left:20px}
202 198 input.span12,textarea.span12,.uneditable-input.span12{width:926px}
203 199 input.span11,textarea.span11,.uneditable-input.span11{width:846px}
204 200 input.span10,textarea.span10,.uneditable-input.span10{width:766px}
205 201 input.span9,textarea.span9,.uneditable-input.span9{width:686px}
206 202 input.span8,textarea.span8,.uneditable-input.span8{width:606px}
207 203 input.span7,textarea.span7,.uneditable-input.span7{width:526px}
208 204 input.span6,textarea.span6,.uneditable-input.span6{width:446px}
209 205 input.span5,textarea.span5,.uneditable-input.span5{width:366px}
210 206 input.span4,textarea.span4,.uneditable-input.span4{width:286px}
211 207 input.span3,textarea.span3,.uneditable-input.span3{width:206px}
212 208 input.span2,textarea.span2,.uneditable-input.span2{width:126px}
213 209 input.span1,textarea.span1,.uneditable-input.span1{width:46px}
214 210 .controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;content:"";line-height:0}
215 211 .controls-row:after{clear:both}
216 212 .controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}
217 213 .controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}
218 214 input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}
219 215 input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}
220 216 .control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}
221 217 .control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}
222 218 .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}
223 219 .control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}
224 220 .control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}
225 221 .control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}
226 222 .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}
227 223 .control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}
228 224 .control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}
229 225 .control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}
230 226 .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}
231 227 .control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}
232 228 .control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}
233 229 .control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}
234 230 .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}
235 231 .control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}
236 232 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}
237 233 .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}
238 234 .form-actions:after{clear:both}
239 235 .help-block,.help-inline{color:#262626}
240 236 .help-block{display:block;margin-bottom:10px}
241 237 .help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px}
242 238 .input-append,.input-prepend{display:inline-block;margin-bottom:10px;vertical-align:middle;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,.input-append .popover,.input-prepend .popover{font-size:13px}
243 239 .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;border-radius:0 4px 4px 0;-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}
244 240 .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 #fff;background-color:#eee;border:1px solid #ccc}
245 241 .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;border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
246 242 .input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}
247 243 .input-prepend .add-on,.input-prepend .btn{margin-right:-1px}
248 244 .input-prepend .add-on:first-child,.input-prepend .btn:first-child{border-radius:4px 0 0 4px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}
249 245 .input-append input,.input-append select,.input-append .uneditable-input{border-radius:4px 0 0 4px;-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{border-radius:0 4px 4px 0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}
250 246 .input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}
251 247 .input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{border-radius:0 4px 4px 0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}
252 248 .input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{border-radius:0;-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{border-radius:0 4px 4px 0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}
253 249 .input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;border-radius:4px 0 0 4px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}
254 250 .input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;border-radius:0 4px 4px 0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}
255 251 .input-prepend.input-append .btn-group:first-child{margin-left:0}
256 252 input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;border-radius:15px;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}
257 253 .form-search .input-append .search-query,.form-search .input-prepend .search-query{border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
258 254 .form-search .input-append .search-query{border-radius:14px 0 0 14px;-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}
259 255 .form-search .input-append .btn{border-radius:0 14px 14px 0;-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}
260 256 .form-search .input-prepend .search-query{border-radius:0 14px 14px 0;-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}
261 257 .form-search .input-prepend .btn{border-radius:14px 0 0 14px;-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}
262 258 .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}
263 259 .form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}
264 260 .form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}
265 261 .form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}
266 262 .form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}
267 263 .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}
268 264 .control-group{margin-bottom:10px}
269 265 legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}
270 266 .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}
271 267 .form-horizontal .control-group:after{clear:both}
272 268 .form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}
273 269 .form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}
274 270 .form-horizontal .help-block{margin-bottom:0}
275 271 .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}
276 272 .form-horizontal .form-actions{padding-left:180px}
277 273 table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}
278 274 .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 #ddd}
279 275 .table th{font-weight:bold}
280 276 .table thead th{vertical-align:bottom}
281 277 .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}
282 278 .table tbody+tbody{border-top:2px solid #ddd}
283 279 .table .table{background-color:#fff}
284 280 .table-condensed th,.table-condensed td{padding:4px 5px}
285 281 .table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}
286 282 .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}
287 283 .table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px}
288 284 .table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px}
289 285 .table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}
290 286 .table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px}
291 287 .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}
292 288 .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}
293 289 .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}
294 290 .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}
295 291 .table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}
296 292 .table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}
297 293 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}
298 294 .table td.span1,.table th.span1{float:none;width:44px;margin-left:0}
299 295 .table td.span2,.table th.span2{float:none;width:124px;margin-left:0}
300 296 .table td.span3,.table th.span3{float:none;width:204px;margin-left:0}
301 297 .table td.span4,.table th.span4{float:none;width:284px;margin-left:0}
302 298 .table td.span5,.table th.span5{float:none;width:364px;margin-left:0}
303 299 .table td.span6,.table th.span6{float:none;width:444px;margin-left:0}
304 300 .table td.span7,.table th.span7{float:none;width:524px;margin-left:0}
305 301 .table td.span8,.table th.span8{float:none;width:604px;margin-left:0}
306 302 .table td.span9,.table th.span9{float:none;width:684px;margin-left:0}
307 303 .table td.span10,.table th.span10{float:none;width:764px;margin-left:0}
308 304 .table td.span11,.table th.span11{float:none;width:844px;margin-left:0}
309 305 .table td.span12,.table th.span12{float:none;width:924px;margin-left:0}
310 306 .table tbody tr.success>td{background-color:#dff0d8}
311 307 .table tbody tr.error>td{background-color:#f2dede}
312 308 .table tbody tr.warning>td{background-color:#fcf8e3}
313 309 .table tbody tr.info>td{background-color:#d9edf7}
314 310 .table-hover tbody tr.success:hover>td{background-color:#d0e9c6}
315 311 .table-hover tbody tr.error:hover>td{background-color:#ebcccc}
316 312 .table-hover tbody tr.warning:hover>td{background-color:#faf2cc}
317 313 .table-hover tbody tr.info:hover>td{background-color:#c4e3f3}
318 314 [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}
319 315 .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:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}
320 316 .icon-glass{background-position:0 0}
321 317 .icon-music{background-position:-24px 0}
322 318 .icon-search{background-position:-48px 0}
323 319 .icon-envelope{background-position:-72px 0}
324 320 .icon-heart{background-position:-96px 0}
325 321 .icon-star{background-position:-120px 0}
326 322 .icon-star-empty{background-position:-144px 0}
327 323 .icon-user{background-position:-168px 0}
328 324 .icon-film{background-position:-192px 0}
329 325 .icon-th-large{background-position:-216px 0}
330 326 .icon-th{background-position:-240px 0}
331 327 .icon-th-list{background-position:-264px 0}
332 328 .icon-ok{background-position:-288px 0}
333 329 .icon-remove{background-position:-312px 0}
334 330 .icon-zoom-in{background-position:-336px 0}
335 331 .icon-zoom-out{background-position:-360px 0}
336 332 .icon-off{background-position:-384px 0}
337 333 .icon-signal{background-position:-408px 0}
338 334 .icon-cog{background-position:-432px 0}
339 335 .icon-trash{background-position:-456px 0}
340 336 .icon-home{background-position:0 -24px}
341 337 .icon-file{background-position:-24px -24px}
342 338 .icon-time{background-position:-48px -24px}
343 339 .icon-road{background-position:-72px -24px}
344 340 .icon-download-alt{background-position:-96px -24px}
345 341 .icon-download{background-position:-120px -24px}
346 342 .icon-upload{background-position:-144px -24px}
347 343 .icon-inbox{background-position:-168px -24px}
348 344 .icon-play-circle{background-position:-192px -24px}
349 345 .icon-repeat{background-position:-216px -24px}
350 346 .icon-refresh{background-position:-240px -24px}
351 347 .icon-list-alt{background-position:-264px -24px}
352 348 .icon-lock{background-position:-287px -24px}
353 349 .icon-flag{background-position:-312px -24px}
354 350 .icon-headphones{background-position:-336px -24px}
355 351 .icon-volume-off{background-position:-360px -24px}
356 352 .icon-volume-down{background-position:-384px -24px}
357 353 .icon-volume-up{background-position:-408px -24px}
358 354 .icon-qrcode{background-position:-432px -24px}
359 355 .icon-barcode{background-position:-456px -24px}
360 356 .icon-tag{background-position:0 -48px}
361 357 .icon-tags{background-position:-25px -48px}
362 358 .icon-book{background-position:-48px -48px}
363 359 .icon-bookmark{background-position:-72px -48px}
364 360 .icon-print{background-position:-96px -48px}
365 361 .icon-camera{background-position:-120px -48px}
366 362 .icon-font{background-position:-144px -48px}
367 363 .icon-bold{background-position:-167px -48px}
368 364 .icon-italic{background-position:-192px -48px}
369 365 .icon-text-height{background-position:-216px -48px}
370 366 .icon-text-width{background-position:-240px -48px}
371 367 .icon-align-left{background-position:-264px -48px}
372 368 .icon-align-center{background-position:-288px -48px}
373 369 .icon-align-right{background-position:-312px -48px}
374 370 .icon-align-justify{background-position:-336px -48px}
375 371 .icon-list{background-position:-360px -48px}
376 372 .icon-indent-left{background-position:-384px -48px}
377 373 .icon-indent-right{background-position:-408px -48px}
378 374 .icon-facetime-video{background-position:-432px -48px}
379 375 .icon-picture{background-position:-456px -48px}
380 376 .icon-pencil{background-position:0 -72px}
381 377 .icon-map-marker{background-position:-24px -72px}
382 378 .icon-adjust{background-position:-48px -72px}
383 379 .icon-tint{background-position:-72px -72px}
384 380 .icon-edit{background-position:-96px -72px}
385 381 .icon-share{background-position:-120px -72px}
386 382 .icon-check{background-position:-144px -72px}
387 383 .icon-move{background-position:-168px -72px}
388 384 .icon-step-backward{background-position:-192px -72px}
389 385 .icon-fast-backward{background-position:-216px -72px}
390 386 .icon-backward{background-position:-240px -72px}
391 387 .icon-play{background-position:-264px -72px}
392 388 .icon-pause{background-position:-288px -72px}
393 389 .icon-stop{background-position:-312px -72px}
394 390 .icon-forward{background-position:-336px -72px}
395 391 .icon-fast-forward{background-position:-360px -72px}
396 392 .icon-step-forward{background-position:-384px -72px}
397 393 .icon-eject{background-position:-408px -72px}
398 394 .icon-chevron-left{background-position:-432px -72px}
399 395 .icon-chevron-right{background-position:-456px -72px}
400 396 .icon-plus-sign{background-position:0 -96px}
401 397 .icon-minus-sign{background-position:-24px -96px}
402 398 .icon-remove-sign{background-position:-48px -96px}
403 399 .icon-ok-sign{background-position:-72px -96px}
404 400 .icon-question-sign{background-position:-96px -96px}
405 401 .icon-info-sign{background-position:-120px -96px}
406 402 .icon-screenshot{background-position:-144px -96px}
407 403 .icon-remove-circle{background-position:-168px -96px}
408 404 .icon-ok-circle{background-position:-192px -96px}
409 405 .icon-ban-circle{background-position:-216px -96px}
410 406 .icon-arrow-left{background-position:-240px -96px}
411 407 .icon-arrow-right{background-position:-264px -96px}
412 408 .icon-arrow-up{background-position:-289px -96px}
413 409 .icon-arrow-down{background-position:-312px -96px}
414 410 .icon-share-alt{background-position:-336px -96px}
415 411 .icon-resize-full{background-position:-360px -96px}
416 412 .icon-resize-small{background-position:-384px -96px}
417 413 .icon-plus{background-position:-408px -96px}
418 414 .icon-minus{background-position:-433px -96px}
419 415 .icon-asterisk{background-position:-456px -96px}
420 416 .icon-exclamation-sign{background-position:0 -120px}
421 417 .icon-gift{background-position:-24px -120px}
422 418 .icon-leaf{background-position:-48px -120px}
423 419 .icon-fire{background-position:-72px -120px}
424 420 .icon-eye-open{background-position:-96px -120px}
425 421 .icon-eye-close{background-position:-120px -120px}
426 422 .icon-warning-sign{background-position:-144px -120px}
427 423 .icon-plane{background-position:-168px -120px}
428 424 .icon-calendar{background-position:-192px -120px}
429 425 .icon-random{background-position:-216px -120px;width:16px}
430 426 .icon-comment{background-position:-240px -120px}
431 427 .icon-magnet{background-position:-264px -120px}
432 428 .icon-chevron-up{background-position:-288px -120px}
433 429 .icon-chevron-down{background-position:-313px -119px}
434 430 .icon-retweet{background-position:-336px -120px}
435 431 .icon-shopping-cart{background-position:-360px -120px}
436 432 .icon-folder-close{background-position:-384px -120px;width:16px}
437 433 .icon-folder-open{background-position:-408px -120px;width:16px}
438 434 .icon-resize-vertical{background-position:-432px -119px}
439 435 .icon-resize-horizontal{background-position:-456px -118px}
440 436 .icon-hdd{background-position:0 -144px}
441 437 .icon-bullhorn{background-position:-24px -144px}
442 438 .icon-bell{background-position:-48px -144px}
443 439 .icon-certificate{background-position:-72px -144px}
444 440 .icon-thumbs-up{background-position:-96px -144px}
445 441 .icon-thumbs-down{background-position:-120px -144px}
446 442 .icon-hand-right{background-position:-144px -144px}
447 443 .icon-hand-left{background-position:-168px -144px}
448 444 .icon-hand-up{background-position:-192px -144px}
449 445 .icon-hand-down{background-position:-216px -144px}
450 446 .icon-circle-arrow-right{background-position:-240px -144px}
451 447 .icon-circle-arrow-left{background-position:-264px -144px}
452 448 .icon-circle-arrow-up{background-position:-288px -144px}
453 449 .icon-circle-arrow-down{background-position:-312px -144px}
454 450 .icon-globe{background-position:-336px -144px}
455 451 .icon-wrench{background-position:-360px -144px}
456 452 .icon-tasks{background-position:-384px -144px}
457 453 .icon-filter{background-position:-408px -144px}
458 454 .icon-briefcase{background-position:-432px -144px}
459 455 .icon-fullscreen{background-position:-456px -144px}
460 456 .dropup,.dropdown{position:relative}
461 457 .dropdown-toggle{*margin-bottom:-3px}
462 458 .dropdown-toggle:active,.open .dropdown-toggle{outline:0}
463 459 .caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}
464 460 .dropdown .caret{margin-top:8px;margin-left:2px}
465 461 .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:#fff;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}
466 462 .dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}
467 463 .dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}
468 464 .dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top, #08c, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0077b3));background-image:-webkit-linear-gradient(top, #08c, #0077b3);background-image:-o-linear-gradient(top, #08c, #0077b3);background-image:linear-gradient(to bottom, #08c, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0)}
469 465 .dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#0081c2;background-image:-moz-linear-gradient(top, #08c, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0077b3));background-image:-webkit-linear-gradient(top, #08c, #0077b3);background-image:-o-linear-gradient(top, #08c, #0077b3);background-image:linear-gradient(to bottom, #08c, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0)}
470 466 .dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}
471 467 .dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:default}
472 468 .open{*z-index:1000}.open>.dropdown-menu{display:block}
473 469 .dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}
474 470 .pull-right>.dropdown-menu{right:0;left:auto}
475 471 .dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}
476 472 .dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}
477 473 .dropdown-submenu{position:relative}
478 474 .dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;border-radius:0 6px 6px 6px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}
479 475 .dropdown-submenu:hover>.dropdown-menu{display:block}
480 476 .dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;border-radius:5px 5px 5px 0;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}
481 477 .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:#ccc;margin-top:5px;margin-right:-10px}
482 478 .dropdown-submenu:hover>a:after{border-left-color:#fff}
483 479 .dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;border-radius:6px 0 6px 6px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}
484 480 .dropdown .dropdown-menu .nav-header{padding-left:20px;padding-right:20px}
485 481 .typeahead{z-index:1051;margin-top:2px;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}
486 482 .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)}
487 483 .well-large{padding:24px;border-radius:6px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}
488 484 .well-small{padding:9px;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}
489 485 .fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}
490 486 .collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}
491 487 .close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}
492 488 button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}
493 489 .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:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #fff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #fff, #e6e6e6);background-image:-o-linear-gradient(top, #fff, #e6e6e6);background-image:linear-gradient(to bottom, #fff, #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 #ccc;*border:0;border-bottom-color:#b3b3b3;-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:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}
494 490 .btn:active,.btn.active{background-color:#ccc \9}
495 491 .btn:first-child{*margin-left:0}
496 492 .btn:hover,.btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}
497 493 .btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}
498 494 .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)}
499 495 .btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}
500 496 .btn-large{padding:11px 19px;font-size:16.25px;border-radius:6px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}
501 497 .btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}
502 498 .btn-small{padding:2px 10px;font-size:11.049999999999999px;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}
503 499 .btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}
504 500 .btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}
505 501 .btn-mini{padding:0 6px;font-size:9.75px;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}
506 502 .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}
507 503 .btn-block+.btn-block{margin-top:5px}
508 504 input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}
509 505 .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)}
510 506 .btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;background-image:-moz-linear-gradient(top, #08c, #04c);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#04c));background-image:-webkit-linear-gradient(top, #08c, #04c);background-image:-o-linear-gradient(top, #08c, #04c);background-image:linear-gradient(to bottom, #08c, #04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}
511 507 .btn-primary:active,.btn-primary.active{background-color:#039 \9}
512 508 .btn-warning{color:#fff;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:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}
513 509 .btn-warning:active,.btn-warning.active{background-color:#c67605 \9}
514 510 .btn-danger{color:#fff;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:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}
515 511 .btn-danger:active,.btn-danger.active{background-color:#942a25 \9}
516 512 .btn-success{color:#fff;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:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}
517 513 .btn-success:active,.btn-success.active{background-color:#408140 \9}
518 514 .btn-info{color:#fff;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:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}
519 515 .btn-info:active,.btn-info.active{background-color:#24748c \9}
520 516 .btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;background-image:-moz-linear-gradient(top, #444, #222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#444), to(#222));background-image:-webkit-linear-gradient(top, #444, #222);background-image:-o-linear-gradient(top, #444, #222);background-image:linear-gradient(to bottom, #444, #222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}
521 517 .btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}
522 518 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}
523 519 button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}
524 520 button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}
525 521 button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}
526 522 .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}
527 523 .btn-link{border-color:transparent;cursor:pointer;color:#08c;border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
528 524 .btn-link:hover,.btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}
529 525 .btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333;text-decoration:none}
530 526 .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}
531 527 .btn-group+.btn-group{margin-left:5px}
532 528 .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}
533 529 .btn-group>.btn{position:relative;border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
534 530 .btn-group>.btn+.btn{margin-left:-1px}
535 531 .btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:13px}
536 532 .btn-group>.btn-mini{font-size:9.75px}
537 533 .btn-group>.btn-small{font-size:11.049999999999999px}
538 534 .btn-group>.btn-large{font-size:16.25px}
539 535 .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}
540 536 .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}
541 537 .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}
542 538 .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}
543 539 .btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}
544 540 .btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}
545 541 .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}
546 542 .btn-group>.btn-mini+.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:2px;*padding-bottom:2px}
547 543 .btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}
548 544 .btn-group>.btn-large+.dropdown-toggle{padding-left:12px;padding-right:12px;*padding-top:7px;*padding-bottom:7px}
549 545 .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)}
550 546 .btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}
551 547 .btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}
552 548 .btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}
553 549 .btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}
554 550 .btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}
555 551 .btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}
556 552 .btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}
557 553 .btn .caret{margin-top:8px;margin-left:0}
558 554 .btn-large .caret{margin-top:6px}
559 555 .btn-large .caret{border-left-width:5px;border-right-width:5px;border-top-width:5px}
560 556 .btn-mini .caret,.btn-small .caret{margin-top:8px}
561 557 .dropup .btn-large .caret{border-bottom-width:5px}
562 558 .btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}
563 559 .btn-group-vertical{display:inline-block;*display:inline;*zoom:1}
564 560 .btn-group-vertical>.btn{display:block;float:none;max-width:100%;border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
565 561 .btn-group-vertical>.btn+.btn{margin-left:0;margin-top:-1px}
566 562 .btn-group-vertical>.btn:first-child{border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}
567 563 .btn-group-vertical>.btn:last-child{border-radius:0 0 4px 4px;-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}
568 564 .btn-group-vertical>.btn-large:first-child{border-radius:6px 6px 0 0;-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}
569 565 .btn-group-vertical>.btn-large:last-child{border-radius:0 0 6px 6px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}
570 566 .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;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}
571 567 .alert,.alert h4{color:#c09853}
572 568 .alert h4{margin:0}
573 569 .alert .close{position:relative;top:-2px;right:-21px;line-height:20px}
574 570 .alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847}
575 571 .alert-success h4{color:#468847}
576 572 .alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48}
577 573 .alert-danger h4,.alert-error h4{color:#b94a48}
578 574 .alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad}
579 575 .alert-info h4{color:#3a87ad}
580 576 .alert-block{padding-top:14px;padding-bottom:14px}
581 577 .alert-block>p,.alert-block>ul{margin-bottom:0}
582 578 .alert-block p+p{margin-top:5px}
583 579 .nav{margin-left:0;margin-bottom:20px;list-style:none}
584 580 .nav>li>a{display:block}
585 581 .nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}
586 582 .nav>li>a>img{max-width:none}
587 583 .nav>.pull-right{float:right}
588 584 .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}
589 585 .nav li+.nav-header{margin-top:9px}
590 586 .nav-list{padding-left:15px;padding-right:15px;margin-bottom:0}
591 587 .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)}
592 588 .nav-list>li>a{padding:3px 15px}
593 589 .nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}
594 590 .nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}
595 591 .nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}
596 592 .nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";line-height:0}
597 593 .nav-tabs:after,.nav-pills:after{clear:both}
598 594 .nav-tabs>li,.nav-pills>li{float:left}
599 595 .nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}
600 596 .nav-tabs{border-bottom:1px solid #ddd}
601 597 .nav-tabs>li{margin-bottom:-1px}
602 598 .nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;border-radius:4px 4px 0 0;-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,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}
603 599 .nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}
604 600 .nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;border-radius:5px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}
605 601 .nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#08c}
606 602 .nav-stacked>li{float:none}
607 603 .nav-stacked>li>a{margin-right:0}
608 604 .nav-tabs.nav-stacked{border-bottom:0}
609 605 .nav-tabs.nav-stacked>li>a{border:1px solid #ddd;border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
610 606 .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}
611 607 .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}
612 608 .nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{border-color:#ddd;z-index:2}
613 609 .nav-pills.nav-stacked>li>a{margin-bottom:3px}
614 610 .nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}
615 611 .nav-tabs .dropdown-menu{border-radius:0 0 6px 6px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}
616 612 .nav-pills .dropdown-menu{border-radius:6px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}
617 613 .nav .dropdown-toggle .caret{border-top-color:#08c;border-bottom-color:#08c;margin-top:6px}
618 614 .nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}
619 615 .nav-tabs .dropdown-toggle .caret{margin-top:8px}
620 616 .nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}
621 617 .nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}
622 618 .nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}
623 619 .nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}
624 620 .nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}
625 621 .tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#999}
626 622 .tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;content:"";line-height:0}
627 623 .tabbable:after{clear:both}
628 624 .tab-content{overflow:auto}
629 625 .tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}
630 626 .tab-content>.tab-pane,.pill-content>.pill-pane{display:none}
631 627 .tab-content>.active,.pill-content>.active{display:block}
632 628 .tabs-below>.nav-tabs{border-top:1px solid #ddd}
633 629 .tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}
634 630 .tabs-below>.nav-tabs>li>a{border-radius:0 0 4px 4px;-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,.tabs-below>.nav-tabs>li>a:focus{border-bottom-color:transparent;border-top-color:#ddd}
635 631 .tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}
636 632 .tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}
637 633 .tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}
638 634 .tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}
639 635 .tabs-left>.nav-tabs>li>a{margin-right:-1px;border-radius:4px 0 0 4px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}
640 636 .tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}
641 637 .tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}
642 638 .tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}
643 639 .tabs-right>.nav-tabs>li>a{margin-left:-1px;border-radius:0 4px 4px 0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}
644 640 .tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}
645 641 .tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}
646 642 .nav>.disabled>a{color:#999}
647 643 .nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;background-color:transparent;cursor:default}
648 644 .navbar{overflow:visible;margin-bottom:20px;*position:relative;*z-index:2}
649 645 .navbar-inner{min-height:36px;padding-left:20px;padding-right:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top, #fff, #f2f2f2);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#f2f2f2));background-image:-webkit-linear-gradient(top, #fff, #f2f2f2);background-image:-o-linear-gradient(top, #fff, #f2f2f2);background-image:linear-gradient(to bottom, #fff, #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}
650 646 .navbar-inner:after{clear:both}
651 647 .navbar .container{width:auto}
652 648 .nav-collapse.collapse{height:auto;overflow:visible}
653 649 .navbar .brand{float:left;display:block;padding:8px 20px 8px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}
654 650 .navbar-text{margin-bottom:0;line-height:36px;color:#777}
655 651 .navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#333}
656 652 .navbar .divider-vertical{height:36px;margin:0 9px;border-left:1px solid #f2f2f2;border-right:1px solid #fff}
657 653 .navbar .btn,.navbar .btn-group{margin-top:3px}
658 654 .navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}
659 655 .navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;content:"";line-height:0}
660 656 .navbar-form:after{clear:both}
661 657 .navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:3px}
662 658 .navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}
663 659 .navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}
664 660 .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}
665 661 .navbar-search{position:relative;float:left;margin-top:3px;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;border-radius:15px;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}
666 662 .navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
667 663 .navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}
668 664 .navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}
669 665 .navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}
670 666 .navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
671 667 .navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}
672 668 .navbar-fixed-top{top:0}
673 669 .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)}
674 670 .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)}
675 671 .navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}
676 672 .navbar .nav.pull-right{float:right;margin-right:0}
677 673 .navbar .nav>li{float:left}
678 674 .navbar .nav>li>a{float:none;padding:8px 15px 8px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}
679 675 .navbar .nav .dropdown-toggle .caret{margin-top:8px}
680 676 .navbar .nav>li>a:focus,.navbar .nav>li>a:hover{background-color:transparent;color:#333;text-decoration:none}
681 677 .navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;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)}
682 678 .navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;color:#fff;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:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}
683 679 .navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}
684 680 .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)}
685 681 .btn-navbar .icon-bar+.icon-bar{margin-top:3px}
686 682 .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}
687 683 .navbar .nav>li>.dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:10px}
688 684 .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}
689 685 .navbar-fixed-bottom .nav>li>.dropdown-menu:after{border-top:6px solid #fff;border-bottom:0;bottom:-6px;top:auto}
690 686 .navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}
691 687 .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:#555}
692 688 .navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}
693 689 .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:#555;border-bottom-color:#555}
694 690 .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}
695 691 .navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{left:auto;right:13px}
696 692 .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;border-radius:6px 0 6px 6px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}
697 693 .navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top, #222, #111);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#222), to(#111));background-image:-webkit-linear-gradient(top, #222, #111);background-image:-o-linear-gradient(top, #222, #111);background-image:linear-gradient(to bottom, #222, #111);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);border-color:#252525}
698 694 .navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}
699 695 .navbar-inverse .brand{color:#999}
700 696 .navbar-inverse .navbar-text{color:#999}
701 697 .navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{background-color:transparent;color:#fff}
702 698 .navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}
703 699 .navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}
704 700 .navbar-inverse .divider-vertical{border-left-color:#111;border-right-color:#222}
705 701 .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:#111;color:#fff}
706 702 .navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}
707 703 .navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}
708 704 .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:#fff;border-bottom-color:#fff}
709 705 .navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-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:#ccc}
710 706 .navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}
711 707 .navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}
712 708 .navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;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}
713 709 .navbar-inverse .btn-navbar{color:#fff;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 #000;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:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}
714 710 .navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}
715 711 .breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;border-radius:4px;-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 #fff}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}
716 712 .breadcrumb>.active{color:#999}
717 713 .pagination{margin:20px 0}
718 714 .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)}
719 715 .pagination ul>li{display:inline}
720 716 .pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}
721 717 .pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}
722 718 .pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}
723 719 .pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#999;background-color:transparent;cursor:default}
724 720 .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}
725 721 .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}
726 722 .pagination-centered{text-align:center}
727 723 .pagination-right{text-align:right}
728 724 .pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:16.25px}
729 725 .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}
730 726 .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}
731 727 .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}
732 728 .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}
733 729 .pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.049999999999999px}
734 730 .pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:9.75px}
735 731 .pager{margin:20px 0;list-style:none;text-align:center;*zoom:1}.pager:before,.pager:after{display:table;content:"";line-height:0}
736 732 .pager:after{clear:both}
737 733 .pager li{display:inline}
738 734 .pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}
739 735 .pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}
740 736 .pager .next>a,.pager .next>span{float:right}
741 737 .pager .previous>a,.pager .previous>span{float:left}
742 738 .pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:default}
743 739 .modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}
744 740 .modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}
745 741 .modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;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%}
746 742 .modal.fade.in{top:10%}
747 743 .modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}
748 744 .modal-header h3{margin:0;line-height:30px}
749 745 .modal-body{position:relative;overflow-y:auto;max-height:400px;padding:15px}
750 746 .modal-form{margin-bottom:0}
751 747 .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 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff;*zoom:1}.modal-footer:before,.modal-footer:after{display:table;content:"";line-height:0}
752 748 .modal-footer:after{clear:both}
753 749 .modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}
754 750 .modal-footer .btn-group .btn+.btn{margin-left:-1px}
755 751 .modal-footer .btn-block+.btn-block{margin-left:0}
756 752 .tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}
757 753 .tooltip.top{margin-top:-3px;padding:5px 0}
758 754 .tooltip.right{margin-left:3px;padding:0 5px}
759 755 .tooltip.bottom{margin-top:3px;padding:5px 0}
760 756 .tooltip.left{margin-left:-3px;padding:0 5px}
761 757 .tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}
762 758 .tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}
763 759 .tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}
764 760 .tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}
765 761 .tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}
766 762 .tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}
767 763 .popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#fff;-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}
768 764 .popover.right{margin-left:10px}
769 765 .popover.bottom{margin-top:10px}
770 766 .popover.left{margin-left:-10px}
771 767 .popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}
772 768 .popover-content{padding:9px 14px}
773 769 .popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}
774 770 .popover .arrow{border-width:11px}
775 771 .popover .arrow:after{border-width:10px;content:""}
776 772 .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:#fff}
777 773 .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:#fff}
778 774 .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:#fff}
779 775 .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:#fff;bottom:-10px}
780 776 .thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;content:"";line-height:0}
781 777 .thumbnails:after{clear:both}
782 778 .row-fluid .thumbnails{margin-left:0}
783 779 .thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}
784 780 .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 .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}
785 781 a.thumbnail:hover,a.thumbnail:focus{border-color:#08c;-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)}
786 782 .thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto}
787 783 .thumbnail .caption{padding:9px;color:#555}
788 784 .media,.media-body{overflow:hidden;*overflow:visible;zoom:1}
789 785 .media,.media .media{margin-top:15px}
790 786 .media:first-child{margin-top:0}
791 787 .media-object{display:block}
792 788 .media-heading{margin:0 0 5px}
793 789 .media>.pull-left{margin-right:10px}
794 790 .media>.pull-right{margin-left:10px}
795 791 .media-list{margin-left:0;list-style:none}
796 792 .label,.badge{display:inline-block;padding:2px 4px;font-size:10.998px;font-weight:bold;line-height:14px;color:#fff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#999}
797 793 .label{border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}
798 794 .badge{padding-left:9px;padding-right:9px;border-radius:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}
799 795 .label:empty,.badge:empty{display:none}
800 796 a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}
801 797 .label-important,.badge-important{background-color:#b94a48}
802 798 .label-important[href],.badge-important[href]{background-color:#953b39}
803 799 .label-warning,.badge-warning{background-color:#f89406}
804 800 .label-warning[href],.badge-warning[href]{background-color:#c67605}
805 801 .label-success,.badge-success{background-color:#468847}
806 802 .label-success[href],.badge-success[href]{background-color:#356635}
807 803 .label-info,.badge-info{background-color:#3a87ad}
808 804 .label-info[href],.badge-info[href]{background-color:#2d6987}
809 805 .label-inverse,.badge-inverse{background-color:#333}
810 806 .label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}
811 807 .btn .label,.btn .badge{position:relative;top:-1px}
812 808 .btn-mini .label,.btn-mini .badge{top:0}
813 809 @-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);border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}
814 810 .progress .bar{width:0;height:100%;color:#fff;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 .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}
815 811 .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)}
816 812 .progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,0.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,0.15)), color-stop(.75, rgba(255,255,255,0.15)), color-stop(.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}
817 813 .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}
818 814 .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)}
819 815 .progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,0.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,0.15)), color-stop(.75, rgba(255,255,255,0.15)), color-stop(.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)}
820 816 .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)}
821 817 .progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,0.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,0.15)), color-stop(.75, rgba(255,255,255,0.15)), color-stop(.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)}
822 818 .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)}
823 819 .progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,0.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,0.15)), color-stop(.75, rgba(255,255,255,0.15)), color-stop(.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)}
824 820 .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)}
825 821 .progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255,255,255,0.15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255,255,255,0.15)), color-stop(.75, rgba(255,255,255,0.15)), color-stop(.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)}
826 822 .accordion{margin-bottom:20px}
827 823 .accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}
828 824 .accordion-heading{border-bottom:0}
829 825 .accordion-heading .accordion-toggle{display:block;padding:8px 15px}
830 826 .accordion-toggle{cursor:pointer}
831 827 .accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}
832 828 .carousel{position:relative;margin-bottom:20px;line-height:1}
833 829 .carousel-inner{overflow:hidden;width:100%;position:relative}
834 830 .carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}
835 831 .carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}
836 832 .carousel-inner>.active{left:0}
837 833 .carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}
838 834 .carousel-inner>.next{left:100%}
839 835 .carousel-inner>.prev{left:-100%}
840 836 .carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}
841 837 .carousel-inner>.active.left{left:-100%}
842 838 .carousel-inner>.active.right{left:100%}
843 839 .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:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{left:auto;right:15px}
844 840 .carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}
845 841 .carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}
846 842 .carousel-indicators .active{background-color:#fff}
847 843 .carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}
848 844 .carousel-caption h4,.carousel-caption p{color:#fff;line-height:20px}
849 845 .carousel-caption h4{margin:0 0 5px}
850 846 .carousel-caption p{margin-bottom:0}
851 847 .hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;border-radius:6px;-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}
852 848 .hero-unit li{line-height:30px}
853 849 .pull-right{float:right}
854 850 .pull-left{float:left}
855 851 .hide{display:none}
856 852 .show{display:block}
857 853 .invisible{visibility:hidden}
858 854 .affix{position:fixed}
855 .clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0}
856 .clearfix:after{clear:both}
857 .hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}
858 .input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
859 859 @-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}
860 860 .visible-phone{display:none !important}
861 861 .visible-tablet{display:none !important}
862 862 .hidden-desktop{display:none !important}
863 863 .visible-desktop{display:inherit !important}
864 864 @media (min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit !important} .visible-desktop{display:none !important} .visible-tablet{display:inherit !important} .hidden-tablet{display:none !important}}@media (max-width:767px){.hidden-desktop{display:inherit !important} .visible-desktop{display:none !important} .visible-phone{display:inherit !important} .hidden-phone{display:none !important}}.visible-print{display:none !important}
865 865 @media print{.visible-print{display:inherit !important} .hidden-print{display:none !important}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;content:"";line-height:0} .row:after{clear:both} [class*="span"]{float:left;min-height:1px;margin-left:30px} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px} .span12{width:1170px} .span11{width:1070px} .span10{width:970px} .span9{width:870px} .span8{width:770px} .span7{width:670px} .span6{width:570px} .span5{width:470px} .span4{width:370px} .span3{width:270px} .span2{width:170px} .span1{width:70px} .offset12{margin-left:1230px} .offset11{margin-left:1130px} .offset10{margin-left:1030px} .offset9{margin-left:930px} .offset8{margin-left:830px} .offset7{margin-left:730px} .offset6{margin-left:630px} .offset5{margin-left:530px} .offset4{margin-left:430px} .offset3{margin-left:330px} .offset2{margin-left:230px} .offset1{margin-left:130px} .row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0} .row-fluid:after{clear:both} .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.564102564102564%;*margin-left:2.5109110747408616%} .row-fluid [class*="span"]:first-child{margin-left:0} .row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%} .row-fluid .span12{width:100%;*width:99.94680851063829%} .row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%} .row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%} .row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%} .row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%} .row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%} .row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%} .row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%} .row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%} .row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%} .row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%} .row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%} .row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%} .row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%} .row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%} .row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%} .row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%} .row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%} .row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%} .row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%} .row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%} .row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%} .row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%} .row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%} .row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%} .row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%} .row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%} .row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%} .row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%} .row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%} .row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%} .row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%} .row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%} .row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%} .row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%} .row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%} input,textarea,.uneditable-input{margin-left:0} .controls-row [class*="span"]+[class*="span"]{margin-left:30px} input.span12,textarea.span12,.uneditable-input.span12{width:1156px} input.span11,textarea.span11,.uneditable-input.span11{width:1056px} input.span10,textarea.span10,.uneditable-input.span10{width:956px} input.span9,textarea.span9,.uneditable-input.span9{width:856px} input.span8,textarea.span8,.uneditable-input.span8{width:756px} input.span7,textarea.span7,.uneditable-input.span7{width:656px} input.span6,textarea.span6,.uneditable-input.span6{width:556px} input.span5,textarea.span5,.uneditable-input.span5{width:456px} input.span4,textarea.span4,.uneditable-input.span4{width:356px} input.span3,textarea.span3,.uneditable-input.span3{width:256px} input.span2,textarea.span2,.uneditable-input.span2{width:156px} input.span1,textarea.span1,.uneditable-input.span1{width:56px} .thumbnails{margin-left:-30px} .thumbnails>li{margin-left:30px} .row-fluid .thumbnails{margin-left:0}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:"";line-height:0} .row:after{clear:both} [class*="span"]{float:left;min-height:1px;margin-left:20px} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px} .span12{width:724px} .span11{width:662px} .span10{width:600px} .span9{width:538px} .span8{width:476px} .span7{width:414px} .span6{width:352px} .span5{width:290px} .span4{width:228px} .span3{width:166px} .span2{width:104px} .span1{width:42px} .offset12{margin-left:764px} .offset11{margin-left:702px} .offset10{margin-left:640px} .offset9{margin-left:578px} .offset8{margin-left:516px} .offset7{margin-left:454px} .offset6{margin-left:392px} .offset5{margin-left:330px} .offset4{margin-left:268px} .offset3{margin-left:206px} .offset2{margin-left:144px} .offset1{margin-left:82px} .row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0} .row-fluid:after{clear:both} .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.7624309392265194%;*margin-left:2.709239449864817%} .row-fluid [class*="span"]:first-child{margin-left:0} .row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%} .row-fluid .span12{width:100%;*width:99.94680851063829%} .row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%} .row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%} .row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%} .row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%} .row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%} .row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%} .row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%} .row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%} .row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%} .row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%} .row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%} .row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%} .row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%} .row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%} .row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%} .row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%} .row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%} .row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%} .row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%} .row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%} .row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%} .row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%} .row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%} .row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%} .row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%} .row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%} .row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%} .row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%} .row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%} .row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%} .row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%} .row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%} .row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%} .row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%} .row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%} input,textarea,.uneditable-input{margin-left:0} .controls-row [class*="span"]+[class*="span"]{margin-left:20px} input.span12,textarea.span12,.uneditable-input.span12{width:710px} input.span11,textarea.span11,.uneditable-input.span11{width:648px} input.span10,textarea.span10,.uneditable-input.span10{width:586px} input.span9,textarea.span9,.uneditable-input.span9{width:524px} input.span8,textarea.span8,.uneditable-input.span8{width:462px} input.span7,textarea.span7,.uneditable-input.span7{width:400px} input.span6,textarea.span6,.uneditable-input.span6{width:338px} input.span5,textarea.span5,.uneditable-input.span5{width:276px} input.span4,textarea.span4,.uneditable-input.span4{width:214px} input.span3,textarea.span3,.uneditable-input.span3{width:152px} input.span2,textarea.span2,.uneditable-input.span2{width:90px} input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media (max-width:767px){body{padding-left:20px;padding-right:20px} .navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-left:-20px;margin-right:-20px} .container-fluid{padding:0} .dl-horizontal dt{float:none;clear:none;width:auto;text-align:left} .dl-horizontal dd{margin-left:0} .container{width:auto} .row-fluid{width:100%} .row,.thumbnails{margin-left:0} .thumbnails>li{float:none;margin-left:0} [class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{float:none;display:block;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} .span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} .row-fluid [class*="offset"]:first-child{margin-left:0} .input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} .input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto} .controls-row [class*="span"]+[class*="span"]{margin-left:0} .modal{position:fixed;top:20px;left:20px;right:20px;width:auto;margin:0}.modal.fade{top:-100px} .modal.fade.in{top:20px}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0)} .page-header h1 small{display:block;line-height:20px} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc} .form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left} .form-horizontal .controls{margin-left:0} .form-horizontal .control-list{padding-top:0} .form-horizontal .form-actions{padding-left:10px;padding-right:10px} .media .pull-left,.media .pull-right{float:none;display:block;margin-bottom:10px} .media-object{margin-right:0;margin-left:0} .modal{top:10px;left:10px;right:10px} .modal-header .close{padding:10px;margin:-10px} .carousel-caption{position:static}}@media (max-width:979px){body{padding-top:0} .navbar-fixed-top,.navbar-fixed-bottom{position:static} .navbar-fixed-top{margin-bottom:20px} .navbar-fixed-bottom{margin-top:20px} .navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px} .navbar .container{width:auto;padding:0} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px} .nav-collapse{clear:both} .nav-collapse .nav{float:none;margin:0 0 10px} .nav-collapse .nav>li{float:none} .nav-collapse .nav>li>a{margin-bottom:2px} .nav-collapse .nav>.divider-vertical{display:none} .nav-collapse .nav .nav-header{color:#777;text-shadow:none} .nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px} .nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px} .nav-collapse .dropdown-menu li+li a{margin-bottom:2px} .nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2} .navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999} .navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111} .nav-collapse.in .btn-group{margin-top:5px;padding:0} .nav-collapse .dropdown-menu{position:static;top:auto;left:auto;float:none;display:none;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none} .nav-collapse .open>.dropdown-menu{display:block} .nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none} .nav-collapse .dropdown-menu .divider{display:none} .nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none} .nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1)} .navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111} .navbar .nav-collapse .nav.pull-right{float:none;margin-left:0} .nav-collapse,.nav-collapse.collapse{overflow:hidden;height:0} .navbar .btn-navbar{display:block} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px}}@media (min-width:979px + 1){.nav-collapse.collapse{height:auto !important;overflow:visible !important}}@font-face{font-family:'FontAwesome';src:url('../components/font-awesome/font/fontawesome-webfont.eot?v=3.2.1');src:url('../components/font-awesome/font/fontawesome-webfont.eot?#iefix&v=3.2.1') format('embedded-opentype'),url('../components/font-awesome/font/fontawesome-webfont.woff?v=3.2.1') format('woff'),url('../components/font-awesome/font/fontawesome-webfont.ttf?v=3.2.1') format('truetype'),url('../components/font-awesome/font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg');font-weight:normal;font-style:normal}[class^="icon-"],[class*=" icon-"]{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em}
866 866 [class^="icon-"]:before,[class*=" icon-"]:before{text-decoration:inherit;display:inline-block;speak:none}
867 867 .icon-large:before{vertical-align:-10%;font-size:1.3333333333333333em}
868 868 a [class^="icon-"],a [class*=" icon-"]{display:inline}
869 869 [class^="icon-"].icon-fixed-width,[class*=" icon-"].icon-fixed-width{display:inline-block;width:1.1428571428571428em;text-align:right;padding-right:.2857142857142857em}[class^="icon-"].icon-fixed-width.icon-large,[class*=" icon-"].icon-fixed-width.icon-large{width:1.4285714285714286em}
870 870 .icons-ul{margin-left:2.142857142857143em;list-style-type:none}.icons-ul>li{position:relative}
871 871 .icons-ul .icon-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;text-align:center;line-height:inherit}
872 872 [class^="icon-"].hide,[class*=" icon-"].hide{display:none}
873 873 .icon-muted{color:#eee}
874 874 .icon-light{color:#fff}
875 875 .icon-dark{color:#333}
876 876 .icon-border{border:solid 1px #eee;padding:.2em .25em .15em;border-radius:3px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}
877 877 .icon-2x{font-size:2em}.icon-2x.icon-border{border-width:2px;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}
878 878 .icon-3x{font-size:3em}.icon-3x.icon-border{border-width:3px;border-radius:5px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}
879 879 .icon-4x{font-size:4em}.icon-4x.icon-border{border-width:4px;border-radius:6px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}
880 880 .icon-5x{font-size:5em}.icon-5x.icon-border{border-width:5px;border-radius:7px;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}
881 881 .pull-right{float:right}
882 882 .pull-left{float:left}
883 883 [class^="icon-"].pull-left,[class*=" icon-"].pull-left{margin-right:.3em}
884 884 [class^="icon-"].pull-right,[class*=" icon-"].pull-right{margin-left:.3em}
885 885 [class^="icon-"],[class*=" icon-"]{display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0 0;background-repeat:repeat;margin-top:0}
886 886 .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:none}
887 887 .btn [class^="icon-"].icon-large,.nav [class^="icon-"].icon-large,.btn [class*=" icon-"].icon-large,.nav [class*=" icon-"].icon-large{line-height:.9em}
888 888 .btn [class^="icon-"].icon-spin,.nav [class^="icon-"].icon-spin,.btn [class*=" icon-"].icon-spin,.nav [class*=" icon-"].icon-spin{display:inline-block}
889 889 .nav-tabs [class^="icon-"],.nav-pills [class^="icon-"],.nav-tabs [class*=" icon-"],.nav-pills [class*=" icon-"],.nav-tabs [class^="icon-"].icon-large,.nav-pills [class^="icon-"].icon-large,.nav-tabs [class*=" icon-"].icon-large,.nav-pills [class*=" icon-"].icon-large{line-height:.9em}
890 890 .btn [class^="icon-"].pull-left.icon-2x,.btn [class*=" icon-"].pull-left.icon-2x,.btn [class^="icon-"].pull-right.icon-2x,.btn [class*=" icon-"].pull-right.icon-2x{margin-top:.18em}
891 891 .btn [class^="icon-"].icon-spin.icon-large,.btn [class*=" icon-"].icon-spin.icon-large{line-height:.8em}
892 892 .btn.btn-small [class^="icon-"].pull-left.icon-2x,.btn.btn-small [class*=" icon-"].pull-left.icon-2x,.btn.btn-small [class^="icon-"].pull-right.icon-2x,.btn.btn-small [class*=" icon-"].pull-right.icon-2x{margin-top:.25em}
893 893 .btn.btn-large [class^="icon-"],.btn.btn-large [class*=" icon-"]{margin-top:0}.btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x,.btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-top:.05em}
894 894 .btn.btn-large [class^="icon-"].pull-left.icon-2x,.btn.btn-large [class*=" icon-"].pull-left.icon-2x{margin-right:.2em}
895 895 .btn.btn-large [class^="icon-"].pull-right.icon-2x,.btn.btn-large [class*=" icon-"].pull-right.icon-2x{margin-left:.2em}
896 896 .nav-list [class^="icon-"],.nav-list [class*=" icon-"]{line-height:inherit}
897 897 .icon-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:-35%}.icon-stack [class^="icon-"],.icon-stack [class*=" icon-"]{display:block;text-align:center;position:absolute;width:100%;height:100%;font-size:1em;line-height:inherit;*line-height:2em}
898 898 .icon-stack .icon-stack-base{font-size:2em;*line-height:1em}
899 899 .icon-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear}
900 900 a .icon-stack,a .icon-spin{display:inline-block;text-decoration:none}
901 901 @-moz-keyframes spin{0%{-moz-transform:rotate(0deg)} 100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)} 100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)} 100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)} 100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)} 100%{transform:rotate(359deg)}}.icon-rotate-90:before{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1)}
902 902 .icon-rotate-180:before{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2)}
903 903 .icon-rotate-270:before{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3)}
904 904 .icon-flip-horizontal:before{-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}
905 905 .icon-flip-vertical:before{-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}
906 906 a .icon-rotate-90:before,a .icon-rotate-180:before,a .icon-rotate-270:before,a .icon-flip-horizontal:before,a .icon-flip-vertical:before{display:inline-block}
907 907 .icon-glass:before{content:"\f000"}
908 908 .icon-music:before{content:"\f001"}
909 909 .icon-search:before{content:"\f002"}
910 910 .icon-envelope-alt:before{content:"\f003"}
911 911 .icon-heart:before{content:"\f004"}
912 912 .icon-star:before{content:"\f005"}
913 913 .icon-star-empty:before{content:"\f006"}
914 914 .icon-user:before{content:"\f007"}
915 915 .icon-film:before{content:"\f008"}
916 916 .icon-th-large:before{content:"\f009"}
917 917 .icon-th:before{content:"\f00a"}
918 918 .icon-th-list:before{content:"\f00b"}
919 919 .icon-ok:before{content:"\f00c"}
920 920 .icon-remove:before{content:"\f00d"}
921 921 .icon-zoom-in:before{content:"\f00e"}
922 922 .icon-zoom-out:before{content:"\f010"}
923 923 .icon-power-off:before,.icon-off:before{content:"\f011"}
924 924 .icon-signal:before{content:"\f012"}
925 925 .icon-gear:before,.icon-cog:before{content:"\f013"}
926 926 .icon-trash:before{content:"\f014"}
927 927 .icon-home:before{content:"\f015"}
928 928 .icon-file-alt:before{content:"\f016"}
929 929 .icon-time:before{content:"\f017"}
930 930 .icon-road:before{content:"\f018"}
931 931 .icon-download-alt:before{content:"\f019"}
932 932 .icon-download:before{content:"\f01a"}
933 933 .icon-upload:before{content:"\f01b"}
934 934 .icon-inbox:before{content:"\f01c"}
935 935 .icon-play-circle:before{content:"\f01d"}
936 936 .icon-rotate-right:before,.icon-repeat:before{content:"\f01e"}
937 937 .icon-refresh:before{content:"\f021"}
938 938 .icon-list-alt:before{content:"\f022"}
939 939 .icon-lock:before{content:"\f023"}
940 940 .icon-flag:before{content:"\f024"}
941 941 .icon-headphones:before{content:"\f025"}
942 942 .icon-volume-off:before{content:"\f026"}
943 943 .icon-volume-down:before{content:"\f027"}
944 944 .icon-volume-up:before{content:"\f028"}
945 945 .icon-qrcode:before{content:"\f029"}
946 946 .icon-barcode:before{content:"\f02a"}
947 947 .icon-tag:before{content:"\f02b"}
948 948 .icon-tags:before{content:"\f02c"}
949 949 .icon-book:before{content:"\f02d"}
950 950 .icon-bookmark:before{content:"\f02e"}
951 951 .icon-print:before{content:"\f02f"}
952 952 .icon-camera:before{content:"\f030"}
953 953 .icon-font:before{content:"\f031"}
954 954 .icon-bold:before{content:"\f032"}
955 955 .icon-italic:before{content:"\f033"}
956 956 .icon-text-height:before{content:"\f034"}
957 957 .icon-text-width:before{content:"\f035"}
958 958 .icon-align-left:before{content:"\f036"}
959 959 .icon-align-center:before{content:"\f037"}
960 960 .icon-align-right:before{content:"\f038"}
961 961 .icon-align-justify:before{content:"\f039"}
962 962 .icon-list:before{content:"\f03a"}
963 963 .icon-indent-left:before{content:"\f03b"}
964 964 .icon-indent-right:before{content:"\f03c"}
965 965 .icon-facetime-video:before{content:"\f03d"}
966 966 .icon-picture:before{content:"\f03e"}
967 967 .icon-pencil:before{content:"\f040"}
968 968 .icon-map-marker:before{content:"\f041"}
969 969 .icon-adjust:before{content:"\f042"}
970 970 .icon-tint:before{content:"\f043"}
971 971 .icon-edit:before{content:"\f044"}
972 972 .icon-share:before{content:"\f045"}
973 973 .icon-check:before{content:"\f046"}
974 974 .icon-move:before{content:"\f047"}
975 975 .icon-step-backward:before{content:"\f048"}
976 976 .icon-fast-backward:before{content:"\f049"}
977 977 .icon-backward:before{content:"\f04a"}
978 978 .icon-play:before{content:"\f04b"}
979 979 .icon-pause:before{content:"\f04c"}
980 980 .icon-stop:before{content:"\f04d"}
981 981 .icon-forward:before{content:"\f04e"}
982 982 .icon-fast-forward:before{content:"\f050"}
983 983 .icon-step-forward:before{content:"\f051"}
984 984 .icon-eject:before{content:"\f052"}
985 985 .icon-chevron-left:before{content:"\f053"}
986 986 .icon-chevron-right:before{content:"\f054"}
987 987 .icon-plus-sign:before{content:"\f055"}
988 988 .icon-minus-sign:before{content:"\f056"}
989 989 .icon-remove-sign:before{content:"\f057"}
990 990 .icon-ok-sign:before{content:"\f058"}
991 991 .icon-question-sign:before{content:"\f059"}
992 992 .icon-info-sign:before{content:"\f05a"}
993 993 .icon-screenshot:before{content:"\f05b"}
994 994 .icon-remove-circle:before{content:"\f05c"}
995 995 .icon-ok-circle:before{content:"\f05d"}
996 996 .icon-ban-circle:before{content:"\f05e"}
997 997 .icon-arrow-left:before{content:"\f060"}
998 998 .icon-arrow-right:before{content:"\f061"}
999 999 .icon-arrow-up:before{content:"\f062"}
1000 1000 .icon-arrow-down:before{content:"\f063"}
1001 1001 .icon-mail-forward:before,.icon-share-alt:before{content:"\f064"}
1002 1002 .icon-resize-full:before{content:"\f065"}
1003 1003 .icon-resize-small:before{content:"\f066"}
1004 1004 .icon-plus:before{content:"\f067"}
1005 1005 .icon-minus:before{content:"\f068"}
1006 1006 .icon-asterisk:before{content:"\f069"}
1007 1007 .icon-exclamation-sign:before{content:"\f06a"}
1008 1008 .icon-gift:before{content:"\f06b"}
1009 1009 .icon-leaf:before{content:"\f06c"}
1010 1010 .icon-fire:before{content:"\f06d"}
1011 1011 .icon-eye-open:before{content:"\f06e"}
1012 1012 .icon-eye-close:before{content:"\f070"}
1013 1013 .icon-warning-sign:before{content:"\f071"}
1014 1014 .icon-plane:before{content:"\f072"}
1015 1015 .icon-calendar:before{content:"\f073"}
1016 1016 .icon-random:before{content:"\f074"}
1017 1017 .icon-comment:before{content:"\f075"}
1018 1018 .icon-magnet:before{content:"\f076"}
1019 1019 .icon-chevron-up:before{content:"\f077"}
1020 1020 .icon-chevron-down:before{content:"\f078"}
1021 1021 .icon-retweet:before{content:"\f079"}
1022 1022 .icon-shopping-cart:before{content:"\f07a"}
1023 1023 .icon-folder-close:before{content:"\f07b"}
1024 1024 .icon-folder-open:before{content:"\f07c"}
1025 1025 .icon-resize-vertical:before{content:"\f07d"}
1026 1026 .icon-resize-horizontal:before{content:"\f07e"}
1027 1027 .icon-bar-chart:before{content:"\f080"}
1028 1028 .icon-twitter-sign:before{content:"\f081"}
1029 1029 .icon-facebook-sign:before{content:"\f082"}
1030 1030 .icon-camera-retro:before{content:"\f083"}
1031 1031 .icon-key:before{content:"\f084"}
1032 1032 .icon-gears:before,.icon-cogs:before{content:"\f085"}
1033 1033 .icon-comments:before{content:"\f086"}
1034 1034 .icon-thumbs-up-alt:before{content:"\f087"}
1035 1035 .icon-thumbs-down-alt:before{content:"\f088"}
1036 1036 .icon-star-half:before{content:"\f089"}
1037 1037 .icon-heart-empty:before{content:"\f08a"}
1038 1038 .icon-signout:before{content:"\f08b"}
1039 1039 .icon-linkedin-sign:before{content:"\f08c"}
1040 1040 .icon-pushpin:before{content:"\f08d"}
1041 1041 .icon-external-link:before{content:"\f08e"}
1042 1042 .icon-signin:before{content:"\f090"}
1043 1043 .icon-trophy:before{content:"\f091"}
1044 1044 .icon-github-sign:before{content:"\f092"}
1045 1045 .icon-upload-alt:before{content:"\f093"}
1046 1046 .icon-lemon:before{content:"\f094"}
1047 1047 .icon-phone:before{content:"\f095"}
1048 1048 .icon-unchecked:before,.icon-check-empty:before{content:"\f096"}
1049 1049 .icon-bookmark-empty:before{content:"\f097"}
1050 1050 .icon-phone-sign:before{content:"\f098"}
1051 1051 .icon-twitter:before{content:"\f099"}
1052 1052 .icon-facebook:before{content:"\f09a"}
1053 1053 .icon-github:before{content:"\f09b"}
1054 1054 .icon-unlock:before{content:"\f09c"}
1055 1055 .icon-credit-card:before{content:"\f09d"}
1056 1056 .icon-rss:before{content:"\f09e"}
1057 1057 .icon-hdd:before{content:"\f0a0"}
1058 1058 .icon-bullhorn:before{content:"\f0a1"}
1059 1059 .icon-bell:before{content:"\f0a2"}
1060 1060 .icon-certificate:before{content:"\f0a3"}
1061 1061 .icon-hand-right:before{content:"\f0a4"}
1062 1062 .icon-hand-left:before{content:"\f0a5"}
1063 1063 .icon-hand-up:before{content:"\f0a6"}
1064 1064 .icon-hand-down:before{content:"\f0a7"}
1065 1065 .icon-circle-arrow-left:before{content:"\f0a8"}
1066 1066 .icon-circle-arrow-right:before{content:"\f0a9"}
1067 1067 .icon-circle-arrow-up:before{content:"\f0aa"}
1068 1068 .icon-circle-arrow-down:before{content:"\f0ab"}
1069 1069 .icon-globe:before{content:"\f0ac"}
1070 1070 .icon-wrench:before{content:"\f0ad"}
1071 1071 .icon-tasks:before{content:"\f0ae"}
1072 1072 .icon-filter:before{content:"\f0b0"}
1073 1073 .icon-briefcase:before{content:"\f0b1"}
1074 1074 .icon-fullscreen:before{content:"\f0b2"}
1075 1075 .icon-group:before{content:"\f0c0"}
1076 1076 .icon-link:before{content:"\f0c1"}
1077 1077 .icon-cloud:before{content:"\f0c2"}
1078 1078 .icon-beaker:before{content:"\f0c3"}
1079 1079 .icon-cut:before{content:"\f0c4"}
1080 1080 .icon-copy:before{content:"\f0c5"}
1081 1081 .icon-paperclip:before,.icon-paper-clip:before{content:"\f0c6"}
1082 1082 .icon-save:before{content:"\f0c7"}
1083 1083 .icon-sign-blank:before{content:"\f0c8"}
1084 1084 .icon-reorder:before{content:"\f0c9"}
1085 1085 .icon-list-ul:before{content:"\f0ca"}
1086 1086 .icon-list-ol:before{content:"\f0cb"}
1087 1087 .icon-strikethrough:before{content:"\f0cc"}
1088 1088 .icon-underline:before{content:"\f0cd"}
1089 1089 .icon-table:before{content:"\f0ce"}
1090 1090 .icon-magic:before{content:"\f0d0"}
1091 1091 .icon-truck:before{content:"\f0d1"}
1092 1092 .icon-pinterest:before{content:"\f0d2"}
1093 1093 .icon-pinterest-sign:before{content:"\f0d3"}
1094 1094 .icon-google-plus-sign:before{content:"\f0d4"}
1095 1095 .icon-google-plus:before{content:"\f0d5"}
1096 1096 .icon-money:before{content:"\f0d6"}
1097 1097 .icon-caret-down:before{content:"\f0d7"}
1098 1098 .icon-caret-up:before{content:"\f0d8"}
1099 1099 .icon-caret-left:before{content:"\f0d9"}
1100 1100 .icon-caret-right:before{content:"\f0da"}
1101 1101 .icon-columns:before{content:"\f0db"}
1102 1102 .icon-sort:before{content:"\f0dc"}
1103 1103 .icon-sort-down:before{content:"\f0dd"}
1104 1104 .icon-sort-up:before{content:"\f0de"}
1105 1105 .icon-envelope:before{content:"\f0e0"}
1106 1106 .icon-linkedin:before{content:"\f0e1"}
1107 1107 .icon-rotate-left:before,.icon-undo:before{content:"\f0e2"}
1108 1108 .icon-legal:before{content:"\f0e3"}
1109 1109 .icon-dashboard:before{content:"\f0e4"}
1110 1110 .icon-comment-alt:before{content:"\f0e5"}
1111 1111 .icon-comments-alt:before{content:"\f0e6"}
1112 1112 .icon-bolt:before{content:"\f0e7"}
1113 1113 .icon-sitemap:before{content:"\f0e8"}
1114 1114 .icon-umbrella:before{content:"\f0e9"}
1115 1115 .icon-paste:before{content:"\f0ea"}
1116 1116 .icon-lightbulb:before{content:"\f0eb"}
1117 1117 .icon-exchange:before{content:"\f0ec"}
1118 1118 .icon-cloud-download:before{content:"\f0ed"}
1119 1119 .icon-cloud-upload:before{content:"\f0ee"}
1120 1120 .icon-user-md:before{content:"\f0f0"}
1121 1121 .icon-stethoscope:before{content:"\f0f1"}
1122 1122 .icon-suitcase:before{content:"\f0f2"}
1123 1123 .icon-bell-alt:before{content:"\f0f3"}
1124 1124 .icon-coffee:before{content:"\f0f4"}
1125 1125 .icon-food:before{content:"\f0f5"}
1126 1126 .icon-file-text-alt:before{content:"\f0f6"}
1127 1127 .icon-building:before{content:"\f0f7"}
1128 1128 .icon-hospital:before{content:"\f0f8"}
1129 1129 .icon-ambulance:before{content:"\f0f9"}
1130 1130 .icon-medkit:before{content:"\f0fa"}
1131 1131 .icon-fighter-jet:before{content:"\f0fb"}
1132 1132 .icon-beer:before{content:"\f0fc"}
1133 1133 .icon-h-sign:before{content:"\f0fd"}
1134 1134 .icon-plus-sign-alt:before{content:"\f0fe"}
1135 1135 .icon-double-angle-left:before{content:"\f100"}
1136 1136 .icon-double-angle-right:before{content:"\f101"}
1137 1137 .icon-double-angle-up:before{content:"\f102"}
1138 1138 .icon-double-angle-down:before{content:"\f103"}
1139 1139 .icon-angle-left:before{content:"\f104"}
1140 1140 .icon-angle-right:before{content:"\f105"}
1141 1141 .icon-angle-up:before{content:"\f106"}
1142 1142 .icon-angle-down:before{content:"\f107"}
1143 1143 .icon-desktop:before{content:"\f108"}
1144 1144 .icon-laptop:before{content:"\f109"}
1145 1145 .icon-tablet:before{content:"\f10a"}
1146 1146 .icon-mobile-phone:before{content:"\f10b"}
1147 1147 .icon-circle-blank:before{content:"\f10c"}
1148 1148 .icon-quote-left:before{content:"\f10d"}
1149 1149 .icon-quote-right:before{content:"\f10e"}
1150 1150 .icon-spinner:before{content:"\f110"}
1151 1151 .icon-circle:before{content:"\f111"}
1152 1152 .icon-mail-reply:before,.icon-reply:before{content:"\f112"}
1153 1153 .icon-github-alt:before{content:"\f113"}
1154 1154 .icon-folder-close-alt:before{content:"\f114"}
1155 1155 .icon-folder-open-alt:before{content:"\f115"}
1156 1156 .icon-expand-alt:before{content:"\f116"}
1157 1157 .icon-collapse-alt:before{content:"\f117"}
1158 1158 .icon-smile:before{content:"\f118"}
1159 1159 .icon-frown:before{content:"\f119"}
1160 1160 .icon-meh:before{content:"\f11a"}
1161 1161 .icon-gamepad:before{content:"\f11b"}
1162 1162 .icon-keyboard:before{content:"\f11c"}
1163 1163 .icon-flag-alt:before{content:"\f11d"}
1164 1164 .icon-flag-checkered:before{content:"\f11e"}
1165 1165 .icon-terminal:before{content:"\f120"}
1166 1166 .icon-code:before{content:"\f121"}
1167 1167 .icon-reply-all:before{content:"\f122"}
1168 1168 .icon-mail-reply-all:before{content:"\f122"}
1169 1169 .icon-star-half-full:before,.icon-star-half-empty:before{content:"\f123"}
1170 1170 .icon-location-arrow:before{content:"\f124"}
1171 1171 .icon-crop:before{content:"\f125"}
1172 1172 .icon-code-fork:before{content:"\f126"}
1173 1173 .icon-unlink:before{content:"\f127"}
1174 1174 .icon-question:before{content:"\f128"}
1175 1175 .icon-info:before{content:"\f129"}
1176 1176 .icon-exclamation:before{content:"\f12a"}
1177 1177 .icon-superscript:before{content:"\f12b"}
1178 1178 .icon-subscript:before{content:"\f12c"}
1179 1179 .icon-eraser:before{content:"\f12d"}
1180 1180 .icon-puzzle-piece:before{content:"\f12e"}
1181 1181 .icon-microphone:before{content:"\f130"}
1182 1182 .icon-microphone-off:before{content:"\f131"}
1183 1183 .icon-shield:before{content:"\f132"}
1184 1184 .icon-calendar-empty:before{content:"\f133"}
1185 1185 .icon-fire-extinguisher:before{content:"\f134"}
1186 1186 .icon-rocket:before{content:"\f135"}
1187 1187 .icon-maxcdn:before{content:"\f136"}
1188 1188 .icon-chevron-sign-left:before{content:"\f137"}
1189 1189 .icon-chevron-sign-right:before{content:"\f138"}
1190 1190 .icon-chevron-sign-up:before{content:"\f139"}
1191 1191 .icon-chevron-sign-down:before{content:"\f13a"}
1192 1192 .icon-html5:before{content:"\f13b"}
1193 1193 .icon-css3:before{content:"\f13c"}
1194 1194 .icon-anchor:before{content:"\f13d"}
1195 1195 .icon-unlock-alt:before{content:"\f13e"}
1196 1196 .icon-bullseye:before{content:"\f140"}
1197 1197 .icon-ellipsis-horizontal:before{content:"\f141"}
1198 1198 .icon-ellipsis-vertical:before{content:"\f142"}
1199 1199 .icon-rss-sign:before{content:"\f143"}
1200 1200 .icon-play-sign:before{content:"\f144"}
1201 1201 .icon-ticket:before{content:"\f145"}
1202 1202 .icon-minus-sign-alt:before{content:"\f146"}
1203 1203 .icon-check-minus:before{content:"\f147"}
1204 1204 .icon-level-up:before{content:"\f148"}
1205 1205 .icon-level-down:before{content:"\f149"}
1206 1206 .icon-check-sign:before{content:"\f14a"}
1207 1207 .icon-edit-sign:before{content:"\f14b"}
1208 1208 .icon-external-link-sign:before{content:"\f14c"}
1209 1209 .icon-share-sign:before{content:"\f14d"}
1210 1210 .icon-compass:before{content:"\f14e"}
1211 1211 .icon-collapse:before{content:"\f150"}
1212 1212 .icon-collapse-top:before{content:"\f151"}
1213 1213 .icon-expand:before{content:"\f152"}
1214 1214 .icon-euro:before,.icon-eur:before{content:"\f153"}
1215 1215 .icon-gbp:before{content:"\f154"}
1216 1216 .icon-dollar:before,.icon-usd:before{content:"\f155"}
1217 1217 .icon-rupee:before,.icon-inr:before{content:"\f156"}
1218 1218 .icon-yen:before,.icon-jpy:before{content:"\f157"}
1219 1219 .icon-renminbi:before,.icon-cny:before{content:"\f158"}
1220 1220 .icon-won:before,.icon-krw:before{content:"\f159"}
1221 1221 .icon-bitcoin:before,.icon-btc:before{content:"\f15a"}
1222 1222 .icon-file:before{content:"\f15b"}
1223 1223 .icon-file-text:before{content:"\f15c"}
1224 1224 .icon-sort-by-alphabet:before{content:"\f15d"}
1225 1225 .icon-sort-by-alphabet-alt:before{content:"\f15e"}
1226 1226 .icon-sort-by-attributes:before{content:"\f160"}
1227 1227 .icon-sort-by-attributes-alt:before{content:"\f161"}
1228 1228 .icon-sort-by-order:before{content:"\f162"}
1229 1229 .icon-sort-by-order-alt:before{content:"\f163"}
1230 1230 .icon-thumbs-up:before{content:"\f164"}
1231 1231 .icon-thumbs-down:before{content:"\f165"}
1232 1232 .icon-youtube-sign:before{content:"\f166"}
1233 1233 .icon-youtube:before{content:"\f167"}
1234 1234 .icon-xing:before{content:"\f168"}
1235 1235 .icon-xing-sign:before{content:"\f169"}
1236 1236 .icon-youtube-play:before{content:"\f16a"}
1237 1237 .icon-dropbox:before{content:"\f16b"}
1238 1238 .icon-stackexchange:before{content:"\f16c"}
1239 1239 .icon-instagram:before{content:"\f16d"}
1240 1240 .icon-flickr:before{content:"\f16e"}
1241 1241 .icon-adn:before{content:"\f170"}
1242 1242 .icon-bitbucket:before{content:"\f171"}
1243 1243 .icon-bitbucket-sign:before{content:"\f172"}
1244 1244 .icon-tumblr:before{content:"\f173"}
1245 1245 .icon-tumblr-sign:before{content:"\f174"}
1246 1246 .icon-long-arrow-down:before{content:"\f175"}
1247 1247 .icon-long-arrow-up:before{content:"\f176"}
1248 1248 .icon-long-arrow-left:before{content:"\f177"}
1249 1249 .icon-long-arrow-right:before{content:"\f178"}
1250 1250 .icon-apple:before{content:"\f179"}
1251 1251 .icon-windows:before{content:"\f17a"}
1252 1252 .icon-android:before{content:"\f17b"}
1253 1253 .icon-linux:before{content:"\f17c"}
1254 1254 .icon-dribbble:before{content:"\f17d"}
1255 1255 .icon-skype:before{content:"\f17e"}
1256 1256 .icon-foursquare:before{content:"\f180"}
1257 1257 .icon-trello:before{content:"\f181"}
1258 1258 .icon-female:before{content:"\f182"}
1259 1259 .icon-male:before{content:"\f183"}
1260 1260 .icon-gittip:before{content:"\f184"}
1261 1261 .icon-sun:before{content:"\f185"}
1262 1262 .icon-moon:before{content:"\f186"}
1263 1263 .icon-archive:before{content:"\f187"}
1264 1264 .icon-bug:before{content:"\f188"}
1265 1265 .icon-vk:before{content:"\f189"}
1266 1266 .icon-weibo:before{content:"\f18a"}
1267 1267 .icon-renren:before{content:"\f18b"}
1268 1268 code{color:#000}
1269 1269 .border-box-sizing{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}
1270 1270 .corner-all{border-radius:4px}
1271 1271 .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;display:flex;flex-direction:row;align-items:stretch}
1272 1272 .hbox>*{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;flex:none}
1273 1273 .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%;display:flex;flex-direction:column;align-items:stretch}
1274 1274 .vbox>*{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;flex:none}
1275 1275 .hbox.reverse,.vbox.reverse,.reverse{-webkit-box-direction:reverse;-moz-box-direction:reverse;box-direction:reverse;flex-direction:row-reverse}
1276 1276 .hbox.box-flex0,.vbox.box-flex0,.box-flex0{-webkit-box-flex:0;-moz-box-flex:0;box-flex:0;flex:none;width:auto}
1277 1277 .hbox.box-flex1,.vbox.box-flex1,.box-flex1{-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}
1278 1278 .hbox.box-flex,.vbox.box-flex,.box-flex{-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}
1279 1279 .hbox.box-flex2,.vbox.box-flex2,.box-flex2{-webkit-box-flex:2;-moz-box-flex:2;box-flex:2;flex:2}
1280 1280 .box-group1{-webkit-box-flex-group:1;-moz-box-flex-group:1;box-flex-group:1}
1281 1281 .box-group2{-webkit-box-flex-group:2;-moz-box-flex-group:2;box-flex-group:2}
1282 1282 .hbox.start,.vbox.start,.start{-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;justify-content:flex-start}
1283 1283 .hbox.end,.vbox.end,.end{-webkit-box-pack:end;-moz-box-pack:end;box-pack:end;justify-content:flex-end}
1284 1284 .hbox.center,.vbox.center,.center{-webkit-box-pack:center;-moz-box-pack:center;box-pack:center;justify-content:center}
1285 1285 .hbox.align-start,.vbox.align-start,.align-start{-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start}
1286 1286 .hbox.align-end,.vbox.align-end,.align-end{-webkit-box-align:end;-moz-box-align:end;box-align:end;align-items:flex-end}
1287 1287 .hbox.align-center,.vbox.align-center,.align-center{-webkit-box-align:center;-moz-box-align:center;box-align:center;align-items:center}
1288 1288 div.error{margin:2em;text-align:center}
1289 1289 div.error>h1{font-size:500%;line-height:normal}
1290 1290 div.error>p{font-size:200%;line-height:normal}
1291 1291 div.traceback-wrapper{text-align:left;max-width:800px;margin:auto}
1292 1292 body{background-color:#fff;position:absolute;left:0;right:0;top:0;bottom:0;overflow:visible}
1293 1293 div#header{display:none}
1294 1294 #ipython_notebook{padding-left:16px}
1295 1295 #noscript{width:auto;padding-top:16px;padding-bottom:16px;text-align:center;font-size:22px;color:#f00;font-weight:bold}
1296 1296 #ipython_notebook img{font-family:Verdana,"Helvetica Neue",Arial,Helvetica,Geneva,sans-serif;height:24px;text-decoration:none;color:#000}
1297 1297 #site{width:100%;display:none}
1298 1298 .ui-button .ui-button-text{padding:.2em .8em;font-size:77%}
1299 1299 input.ui-button{padding:.3em .9em}
1300 1300 .navbar span{margin-top:3px}
1301 1301 span#login_widget{float:right}
1302 1302 .nav-header{text-transform:none}
1303 1303 .navbar-nobg{background-color:transparent;background-image:none}
1304 1304 #header>span{margin-top:10px}
1305 1305 .modal-body{max-height:500px}
1306 1306 @media (min-width:768px){.modal{width:700px;margin-left:-350px}}.center-nav{display:inline-block;margin-bottom:-4px}
1307 1307 .alternate_upload{background-color:none;display:inline}
1308 1308 .alternate_upload.form{padding:0;margin:0}
1309 1309 .alternate_upload input.fileinput{background-color:#f00;position:relative;opacity:0;z-index:2;width:295px;margin-left:163px;cursor:pointer;height:26px}
1310 1310 ul#tabs{margin-bottom:4px}
1311 1311 ul#tabs a{padding-top:4px;padding-bottom:4px}
1312 1312 ul.breadcrumb a:focus,ul.breadcrumb a:hover{text-decoration:none}
1313 1313 ul.breadcrumb i.icon-home{font-size:16px;margin-right:4px}
1314 1314 ul.breadcrumb span{color:#5e5e5e}
1315 1315 .list_toolbar{padding:4px 0 4px 0}
1316 1316 .list_toolbar [class*="span"]{min-height:26px}
1317 1317 .list_header{font-weight:bold}
1318 1318 .list_container{margin-top:4px;margin-bottom:20px;border:1px solid #ababab;border-radius:4px}
1319 1319 .list_container>div{border-bottom:1px solid #ababab}.list_container>div:hover .list-item{background-color:#f00}
1320 1320 .list_container>div:last-child{border:none}
1321 1321 .list_item:hover .list_item{background-color:#ddd}
1322 1322 .list_item a{text-decoration:none}
1323 1323 .list_header>div,.list_item>div{padding-top:4px;padding-bottom:4px;padding-left:7px;padding-right:7px;height:22px;line-height:22px}
1324 1324 .item_name{line-height:22px;height:26px}
1325 1325 .item_icon{font-size:14px;color:#5e5e5e;margin-right:7px}
1326 1326 .item_buttons{line-height:1em}
1327 1327 .toolbar_info{height:26px;line-height:26px}
1328 1328 input.nbname_input,input.engine_num_input{padding-top:3px;padding-bottom:3px;height:14px;line-height:14px;margin:0}
1329 1329 input.engine_num_input{width:60px}
1330 1330 .highlight_text{color:#00f}
1331 1331 #project_name>.breadcrumb{padding:0;margin-bottom:0;background-color:transparent;font-weight:bold}
1332 1332 .ansibold{font-weight:bold}
1333 1333 .ansiblack{color:#000}
1334 1334 .ansired{color:#8b0000}
1335 1335 .ansigreen{color:#006400}
1336 1336 .ansiyellow{color:#a52a2a}
1337 1337 .ansiblue{color:#00008b}
1338 1338 .ansipurple{color:#9400d3}
1339 1339 .ansicyan{color:#4682b4}
1340 1340 .ansigray{color:#808080}
1341 1341 .ansibgblack{background-color:#000}
1342 1342 .ansibgred{background-color:#f00}
1343 1343 .ansibggreen{background-color:#008000}
1344 1344 .ansibgyellow{background-color:#ff0}
1345 1345 .ansibgblue{background-color:#00f}
1346 1346 .ansibgpurple{background-color:#f0f}
1347 1347 .ansibgcyan{background-color:#0ff}
1348 1348 .ansibggray{background-color:#808080}
1349 1349 div.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%;display:flex;flex-direction:column;align-items:stretch}div.cell.selected{border-radius:4px;border:thin #ababab solid}
1350 1350 div.cell.edit_mode{border-radius:4px;border:thin #008000 solid}
1351 1351 div.cell{width:100%;padding:5px 5px 5px 0;margin:0;outline:none}
1352 1352 div.prompt{min-width:11ex;padding:.4em;margin:0;font-family:monospace;text-align:right;line-height:1.21429em}
1353 1353 div.inner_cell{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%;display:flex;flex-direction:column;align-items:stretch;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}
1354 1354 div.input_area{border:1px solid #cfcfcf;border-radius:4px;background:#f7f7f7}
1355 1355 div.prompt:empty{padding-top:0;padding-bottom:0}
1356 1356 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;display:flex;flex-direction:row;align-items:stretch}
1357 1357 div.input_prompt{color:#000080;border-top:1px solid transparent}
1358 1358 div.input_area>div.highlight{margin:.4em;border:none;padding:0;background-color:transparent}
1359 1359 div.input_area>div.highlight>pre{margin:0;border:0;padding:0;background-color:transparent;font-size:14px;line-height:1.21429em}
1360 1360 .CodeMirror{line-height:1.21429em;height:auto;background:none;}
1361 1361 .CodeMirror-scroll{overflow-y:hidden;overflow-x:auto}
1362 1362 @-moz-document url-prefix(){.CodeMirror-scroll{overflow-x:hidden}}.CodeMirror-lines{padding:.4em}
1363 1363 .CodeMirror-linenumber{padding:0 8px 0 4px}
1364 1364 .CodeMirror-gutters{border-bottom-left-radius:4px;border-top-left-radius:4px}
1365 1365 .CodeMirror pre{padding:0;border:0;border-radius:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}
1366 1366 pre code{display:block;padding:.5em}
1367 1367 .highlight-base,pre code,pre .subst,pre .tag .title,pre .lisp .title,pre .clojure .built_in,pre .nginx .title{color:#000}
1368 1368 .highlight-string,pre .string,pre .constant,pre .parent,pre .tag .value,pre .rules .value,pre .rules .value .number,pre .preprocessor,pre .ruby .symbol,pre .ruby .symbol .string,pre .aggregate,pre .template_tag,pre .django .variable,pre .smalltalk .class,pre .addition,pre .flow,pre .stream,pre .bash .variable,pre .apache .tag,pre .apache .cbracket,pre .tex .command,pre .tex .special,pre .erlang_repl .function_or_atom,pre .markdown .header{color:#ba2121}
1369 1369 .highlight-comment,pre .comment,pre .annotation,pre .template_comment,pre .diff .header,pre .chunk,pre .markdown .blockquote{color:#408080;font-style:italic}
1370 1370 .highlight-number,pre .number,pre .date,pre .regexp,pre .literal,pre .smalltalk .symbol,pre .smalltalk .char,pre .go .constant,pre .change,pre .markdown .bullet,pre .markdown .link_url{color:#080}
1371 1371 pre .label,pre .javadoc,pre .ruby .string,pre .decorator,pre .filter .argument,pre .localvars,pre .array,pre .attr_selector,pre .important,pre .pseudo,pre .pi,pre .doctype,pre .deletion,pre .envvar,pre .shebang,pre .apache .sqbracket,pre .nginx .built_in,pre .tex .formula,pre .erlang_repl .reserved,pre .prompt,pre .markdown .link_label,pre .vhdl .attribute,pre .clojure .attribute,pre .coffeescript .property{color:#88f}
1372 1372 .highlight-keyword,pre .keyword,pre .id,pre .phpdoc,pre .aggregate,pre .css .tag,pre .javadoctag,pre .phpdoc,pre .yardoctag,pre .smalltalk .class,pre .winutils,pre .bash .variable,pre .apache .tag,pre .go .typename,pre .tex .command,pre .markdown .strong,pre .request,pre .status{color:#008000;font-weight:bold}
1373 1373 .highlight-builtin,pre .built_in{color:#008000}
1374 1374 pre .markdown .emphasis{font-style:italic}
1375 1375 pre .nginx .built_in{font-weight:normal}
1376 1376 pre .coffeescript .javascript,pre .javascript .xml,pre .tex .formula,pre .xml .javascript,pre .xml .vbscript,pre .xml .css,pre .xml .cdata{opacity:.5}
1377 1377 .cm-s-ipython span.cm-variable{color:#000}
1378 1378 .cm-s-ipython span.cm-keyword{color:#008000;font-weight:bold}
1379 1379 .cm-s-ipython span.cm-number{color:#080}
1380 1380 .cm-s-ipython span.cm-comment{color:#408080;font-style:italic}
1381 1381 .cm-s-ipython span.cm-string{color:#ba2121}
1382 1382 .cm-s-ipython span.cm-builtin{color:#008000}
1383 1383 .cm-s-ipython span.cm-error{color:#f00}
1384 1384 .cm-s-ipython span.cm-operator{color:#a2f;font-weight:bold}
1385 1385 .cm-s-ipython span.cm-meta{color:#a2f}
1386 1386 .cm-s-ipython span.cm-tab{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAGFJREFUSMft1LsRQFAQheHPowAKoACx3IgEKtaEHujDjORSgWTH/ZOdnZOcM/sgk/kFFWY0qV8foQwS4MKBCS3qR6ixBJvElOobYAtivseIE120FaowJPN75GMu8j/LfMwNjh4HUpwg4LUAAAAASUVORK5CYII=);background-position:right;background-repeat:no-repeat}
1387 1387 div.output_wrapper{position:relative;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%;display:flex;flex-direction:column;align-items:stretch}
1388 1388 div.output_scroll{height:24em;width:100%;overflow:auto;border-radius:4px;-webkit-box-shadow:inset 0 2px 8px rgba(0,0,0,0.8);-moz-box-shadow:inset 0 2px 8px rgba(0,0,0,0.8);box-shadow:inset 0 2px 8px rgba(0,0,0,0.8);display:block}
1389 1389 div.output_collapsed{margin:0;padding:0;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%;display:flex;flex-direction:column;align-items:stretch}
1390 1390 div.out_prompt_overlay{height:100%;padding:0 .4em;position:absolute;border-radius:4px}
1391 1391 div.out_prompt_overlay:hover{-webkit-box-shadow:inset 0 0 1px #000;-moz-box-shadow:inset 0 0 1px #000;box-shadow:inset 0 0 1px #000;background:rgba(240,240,240,0.5)}
1392 1392 div.output_prompt{color:#8b0000}
1393 1393 div.output_area{padding:0;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;display:flex;flex-direction:row;align-items:stretch}div.output_area .MathJax_Display{text-align:left !important}
1394 1394 div.output_area .rendered_html table{margin-left:0;margin-right:0}
1395 1395 div.output_area .rendered_html img{margin-left:0;margin-right:0}
1396 1396 .output{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%;display:flex;flex-direction:column;align-items:stretch}
1397 1397 div.output_area pre{margin:0;padding:0;border:0;font-size:100%;vertical-align:baseline;color:#000;background-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;line-height:inherit}
1398 1398 div.output_subarea{padding:.4em .4em 0 .4em;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}
1399 1399 div.output_text{text-align:left;color:#000;line-height:1.21429em}
1400 1400 div.output_stderr{background:#fdd;}
1401 1401 div.output_latex{text-align:left}
1402 1402 div.output_javascript:empty{padding:0}
1403 1403 .js-error{color:#8b0000}
1404 1404 div.raw_input_container{font-family:monospace;padding-top:5px}
1405 1405 span.raw_input_prompt{}
1406 1406 input.raw_input{font-family:inherit;font-size:inherit;color:inherit;width:auto;vertical-align:baseline;padding:0 .25em;margin:0 .25em}
1407 1407 input.raw_input:focus{box-shadow:none}
1408 1408 p.p-space{margin-bottom:10px}
1409 1409 .rendered_html{color:#000;}.rendered_html em{font-style:italic}
1410 1410 .rendered_html strong{font-weight:bold}
1411 1411 .rendered_html u{text-decoration:underline}
1412 1412 .rendered_html :link{text-decoration:underline}
1413 1413 .rendered_html :visited{text-decoration:underline}
1414 1414 .rendered_html h1{font-size:185.7%;margin:1.08em 0 0 0;font-weight:bold;line-height:1}
1415 1415 .rendered_html h2{font-size:157.1%;margin:1.27em 0 0 0;font-weight:bold;line-height:1}
1416 1416 .rendered_html h3{font-size:128.6%;margin:1.55em 0 0 0;font-weight:bold;line-height:1}
1417 1417 .rendered_html h4{font-size:100%;margin:2em 0 0 0;font-weight:bold;line-height:1}
1418 1418 .rendered_html h5{font-size:100%;margin:2em 0 0 0;font-weight:bold;line-height:1;font-style:italic}
1419 1419 .rendered_html h6{font-size:100%;margin:2em 0 0 0;font-weight:bold;line-height:1;font-style:italic}
1420 1420 .rendered_html h1:first-child{margin-top:.538em}
1421 1421 .rendered_html h2:first-child{margin-top:.636em}
1422 1422 .rendered_html h3:first-child{margin-top:.777em}
1423 1423 .rendered_html h4:first-child{margin-top:1em}
1424 1424 .rendered_html h5:first-child{margin-top:1em}
1425 1425 .rendered_html h6:first-child{margin-top:1em}
1426 1426 .rendered_html ul{list-style:disc;margin:0 2em}
1427 1427 .rendered_html ul ul{list-style:square;margin:0 2em}
1428 1428 .rendered_html ul ul ul{list-style:circle;margin:0 2em}
1429 1429 .rendered_html ol{list-style:decimal;margin:0 2em}
1430 1430 .rendered_html ol ol{list-style:upper-alpha;margin:0 2em}
1431 1431 .rendered_html ol ol ol{list-style:lower-alpha;margin:0 2em}
1432 1432 .rendered_html ol ol ol ol{list-style:lower-roman;margin:0 2em}
1433 1433 .rendered_html ol ol ol ol ol{list-style:decimal;margin:0 2em}
1434 1434 .rendered_html *+ul{margin-top:1em}
1435 1435 .rendered_html *+ol{margin-top:1em}
1436 1436 .rendered_html hr{color:#000;background-color:#000}
1437 1437 .rendered_html pre{margin:1em 2em}
1438 1438 .rendered_html pre,.rendered_html code{border:0;background-color:#fff;color:#000;font-size:100%;padding:0}
1439 1439 .rendered_html blockquote{margin:1em 2em}
1440 1440 .rendered_html table{margin-left:auto;margin-right:auto;border:1px solid #000;border-collapse:collapse}
1441 1441 .rendered_html tr,.rendered_html th,.rendered_html td{border:1px solid #000;border-collapse:collapse;margin:1em 2em}
1442 1442 .rendered_html td,.rendered_html th{text-align:left;vertical-align:middle;padding:4px}
1443 1443 .rendered_html th{font-weight:bold}
1444 1444 .rendered_html *+table{margin-top:1em}
1445 1445 .rendered_html p{text-align:justify}
1446 1446 .rendered_html *+p{margin-top:1em}
1447 1447 .rendered_html img{display:block;margin-left:auto;margin-right:auto}
1448 1448 .rendered_html *+img{margin-top:1em}
1449 1449 div.text_cell{padding:5px 5px 5px 0;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;display:flex;flex-direction:row;align-items:stretch}
1450 1450 div.text_cell_render{outline:none;resize:none;width:inherit;border-style:none;padding:.5em .5em .5em .4em;color:#000}
1451 1451 a.anchor-link:link{text-decoration:none;padding:0 20px;visibility:hidden}
1452 1452 h1:hover .anchor-link,h2:hover .anchor-link,h3:hover .anchor-link,h4:hover .anchor-link,h5:hover .anchor-link,h6:hover .anchor-link{visibility:visible}
1453 1453 div.cell.text_cell.rendered{padding:0}
1454 1454 .widget-area{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;display:flex;flex-direction:row;align-items:stretch}.widget-area .widget-subarea{padding:.44em .4em .4em 1px;margin-left:6px;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;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%;display:flex;flex-direction:column;align-items:stretch;-webkit-box-flex:2;-moz-box-flex:2;box-flex:2;flex:2;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start}
1455 1455 .widget-hlabel{min-width:10ex;padding-right:8px;padding-top:3px;text-align:right;vertical-align:text-top}
1456 1456 .widget-vlabel{padding-bottom:5px;text-align:center;vertical-align:text-bottom}
1457 1457 .widget-hreadout{padding-left:8px;padding-top:3px;text-align:left;vertical-align:text-top}
1458 1458 .widget-vreadout{padding-top:5px;text-align:center;vertical-align:text-top}
1459 1459 .slide-track{border:1px solid #ccc;background:#fff;border-radius:4px;}
1460 1460 .widget-hslider{padding-left:8px;padding-right:5px;overflow:visible;width:348px;height:5px;max-height:5px;margin-top:11px;margin-bottom:10px;border:1px solid #ccc;background:#fff;border-radius:4px;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;display:flex;flex-direction:row;align-items:stretch}.widget-hslider .ui-slider{border:0 !important;background:none !important;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;display:flex;flex-direction:row;align-items:stretch;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}.widget-hslider .ui-slider .ui-slider-handle{width:14px !important;height:28px !important;margin-top:-8px !important}
1461 1461 .widget-vslider{padding-bottom:8px;overflow:visible;width:5px;max-width:5px;height:250px;margin-left:12px;border:1px solid #ccc;background:#fff;border-radius:4px;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%;display:flex;flex-direction:column;align-items:stretch}.widget-vslider .ui-slider{border:0 !important;background:none !important;margin-left:-4px;margin-top:5px;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%;display:flex;flex-direction:column;align-items:stretch;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}.widget-vslider .ui-slider .ui-slider-handle{width:28px !important;height:14px !important;margin-left:-9px}
1462 1462 .widget-text{width:350px;margin:0 !important}
1463 1463 .widget-listbox{width:364px;margin-bottom:0}
1464 1464 .widget-numeric-text{width:150px;margin:0 !important}
1465 1465 .widget-progress{width:363px}.widget-progress .bar{-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}
1466 1466 .widget-combo-btn{min-width:138px;}
1467 1467 .widget-box{margin:5px;-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;justify-content:flex-start;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start}
1468 1468 .widget-hbox{margin:5px;-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;justify-content:flex-start;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start;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;display:flex;flex-direction:row;align-items:stretch}
1469 1469 .widget-hbox-single{margin:5px;-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;justify-content:flex-start;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start;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;display:flex;flex-direction:row;align-items:stretch;height:30px}
1470 1470 .widget-vbox{margin:5px;-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;justify-content:flex-start;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start;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%;display:flex;flex-direction:column;align-items:stretch}
1471 1471 .widget-vbox-single{margin:5px;-webkit-box-pack:start;-moz-box-pack:start;box-pack:start;justify-content:flex-start;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start;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%;display:flex;flex-direction:column;align-items:stretch;width:30px}
1472 1472 .widget-modal{overflow:hidden;position:absolute !important;top:0;left:0;margin-left:0 !important}
1473 1473 .widget-modal-body{max-height:none !important}
1474 1474 .widget-container{box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-box-align:start;-moz-box-align:start;box-align:start;align-items:flex-start}
1475 1475 .widget-radio-box{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%;display:flex;flex-direction:column;align-items:stretch;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding-top:4px}
1476 1476 .docked-widget-modal{overflow:hidden;position:relative !important;top:0 !important;left:0 !important;margin-left:0 !important}
1477 1477 body{background-color:#fff}
1478 1478 body.notebook_app{overflow:hidden}
1479 1479 span#notebook_name{height:1em;line-height:1em;padding:3px;border:none;font-size:146.5%}
1480 1480 div#notebook_panel{margin:0 0 0 0;padding:0;-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}
1481 1481 div#notebook{font-size:14px;line-height:20px;overflow-y:scroll;overflow-x:auto;width:100%;padding:1em 0 1em 0;margin:0;border-top:1px solid #ababab;outline:none;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box}
1482 1482 div.ui-widget-content{border:1px solid #ababab;outline:none}
1483 1483 pre.dialog{background-color:#f7f7f7;border:1px solid #ddd;border-radius:4px;padding:.4em;padding-left:2em}
1484 1484 p.dialog{padding:.2em}
1485 1485 pre,code,kbd,samp{white-space:pre-wrap}
1486 1486 #fonttest{font-family:monospace}
1487 1487 p{margin-bottom:0}
1488 1488 .end_space{height:200px}
1489 1489 .celltoolbar{border:thin solid #cfcfcf;border-bottom:none;background:#eee;border-radius:3px 3px 0 0;width:100%;-webkit-box-pack:end;height:22px;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;display:flex;flex-direction:row;align-items:stretch;-webkit-box-direction:reverse;-moz-box-direction:reverse;box-direction:reverse;flex-direction:row-reverse}
1490 1490 .ctb_hideshow{display:none;vertical-align:bottom;padding-right:2px}
1491 1491 .celltoolbar>div{padding-top:0}
1492 1492 .ctb_global_show .ctb_show.ctb_hideshow{display:block}
1493 1493 .ctb_global_show .ctb_show+.input_area,.ctb_global_show .ctb_show+div.text_cell_input{border-top-right-radius:0;border-top-left-radius:0}
1494 1494 .celltoolbar .button_container select{margin:10px;margin-top:1px;margin-bottom:0;padding:0;font-size:87%;width:auto;display:inline-block;height:18px;line-height:18px;vertical-align:top}
1495 1495 .celltoolbar label{display:inline-block;height:15px;line-height:15px;vertical-align:top}
1496 1496 .celltoolbar label span{font-size:85%}
1497 1497 .celltoolbar input[type=checkbox]{margin:0;margin-left:4px;margin-right:4px}
1498 1498 .celltoolbar .ui-button{border:none;vertical-align:top;height:20px;min-width:30px}
1499 1499 .completions{position:absolute;z-index:10;overflow:hidden;border:1px solid #ababab;border-radius:4px;-webkit-box-shadow:0 6px 10px -1px #adadad;-moz-box-shadow:0 6px 10px -1px #adadad;box-shadow:0 6px 10px -1px #adadad}
1500 .completions select{background:#fff;outline:none;border:none;padding:0;margin:0;overflow:auto;font-family:monospace;font-size:110%;color:#000}
1500 .completions select{background:#fff;outline:none;border:none;padding:0;margin:0;overflow:auto;font-family:monospace;font-size:110%;color:#000;width:auto}
1501 1501 .completions select option.context{color:#0064cd}
1502 1502 #menubar .navbar-inner{min-height:28px;border-top:1px;border-radius:0 0 4px 4px}
1503 1503 #menubar .navbar{margin-bottom:8px}
1504 1504 .nav-wrapper{border-bottom:1px solid #d4d4d4}
1505 1505 #menubar li.dropdown{line-height:12px}
1506 1506 i.menu-icon{padding-top:4px}
1507 1507 ul#help_menu li a{overflow:hidden;padding-right:2.2em}ul#help_menu li a i{margin-right:-1.2em}
1508 1508 #notification_area{z-index:10}
1509 1509 .indicator_area{color:#777;padding:4px 3px;margin:0;width:11px;z-index:10;text-align:center}
1510 1510 #kernel_indicator{margin-right:-16px}
1511 1511 .edit_mode_icon:before{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f040"}
1512 1512 .command_mode_icon:before{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:' '}
1513 1513 .kernel_idle_icon:before{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f10c"}
1514 1514 .kernel_busy_icon:before{font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f111"}
1515 1515 .notification_widget{color:#777;padding:1px 12px;margin:2px 4px;z-index:10;border:1px solid #ccc;border-radius:4px;background:rgba(240,240,240,0.5)}.notification_widget.span{padding-right:2px}
1516 1516 div#pager_splitter{height:8px}
1517 1517 #pager-container{position:relative;padding:15px 0}
1518 1518 div#pager{font-size:14px;line-height:20px;overflow:auto;display:none}div#pager pre{font-size:13px;line-height:1.21429em;color:#000;background-color:#f7f7f7;padding:.4em}
1519 1519 .quickhelp{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;display:flex;flex-direction:row;align-items:stretch}
1520 1520 .shortcut_key{display:inline-block;width:20ex;text-align:right;font-family:monospace}
1521 1521 .shortcut_descr{display:inline-block;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;flex:1}
1522 1522 span#save_widget{padding:0 5px;margin-top:12px}
1523 1523 span#checkpoint_status,span#autosave_status{font-size:small}
1524 1524 @media (max-width:767px){span#save_widget{font-size:small} span#checkpoint_status,span#autosave_status{font-size:x-small}}@media (max-width:767px){span#checkpoint_status,span#autosave_status{display:none}}@media (min-width:768px) and (max-width:979px){span#checkpoint_status{display:none} span#autosave_status{font-size:x-small}}.toolbar{padding:0 10px;margin-top:-5px}.toolbar select,.toolbar label{width:auto;height:26px;vertical-align:middle;margin-right:2px;margin-bottom:0;display:inline;font-size:92%;margin-left:.3em;margin-right:.3em;padding:0;padding-top:3px}
1525 1525 .toolbar .btn{padding:2px 8px}
1526 1526 .toolbar .btn-group{margin-top:0}
1527 1527 .toolbar-inner{border:none !important;-webkit-box-shadow:none !important;-moz-box-shadow:none !important;box-shadow:none !important}
1528 1528 #maintoolbar{margin-bottom:0}
1529 1529 @-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}
1530 1530 .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}
1531 1531 .tooltipbuttons{position:absolute;padding-right:15px;top:0;right:0}
1532 1532 .tooltiptext{padding-right:30px}
1533 1533 .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:0;padding-left:7px;font-family:monospace;min-height:50px;-moz-box-shadow:0 6px 10px -1px #adadad;-webkit-box-shadow:0 6px 10px -1px #adadad;box-shadow:0 6px 10px -1px #adadad;border-radius:4px;position:absolute;z-index:2}.ipython_tooltip a{float:right}
1534 1534 .ipython_tooltip .tooltiptext pre{border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;font-size:100%;background-color:#f7f7f7}
1535 1535 .pretooltiparrow{left:0;margin:0;top:-16px;width:40px;height:16px;overflow:hidden;position:absolute}
1536 1536 .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,450 +1,451 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 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 // Base Widget Model and View classes
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["widgets/js/manager",
18 18 "underscore",
19 19 "backbone"],
20 20 function(WidgetManager, _, Backbone){
21 21
22 22 var WidgetModel = Backbone.Model.extend({
23 23 constructor: function (widget_manager, model_id, comm) {
24 24 // Constructor
25 25 //
26 26 // Creates a WidgetModel instance.
27 27 //
28 28 // Parameters
29 29 // ----------
30 30 // widget_manager : WidgetManager instance
31 31 // model_id : string
32 32 // An ID unique to this model.
33 33 // comm : Comm instance (optional)
34 34 this.widget_manager = widget_manager;
35 35 this._buffered_state_diff = {};
36 36 this.pending_msgs = 0;
37 37 this.msg_buffer = null;
38 38 this.key_value_lock = null;
39 39 this.id = model_id;
40 40 this.views = [];
41 41
42 42 if (comm !== undefined) {
43 43 // Remember comm associated with the model.
44 44 this.comm = comm;
45 45 comm.model = this;
46 46
47 47 // Hook comm messages up to model.
48 48 comm.on_close($.proxy(this._handle_comm_closed, this));
49 49 comm.on_msg($.proxy(this._handle_comm_msg, this));
50 50 }
51 51 return Backbone.Model.apply(this);
52 52 },
53 53
54 54 send: function (content, callbacks) {
55 55 // Send a custom msg over the comm.
56 56 if (this.comm !== undefined) {
57 57 var data = {method: 'custom', content: content};
58 58 this.comm.send(data, callbacks);
59 59 this.pending_msgs++;
60 60 }
61 61 },
62 62
63 63 _handle_comm_closed: function (msg) {
64 64 // Handle when a widget is closed.
65 65 this.trigger('comm:close');
66 66 delete this.comm.model; // Delete ref so GC will collect widget model.
67 67 delete this.comm;
68 68 delete this.model_id; // Delete id from model so widget manager cleans up.
69 69 _.each(this.views, function(view, i) {
70 70 view.remove();
71 71 });
72 72 },
73 73
74 74 _handle_comm_msg: function (msg) {
75 75 // Handle incoming comm msg.
76 76 var method = msg.content.data.method;
77 77 switch (method) {
78 78 case 'update':
79 79 this.apply_update(msg.content.data.state);
80 80 break;
81 81 case 'custom':
82 82 this.trigger('msg:custom', msg.content.data.content);
83 83 break;
84 84 case 'display':
85 85 this.widget_manager.display_view(msg, this);
86 this.trigger('displayed');
86 87 break;
87 88 }
88 89 },
89 90
90 91 apply_update: function (state) {
91 92 // Handle when a widget is updated via the python side.
92 93 var that = this;
93 94 _.each(state, function(value, key) {
94 95 that.key_value_lock = [key, value];
95 96 try {
96 97 WidgetModel.__super__.set.apply(that, [key, that._unpack_models(value)]);
97 98 } finally {
98 99 that.key_value_lock = null;
99 100 }
100 101 });
101 102 },
102 103
103 104 _handle_status: function (msg, callbacks) {
104 105 // Handle status msgs.
105 106
106 107 // execution_state : ('busy', 'idle', 'starting')
107 108 if (this.comm !== undefined) {
108 109 if (msg.content.execution_state ==='idle') {
109 110 // Send buffer if this message caused another message to be
110 111 // throttled.
111 112 if (this.msg_buffer !== null &&
112 113 (this.get('msg_throttle') || 3) === this.pending_msgs) {
113 114 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
114 115 this.comm.send(data, callbacks);
115 116 this.msg_buffer = null;
116 117 } else {
117 118 --this.pending_msgs;
118 119 }
119 120 }
120 121 }
121 122 },
122 123
123 124 callbacks: function(view) {
124 125 // Create msg callbacks for a comm msg.
125 126 var callbacks = this.widget_manager.callbacks(view);
126 127
127 128 if (callbacks.iopub === undefined) {
128 129 callbacks.iopub = {};
129 130 }
130 131
131 132 var that = this;
132 133 callbacks.iopub.status = function (msg) {
133 134 that._handle_status(msg, callbacks);
134 135 };
135 136 return callbacks;
136 137 },
137 138
138 139 set: function(key, val, options) {
139 140 // Set a value.
140 141 var return_value = WidgetModel.__super__.set.apply(this, arguments);
141 142
142 143 // Backbone only remembers the diff of the most recent set()
143 144 // operation. Calling set multiple times in a row results in a
144 145 // loss of diff information. Here we keep our own running diff.
145 146 this._buffered_state_diff = $.extend(this._buffered_state_diff, this.changedAttributes() || {});
146 147 return return_value;
147 148 },
148 149
149 150 sync: function (method, model, options) {
150 151 // Handle sync to the back-end. Called when a model.save() is called.
151 152
152 153 // Make sure a comm exists.
153 154 var error = options.error || function() {
154 155 console.error('Backbone sync error:', arguments);
155 156 };
156 157 if (this.comm === undefined) {
157 158 error();
158 159 return false;
159 160 }
160 161
161 162 // Delete any key value pairs that the back-end already knows about.
162 163 var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
163 164 if (this.key_value_lock !== null) {
164 165 var key = this.key_value_lock[0];
165 166 var value = this.key_value_lock[1];
166 167 if (attrs[key] === value) {
167 168 delete attrs[key];
168 169 }
169 170 }
170 171
171 172 // Only sync if there are attributes to send to the back-end.
172 173 attrs = this._pack_models(attrs);
173 174 if (_.size(attrs) > 0) {
174 175
175 176 // If this message was sent via backbone itself, it will not
176 177 // have any callbacks. It's important that we create callbacks
177 178 // so we can listen for status messages, etc...
178 179 var callbacks = options.callbacks || this.callbacks();
179 180
180 181 // Check throttle.
181 182 if (this.pending_msgs >= (this.get('msg_throttle') || 3)) {
182 183 // The throttle has been exceeded, buffer the current msg so
183 184 // it can be sent once the kernel has finished processing
184 185 // some of the existing messages.
185 186
186 187 // Combine updates if it is a 'patch' sync, otherwise replace updates
187 188 switch (method) {
188 189 case 'patch':
189 190 this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
190 191 break;
191 192 case 'update':
192 193 case 'create':
193 194 this.msg_buffer = attrs;
194 195 break;
195 196 default:
196 197 error();
197 198 return false;
198 199 }
199 200 this.msg_buffer_callbacks = callbacks;
200 201
201 202 } else {
202 203 // We haven't exceeded the throttle, send the message like
203 204 // normal.
204 205 var data = {method: 'backbone', sync_data: attrs};
205 206 this.comm.send(data, callbacks);
206 207 this.pending_msgs++;
207 208 }
208 209 }
209 210 // Since the comm is a one-way communication, assume the message
210 211 // arrived. Don't call success since we don't have a model back from the server
211 212 // this means we miss out on the 'sync' event.
212 213 this._buffered_state_diff = {};
213 214 },
214 215
215 216 save_changes: function(callbacks) {
216 217 // Push this model's state to the back-end
217 218 //
218 219 // This invokes a Backbone.Sync.
219 220 this.save(this._buffered_state_diff, {patch: true, callbacks: callbacks});
220 221 },
221 222
222 223 _pack_models: function(value) {
223 224 // Replace models with model ids recursively.
224 225 if (value instanceof Backbone.Model) {
225 226 return value.id;
226 227
227 228 } else if ($.isArray(value)) {
228 229 var packed = [];
229 230 var that = this;
230 231 _.each(value, function(sub_value, key) {
231 232 packed.push(that._pack_models(sub_value));
232 233 });
233 234 return packed;
234 235
235 236 } else if (value instanceof Object) {
236 237 var packed = {};
237 238 var that = this;
238 239 _.each(value, function(sub_value, key) {
239 240 packed[key] = that._pack_models(sub_value);
240 241 });
241 242 return packed;
242 243
243 244 } else {
244 245 return value;
245 246 }
246 247 },
247 248
248 249 _unpack_models: function(value) {
249 250 // Replace model ids with models recursively.
250 251 if ($.isArray(value)) {
251 252 var unpacked = [];
252 253 var that = this;
253 254 _.each(value, function(sub_value, key) {
254 255 unpacked.push(that._unpack_models(sub_value));
255 256 });
256 257 return unpacked;
257 258
258 259 } else if (value instanceof Object) {
259 260 var unpacked = {};
260 261 var that = this;
261 262 _.each(value, function(sub_value, key) {
262 263 unpacked[key] = that._unpack_models(sub_value);
263 264 });
264 265 return unpacked;
265 266
266 267 } else {
267 268 var model = this.widget_manager.get_model(value);
268 269 if (model) {
269 270 return model;
270 271 } else {
271 272 return value;
272 273 }
273 274 }
274 275 },
275 276
276 277 });
277 278 WidgetManager.register_widget_model('WidgetModel', WidgetModel);
278 279
279 280
280 281 var WidgetView = Backbone.View.extend({
281 282 initialize: function(parameters) {
282 283 // Public constructor.
283 284 this.model.on('change',this.update,this);
284 285 this.options = parameters.options;
285 286 this.child_views = [];
286 287 this.model.views.push(this);
287 288 },
288 289
289 290 update: function(){
290 291 // Triggered on model change.
291 292 //
292 293 // Update view to be consistent with this.model
293 294 },
294 295
295 296 create_child_view: function(child_model, options) {
296 297 // Create and return a child view.
297 298 //
298 299 // -given a model and (optionally) a view name if the view name is
299 300 // not given, it defaults to the model's default view attribute.
300 301
301 302 // TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior
302 303 // it would be great to have the widget manager add the cell metadata
303 304 // to the subview without having to add it here.
304 305 var child_view = this.model.widget_manager.create_view(child_model, options || {}, this);
305 306 this.child_views[child_model.id] = child_view;
306 307 return child_view;
307 308 },
308 309
309 310 delete_child_view: function(child_model, options) {
310 311 // Delete a child view that was previously created using create_child_view.
311 312 var view = this.child_views[child_model.id];
312 313 if (view !== undefined) {
313 314 delete this.child_views[child_model.id];
314 315 view.remove();
315 316 }
316 317 },
317 318
318 319 do_diff: function(old_list, new_list, removed_callback, added_callback) {
319 320 // Difference a changed list and call remove and add callbacks for
320 321 // each removed and added item in the new list.
321 322 //
322 323 // Parameters
323 324 // ----------
324 325 // old_list : array
325 326 // new_list : array
326 327 // removed_callback : Callback(item)
327 328 // Callback that is called for each item removed.
328 329 // added_callback : Callback(item)
329 330 // Callback that is called for each item added.
330 331
331 332
332 333 // removed items
333 334 _.each(_.difference(old_list, new_list), function(item, index, list) {
334 335 removed_callback(item);
335 336 }, this);
336 337
337 338 // added items
338 339 _.each(_.difference(new_list, old_list), function(item, index, list) {
339 340 added_callback(item);
340 341 }, this);
341 342 },
342 343
343 344 callbacks: function(){
344 345 // Create msg callbacks for a comm msg.
345 346 return this.model.callbacks(this);
346 347 },
347 348
348 349 render: function(){
349 350 // Render the view.
350 351 //
351 352 // By default, this is only called the first time the view is created
352 353 },
353 354
354 355 send: function (content) {
355 356 // Send a custom msg associated with this view.
356 357 this.model.send(content, this.callbacks());
357 358 },
358 359
359 360 touch: function () {
360 361 this.model.save_changes(this.callbacks());
361 362 },
362 363 });
363 364
364 365
365 366 var DOMWidgetView = WidgetView.extend({
366 367 initialize: function (options) {
367 368 // Public constructor
368 369
369 370 // In the future we may want to make changes more granular
370 371 // (e.g., trigger on visible:change).
371 372 this.model.on('change', this.update, this);
372 373 this.model.on('msg:custom', this.on_msg, this);
373 374 DOMWidgetView.__super__.initialize.apply(this, arguments);
374 375 },
375 376
376 377 on_msg: function(msg) {
377 378 // Handle DOM specific msgs.
378 379 switch(msg.msg_type) {
379 380 case 'add_class':
380 381 this.add_class(msg.selector, msg.class_list);
381 382 break;
382 383 case 'remove_class':
383 384 this.remove_class(msg.selector, msg.class_list);
384 385 break;
385 386 }
386 387 },
387 388
388 389 add_class: function (selector, class_list) {
389 390 // Add a DOM class to an element.
390 391 this._get_selector_element(selector).addClass(class_list);
391 392 },
392 393
393 394 remove_class: function (selector, class_list) {
394 395 // Remove a DOM class from an element.
395 396 this._get_selector_element(selector).removeClass(class_list);
396 397 },
397 398
398 399 update: function () {
399 400 // Update the contents of this view
400 401 //
401 402 // Called when the model is changed. The model may have been
402 403 // changed by another view or by a state update from the back-end.
403 404 // The very first update seems to happen before the element is
404 405 // finished rendering so we use setTimeout to give the element time
405 406 // to render
406 407 var e = this.$el;
407 408 var visible = this.model.get('visible');
408 409 setTimeout(function() {e.toggle(visible);},0);
409 410
410 411 var css = this.model.get('_css');
411 412 if (css === undefined) {return;}
412 413 var that = this;
413 414 _.each(css, function(css_traits, selector){
414 415 // Apply the css traits to all elements that match the selector.
415 416 var elements = that._get_selector_element(selector);
416 417 if (elements.length > 0) {
417 418 _.each(css_traits, function(css_value, css_key){
418 419 elements.css(css_key, css_value);
419 420 });
420 421 }
421 422 });
422 423 },
423 424
424 425 _get_selector_element: function (selector) {
425 426 // Get the elements via the css selector.
426 427
427 428 // If the selector is blank, apply the style to the $el_to_style
428 429 // element. If the $el_to_style element is not defined, use apply
429 430 // the style to the view's element.
430 431 var elements;
431 432 if (!selector) {
432 433 if (this.$el_to_style === undefined) {
433 434 elements = this.$el;
434 435 } else {
435 436 elements = this.$el_to_style;
436 437 }
437 438 } else {
438 439 elements = this.$el.find(selector);
439 440 }
440 441 return elements;
441 442 },
442 443 });
443 444
444 445 IPython.WidgetModel = WidgetModel;
445 446 IPython.WidgetView = WidgetView;
446 447 IPython.DOMWidgetView = DOMWidgetView;
447 448
448 449 // Pass through WidgetManager namespace.
449 450 return WidgetManager;
450 451 });
@@ -1,244 +1,243 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 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 // SelectionContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["widgets/js/widget"], function(WidgetManager){
18 18
19 19 var AccordionView = IPython.DOMWidgetView.extend({
20 20 render: function(){
21 21 // Called when view is rendered.
22 22 var guid = 'accordion' + IPython.utils.uuid();
23 23 this.$el
24 24 .attr('id', guid)
25 25 .addClass('accordion');
26 26 this.containers = [];
27 27 this.model_containers = {};
28 28 this.update_children([], this.model.get('_children'));
29 29 this.model.on('change:_children', function(model, value, options) {
30 30 this.update_children(model.previous('_children'), value);
31 31 }, this);
32 this.model.on('change:selected_index', function(model, value, options) {
33 this.update_selected_index(model.previous('selected_index'), value, options);
34 }, this);
35 this.model.on('change:_titles', function(model, value, options) {
36 this.update_titles(value);
37 }, this);
38 this.model.on('displayed', function() {
39 this.update_titles();
40 }, this);
32 41 },
33
34 update: function(options) {
35 // Update the contents of this view
36 //
37 // Called when the model is changed. The model may have been
38 // changed by another view or by a state update from the back-end.
39 if (options === undefined || options.updated_view != this) {
40 // Set tab titles
41 var titles = this.model.get('_titles');
42 var that = this;
43 _.each(titles, function(title, page_index) {
44 var accordian = that.containers[page_index];
45 if (accordian !== undefined) {
46 accordian
47 .find('.accordion-heading')
48 .find('.accordion-toggle')
49 .text(title);
50 }
51 });
52 42
53 // Set selected page
54 var selected_index = this.model.get("selected_index");
55 if (0 <= selected_index && selected_index < this.containers.length) {
56 _.each(this.containers, function(container, index) {
57 if (index==selected_index) {
58 container.find('.accordion-body').collapse('show');
59 } else {
60 container.find('.accordion-body').collapse('hide');
61 }
62 });
43 update_titles: function(titles) {
44 // Set tab titles
45 if (!titles) {
46 titles = this.model.get('_titles');
47 }
48
49 var that = this;
50 _.each(titles, function(title, page_index) {
51 var accordian = that.containers[page_index];
52 if (accordian !== undefined) {
53 accordian
54 .find('.accordion-heading')
55 .find('.accordion-toggle')
56 .text(title);
57 }
58 });
59 },
60
61 update_selected_index: function(old_index, new_index, options) {
62 // Only update the selection if the selection wasn't triggered
63 // by the front-end. It must be triggered by the back-end.
64 if (options === undefined || options.updated_view != this) {
65 this.containers[old_index].find('.accordion-body').collapse('hide');
66 if (0 <= new_index && new_index < this.containers.length) {
67 this.containers[new_index].find('.accordion-body').collapse('show');
63 68 }
64 69 }
65 return AccordionView.__super__.update.apply(this);
66 70 },
67 71
68 72 update_children: function(old_list, new_list) {
69 73 // Called when the children list is modified.
70 74 this.do_diff(old_list,
71 75 new_list,
72 76 $.proxy(this.remove_child_model, this),
73 77 $.proxy(this.add_child_model, this));
74 78 },
75 79
76 80 remove_child_model: function(model) {
77 81 // Called when a child is removed from children list.
78 82 var accordion_group = this.model_containers[model.id];
79 83 this.containers.splice(accordion_group.container_index, 1);
80 84 delete this.model_containers[model.id];
81 85 accordion_group.remove();
82 86 this.delete_child_view(model);
83 87 },
84 88
85 89 add_child_model: function(model) {
86 90 // Called when a child is added to children list.
87 91 var view = this.create_child_view(model);
88 92 var index = this.containers.length;
89 93 var uuid = IPython.utils.uuid();
90 94 var accordion_group = $('<div />')
91 95 .addClass('accordion-group')
92 96 .appendTo(this.$el);
93 97 var accordion_heading = $('<div />')
94 98 .addClass('accordion-heading')
95 99 .appendTo(accordion_group);
96 100 var that = this;
97 101 var accordion_toggle = $('<a />')
98 102 .addClass('accordion-toggle')
99 103 .attr('data-toggle', 'collapse')
100 104 .attr('data-parent', '#' + this.$el.attr('id'))
101 105 .attr('href', '#' + uuid)
102 106 .click(function(evt){
103 107
104 108 // Calling model.set will trigger all of the other views of the
105 109 // model to update.
106 that.model.set("selected_index", index, {updated_view: this});
110 that.model.set("selected_index", index, {updated_view: that});
107 111 that.touch();
108 112 })
109 113 .text('Page ' + index)
110 114 .appendTo(accordion_heading);
111 115 var accordion_body = $('<div />', {id: uuid})
112 116 .addClass('accordion-body collapse')
113 117 .appendTo(accordion_group);
114 118 var accordion_inner = $('<div />')
115 119 .addClass('accordion-inner')
116 120 .appendTo(accordion_body);
117 121 var container_index = this.containers.push(accordion_group) - 1;
118 122 accordion_group.container_index = container_index;
119 123 this.model_containers[model.id] = accordion_group;
120 124 accordion_inner.append(view.$el);
121 125
122 126 this.update();
123
124 // Stupid workaround to close the bootstrap accordion tabs which
125 // open by default even though they don't have the `in` class
126 // attached to them. For some reason a delay is required.
127 // TODO: Better fix.
128 setTimeout(function(){ that.update(); }, 500);
127 this.update_titles();
129 128 },
130 129 });
131 130 WidgetManager.register_widget_view('AccordionView', AccordionView);
132 131
133 132
134 133 var TabView = IPython.DOMWidgetView.extend({
135 134 initialize: function() {
136 135 // Public constructor.
137 136 this.containers = [];
138 137 TabView.__super__.initialize.apply(this, arguments);
139 138 },
140 139
141 140 render: function(){
142 141 // Called when view is rendered.
143 142 var uuid = 'tabs'+IPython.utils.uuid();
144 143 var that = this;
145 144 this.$tabs = $('<div />', {id: uuid})
146 145 .addClass('nav')
147 146 .addClass('nav-tabs')
148 147 .appendTo(this.$el);
149 148 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
150 149 .addClass('tab-content')
151 150 .appendTo(this.$el);
152 151 this.containers = [];
153 152 this.update_children([], this.model.get('_children'));
154 153 this.model.on('change:_children', function(model, value, options) {
155 154 this.update_children(model.previous('_children'), value);
156 155 }, this);
157 156 },
158 157
159 158 update_children: function(old_list, new_list) {
160 159 // Called when the children list is modified.
161 160 this.do_diff(old_list,
162 161 new_list,
163 162 $.proxy(this.remove_child_model, this),
164 163 $.proxy(this.add_child_model, this));
165 164 },
166 165
167 166 remove_child_model: function(model) {
168 167 // Called when a child is removed from children list.
169 168 var view = this.child_views[model.id];
170 169 this.containers.splice(view.parent_tab.tab_text_index, 1);
171 170 view.parent_tab.remove();
172 171 view.parent_container.remove();
173 172 view.remove();
174 173 this.delete_child_view(model);
175 174 },
176 175
177 176 add_child_model: function(model) {
178 177 // Called when a child is added to children list.
179 178 var view = this.create_child_view(model);
180 179 var index = this.containers.length;
181 180 var uuid = IPython.utils.uuid();
182 181
183 182 var that = this;
184 183 var tab = $('<li />')
185 184 .css('list-style-type', 'none')
186 185 .appendTo(this.$tabs);
187 186 view.parent_tab = tab;
188 187
189 188 var tab_text = $('<a />')
190 189 .attr('href', '#' + uuid)
191 190 .attr('data-toggle', 'tab')
192 191 .text('Page ' + index)
193 192 .appendTo(tab)
194 193 .click(function (e) {
195 194
196 195 // Calling model.set will trigger all of the other views of the
197 196 // model to update.
198 197 that.model.set("selected_index", index, {updated_view: this});
199 198 that.touch();
200 199 that.select_page(index);
201 200 });
202 201 tab.tab_text_index = this.containers.push(tab_text) - 1;
203 202
204 203 var contents_div = $('<div />', {id: uuid})
205 204 .addClass('tab-pane')
206 205 .addClass('fade')
207 206 .append(view.$el)
208 207 .appendTo(this.$tab_contents);
209 208 view.parent_container = contents_div;
210 209 },
211 210
212 211 update: function(options) {
213 212 // Update the contents of this view
214 213 //
215 214 // Called when the model is changed. The model may have been
216 215 // changed by another view or by a state update from the back-end.
217 216 if (options === undefined || options.updated_view != this) {
218 217 // Set tab titles
219 218 var titles = this.model.get('_titles');
220 219 var that = this;
221 220 _.each(titles, function(title, page_index) {
222 221 var tab_text = that.containers[page_index];
223 222 if (tab_text !== undefined) {
224 223 tab_text.text(title);
225 224 }
226 225 });
227 226
228 227 var selected_index = this.model.get('selected_index');
229 228 if (0 <= selected_index && selected_index < this.containers.length) {
230 229 this.select_page(selected_index);
231 230 }
232 231 }
233 232 return TabView.__super__.update.apply(this);
234 233 },
235 234
236 235 select_page: function(index) {
237 236 // Select a page.
238 237 this.$tabs.find('li')
239 238 .removeClass('active');
240 239 this.containers[index].tab('show');
241 240 },
242 241 });
243 242 WidgetManager.register_widget_view('TabView', TabView);
244 243 });
@@ -1,108 +1,113 b''
1 1 // Test multicontainer class
2 2 casper.notebook_test(function () {
3 3 index = this.append_cell(
4 4 'from IPython.html import widgets\n' +
5 5 'from IPython.display import display, clear_output\n' +
6 6 'print("Success")');
7 7 this.execute_cell_then(index);
8 8
9 9 // Test tab view
10 10 var multicontainer1_query = '.widget-area .widget-subarea div div.nav-tabs';
11 11 var multicontainer1_index = this.append_cell(
12 12 'multicontainer = widgets.TabWidget()\n' +
13 13 'page1 = widgets.TextWidget()\n' +
14 14 'page2 = widgets.TextWidget()\n' +
15 15 'page3 = widgets.TextWidget()\n' +
16 16 'multicontainer.children = [page1, page2, page3]\n' +
17 17 'display(multicontainer)\n' +
18 18 'multicontainer.selected_index = 0\n' +
19 19 'print("Success")\n');
20 20 this.execute_cell_then(multicontainer1_index, function(index){
21 21
22 22 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
23 23 'Create multicontainer cell executed with correct output. (1)');
24 24
25 25 this.test.assert(this.cell_element_exists(index,
26 26 '.widget-area .widget-subarea'),
27 27 'Widget subarea exists.');
28 28
29 29 this.test.assert(this.cell_element_exists(index, multicontainer1_query),
30 30 'Widget tab list exists.');
31 31
32 32 this.test.assert(this.cell_element_exists(index, multicontainer1_query),
33 33 'First widget tab list exists.');
34 34
35 35 // JQuery selector is 1 based
36 36 this.click(multicontainer1_query + ' li:nth-child(2) a');
37 37 });
38 38
39 39 this.wait_for_idle();
40 40
41 41 index = this.append_cell(
42 42 'print(multicontainer.selected_index)\n' +
43 43 'multicontainer.selected_index = 2'); // 0 based
44 44 this.execute_cell_then(index, function(index){
45 45 this.test.assertEquals(this.get_output_cell(index).text, '1\n', // 0 based
46 46 'selected_index property updated with tab change.');
47 47
48 48 // JQuery selector is 1 based
49 49 this.test.assert(!this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(1)', 'hasClass', ['active']),
50 50 "Tab 1 is not selected.");
51 51 this.test.assert(!this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(2)', 'hasClass', ['active']),
52 52 "Tab 2 is not selected.");
53 53 this.test.assert(this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(3)', 'hasClass', ['active']),
54 54 "Tab 3 is selected.");
55 55 });
56 56
57 57 index = this.append_cell('multicontainer.set_title(1, "hello")\nprint("Success")'); // 0 based
58 58 this.execute_cell_then(index, function(index){
59 59 this.test.assert(this.cell_element_function(multicontainer1_index, multicontainer1_query +
60 60 ' li:nth-child(2) a', 'html') == 'hello',
61 61 'Tab page title set (after display).');
62 62 });
63 63
64 64 // Test accordion view
65 65 var multicontainer2_query = '.widget-area .widget-subarea .accordion';
66 66 var multicontainer2_index = this.append_cell(
67 67 'multicontainer = widgets.AccordionWidget()\n' +
68 68 'page1 = widgets.TextWidget()\n' +
69 69 'page2 = widgets.TextWidget()\n' +
70 70 'page3 = widgets.TextWidget()\n' +
71 71 'multicontainer.children = [page1, page2, page3]\n' +
72 72 'multicontainer.set_title(2, "good")\n' +
73 73 'display(multicontainer)\n' +
74 74 'multicontainer.selected_index = 0\n' +
75 75 'print("Success")\n');
76 76 this.execute_cell_then(multicontainer2_index, function(index){
77 77
78 78 this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
79 79 'Create multicontainer cell executed with correct output. (2)');
80 80
81 81 this.test.assert(this.cell_element_exists(index,
82 82 '.widget-area .widget-subarea'),
83 83 'Widget subarea exists.');
84 84
85 85 this.test.assert(this.cell_element_exists(index, multicontainer2_query),
86 86 'Widget accordion exists.');
87 87
88 88 this.test.assert(this.cell_element_exists(index, multicontainer2_query +
89 89 ' .accordion-group:nth-child(1) .accordion-body'),
90 90 'First accordion page exists.');
91 91
92 92 // JQuery selector is 1 based
93 93 this.test.assert(this.cell_element_function(index, multicontainer2_query +
94 94 ' .accordion-group:nth-child(3) .accordion-heading .accordion-toggle',
95 95 'html')=='good', 'Accordion page title set (before display).');
96 96
97 97 // JQuery selector is 1 based
98 98 this.click(multicontainer2_query + ' .accordion-group:nth-child(2) .accordion-heading .accordion-toggle');
99 99 });
100 100
101 101 this.wait_for_idle();
102 102
103 103 index = this.append_cell('print(multicontainer.selected_index)'); // 0 based
104 104 this.execute_cell_then(index, function(index){
105 105 this.test.assertEquals(this.get_output_cell(index).text, '1\n', // 0 based
106 106 'selected_index property updated with tab change.');
107
108 var is_collapsed = this.evaluate(function(s){
109 return $(s + ' div.accordion-group:nth-child(2) a').hasClass('collapsed'); // 1 based
110 }, {s: multicontainer2_query});
111 this.test.assertEquals(is_collapsed, false, 'Was tab actually opened?');
107 112 });
108 113 }); No newline at end of file
@@ -1,583 +1,583 b''
1 1 """Module for interactive demos using IPython.
2 2
3 3 This module implements a few classes for running Python scripts interactively
4 4 in IPython for demonstrations. With very simple markup (a few tags in
5 5 comments), you can control points where the script stops executing and returns
6 6 control to IPython.
7 7
8 8
9 9 Provided classes
10 10 ----------------
11 11
12 12 The classes are (see their docstrings for further details):
13 13
14 14 - Demo: pure python demos
15 15
16 16 - IPythonDemo: demos with input to be processed by IPython as if it had been
17 17 typed interactively (so magics work, as well as any other special syntax you
18 18 may have added via input prefilters).
19 19
20 20 - LineDemo: single-line version of the Demo class. These demos are executed
21 21 one line at a time, and require no markup.
22 22
23 23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
24 24 executed a line at a time, but processed via IPython).
25 25
26 26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 27 declares an empty marquee and a pre_cmd that clears the screen before each
28 28 block (see Subclassing below).
29 29
30 30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
31 31 classes.
32 32
33 33 Inheritance diagram:
34 34
35 35 .. inheritance-diagram:: IPython.lib.demo
36 36 :parts: 3
37 37
38 38 Subclassing
39 39 -----------
40 40
41 41 The classes here all include a few methods meant to make customization by
42 42 subclassing more convenient. Their docstrings below have some more details:
43 43
44 44 - marquee(): generates a marquee to provide visible on-screen markers at each
45 45 block start and end.
46 46
47 47 - pre_cmd(): run right before the execution of each block.
48 48
49 49 - post_cmd(): run right after the execution of each block. If the block
50 50 raises an exception, this is NOT called.
51 51
52 52
53 53 Operation
54 54 ---------
55 55
56 56 The file is run in its own empty namespace (though you can pass it a string of
57 57 arguments as if in a command line environment, and it will see those as
58 58 sys.argv). But at each stop, the global IPython namespace is updated with the
59 59 current internal demo namespace, so you can work interactively with the data
60 60 accumulated so far.
61 61
62 62 By default, each block of code is printed (with syntax highlighting) before
63 63 executing it and you have to confirm execution. This is intended to show the
64 64 code to an audience first so you can discuss it, and only proceed with
65 65 execution once you agree. There are a few tags which allow you to modify this
66 66 behavior.
67 67
68 68 The supported tags are:
69 69
70 70 # <demo> stop
71 71
72 72 Defines block boundaries, the points where IPython stops execution of the
73 73 file and returns to the interactive prompt.
74 74
75 75 You can optionally mark the stop tag with extra dashes before and after the
76 76 word 'stop', to help visually distinguish the blocks in a text editor:
77 77
78 78 # <demo> --- stop ---
79 79
80 80
81 81 # <demo> silent
82 82
83 83 Make a block execute silently (and hence automatically). Typically used in
84 84 cases where you have some boilerplate or initialization code which you need
85 85 executed but do not want to be seen in the demo.
86 86
87 87 # <demo> auto
88 88
89 89 Make a block execute automatically, but still being printed. Useful for
90 90 simple code which does not warrant discussion, since it avoids the extra
91 91 manual confirmation.
92 92
93 93 # <demo> auto_all
94 94
95 95 This tag can _only_ be in the first block, and if given it overrides the
96 96 individual auto tags to make the whole demo fully automatic (no block asks
97 97 for confirmation). It can also be given at creation time (or the attribute
98 98 set later) to override what's in the file.
99 99
100 100 While _any_ python file can be run as a Demo instance, if there are no stop
101 101 tags the whole file will run in a single block (no different that calling
102 102 first %pycat and then %run). The minimal markup to make this useful is to
103 103 place a set of stop tags; the other tags are only there to let you fine-tune
104 104 the execution.
105 105
106 106 This is probably best explained with the simple example file below. You can
107 107 copy this into a file named ex_demo.py, and try running it via::
108 108
109 109 from IPython.demo import Demo
110 110 d = Demo('ex_demo.py')
111 111 d()
112 112
113 113 Each time you call the demo object, it runs the next block. The demo object
114 114 has a few useful methods for navigation, like again(), edit(), jump(), seek()
115 115 and back(). It can be reset for a new run via reset() or reloaded from disk
116 116 (in case you've edited the source) via reload(). See their docstrings below.
117 117
118 118 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
119 119 been added to the "docs/examples/core" directory. Just cd to this directory in
120 120 an IPython session, and type::
121 121
122 122 %run demo-exercizer.py
123 123
124 124 and then follow the directions.
125 125
126 126 Example
127 127 -------
128 128
129 129 The following is a very simple example of a valid demo file.
130 130
131 131 ::
132 132
133 133 #################### EXAMPLE DEMO <ex_demo.py> ###############################
134 134 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
135 135
136 136 print 'Hello, welcome to an interactive IPython demo.'
137 137
138 138 # The mark below defines a block boundary, which is a point where IPython will
139 139 # stop execution and return to the interactive prompt. The dashes are actually
140 140 # optional and used only as a visual aid to clearly separate blocks while
141 141 # editing the demo code.
142 142 # <demo> stop
143 143
144 144 x = 1
145 145 y = 2
146 146
147 147 # <demo> stop
148 148
149 149 # the mark below makes this block as silent
150 150 # <demo> silent
151 151
152 152 print 'This is a silent block, which gets executed but not printed.'
153 153
154 154 # <demo> stop
155 155 # <demo> auto
156 156 print 'This is an automatic block.'
157 157 print 'It is executed without asking for confirmation, but printed.'
158 158 z = x+y
159 159
160 160 print 'z=',x
161 161
162 162 # <demo> stop
163 163 # This is just another normal block.
164 164 print 'z is now:', z
165 165
166 166 print 'bye!'
167 167 ################### END EXAMPLE DEMO <ex_demo.py> ############################
168 168 """
169 169
170 170 from __future__ import unicode_literals
171 171
172 172 #*****************************************************************************
173 173 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
174 174 #
175 175 # Distributed under the terms of the BSD License. The full license is in
176 176 # the file COPYING, distributed as part of this software.
177 177 #
178 178 #*****************************************************************************
179 179 from __future__ import print_function
180 180
181 181 import os
182 182 import re
183 183 import shlex
184 184 import sys
185 185
186 186 from IPython.utils import io
187 187 from IPython.utils.text import marquee
188 188 from IPython.utils import openpy
189 189 from IPython.utils import py3compat
190 190 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
191 191
192 192 class DemoError(Exception): pass
193 193
194 194 def re_mark(mark):
195 195 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
196 196
197 197 class Demo(object):
198 198
199 199 re_stop = re_mark('-*\s?stop\s?-*')
200 200 re_silent = re_mark('silent')
201 201 re_auto = re_mark('auto')
202 202 re_auto_all = re_mark('auto_all')
203 203
204 204 def __init__(self,src,title='',arg_str='',auto_all=None):
205 205 """Make a new demo object. To run the demo, simply call the object.
206 206
207 207 See the module docstring for full details and an example (you can use
208 208 IPython.Demo? in IPython to see it).
209 209
210 210 Inputs:
211 211
212 212 - src is either a file, or file-like object, or a
213 213 string that can be resolved to a filename.
214 214
215 215 Optional inputs:
216 216
217 217 - title: a string to use as the demo name. Of most use when the demo
218 218 you are making comes from an object that has no filename, or if you
219 219 want an alternate denotation distinct from the filename.
220 220
221 221 - arg_str(''): a string of arguments, internally converted to a list
222 222 just like sys.argv, so the demo script can see a similar
223 223 environment.
224 224
225 225 - auto_all(None): global flag to run all blocks automatically without
226 226 confirmation. This attribute overrides the block-level tags and
227 227 applies to the whole demo. It is an attribute of the object, and
228 228 can be changed at runtime simply by reassigning it to a boolean
229 229 value.
230 230 """
231 231 if hasattr(src, "read"):
232 232 # It seems to be a file or a file-like object
233 233 self.fname = "from a file-like object"
234 234 if title == '':
235 235 self.title = "from a file-like object"
236 236 else:
237 237 self.title = title
238 238 else:
239 239 # Assume it's a string or something that can be converted to one
240 240 self.fname = src
241 241 if title == '':
242 242 (filepath, filename) = os.path.split(src)
243 243 self.title = filename
244 244 else:
245 245 self.title = title
246 246 self.sys_argv = [src] + shlex.split(arg_str)
247 247 self.auto_all = auto_all
248 248 self.src = src
249 249
250 250 # get a few things from ipython. While it's a bit ugly design-wise,
251 251 # it ensures that things like color scheme and the like are always in
252 252 # sync with the ipython mode being used. This class is only meant to
253 253 # be used inside ipython anyways, so it's OK.
254 254 ip = get_ipython() # this is in builtins whenever IPython is running
255 255 self.ip_ns = ip.user_ns
256 256 self.ip_colorize = ip.pycolorize
257 257 self.ip_showtb = ip.showtraceback
258 258 self.ip_run_cell = ip.run_cell
259 259 self.shell = ip
260 260
261 261 # load user data and initialize data structures
262 262 self.reload()
263 263
264 264 def fload(self):
265 265 """Load file object."""
266 266 # read data and parse into blocks
267 267 if hasattr(self, 'fobj') and self.fobj is not None:
268 268 self.fobj.close()
269 269 if hasattr(self.src, "read"):
270 270 # It seems to be a file or a file-like object
271 271 self.fobj = self.src
272 272 else:
273 273 # Assume it's a string or something that can be converted to one
274 274 self.fobj = openpy.open(self.fname)
275 275
276 276 def reload(self):
277 277 """Reload source from disk and initialize state."""
278 278 self.fload()
279 279
280 280 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
281 281 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
282 282 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
283 283 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
284 284
285 285 # if auto_all is not given (def. None), we read it from the file
286 286 if self.auto_all is None:
287 287 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
288 288 else:
289 289 self.auto_all = bool(self.auto_all)
290 290
291 291 # Clean the sources from all markup so it doesn't get displayed when
292 292 # running the demo
293 293 src_blocks = []
294 294 auto_strip = lambda s: self.re_auto.sub('',s)
295 295 for i,b in enumerate(src_b):
296 296 if self._auto[i]:
297 297 src_blocks.append(auto_strip(b))
298 298 else:
299 299 src_blocks.append(b)
300 300 # remove the auto_all marker
301 301 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
302 302
303 303 self.nblocks = len(src_blocks)
304 304 self.src_blocks = src_blocks
305 305
306 306 # also build syntax-highlighted source
307 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
307 self.src_blocks_colored = list(map(self.ip_colorize,self.src_blocks))
308 308
309 309 # ensure clean namespace and seek offset
310 310 self.reset()
311 311
312 312 def reset(self):
313 313 """Reset the namespace and seek pointer to restart the demo"""
314 314 self.user_ns = {}
315 315 self.finished = False
316 316 self.block_index = 0
317 317
318 318 def _validate_index(self,index):
319 319 if index<0 or index>=self.nblocks:
320 320 raise ValueError('invalid block index %s' % index)
321 321
322 322 def _get_index(self,index):
323 323 """Get the current block index, validating and checking status.
324 324
325 325 Returns None if the demo is finished"""
326 326
327 327 if index is None:
328 328 if self.finished:
329 329 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.', file=io.stdout)
330 330 return None
331 331 index = self.block_index
332 332 else:
333 333 self._validate_index(index)
334 334 return index
335 335
336 336 def seek(self,index):
337 337 """Move the current seek pointer to the given block.
338 338
339 339 You can use negative indices to seek from the end, with identical
340 340 semantics to those of Python lists."""
341 341 if index<0:
342 342 index = self.nblocks + index
343 343 self._validate_index(index)
344 344 self.block_index = index
345 345 self.finished = False
346 346
347 347 def back(self,num=1):
348 348 """Move the seek pointer back num blocks (default is 1)."""
349 349 self.seek(self.block_index-num)
350 350
351 351 def jump(self,num=1):
352 352 """Jump a given number of blocks relative to the current one.
353 353
354 354 The offset can be positive or negative, defaults to 1."""
355 355 self.seek(self.block_index+num)
356 356
357 357 def again(self):
358 358 """Move the seek pointer back one block and re-execute."""
359 359 self.back(1)
360 360 self()
361 361
362 362 def edit(self,index=None):
363 363 """Edit a block.
364 364
365 365 If no number is given, use the last block executed.
366 366
367 367 This edits the in-memory copy of the demo, it does NOT modify the
368 368 original source file. If you want to do that, simply open the file in
369 369 an editor and use reload() when you make changes to the file. This
370 370 method is meant to let you change a block during a demonstration for
371 371 explanatory purposes, without damaging your original script."""
372 372
373 373 index = self._get_index(index)
374 374 if index is None:
375 375 return
376 376 # decrease the index by one (unless we're at the very beginning), so
377 377 # that the default demo.edit() call opens up the sblock we've last run
378 378 if index>0:
379 379 index -= 1
380 380
381 381 filename = self.shell.mktempfile(self.src_blocks[index])
382 382 self.shell.hooks.editor(filename,1)
383 383 with open(filename, 'r') as f:
384 384 new_block = f.read()
385 385 # update the source and colored block
386 386 self.src_blocks[index] = new_block
387 387 self.src_blocks_colored[index] = self.ip_colorize(new_block)
388 388 self.block_index = index
389 389 # call to run with the newly edited index
390 390 self()
391 391
392 392 def show(self,index=None):
393 393 """Show a single block on screen"""
394 394
395 395 index = self._get_index(index)
396 396 if index is None:
397 397 return
398 398
399 399 print(self.marquee('<%s> block # %s (%s remaining)' %
400 400 (self.title,index,self.nblocks-index-1)), file=io.stdout)
401 401 print(self.src_blocks_colored[index], file=io.stdout)
402 402 sys.stdout.flush()
403 403
404 404 def show_all(self):
405 405 """Show entire demo on screen, block by block"""
406 406
407 407 fname = self.title
408 408 title = self.title
409 409 nblocks = self.nblocks
410 410 silent = self._silent
411 411 marquee = self.marquee
412 412 for index,block in enumerate(self.src_blocks_colored):
413 413 if silent[index]:
414 414 print(marquee('<%s> SILENT block # %s (%s remaining)' %
415 415 (title,index,nblocks-index-1)), file=io.stdout)
416 416 else:
417 417 print(marquee('<%s> block # %s (%s remaining)' %
418 418 (title,index,nblocks-index-1)), file=io.stdout)
419 419 print(block, end=' ', file=io.stdout)
420 420 sys.stdout.flush()
421 421
422 422 def run_cell(self,source):
423 423 """Execute a string with one or more lines of code"""
424 424
425 425 exec(source, self.user_ns)
426 426
427 427 def __call__(self,index=None):
428 428 """run a block of the demo.
429 429
430 430 If index is given, it should be an integer >=1 and <= nblocks. This
431 431 means that the calling convention is one off from typical Python
432 432 lists. The reason for the inconsistency is that the demo always
433 433 prints 'Block n/N, and N is the total, so it would be very odd to use
434 434 zero-indexing here."""
435 435
436 436 index = self._get_index(index)
437 437 if index is None:
438 438 return
439 439 try:
440 440 marquee = self.marquee
441 441 next_block = self.src_blocks[index]
442 442 self.block_index += 1
443 443 if self._silent[index]:
444 444 print(marquee('Executing silent block # %s (%s remaining)' %
445 445 (index,self.nblocks-index-1)), file=io.stdout)
446 446 else:
447 447 self.pre_cmd()
448 448 self.show(index)
449 449 if self.auto_all or self._auto[index]:
450 450 print(marquee('output:'), file=io.stdout)
451 451 else:
452 452 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ', file=io.stdout)
453 453 ans = py3compat.input().strip()
454 454 if ans:
455 455 print(marquee('Block NOT executed'), file=io.stdout)
456 456 return
457 457 try:
458 458 save_argv = sys.argv
459 459 sys.argv = self.sys_argv
460 460 self.run_cell(next_block)
461 461 self.post_cmd()
462 462 finally:
463 463 sys.argv = save_argv
464 464
465 465 except:
466 466 self.ip_showtb(filename=self.fname)
467 467 else:
468 468 self.ip_ns.update(self.user_ns)
469 469
470 470 if self.block_index == self.nblocks:
471 471 mq1 = self.marquee('END OF DEMO')
472 472 if mq1:
473 473 # avoid spurious print >>io.stdout,s if empty marquees are used
474 474 print(file=io.stdout)
475 475 print(mq1, file=io.stdout)
476 476 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'), file=io.stdout)
477 477 self.finished = True
478 478
479 479 # These methods are meant to be overridden by subclasses who may wish to
480 480 # customize the behavior of of their demos.
481 481 def marquee(self,txt='',width=78,mark='*'):
482 482 """Return the input string centered in a 'marquee'."""
483 483 return marquee(txt,width,mark)
484 484
485 485 def pre_cmd(self):
486 486 """Method called before executing each block."""
487 487 pass
488 488
489 489 def post_cmd(self):
490 490 """Method called after executing each block."""
491 491 pass
492 492
493 493
494 494 class IPythonDemo(Demo):
495 495 """Class for interactive demos with IPython's input processing applied.
496 496
497 497 This subclasses Demo, but instead of executing each block by the Python
498 498 interpreter (via exec), it actually calls IPython on it, so that any input
499 499 filters which may be in place are applied to the input block.
500 500
501 501 If you have an interactive environment which exposes special input
502 502 processing, you can use this class instead to write demo scripts which
503 503 operate exactly as if you had typed them interactively. The default Demo
504 504 class requires the input to be valid, pure Python code.
505 505 """
506 506
507 507 def run_cell(self,source):
508 508 """Execute a string with one or more lines of code"""
509 509
510 510 self.shell.run_cell(source)
511 511
512 512 class LineDemo(Demo):
513 513 """Demo where each line is executed as a separate block.
514 514
515 515 The input script should be valid Python code.
516 516
517 517 This class doesn't require any markup at all, and it's meant for simple
518 518 scripts (with no nesting or any kind of indentation) which consist of
519 519 multiple lines of input to be executed, one at a time, as if they had been
520 520 typed in the interactive prompt.
521 521
522 522 Note: the input can not have *any* indentation, which means that only
523 523 single-lines of input are accepted, not even function definitions are
524 524 valid."""
525 525
526 526 def reload(self):
527 527 """Reload source from disk and initialize state."""
528 528 # read data and parse into blocks
529 529 self.fload()
530 530 lines = self.fobj.readlines()
531 531 src_b = [l for l in lines if l.strip()]
532 532 nblocks = len(src_b)
533 533 self.src = ''.join(lines)
534 534 self._silent = [False]*nblocks
535 535 self._auto = [True]*nblocks
536 536 self.auto_all = True
537 537 self.nblocks = nblocks
538 538 self.src_blocks = src_b
539 539
540 540 # also build syntax-highlighted source
541 541 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
542 542
543 543 # ensure clean namespace and seek offset
544 544 self.reset()
545 545
546 546
547 547 class IPythonLineDemo(IPythonDemo,LineDemo):
548 548 """Variant of the LineDemo class whose input is processed by IPython."""
549 549 pass
550 550
551 551
552 552 class ClearMixin(object):
553 553 """Use this mixin to make Demo classes with less visual clutter.
554 554
555 555 Demos using this mixin will clear the screen before every block and use
556 556 blank marquees.
557 557
558 558 Note that in order for the methods defined here to actually override those
559 559 of the classes it's mixed with, it must go /first/ in the inheritance
560 560 tree. For example:
561 561
562 562 class ClearIPDemo(ClearMixin,IPythonDemo): pass
563 563
564 564 will provide an IPythonDemo class with the mixin's features.
565 565 """
566 566
567 567 def marquee(self,txt='',width=78,mark='*'):
568 568 """Blank marquee that returns '' no matter what the input."""
569 569 return ''
570 570
571 571 def pre_cmd(self):
572 572 """Method called before executing each block.
573 573
574 574 This one simply clears the screen."""
575 575 from IPython.utils.terminal import term_clear
576 576 term_clear()
577 577
578 578 class ClearDemo(ClearMixin,Demo):
579 579 pass
580 580
581 581
582 582 class ClearIPDemo(ClearMixin,IPythonDemo):
583 583 pass
@@ -1,1463 +1,1463 b''
1 1 # encoding: utf-8
2 2 """
3 3 Facilities for launching IPython processes asynchronously.
4 4
5 5 Authors:
6 6
7 7 * Brian Granger
8 8 * MinRK
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2008-2011 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import copy
23 23 import logging
24 24 import os
25 25 import pipes
26 26 import stat
27 27 import sys
28 28 import time
29 29
30 30 # signal imports, handling various platforms, versions
31 31
32 32 from signal import SIGINT, SIGTERM
33 33 try:
34 34 from signal import SIGKILL
35 35 except ImportError:
36 36 # Windows
37 37 SIGKILL=SIGTERM
38 38
39 39 try:
40 40 # Windows >= 2.7, 3.2
41 41 from signal import CTRL_C_EVENT as SIGINT
42 42 except ImportError:
43 43 pass
44 44
45 45 from subprocess import Popen, PIPE, STDOUT
46 46 try:
47 47 from subprocess import check_output
48 48 except ImportError:
49 49 # pre-2.7, define check_output with Popen
50 50 def check_output(*args, **kwargs):
51 51 kwargs.update(dict(stdout=PIPE))
52 52 p = Popen(*args, **kwargs)
53 53 out,err = p.communicate()
54 54 return out
55 55
56 56 from zmq.eventloop import ioloop
57 57
58 58 from IPython.config.application import Application
59 59 from IPython.config.configurable import LoggingConfigurable
60 60 from IPython.utils.text import EvalFormatter
61 61 from IPython.utils.traitlets import (
62 62 Any, Integer, CFloat, List, Unicode, Dict, Instance, HasTraits, CRegExp
63 63 )
64 64 from IPython.utils.encoding import DEFAULT_ENCODING
65 65 from IPython.utils.path import get_home_dir
66 66 from IPython.utils.process import find_cmd, FindCmdError
67 67 from IPython.utils.py3compat import iteritems, itervalues
68 68
69 69 from .win32support import forward_read_events
70 70
71 71 from .winhpcjob import IPControllerTask, IPEngineTask, IPControllerJob, IPEngineSetJob
72 72
73 73 WINDOWS = os.name == 'nt'
74 74
75 75 #-----------------------------------------------------------------------------
76 76 # Paths to the kernel apps
77 77 #-----------------------------------------------------------------------------
78 78
79 79 ipcluster_cmd_argv = [sys.executable, "-m", "IPython.parallel.cluster"]
80 80
81 81 ipengine_cmd_argv = [sys.executable, "-m", "IPython.parallel.engine"]
82 82
83 83 ipcontroller_cmd_argv = [sys.executable, "-m", "IPython.parallel.controller"]
84 84
85 85 if WINDOWS and sys.version_info < (3,):
86 86 # `python -m package` doesn't work on Windows Python 2,
87 87 # but `python -m module` does.
88 88 ipengine_cmd_argv = [sys.executable, "-m", "IPython.parallel.apps.ipengineapp"]
89 89 ipcontroller_cmd_argv = [sys.executable, "-m", "IPython.parallel.apps.ipcontrollerapp"]
90 90
91 91 #-----------------------------------------------------------------------------
92 92 # Base launchers and errors
93 93 #-----------------------------------------------------------------------------
94 94
95 95 class LauncherError(Exception):
96 96 pass
97 97
98 98
99 99 class ProcessStateError(LauncherError):
100 100 pass
101 101
102 102
103 103 class UnknownStatus(LauncherError):
104 104 pass
105 105
106 106
107 107 class BaseLauncher(LoggingConfigurable):
108 108 """An asbtraction for starting, stopping and signaling a process."""
109 109
110 110 # In all of the launchers, the work_dir is where child processes will be
111 111 # run. This will usually be the profile_dir, but may not be. any work_dir
112 112 # passed into the __init__ method will override the config value.
113 113 # This should not be used to set the work_dir for the actual engine
114 114 # and controller. Instead, use their own config files or the
115 115 # controller_args, engine_args attributes of the launchers to add
116 116 # the work_dir option.
117 117 work_dir = Unicode(u'.')
118 118 loop = Instance('zmq.eventloop.ioloop.IOLoop')
119 119
120 120 start_data = Any()
121 121 stop_data = Any()
122 122
123 123 def _loop_default(self):
124 124 return ioloop.IOLoop.instance()
125 125
126 126 def __init__(self, work_dir=u'.', config=None, **kwargs):
127 127 super(BaseLauncher, self).__init__(work_dir=work_dir, config=config, **kwargs)
128 128 self.state = 'before' # can be before, running, after
129 129 self.stop_callbacks = []
130 130 self.start_data = None
131 131 self.stop_data = None
132 132
133 133 @property
134 134 def args(self):
135 135 """A list of cmd and args that will be used to start the process.
136 136
137 137 This is what is passed to :func:`spawnProcess` and the first element
138 138 will be the process name.
139 139 """
140 140 return self.find_args()
141 141
142 142 def find_args(self):
143 143 """The ``.args`` property calls this to find the args list.
144 144
145 145 Subcommand should implement this to construct the cmd and args.
146 146 """
147 147 raise NotImplementedError('find_args must be implemented in a subclass')
148 148
149 149 @property
150 150 def arg_str(self):
151 151 """The string form of the program arguments."""
152 152 return ' '.join(self.args)
153 153
154 154 @property
155 155 def running(self):
156 156 """Am I running."""
157 157 if self.state == 'running':
158 158 return True
159 159 else:
160 160 return False
161 161
162 162 def start(self):
163 163 """Start the process."""
164 164 raise NotImplementedError('start must be implemented in a subclass')
165 165
166 166 def stop(self):
167 167 """Stop the process and notify observers of stopping.
168 168
169 169 This method will return None immediately.
170 170 To observe the actual process stopping, see :meth:`on_stop`.
171 171 """
172 172 raise NotImplementedError('stop must be implemented in a subclass')
173 173
174 174 def on_stop(self, f):
175 175 """Register a callback to be called with this Launcher's stop_data
176 176 when the process actually finishes.
177 177 """
178 178 if self.state=='after':
179 179 return f(self.stop_data)
180 180 else:
181 181 self.stop_callbacks.append(f)
182 182
183 183 def notify_start(self, data):
184 184 """Call this to trigger startup actions.
185 185
186 186 This logs the process startup and sets the state to 'running'. It is
187 187 a pass-through so it can be used as a callback.
188 188 """
189 189
190 190 self.log.debug('Process %r started: %r', self.args[0], data)
191 191 self.start_data = data
192 192 self.state = 'running'
193 193 return data
194 194
195 195 def notify_stop(self, data):
196 196 """Call this to trigger process stop actions.
197 197
198 198 This logs the process stopping and sets the state to 'after'. Call
199 199 this to trigger callbacks registered via :meth:`on_stop`."""
200 200
201 201 self.log.debug('Process %r stopped: %r', self.args[0], data)
202 202 self.stop_data = data
203 203 self.state = 'after'
204 204 for i in range(len(self.stop_callbacks)):
205 205 d = self.stop_callbacks.pop()
206 206 d(data)
207 207 return data
208 208
209 209 def signal(self, sig):
210 210 """Signal the process.
211 211
212 212 Parameters
213 213 ----------
214 214 sig : str or int
215 215 'KILL', 'INT', etc., or any signal number
216 216 """
217 217 raise NotImplementedError('signal must be implemented in a subclass')
218 218
219 219 class ClusterAppMixin(HasTraits):
220 220 """MixIn for cluster args as traits"""
221 221 profile_dir=Unicode('')
222 222 cluster_id=Unicode('')
223 223
224 224 @property
225 225 def cluster_args(self):
226 226 return ['--profile-dir', self.profile_dir, '--cluster-id', self.cluster_id]
227 227
228 228 class ControllerMixin(ClusterAppMixin):
229 229 controller_cmd = List(ipcontroller_cmd_argv, config=True,
230 230 help="""Popen command to launch ipcontroller.""")
231 231 # Command line arguments to ipcontroller.
232 232 controller_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
233 233 help="""command-line args to pass to ipcontroller""")
234 234
235 235 class EngineMixin(ClusterAppMixin):
236 236 engine_cmd = List(ipengine_cmd_argv, config=True,
237 237 help="""command to launch the Engine.""")
238 238 # Command line arguments for ipengine.
239 239 engine_args = List(['--log-to-file','--log-level=%i' % logging.INFO], config=True,
240 240 help="command-line arguments to pass to ipengine"
241 241 )
242 242
243 243
244 244 #-----------------------------------------------------------------------------
245 245 # Local process launchers
246 246 #-----------------------------------------------------------------------------
247 247
248 248
249 249 class LocalProcessLauncher(BaseLauncher):
250 250 """Start and stop an external process in an asynchronous manner.
251 251
252 252 This will launch the external process with a working directory of
253 253 ``self.work_dir``.
254 254 """
255 255
256 256 # This is used to to construct self.args, which is passed to
257 257 # spawnProcess.
258 258 cmd_and_args = List([])
259 259 poll_frequency = Integer(100) # in ms
260 260
261 261 def __init__(self, work_dir=u'.', config=None, **kwargs):
262 262 super(LocalProcessLauncher, self).__init__(
263 263 work_dir=work_dir, config=config, **kwargs
264 264 )
265 265 self.process = None
266 266 self.poller = None
267 267
268 268 def find_args(self):
269 269 return self.cmd_and_args
270 270
271 271 def start(self):
272 272 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
273 273 if self.state == 'before':
274 274 self.process = Popen(self.args,
275 275 stdout=PIPE,stderr=PIPE,stdin=PIPE,
276 276 env=os.environ,
277 277 cwd=self.work_dir
278 278 )
279 279 if WINDOWS:
280 280 self.stdout = forward_read_events(self.process.stdout)
281 281 self.stderr = forward_read_events(self.process.stderr)
282 282 else:
283 283 self.stdout = self.process.stdout.fileno()
284 284 self.stderr = self.process.stderr.fileno()
285 285 self.loop.add_handler(self.stdout, self.handle_stdout, self.loop.READ)
286 286 self.loop.add_handler(self.stderr, self.handle_stderr, self.loop.READ)
287 287 self.poller = ioloop.PeriodicCallback(self.poll, self.poll_frequency, self.loop)
288 288 self.poller.start()
289 289 self.notify_start(self.process.pid)
290 290 else:
291 291 s = 'The process was already started and has state: %r' % self.state
292 292 raise ProcessStateError(s)
293 293
294 294 def stop(self):
295 295 return self.interrupt_then_kill()
296 296
297 297 def signal(self, sig):
298 298 if self.state == 'running':
299 299 if WINDOWS and sig != SIGINT:
300 300 # use Windows tree-kill for better child cleanup
301 301 check_output(['taskkill', '-pid', str(self.process.pid), '-t', '-f'])
302 302 else:
303 303 self.process.send_signal(sig)
304 304
305 305 def interrupt_then_kill(self, delay=2.0):
306 306 """Send INT, wait a delay and then send KILL."""
307 307 try:
308 308 self.signal(SIGINT)
309 309 except Exception:
310 310 self.log.debug("interrupt failed")
311 311 pass
312 312 self.killer = ioloop.DelayedCallback(lambda : self.signal(SIGKILL), delay*1000, self.loop)
313 313 self.killer.start()
314 314
315 315 # callbacks, etc:
316 316
317 317 def handle_stdout(self, fd, events):
318 318 if WINDOWS:
319 319 line = self.stdout.recv()
320 320 else:
321 321 line = self.process.stdout.readline()
322 322 # a stopped process will be readable but return empty strings
323 323 if line:
324 324 self.log.debug(line[:-1])
325 325 else:
326 326 self.poll()
327 327
328 328 def handle_stderr(self, fd, events):
329 329 if WINDOWS:
330 330 line = self.stderr.recv()
331 331 else:
332 332 line = self.process.stderr.readline()
333 333 # a stopped process will be readable but return empty strings
334 334 if line:
335 335 self.log.debug(line[:-1])
336 336 else:
337 337 self.poll()
338 338
339 339 def poll(self):
340 340 status = self.process.poll()
341 341 if status is not None:
342 342 self.poller.stop()
343 343 self.loop.remove_handler(self.stdout)
344 344 self.loop.remove_handler(self.stderr)
345 345 self.notify_stop(dict(exit_code=status, pid=self.process.pid))
346 346 return status
347 347
348 348 class LocalControllerLauncher(LocalProcessLauncher, ControllerMixin):
349 349 """Launch a controller as a regular external process."""
350 350
351 351 def find_args(self):
352 352 return self.controller_cmd + self.cluster_args + self.controller_args
353 353
354 354 def start(self):
355 355 """Start the controller by profile_dir."""
356 356 return super(LocalControllerLauncher, self).start()
357 357
358 358
359 359 class LocalEngineLauncher(LocalProcessLauncher, EngineMixin):
360 360 """Launch a single engine as a regular externall process."""
361 361
362 362 def find_args(self):
363 363 return self.engine_cmd + self.cluster_args + self.engine_args
364 364
365 365
366 366 class LocalEngineSetLauncher(LocalEngineLauncher):
367 367 """Launch a set of engines as regular external processes."""
368 368
369 369 delay = CFloat(0.1, config=True,
370 370 help="""delay (in seconds) between starting each engine after the first.
371 371 This can help force the engines to get their ids in order, or limit
372 372 process flood when starting many engines."""
373 373 )
374 374
375 375 # launcher class
376 376 launcher_class = LocalEngineLauncher
377 377
378 378 launchers = Dict()
379 379 stop_data = Dict()
380 380
381 381 def __init__(self, work_dir=u'.', config=None, **kwargs):
382 382 super(LocalEngineSetLauncher, self).__init__(
383 383 work_dir=work_dir, config=config, **kwargs
384 384 )
385 385 self.stop_data = {}
386 386
387 387 def start(self, n):
388 388 """Start n engines by profile or profile_dir."""
389 389 dlist = []
390 390 for i in range(n):
391 391 if i > 0:
392 392 time.sleep(self.delay)
393 393 el = self.launcher_class(work_dir=self.work_dir, parent=self, log=self.log,
394 394 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
395 395 )
396 396
397 397 # Copy the engine args over to each engine launcher.
398 398 el.engine_cmd = copy.deepcopy(self.engine_cmd)
399 399 el.engine_args = copy.deepcopy(self.engine_args)
400 400 el.on_stop(self._notice_engine_stopped)
401 401 d = el.start()
402 402 self.launchers[i] = el
403 403 dlist.append(d)
404 404 self.notify_start(dlist)
405 405 return dlist
406 406
407 407 def find_args(self):
408 408 return ['engine set']
409 409
410 410 def signal(self, sig):
411 411 dlist = []
412 412 for el in itervalues(self.launchers):
413 413 d = el.signal(sig)
414 414 dlist.append(d)
415 415 return dlist
416 416
417 417 def interrupt_then_kill(self, delay=1.0):
418 418 dlist = []
419 419 for el in itervalues(self.launchers):
420 420 d = el.interrupt_then_kill(delay)
421 421 dlist.append(d)
422 422 return dlist
423 423
424 424 def stop(self):
425 425 return self.interrupt_then_kill()
426 426
427 427 def _notice_engine_stopped(self, data):
428 428 pid = data['pid']
429 429 for idx,el in iteritems(self.launchers):
430 430 if el.process.pid == pid:
431 431 break
432 432 self.launchers.pop(idx)
433 433 self.stop_data[idx] = data
434 434 if not self.launchers:
435 435 self.notify_stop(self.stop_data)
436 436
437 437
438 438 #-----------------------------------------------------------------------------
439 439 # MPI launchers
440 440 #-----------------------------------------------------------------------------
441 441
442 442
443 443 class MPILauncher(LocalProcessLauncher):
444 444 """Launch an external process using mpiexec."""
445 445
446 446 mpi_cmd = List(['mpiexec'], config=True,
447 447 help="The mpiexec command to use in starting the process."
448 448 )
449 449 mpi_args = List([], config=True,
450 450 help="The command line arguments to pass to mpiexec."
451 451 )
452 452 program = List(['date'],
453 453 help="The program to start via mpiexec.")
454 454 program_args = List([],
455 455 help="The command line argument to the program."
456 456 )
457 457 n = Integer(1)
458 458
459 459 def __init__(self, *args, **kwargs):
460 460 # deprecation for old MPIExec names:
461 461 config = kwargs.get('config', {})
462 462 for oldname in ('MPIExecLauncher', 'MPIExecControllerLauncher', 'MPIExecEngineSetLauncher'):
463 463 deprecated = config.get(oldname)
464 464 if deprecated:
465 465 newname = oldname.replace('MPIExec', 'MPI')
466 466 config[newname].update(deprecated)
467 467 self.log.warn("WARNING: %s name has been deprecated, use %s", oldname, newname)
468 468
469 469 super(MPILauncher, self).__init__(*args, **kwargs)
470 470
471 471 def find_args(self):
472 472 """Build self.args using all the fields."""
473 473 return self.mpi_cmd + ['-n', str(self.n)] + self.mpi_args + \
474 474 self.program + self.program_args
475 475
476 476 def start(self, n):
477 477 """Start n instances of the program using mpiexec."""
478 478 self.n = n
479 479 return super(MPILauncher, self).start()
480 480
481 481
482 482 class MPIControllerLauncher(MPILauncher, ControllerMixin):
483 483 """Launch a controller using mpiexec."""
484 484
485 485 # alias back to *non-configurable* program[_args] for use in find_args()
486 486 # this way all Controller/EngineSetLaunchers have the same form, rather
487 487 # than *some* having `program_args` and others `controller_args`
488 488 @property
489 489 def program(self):
490 490 return self.controller_cmd
491 491
492 492 @property
493 493 def program_args(self):
494 494 return self.cluster_args + self.controller_args
495 495
496 496 def start(self):
497 497 """Start the controller by profile_dir."""
498 498 return super(MPIControllerLauncher, self).start(1)
499 499
500 500
501 501 class MPIEngineSetLauncher(MPILauncher, EngineMixin):
502 502 """Launch engines using mpiexec"""
503 503
504 504 # alias back to *non-configurable* program[_args] for use in find_args()
505 505 # this way all Controller/EngineSetLaunchers have the same form, rather
506 506 # than *some* having `program_args` and others `controller_args`
507 507 @property
508 508 def program(self):
509 509 return self.engine_cmd
510 510
511 511 @property
512 512 def program_args(self):
513 513 return self.cluster_args + self.engine_args
514 514
515 515 def start(self, n):
516 516 """Start n engines by profile or profile_dir."""
517 517 self.n = n
518 518 return super(MPIEngineSetLauncher, self).start(n)
519 519
520 520 # deprecated MPIExec names
521 521 class DeprecatedMPILauncher(object):
522 522 def warn(self):
523 523 oldname = self.__class__.__name__
524 524 newname = oldname.replace('MPIExec', 'MPI')
525 525 self.log.warn("WARNING: %s name is deprecated, use %s", oldname, newname)
526 526
527 527 class MPIExecLauncher(MPILauncher, DeprecatedMPILauncher):
528 528 """Deprecated, use MPILauncher"""
529 529 def __init__(self, *args, **kwargs):
530 530 super(MPIExecLauncher, self).__init__(*args, **kwargs)
531 531 self.warn()
532 532
533 533 class MPIExecControllerLauncher(MPIControllerLauncher, DeprecatedMPILauncher):
534 534 """Deprecated, use MPIControllerLauncher"""
535 535 def __init__(self, *args, **kwargs):
536 536 super(MPIExecControllerLauncher, self).__init__(*args, **kwargs)
537 537 self.warn()
538 538
539 539 class MPIExecEngineSetLauncher(MPIEngineSetLauncher, DeprecatedMPILauncher):
540 540 """Deprecated, use MPIEngineSetLauncher"""
541 541 def __init__(self, *args, **kwargs):
542 542 super(MPIExecEngineSetLauncher, self).__init__(*args, **kwargs)
543 543 self.warn()
544 544
545 545
546 546 #-----------------------------------------------------------------------------
547 547 # SSH launchers
548 548 #-----------------------------------------------------------------------------
549 549
550 550 # TODO: Get SSH Launcher back to level of sshx in 0.10.2
551 551
552 552 class SSHLauncher(LocalProcessLauncher):
553 553 """A minimal launcher for ssh.
554 554
555 555 To be useful this will probably have to be extended to use the ``sshx``
556 556 idea for environment variables. There could be other things this needs
557 557 as well.
558 558 """
559 559
560 560 ssh_cmd = List(['ssh'], config=True,
561 561 help="command for starting ssh")
562 562 ssh_args = List(['-tt'], config=True,
563 563 help="args to pass to ssh")
564 564 scp_cmd = List(['scp'], config=True,
565 565 help="command for sending files")
566 566 program = List(['date'],
567 567 help="Program to launch via ssh")
568 568 program_args = List([],
569 569 help="args to pass to remote program")
570 570 hostname = Unicode('', config=True,
571 571 help="hostname on which to launch the program")
572 572 user = Unicode('', config=True,
573 573 help="username for ssh")
574 574 location = Unicode('', config=True,
575 575 help="user@hostname location for ssh in one setting")
576 576 to_fetch = List([], config=True,
577 577 help="List of (remote, local) files to fetch after starting")
578 578 to_send = List([], config=True,
579 579 help="List of (local, remote) files to send before starting")
580 580
581 581 def _hostname_changed(self, name, old, new):
582 582 if self.user:
583 583 self.location = u'%s@%s' % (self.user, new)
584 584 else:
585 585 self.location = new
586 586
587 587 def _user_changed(self, name, old, new):
588 588 self.location = u'%s@%s' % (new, self.hostname)
589 589
590 590 def find_args(self):
591 591 return self.ssh_cmd + self.ssh_args + [self.location] + \
592 592 list(map(pipes.quote, self.program + self.program_args))
593 593
594 594 def _send_file(self, local, remote):
595 595 """send a single file"""
596 remote = "%s:%s" % (self.location, remote)
596 full_remote = "%s:%s" % (self.location, remote)
597 597 for i in range(10):
598 598 if not os.path.exists(local):
599 599 self.log.debug("waiting for %s" % local)
600 600 time.sleep(1)
601 601 else:
602 602 break
603 603 remote_dir = os.path.dirname(remote)
604 604 self.log.info("ensuring remote %s:%s/ exists", self.location, remote_dir)
605 605 check_output(self.ssh_cmd + self.ssh_args + \
606 606 [self.location, 'mkdir', '-p', '--', remote_dir]
607 607 )
608 self.log.info("sending %s to %s", local, remote)
609 check_output(self.scp_cmd + [local, remote])
608 self.log.info("sending %s to %s", local, full_remote)
609 check_output(self.scp_cmd + [local, full_remote])
610 610
611 611 def send_files(self):
612 612 """send our files (called before start)"""
613 613 if not self.to_send:
614 614 return
615 615 for local_file, remote_file in self.to_send:
616 616 self._send_file(local_file, remote_file)
617 617
618 618 def _fetch_file(self, remote, local):
619 619 """fetch a single file"""
620 620 full_remote = "%s:%s" % (self.location, remote)
621 621 self.log.info("fetching %s from %s", local, full_remote)
622 622 for i in range(10):
623 623 # wait up to 10s for remote file to exist
624 624 check = check_output(self.ssh_cmd + self.ssh_args + \
625 625 [self.location, 'test -e', remote, "&& echo 'yes' || echo 'no'"])
626 626 check = check.decode(DEFAULT_ENCODING, 'replace').strip()
627 627 if check == u'no':
628 628 time.sleep(1)
629 629 elif check == u'yes':
630 630 break
631 631 local_dir = os.path.dirname(local)
632 632 if not os.path.exists(local_dir):
633 633 os.makedirs(local_dir, 775)
634 634 check_output(self.scp_cmd + [full_remote, local])
635 635
636 636 def fetch_files(self):
637 637 """fetch remote files (called after start)"""
638 638 if not self.to_fetch:
639 639 return
640 640 for remote_file, local_file in self.to_fetch:
641 641 self._fetch_file(remote_file, local_file)
642 642
643 643 def start(self, hostname=None, user=None):
644 644 if hostname is not None:
645 645 self.hostname = hostname
646 646 if user is not None:
647 647 self.user = user
648 648
649 649 self.send_files()
650 650 super(SSHLauncher, self).start()
651 651 self.fetch_files()
652 652
653 653 def signal(self, sig):
654 654 if self.state == 'running':
655 655 # send escaped ssh connection-closer
656 656 self.process.stdin.write('~.')
657 657 self.process.stdin.flush()
658 658
659 659 class SSHClusterLauncher(SSHLauncher, ClusterAppMixin):
660 660
661 661 remote_profile_dir = Unicode('', config=True,
662 662 help="""The remote profile_dir to use.
663 663
664 664 If not specified, use calling profile, stripping out possible leading homedir.
665 665 """)
666 666
667 667 def _profile_dir_changed(self, name, old, new):
668 668 if not self.remote_profile_dir:
669 669 # trigger remote_profile_dir_default logic again,
670 670 # in case it was already triggered before profile_dir was set
671 671 self.remote_profile_dir = self._strip_home(new)
672 672
673 673 @staticmethod
674 674 def _strip_home(path):
675 675 """turns /home/you/.ipython/profile_foo into .ipython/profile_foo"""
676 676 home = get_home_dir()
677 677 if not home.endswith('/'):
678 678 home = home+'/'
679 679
680 680 if path.startswith(home):
681 681 return path[len(home):]
682 682 else:
683 683 return path
684 684
685 685 def _remote_profile_dir_default(self):
686 686 return self._strip_home(self.profile_dir)
687 687
688 688 def _cluster_id_changed(self, name, old, new):
689 689 if new:
690 690 raise ValueError("cluster id not supported by SSH launchers")
691 691
692 692 @property
693 693 def cluster_args(self):
694 694 return ['--profile-dir', self.remote_profile_dir]
695 695
696 696 class SSHControllerLauncher(SSHClusterLauncher, ControllerMixin):
697 697
698 698 # alias back to *non-configurable* program[_args] for use in find_args()
699 699 # this way all Controller/EngineSetLaunchers have the same form, rather
700 700 # than *some* having `program_args` and others `controller_args`
701 701
702 702 def _controller_cmd_default(self):
703 703 return ['ipcontroller']
704 704
705 705 @property
706 706 def program(self):
707 707 return self.controller_cmd
708 708
709 709 @property
710 710 def program_args(self):
711 711 return self.cluster_args + self.controller_args
712 712
713 713 def _to_fetch_default(self):
714 714 return [
715 715 (os.path.join(self.remote_profile_dir, 'security', cf),
716 716 os.path.join(self.profile_dir, 'security', cf),)
717 717 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
718 718 ]
719 719
720 720 class SSHEngineLauncher(SSHClusterLauncher, EngineMixin):
721 721
722 722 # alias back to *non-configurable* program[_args] for use in find_args()
723 723 # this way all Controller/EngineSetLaunchers have the same form, rather
724 724 # than *some* having `program_args` and others `controller_args`
725 725
726 726 def _engine_cmd_default(self):
727 727 return ['ipengine']
728 728
729 729 @property
730 730 def program(self):
731 731 return self.engine_cmd
732 732
733 733 @property
734 734 def program_args(self):
735 735 return self.cluster_args + self.engine_args
736 736
737 737 def _to_send_default(self):
738 738 return [
739 739 (os.path.join(self.profile_dir, 'security', cf),
740 740 os.path.join(self.remote_profile_dir, 'security', cf))
741 741 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
742 742 ]
743 743
744 744
745 745 class SSHEngineSetLauncher(LocalEngineSetLauncher):
746 746 launcher_class = SSHEngineLauncher
747 747 engines = Dict(config=True,
748 748 help="""dict of engines to launch. This is a dict by hostname of ints,
749 749 corresponding to the number of engines to start on that host.""")
750 750
751 751 def _engine_cmd_default(self):
752 752 return ['ipengine']
753 753
754 754 @property
755 755 def engine_count(self):
756 756 """determine engine count from `engines` dict"""
757 757 count = 0
758 758 for n in itervalues(self.engines):
759 759 if isinstance(n, (tuple,list)):
760 760 n,args = n
761 761 count += n
762 762 return count
763 763
764 764 def start(self, n):
765 765 """Start engines by profile or profile_dir.
766 766 `n` is ignored, and the `engines` config property is used instead.
767 767 """
768 768
769 769 dlist = []
770 770 for host, n in iteritems(self.engines):
771 771 if isinstance(n, (tuple, list)):
772 772 n, args = n
773 773 else:
774 774 args = copy.deepcopy(self.engine_args)
775 775
776 776 if '@' in host:
777 777 user,host = host.split('@',1)
778 778 else:
779 779 user=None
780 780 for i in range(n):
781 781 if i > 0:
782 782 time.sleep(self.delay)
783 783 el = self.launcher_class(work_dir=self.work_dir, parent=self, log=self.log,
784 784 profile_dir=self.profile_dir, cluster_id=self.cluster_id,
785 785 )
786 786 if i > 0:
787 787 # only send files for the first engine on each host
788 788 el.to_send = []
789 789
790 790 # Copy the engine args over to each engine launcher.
791 791 el.engine_cmd = self.engine_cmd
792 792 el.engine_args = args
793 793 el.on_stop(self._notice_engine_stopped)
794 794 d = el.start(user=user, hostname=host)
795 795 self.launchers[ "%s/%i" % (host,i) ] = el
796 796 dlist.append(d)
797 797 self.notify_start(dlist)
798 798 return dlist
799 799
800 800
801 801 class SSHProxyEngineSetLauncher(SSHClusterLauncher):
802 802 """Launcher for calling
803 803 `ipcluster engines` on a remote machine.
804 804
805 805 Requires that remote profile is already configured.
806 806 """
807 807
808 808 n = Integer()
809 809 ipcluster_cmd = List(['ipcluster'], config=True)
810 810
811 811 @property
812 812 def program(self):
813 813 return self.ipcluster_cmd + ['engines']
814 814
815 815 @property
816 816 def program_args(self):
817 817 return ['-n', str(self.n), '--profile-dir', self.remote_profile_dir]
818 818
819 819 def _to_send_default(self):
820 820 return [
821 821 (os.path.join(self.profile_dir, 'security', cf),
822 822 os.path.join(self.remote_profile_dir, 'security', cf))
823 823 for cf in ('ipcontroller-client.json', 'ipcontroller-engine.json')
824 824 ]
825 825
826 826 def start(self, n):
827 827 self.n = n
828 828 super(SSHProxyEngineSetLauncher, self).start()
829 829
830 830
831 831 #-----------------------------------------------------------------------------
832 832 # Windows HPC Server 2008 scheduler launchers
833 833 #-----------------------------------------------------------------------------
834 834
835 835
836 836 # This is only used on Windows.
837 837 def find_job_cmd():
838 838 if WINDOWS:
839 839 try:
840 840 return find_cmd('job')
841 841 except (FindCmdError, ImportError):
842 842 # ImportError will be raised if win32api is not installed
843 843 return 'job'
844 844 else:
845 845 return 'job'
846 846
847 847
848 848 class WindowsHPCLauncher(BaseLauncher):
849 849
850 850 job_id_regexp = CRegExp(r'\d+', config=True,
851 851 help="""A regular expression used to get the job id from the output of the
852 852 submit_command. """
853 853 )
854 854 job_file_name = Unicode(u'ipython_job.xml', config=True,
855 855 help="The filename of the instantiated job script.")
856 856 # The full path to the instantiated job script. This gets made dynamically
857 857 # by combining the work_dir with the job_file_name.
858 858 job_file = Unicode(u'')
859 859 scheduler = Unicode('', config=True,
860 860 help="The hostname of the scheduler to submit the job to.")
861 861 job_cmd = Unicode(find_job_cmd(), config=True,
862 862 help="The command for submitting jobs.")
863 863
864 864 def __init__(self, work_dir=u'.', config=None, **kwargs):
865 865 super(WindowsHPCLauncher, self).__init__(
866 866 work_dir=work_dir, config=config, **kwargs
867 867 )
868 868
869 869 @property
870 870 def job_file(self):
871 871 return os.path.join(self.work_dir, self.job_file_name)
872 872
873 873 def write_job_file(self, n):
874 874 raise NotImplementedError("Implement write_job_file in a subclass.")
875 875
876 876 def find_args(self):
877 877 return [u'job.exe']
878 878
879 879 def parse_job_id(self, output):
880 880 """Take the output of the submit command and return the job id."""
881 881 m = self.job_id_regexp.search(output)
882 882 if m is not None:
883 883 job_id = m.group()
884 884 else:
885 885 raise LauncherError("Job id couldn't be determined: %s" % output)
886 886 self.job_id = job_id
887 887 self.log.info('Job started with id: %r', job_id)
888 888 return job_id
889 889
890 890 def start(self, n):
891 891 """Start n copies of the process using the Win HPC job scheduler."""
892 892 self.write_job_file(n)
893 893 args = [
894 894 'submit',
895 895 '/jobfile:%s' % self.job_file,
896 896 '/scheduler:%s' % self.scheduler
897 897 ]
898 898 self.log.debug("Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
899 899
900 900 output = check_output([self.job_cmd]+args,
901 901 env=os.environ,
902 902 cwd=self.work_dir,
903 903 stderr=STDOUT
904 904 )
905 905 output = output.decode(DEFAULT_ENCODING, 'replace')
906 906 job_id = self.parse_job_id(output)
907 907 self.notify_start(job_id)
908 908 return job_id
909 909
910 910 def stop(self):
911 911 args = [
912 912 'cancel',
913 913 self.job_id,
914 914 '/scheduler:%s' % self.scheduler
915 915 ]
916 916 self.log.info("Stopping Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
917 917 try:
918 918 output = check_output([self.job_cmd]+args,
919 919 env=os.environ,
920 920 cwd=self.work_dir,
921 921 stderr=STDOUT
922 922 )
923 923 output = output.decode(DEFAULT_ENCODING, 'replace')
924 924 except:
925 925 output = u'The job already appears to be stopped: %r' % self.job_id
926 926 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
927 927 return output
928 928
929 929
930 930 class WindowsHPCControllerLauncher(WindowsHPCLauncher, ClusterAppMixin):
931 931
932 932 job_file_name = Unicode(u'ipcontroller_job.xml', config=True,
933 933 help="WinHPC xml job file.")
934 934 controller_args = List([], config=False,
935 935 help="extra args to pass to ipcontroller")
936 936
937 937 def write_job_file(self, n):
938 938 job = IPControllerJob(parent=self)
939 939
940 940 t = IPControllerTask(parent=self)
941 941 # The tasks work directory is *not* the actual work directory of
942 942 # the controller. It is used as the base path for the stdout/stderr
943 943 # files that the scheduler redirects to.
944 944 t.work_directory = self.profile_dir
945 945 # Add the profile_dir and from self.start().
946 946 t.controller_args.extend(self.cluster_args)
947 947 t.controller_args.extend(self.controller_args)
948 948 job.add_task(t)
949 949
950 950 self.log.debug("Writing job description file: %s", self.job_file)
951 951 job.write(self.job_file)
952 952
953 953 @property
954 954 def job_file(self):
955 955 return os.path.join(self.profile_dir, self.job_file_name)
956 956
957 957 def start(self):
958 958 """Start the controller by profile_dir."""
959 959 return super(WindowsHPCControllerLauncher, self).start(1)
960 960
961 961
962 962 class WindowsHPCEngineSetLauncher(WindowsHPCLauncher, ClusterAppMixin):
963 963
964 964 job_file_name = Unicode(u'ipengineset_job.xml', config=True,
965 965 help="jobfile for ipengines job")
966 966 engine_args = List([], config=False,
967 967 help="extra args to pas to ipengine")
968 968
969 969 def write_job_file(self, n):
970 970 job = IPEngineSetJob(parent=self)
971 971
972 972 for i in range(n):
973 973 t = IPEngineTask(parent=self)
974 974 # The tasks work directory is *not* the actual work directory of
975 975 # the engine. It is used as the base path for the stdout/stderr
976 976 # files that the scheduler redirects to.
977 977 t.work_directory = self.profile_dir
978 978 # Add the profile_dir and from self.start().
979 979 t.engine_args.extend(self.cluster_args)
980 980 t.engine_args.extend(self.engine_args)
981 981 job.add_task(t)
982 982
983 983 self.log.debug("Writing job description file: %s", self.job_file)
984 984 job.write(self.job_file)
985 985
986 986 @property
987 987 def job_file(self):
988 988 return os.path.join(self.profile_dir, self.job_file_name)
989 989
990 990 def start(self, n):
991 991 """Start the controller by profile_dir."""
992 992 return super(WindowsHPCEngineSetLauncher, self).start(n)
993 993
994 994
995 995 #-----------------------------------------------------------------------------
996 996 # Batch (PBS) system launchers
997 997 #-----------------------------------------------------------------------------
998 998
999 999 class BatchClusterAppMixin(ClusterAppMixin):
1000 1000 """ClusterApp mixin that updates the self.context dict, rather than cl-args."""
1001 1001 def _profile_dir_changed(self, name, old, new):
1002 1002 self.context[name] = new
1003 1003 _cluster_id_changed = _profile_dir_changed
1004 1004
1005 1005 def _profile_dir_default(self):
1006 1006 self.context['profile_dir'] = ''
1007 1007 return ''
1008 1008 def _cluster_id_default(self):
1009 1009 self.context['cluster_id'] = ''
1010 1010 return ''
1011 1011
1012 1012
1013 1013 class BatchSystemLauncher(BaseLauncher):
1014 1014 """Launch an external process using a batch system.
1015 1015
1016 1016 This class is designed to work with UNIX batch systems like PBS, LSF,
1017 1017 GridEngine, etc. The overall model is that there are different commands
1018 1018 like qsub, qdel, etc. that handle the starting and stopping of the process.
1019 1019
1020 1020 This class also has the notion of a batch script. The ``batch_template``
1021 1021 attribute can be set to a string that is a template for the batch script.
1022 1022 This template is instantiated using string formatting. Thus the template can
1023 1023 use {n} fot the number of instances. Subclasses can add additional variables
1024 1024 to the template dict.
1025 1025 """
1026 1026
1027 1027 # Subclasses must fill these in. See PBSEngineSet
1028 1028 submit_command = List([''], config=True,
1029 1029 help="The name of the command line program used to submit jobs.")
1030 1030 delete_command = List([''], config=True,
1031 1031 help="The name of the command line program used to delete jobs.")
1032 1032 job_id_regexp = CRegExp('', config=True,
1033 1033 help="""A regular expression used to get the job id from the output of the
1034 1034 submit_command.""")
1035 1035 job_id_regexp_group = Integer(0, config=True,
1036 1036 help="""The group we wish to match in job_id_regexp (0 to match all)""")
1037 1037 batch_template = Unicode('', config=True,
1038 1038 help="The string that is the batch script template itself.")
1039 1039 batch_template_file = Unicode(u'', config=True,
1040 1040 help="The file that contains the batch template.")
1041 1041 batch_file_name = Unicode(u'batch_script', config=True,
1042 1042 help="The filename of the instantiated batch script.")
1043 1043 queue = Unicode(u'', config=True,
1044 1044 help="The PBS Queue.")
1045 1045
1046 1046 def _queue_changed(self, name, old, new):
1047 1047 self.context[name] = new
1048 1048
1049 1049 n = Integer(1)
1050 1050 _n_changed = _queue_changed
1051 1051
1052 1052 # not configurable, override in subclasses
1053 1053 # PBS Job Array regex
1054 1054 job_array_regexp = CRegExp('')
1055 1055 job_array_template = Unicode('')
1056 1056 # PBS Queue regex
1057 1057 queue_regexp = CRegExp('')
1058 1058 queue_template = Unicode('')
1059 1059 # The default batch template, override in subclasses
1060 1060 default_template = Unicode('')
1061 1061 # The full path to the instantiated batch script.
1062 1062 batch_file = Unicode(u'')
1063 1063 # the format dict used with batch_template:
1064 1064 context = Dict()
1065 1065
1066 1066 def _context_default(self):
1067 1067 """load the default context with the default values for the basic keys
1068 1068
1069 1069 because the _trait_changed methods only load the context if they
1070 1070 are set to something other than the default value.
1071 1071 """
1072 1072 return dict(n=1, queue=u'', profile_dir=u'', cluster_id=u'')
1073 1073
1074 1074 # the Formatter instance for rendering the templates:
1075 1075 formatter = Instance(EvalFormatter, (), {})
1076 1076
1077 1077 def find_args(self):
1078 1078 return self.submit_command + [self.batch_file]
1079 1079
1080 1080 def __init__(self, work_dir=u'.', config=None, **kwargs):
1081 1081 super(BatchSystemLauncher, self).__init__(
1082 1082 work_dir=work_dir, config=config, **kwargs
1083 1083 )
1084 1084 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
1085 1085
1086 1086 def parse_job_id(self, output):
1087 1087 """Take the output of the submit command and return the job id."""
1088 1088 m = self.job_id_regexp.search(output)
1089 1089 if m is not None:
1090 1090 job_id = m.group(self.job_id_regexp_group)
1091 1091 else:
1092 1092 raise LauncherError("Job id couldn't be determined: %s" % output)
1093 1093 self.job_id = job_id
1094 1094 self.log.info('Job submitted with job id: %r', job_id)
1095 1095 return job_id
1096 1096
1097 1097 def write_batch_script(self, n):
1098 1098 """Instantiate and write the batch script to the work_dir."""
1099 1099 self.n = n
1100 1100 # first priority is batch_template if set
1101 1101 if self.batch_template_file and not self.batch_template:
1102 1102 # second priority is batch_template_file
1103 1103 with open(self.batch_template_file) as f:
1104 1104 self.batch_template = f.read()
1105 1105 if not self.batch_template:
1106 1106 # third (last) priority is default_template
1107 1107 self.batch_template = self.default_template
1108 1108 # add jobarray or queue lines to user-specified template
1109 1109 # note that this is *only* when user did not specify a template.
1110 1110 self._insert_queue_in_script()
1111 1111 self._insert_job_array_in_script()
1112 1112 script_as_string = self.formatter.format(self.batch_template, **self.context)
1113 1113 self.log.debug('Writing batch script: %s', self.batch_file)
1114 1114 with open(self.batch_file, 'w') as f:
1115 1115 f.write(script_as_string)
1116 1116 os.chmod(self.batch_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
1117 1117
1118 1118 def _insert_queue_in_script(self):
1119 1119 """Inserts a queue if required into the batch script.
1120 1120 """
1121 1121 if self.queue and not self.queue_regexp.search(self.batch_template):
1122 1122 self.log.debug("adding PBS queue settings to batch script")
1123 1123 firstline, rest = self.batch_template.split('\n',1)
1124 1124 self.batch_template = u'\n'.join([firstline, self.queue_template, rest])
1125 1125
1126 1126 def _insert_job_array_in_script(self):
1127 1127 """Inserts a job array if required into the batch script.
1128 1128 """
1129 1129 if not self.job_array_regexp.search(self.batch_template):
1130 1130 self.log.debug("adding job array settings to batch script")
1131 1131 firstline, rest = self.batch_template.split('\n',1)
1132 1132 self.batch_template = u'\n'.join([firstline, self.job_array_template, rest])
1133 1133
1134 1134 def start(self, n):
1135 1135 """Start n copies of the process using a batch system."""
1136 1136 self.log.debug("Starting %s: %r", self.__class__.__name__, self.args)
1137 1137 # Here we save profile_dir in the context so they
1138 1138 # can be used in the batch script template as {profile_dir}
1139 1139 self.write_batch_script(n)
1140 1140 output = check_output(self.args, env=os.environ)
1141 1141 output = output.decode(DEFAULT_ENCODING, 'replace')
1142 1142
1143 1143 job_id = self.parse_job_id(output)
1144 1144 self.notify_start(job_id)
1145 1145 return job_id
1146 1146
1147 1147 def stop(self):
1148 1148 try:
1149 1149 p = Popen(self.delete_command+[self.job_id], env=os.environ,
1150 1150 stdout=PIPE, stderr=PIPE)
1151 1151 out, err = p.communicate()
1152 1152 output = out + err
1153 1153 except:
1154 1154 self.log.exception("Problem stopping cluster with command: %s" %
1155 1155 (self.delete_command + [self.job_id]))
1156 1156 output = ""
1157 1157 output = output.decode(DEFAULT_ENCODING, 'replace')
1158 1158 self.notify_stop(dict(job_id=self.job_id, output=output)) # Pass the output of the kill cmd
1159 1159 return output
1160 1160
1161 1161
1162 1162 class PBSLauncher(BatchSystemLauncher):
1163 1163 """A BatchSystemLauncher subclass for PBS."""
1164 1164
1165 1165 submit_command = List(['qsub'], config=True,
1166 1166 help="The PBS submit command ['qsub']")
1167 1167 delete_command = List(['qdel'], config=True,
1168 1168 help="The PBS delete command ['qsub']")
1169 1169 job_id_regexp = CRegExp(r'\d+', config=True,
1170 1170 help="Regular expresion for identifying the job ID [r'\d+']")
1171 1171
1172 1172 batch_file = Unicode(u'')
1173 1173 job_array_regexp = CRegExp('#PBS\W+-t\W+[\w\d\-\$]+')
1174 1174 job_array_template = Unicode('#PBS -t 1-{n}')
1175 1175 queue_regexp = CRegExp('#PBS\W+-q\W+\$?\w+')
1176 1176 queue_template = Unicode('#PBS -q {queue}')
1177 1177
1178 1178
1179 1179 class PBSControllerLauncher(PBSLauncher, BatchClusterAppMixin):
1180 1180 """Launch a controller using PBS."""
1181 1181
1182 1182 batch_file_name = Unicode(u'pbs_controller', config=True,
1183 1183 help="batch file name for the controller job.")
1184 1184 default_template= Unicode("""#!/bin/sh
1185 1185 #PBS -V
1186 1186 #PBS -N ipcontroller
1187 1187 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1188 1188 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1189 1189
1190 1190 def start(self):
1191 1191 """Start the controller by profile or profile_dir."""
1192 1192 return super(PBSControllerLauncher, self).start(1)
1193 1193
1194 1194
1195 1195 class PBSEngineSetLauncher(PBSLauncher, BatchClusterAppMixin):
1196 1196 """Launch Engines using PBS"""
1197 1197 batch_file_name = Unicode(u'pbs_engines', config=True,
1198 1198 help="batch file name for the engine(s) job.")
1199 1199 default_template= Unicode(u"""#!/bin/sh
1200 1200 #PBS -V
1201 1201 #PBS -N ipengine
1202 1202 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1203 1203 """%(' '.join(map(pipes.quote,ipengine_cmd_argv))))
1204 1204
1205 1205
1206 1206 #SGE is very similar to PBS
1207 1207
1208 1208 class SGELauncher(PBSLauncher):
1209 1209 """Sun GridEngine is a PBS clone with slightly different syntax"""
1210 1210 job_array_regexp = CRegExp('#\$\W+\-t')
1211 1211 job_array_template = Unicode('#$ -t 1-{n}')
1212 1212 queue_regexp = CRegExp('#\$\W+-q\W+\$?\w+')
1213 1213 queue_template = Unicode('#$ -q {queue}')
1214 1214
1215 1215
1216 1216 class SGEControllerLauncher(SGELauncher, BatchClusterAppMixin):
1217 1217 """Launch a controller using SGE."""
1218 1218
1219 1219 batch_file_name = Unicode(u'sge_controller', config=True,
1220 1220 help="batch file name for the ipontroller job.")
1221 1221 default_template= Unicode(u"""#$ -V
1222 1222 #$ -S /bin/sh
1223 1223 #$ -N ipcontroller
1224 1224 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1225 1225 """%(' '.join(map(pipes.quote, ipcontroller_cmd_argv))))
1226 1226
1227 1227 def start(self):
1228 1228 """Start the controller by profile or profile_dir."""
1229 1229 return super(SGEControllerLauncher, self).start(1)
1230 1230
1231 1231
1232 1232 class SGEEngineSetLauncher(SGELauncher, BatchClusterAppMixin):
1233 1233 """Launch Engines with SGE"""
1234 1234 batch_file_name = Unicode(u'sge_engines', config=True,
1235 1235 help="batch file name for the engine(s) job.")
1236 1236 default_template = Unicode("""#$ -V
1237 1237 #$ -S /bin/sh
1238 1238 #$ -N ipengine
1239 1239 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1240 1240 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1241 1241
1242 1242
1243 1243 # LSF launchers
1244 1244
1245 1245 class LSFLauncher(BatchSystemLauncher):
1246 1246 """A BatchSystemLauncher subclass for LSF."""
1247 1247
1248 1248 submit_command = List(['bsub'], config=True,
1249 1249 help="The PBS submit command ['bsub']")
1250 1250 delete_command = List(['bkill'], config=True,
1251 1251 help="The PBS delete command ['bkill']")
1252 1252 job_id_regexp = CRegExp(r'\d+', config=True,
1253 1253 help="Regular expresion for identifying the job ID [r'\d+']")
1254 1254
1255 1255 batch_file = Unicode(u'')
1256 1256 job_array_regexp = CRegExp('#BSUB[ \t]-J+\w+\[\d+-\d+\]')
1257 1257 job_array_template = Unicode('#BSUB -J ipengine[1-{n}]')
1258 1258 queue_regexp = CRegExp('#BSUB[ \t]+-q[ \t]+\w+')
1259 1259 queue_template = Unicode('#BSUB -q {queue}')
1260 1260
1261 1261 def start(self, n):
1262 1262 """Start n copies of the process using LSF batch system.
1263 1263 This cant inherit from the base class because bsub expects
1264 1264 to be piped a shell script in order to honor the #BSUB directives :
1265 1265 bsub < script
1266 1266 """
1267 1267 # Here we save profile_dir in the context so they
1268 1268 # can be used in the batch script template as {profile_dir}
1269 1269 self.write_batch_script(n)
1270 1270 piped_cmd = self.args[0]+'<\"'+self.args[1]+'\"'
1271 1271 self.log.debug("Starting %s: %s", self.__class__.__name__, piped_cmd)
1272 1272 p = Popen(piped_cmd, shell=True,env=os.environ,stdout=PIPE)
1273 1273 output,err = p.communicate()
1274 1274 output = output.decode(DEFAULT_ENCODING, 'replace')
1275 1275 job_id = self.parse_job_id(output)
1276 1276 self.notify_start(job_id)
1277 1277 return job_id
1278 1278
1279 1279
1280 1280 class LSFControllerLauncher(LSFLauncher, BatchClusterAppMixin):
1281 1281 """Launch a controller using LSF."""
1282 1282
1283 1283 batch_file_name = Unicode(u'lsf_controller', config=True,
1284 1284 help="batch file name for the controller job.")
1285 1285 default_template= Unicode("""#!/bin/sh
1286 1286 #BSUB -J ipcontroller
1287 1287 #BSUB -oo ipcontroller.o.%%J
1288 1288 #BSUB -eo ipcontroller.e.%%J
1289 1289 %s --log-to-file --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1290 1290 """%(' '.join(map(pipes.quote,ipcontroller_cmd_argv))))
1291 1291
1292 1292 def start(self):
1293 1293 """Start the controller by profile or profile_dir."""
1294 1294 return super(LSFControllerLauncher, self).start(1)
1295 1295
1296 1296
1297 1297 class LSFEngineSetLauncher(LSFLauncher, BatchClusterAppMixin):
1298 1298 """Launch Engines using LSF"""
1299 1299 batch_file_name = Unicode(u'lsf_engines', config=True,
1300 1300 help="batch file name for the engine(s) job.")
1301 1301 default_template= Unicode(u"""#!/bin/sh
1302 1302 #BSUB -oo ipengine.o.%%J
1303 1303 #BSUB -eo ipengine.e.%%J
1304 1304 %s --profile-dir="{profile_dir}" --cluster-id="{cluster_id}"
1305 1305 """%(' '.join(map(pipes.quote, ipengine_cmd_argv))))
1306 1306
1307 1307
1308 1308
1309 1309 class HTCondorLauncher(BatchSystemLauncher):
1310 1310 """A BatchSystemLauncher subclass for HTCondor.
1311 1311
1312 1312 HTCondor requires that we launch the ipengine/ipcontroller scripts rather
1313 1313 that the python instance but otherwise is very similar to PBS. This is because
1314 1314 HTCondor destroys sys.executable when launching remote processes - a launched
1315 1315 python process depends on sys.executable to effectively evaluate its
1316 1316 module search paths. Without it, regardless of which python interpreter you launch
1317 1317 you will get the to built in module search paths.
1318 1318
1319 1319 We use the ip{cluster, engine, controller} scripts as our executable to circumvent
1320 1320 this - the mechanism of shebanged scripts means that the python binary will be
1321 1321 launched with argv[0] set to the *location of the ip{cluster, engine, controller}
1322 1322 scripts on the remote node*. This means you need to take care that:
1323 1323
1324 1324 a. Your remote nodes have their paths configured correctly, with the ipengine and ipcontroller
1325 1325 of the python environment you wish to execute code in having top precedence.
1326 1326 b. This functionality is untested on Windows.
1327 1327
1328 1328 If you need different behavior, consider making you own template.
1329 1329 """
1330 1330
1331 1331 submit_command = List(['condor_submit'], config=True,
1332 1332 help="The HTCondor submit command ['condor_submit']")
1333 1333 delete_command = List(['condor_rm'], config=True,
1334 1334 help="The HTCondor delete command ['condor_rm']")
1335 1335 job_id_regexp = CRegExp(r'(\d+)\.$', config=True,
1336 1336 help="Regular expression for identifying the job ID [r'(\d+)\.$']")
1337 1337 job_id_regexp_group = Integer(1, config=True,
1338 1338 help="""The group we wish to match in job_id_regexp [1]""")
1339 1339
1340 1340 job_array_regexp = CRegExp('queue\W+\$')
1341 1341 job_array_template = Unicode('queue {n}')
1342 1342
1343 1343
1344 1344 def _insert_job_array_in_script(self):
1345 1345 """Inserts a job array if required into the batch script.
1346 1346 """
1347 1347 if not self.job_array_regexp.search(self.batch_template):
1348 1348 self.log.debug("adding job array settings to batch script")
1349 1349 #HTCondor requires that the job array goes at the bottom of the script
1350 1350 self.batch_template = '\n'.join([self.batch_template,
1351 1351 self.job_array_template])
1352 1352
1353 1353 def _insert_queue_in_script(self):
1354 1354 """AFAIK, HTCondor doesn't have a concept of multiple queues that can be
1355 1355 specified in the script.
1356 1356 """
1357 1357 pass
1358 1358
1359 1359
1360 1360 class HTCondorControllerLauncher(HTCondorLauncher, BatchClusterAppMixin):
1361 1361 """Launch a controller using HTCondor."""
1362 1362
1363 1363 batch_file_name = Unicode(u'htcondor_controller', config=True,
1364 1364 help="batch file name for the controller job.")
1365 1365 default_template = Unicode(r"""
1366 1366 universe = vanilla
1367 1367 executable = ipcontroller
1368 1368 # by default we expect a shared file system
1369 1369 transfer_executable = False
1370 1370 arguments = --log-to-file '--profile-dir={profile_dir}' --cluster-id='{cluster_id}'
1371 1371 """)
1372 1372
1373 1373 def start(self):
1374 1374 """Start the controller by profile or profile_dir."""
1375 1375 return super(HTCondorControllerLauncher, self).start(1)
1376 1376
1377 1377
1378 1378 class HTCondorEngineSetLauncher(HTCondorLauncher, BatchClusterAppMixin):
1379 1379 """Launch Engines using HTCondor"""
1380 1380 batch_file_name = Unicode(u'htcondor_engines', config=True,
1381 1381 help="batch file name for the engine(s) job.")
1382 1382 default_template = Unicode("""
1383 1383 universe = vanilla
1384 1384 executable = ipengine
1385 1385 # by default we expect a shared file system
1386 1386 transfer_executable = False
1387 1387 arguments = "--log-to-file '--profile-dir={profile_dir}' '--cluster-id={cluster_id}'"
1388 1388 """)
1389 1389
1390 1390
1391 1391 #-----------------------------------------------------------------------------
1392 1392 # A launcher for ipcluster itself!
1393 1393 #-----------------------------------------------------------------------------
1394 1394
1395 1395
1396 1396 class IPClusterLauncher(LocalProcessLauncher):
1397 1397 """Launch the ipcluster program in an external process."""
1398 1398
1399 1399 ipcluster_cmd = List(ipcluster_cmd_argv, config=True,
1400 1400 help="Popen command for ipcluster")
1401 1401 ipcluster_args = List(
1402 1402 ['--clean-logs=True', '--log-to-file', '--log-level=%i'%logging.INFO], config=True,
1403 1403 help="Command line arguments to pass to ipcluster.")
1404 1404 ipcluster_subcommand = Unicode('start')
1405 1405 profile = Unicode('default')
1406 1406 n = Integer(2)
1407 1407
1408 1408 def find_args(self):
1409 1409 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
1410 1410 ['--n=%i'%self.n, '--profile=%s'%self.profile] + \
1411 1411 self.ipcluster_args
1412 1412
1413 1413 def start(self):
1414 1414 return super(IPClusterLauncher, self).start()
1415 1415
1416 1416 #-----------------------------------------------------------------------------
1417 1417 # Collections of launchers
1418 1418 #-----------------------------------------------------------------------------
1419 1419
1420 1420 local_launchers = [
1421 1421 LocalControllerLauncher,
1422 1422 LocalEngineLauncher,
1423 1423 LocalEngineSetLauncher,
1424 1424 ]
1425 1425 mpi_launchers = [
1426 1426 MPILauncher,
1427 1427 MPIControllerLauncher,
1428 1428 MPIEngineSetLauncher,
1429 1429 ]
1430 1430 ssh_launchers = [
1431 1431 SSHLauncher,
1432 1432 SSHControllerLauncher,
1433 1433 SSHEngineLauncher,
1434 1434 SSHEngineSetLauncher,
1435 1435 SSHProxyEngineSetLauncher,
1436 1436 ]
1437 1437 winhpc_launchers = [
1438 1438 WindowsHPCLauncher,
1439 1439 WindowsHPCControllerLauncher,
1440 1440 WindowsHPCEngineSetLauncher,
1441 1441 ]
1442 1442 pbs_launchers = [
1443 1443 PBSLauncher,
1444 1444 PBSControllerLauncher,
1445 1445 PBSEngineSetLauncher,
1446 1446 ]
1447 1447 sge_launchers = [
1448 1448 SGELauncher,
1449 1449 SGEControllerLauncher,
1450 1450 SGEEngineSetLauncher,
1451 1451 ]
1452 1452 lsf_launchers = [
1453 1453 LSFLauncher,
1454 1454 LSFControllerLauncher,
1455 1455 LSFEngineSetLauncher,
1456 1456 ]
1457 1457 htcondor_launchers = [
1458 1458 HTCondorLauncher,
1459 1459 HTCondorControllerLauncher,
1460 1460 HTCondorEngineSetLauncher,
1461 1461 ]
1462 1462 all_launchers = local_launchers + mpi_launchers + ssh_launchers + winhpc_launchers\
1463 1463 + pbs_launchers + sge_launchers + lsf_launchers + htcondor_launchers
@@ -1,524 +1,524 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib
30 30 import glob
31 31 from io import BytesIO
32 32 import os
33 33 import os.path as path
34 34 import sys
35 35 from threading import Thread, Lock, Event
36 36 import warnings
37 37
38 38 # Now, proceed to import nose itself
39 39 import nose.plugins.builtin
40 40 from nose.plugins.xunit import Xunit
41 41 from nose import SkipTest
42 42 from nose.core import TestProgram
43 43 from nose.plugins import Plugin
44 44 from nose.util import safe_str
45 45
46 46 # Our own imports
47 47 from IPython.utils.process import is_cmd_found
48 48 from IPython.utils.importstring import import_item
49 49 from IPython.testing.plugin.ipdoctest import IPythonDoctest
50 50 from IPython.external.decorators import KnownFailure, knownfailureif
51 51
52 52 pjoin = path.join
53 53
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Globals
57 57 #-----------------------------------------------------------------------------
58 58
59 59
60 60 #-----------------------------------------------------------------------------
61 61 # Warnings control
62 62 #-----------------------------------------------------------------------------
63 63
64 64 # Twisted generates annoying warnings with Python 2.6, as will do other code
65 65 # that imports 'sets' as of today
66 66 warnings.filterwarnings('ignore', 'the sets module is deprecated',
67 67 DeprecationWarning )
68 68
69 69 # This one also comes from Twisted
70 70 warnings.filterwarnings('ignore', 'the sha module is deprecated',
71 71 DeprecationWarning)
72 72
73 73 # Wx on Fedora11 spits these out
74 74 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
75 75 UserWarning)
76 76
77 77 # ------------------------------------------------------------------------------
78 78 # Monkeypatch Xunit to count known failures as skipped.
79 79 # ------------------------------------------------------------------------------
80 80 def monkeypatch_xunit():
81 81 try:
82 82 knownfailureif(True)(lambda: None)()
83 83 except Exception as e:
84 84 KnownFailureTest = type(e)
85 85
86 86 def addError(self, test, err, capt=None):
87 87 if issubclass(err[0], KnownFailureTest):
88 88 err = (SkipTest,) + err[1:]
89 89 return self.orig_addError(test, err, capt)
90 90
91 91 Xunit.orig_addError = Xunit.addError
92 92 Xunit.addError = addError
93 93
94 94 #-----------------------------------------------------------------------------
95 95 # Check which dependencies are installed and greater than minimum version.
96 96 #-----------------------------------------------------------------------------
97 97 def extract_version(mod):
98 98 return mod.__version__
99 99
100 100 def test_for(item, min_version=None, callback=extract_version):
101 101 """Test to see if item is importable, and optionally check against a minimum
102 102 version.
103 103
104 104 If min_version is given, the default behavior is to check against the
105 105 `__version__` attribute of the item, but specifying `callback` allows you to
106 106 extract the value you are interested in. e.g::
107 107
108 108 In [1]: import sys
109 109
110 110 In [2]: from IPython.testing.iptest import test_for
111 111
112 112 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
113 113 Out[3]: True
114 114
115 115 """
116 116 try:
117 117 check = import_item(item)
118 118 except (ImportError, RuntimeError):
119 119 # GTK reports Runtime error if it can't be initialized even if it's
120 120 # importable.
121 121 return False
122 122 else:
123 123 if min_version:
124 124 if callback:
125 125 # extra processing step to get version to compare
126 126 check = callback(check)
127 127
128 128 return check >= min_version
129 129 else:
130 130 return True
131 131
132 132 # Global dict where we can store information on what we have and what we don't
133 133 # have available at test run time
134 134 have = {}
135 135
136 136 have['curses'] = test_for('_curses')
137 137 have['matplotlib'] = test_for('matplotlib')
138 138 have['numpy'] = test_for('numpy')
139 139 have['pexpect'] = test_for('IPython.external.pexpect')
140 140 have['pymongo'] = test_for('pymongo')
141 141 have['pygments'] = test_for('pygments')
142 142 have['qt'] = test_for('IPython.external.qt')
143 143 have['rpy2'] = test_for('rpy2')
144 144 have['sqlite3'] = test_for('sqlite3')
145 145 have['cython'] = test_for('Cython')
146 146 have['oct2py'] = test_for('oct2py')
147 147 have['tornado'] = test_for('tornado.version_info', (3,1,0), callback=None)
148 148 have['jinja2'] = test_for('jinja2')
149 149 have['requests'] = test_for('requests')
150 150 have['sphinx'] = test_for('sphinx')
151 151 have['casperjs'] = is_cmd_found('casperjs')
152 152
153 153 min_zmq = (2,1,11)
154 154
155 155 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
156 156
157 157 #-----------------------------------------------------------------------------
158 158 # Test suite definitions
159 159 #-----------------------------------------------------------------------------
160 160
161 161 test_group_names = ['parallel', 'kernel', 'kernel.inprocess', 'config', 'core',
162 162 'extensions', 'lib', 'terminal', 'testing', 'utils',
163 163 'nbformat', 'qt', 'html', 'nbconvert'
164 164 ]
165 165
166 166 class TestSection(object):
167 167 def __init__(self, name, includes):
168 168 self.name = name
169 169 self.includes = includes
170 170 self.excludes = []
171 171 self.dependencies = []
172 172 self.enabled = True
173 173
174 174 def exclude(self, module):
175 175 if not module.startswith('IPython'):
176 176 module = self.includes[0] + "." + module
177 177 self.excludes.append(module.replace('.', os.sep))
178 178
179 179 def requires(self, *packages):
180 180 self.dependencies.extend(packages)
181 181
182 182 @property
183 183 def will_run(self):
184 184 return self.enabled and all(have[p] for p in self.dependencies)
185 185
186 186 # Name -> (include, exclude, dependencies_met)
187 187 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
188 188
189 189 # Exclusions and dependencies
190 190 # ---------------------------
191 191
192 192 # core:
193 193 sec = test_sections['core']
194 194 if not have['sqlite3']:
195 195 sec.exclude('tests.test_history')
196 196 sec.exclude('history')
197 197 if not have['matplotlib']:
198 198 sec.exclude('pylabtools'),
199 199 sec.exclude('tests.test_pylabtools')
200 200
201 201 # lib:
202 202 sec = test_sections['lib']
203 203 if not have['zmq']:
204 204 sec.exclude('kernel')
205 205 # We do this unconditionally, so that the test suite doesn't import
206 206 # gtk, changing the default encoding and masking some unicode bugs.
207 207 sec.exclude('inputhookgtk')
208 208 # We also do this unconditionally, because wx can interfere with Unix signals.
209 209 # There are currently no tests for it anyway.
210 210 sec.exclude('inputhookwx')
211 211 # Testing inputhook will need a lot of thought, to figure out
212 212 # how to have tests that don't lock up with the gui event
213 213 # loops in the picture
214 214 sec.exclude('inputhook')
215 215
216 216 # testing:
217 217 sec = test_sections['testing']
218 218 # These have to be skipped on win32 because they use echo, rm, cd, etc.
219 219 # See ticket https://github.com/ipython/ipython/issues/87
220 220 if sys.platform == 'win32':
221 221 sec.exclude('plugin.test_exampleip')
222 222 sec.exclude('plugin.dtexample')
223 223
224 224 # terminal:
225 225 if (not have['pexpect']) or (not have['zmq']):
226 226 test_sections['terminal'].exclude('console')
227 227
228 228 # parallel
229 229 sec = test_sections['parallel']
230 230 sec.requires('zmq')
231 231 if not have['pymongo']:
232 232 sec.exclude('controller.mongodb')
233 233 sec.exclude('tests.test_mongodb')
234 234
235 235 # kernel:
236 236 sec = test_sections['kernel']
237 237 sec.requires('zmq')
238 238 # The in-process kernel tests are done in a separate section
239 239 sec.exclude('inprocess')
240 240 # importing gtk sets the default encoding, which we want to avoid
241 241 sec.exclude('zmq.gui.gtkembed')
242 242 if not have['matplotlib']:
243 243 sec.exclude('zmq.pylab')
244 244
245 245 # kernel.inprocess:
246 246 test_sections['kernel.inprocess'].requires('zmq')
247 247
248 248 # extensions:
249 249 sec = test_sections['extensions']
250 250 if not have['cython']:
251 251 sec.exclude('cythonmagic')
252 252 sec.exclude('tests.test_cythonmagic')
253 253 if not have['oct2py']:
254 254 sec.exclude('octavemagic')
255 255 sec.exclude('tests.test_octavemagic')
256 256 if not have['rpy2'] or not have['numpy']:
257 257 sec.exclude('rmagic')
258 258 sec.exclude('tests.test_rmagic')
259 259 # autoreload does some strange stuff, so move it to its own test section
260 260 sec.exclude('autoreload')
261 261 sec.exclude('tests.test_autoreload')
262 262 test_sections['autoreload'] = TestSection('autoreload',
263 263 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
264 264 test_group_names.append('autoreload')
265 265
266 266 # qt:
267 267 test_sections['qt'].requires('zmq', 'qt', 'pygments')
268 268
269 269 # html:
270 270 sec = test_sections['html']
271 sec.requires('zmq', 'tornado', 'requests')
271 sec.requires('zmq', 'tornado', 'requests', 'sqlite3')
272 272 # The notebook 'static' directory contains JS, css and other
273 273 # files for web serving. Occasionally projects may put a .py
274 274 # file in there (MathJax ships a conf.py), so we might as
275 275 # well play it safe and skip the whole thing.
276 276 sec.exclude('static')
277 277 sec.exclude('fabfile')
278 278 if not have['jinja2']:
279 279 sec.exclude('notebookapp')
280 280 if not have['pygments'] or not have['jinja2']:
281 281 sec.exclude('nbconvert')
282 282
283 283 # config:
284 284 # Config files aren't really importable stand-alone
285 285 test_sections['config'].exclude('profile')
286 286
287 287 # nbconvert:
288 288 sec = test_sections['nbconvert']
289 289 sec.requires('pygments', 'jinja2')
290 290 # Exclude nbconvert directories containing config files used to test.
291 291 # Executing the config files with iptest would cause an exception.
292 292 sec.exclude('tests.files')
293 293 sec.exclude('exporters.tests.files')
294 294 if not have['tornado']:
295 295 sec.exclude('nbconvert.post_processors.serve')
296 296 sec.exclude('nbconvert.post_processors.tests.test_serve')
297 297
298 298 #-----------------------------------------------------------------------------
299 299 # Functions and classes
300 300 #-----------------------------------------------------------------------------
301 301
302 302 def check_exclusions_exist():
303 303 from IPython.utils.path import get_ipython_package_dir
304 304 from IPython.utils.warn import warn
305 305 parent = os.path.dirname(get_ipython_package_dir())
306 306 for sec in test_sections:
307 307 for pattern in sec.exclusions:
308 308 fullpath = pjoin(parent, pattern)
309 309 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
310 310 warn("Excluding nonexistent file: %r" % pattern)
311 311
312 312
313 313 class ExclusionPlugin(Plugin):
314 314 """A nose plugin to effect our exclusions of files and directories.
315 315 """
316 316 name = 'exclusions'
317 317 score = 3000 # Should come before any other plugins
318 318
319 319 def __init__(self, exclude_patterns=None):
320 320 """
321 321 Parameters
322 322 ----------
323 323
324 324 exclude_patterns : sequence of strings, optional
325 325 Filenames containing these patterns (as raw strings, not as regular
326 326 expressions) are excluded from the tests.
327 327 """
328 328 self.exclude_patterns = exclude_patterns or []
329 329 super(ExclusionPlugin, self).__init__()
330 330
331 331 def options(self, parser, env=os.environ):
332 332 Plugin.options(self, parser, env)
333 333
334 334 def configure(self, options, config):
335 335 Plugin.configure(self, options, config)
336 336 # Override nose trying to disable plugin.
337 337 self.enabled = True
338 338
339 339 def wantFile(self, filename):
340 340 """Return whether the given filename should be scanned for tests.
341 341 """
342 342 if any(pat in filename for pat in self.exclude_patterns):
343 343 return False
344 344 return None
345 345
346 346 def wantDirectory(self, directory):
347 347 """Return whether the given directory should be scanned for tests.
348 348 """
349 349 if any(pat in directory for pat in self.exclude_patterns):
350 350 return False
351 351 return None
352 352
353 353
354 354 class StreamCapturer(Thread):
355 355 daemon = True # Don't hang if main thread crashes
356 356 started = False
357 357 def __init__(self):
358 358 super(StreamCapturer, self).__init__()
359 359 self.streams = []
360 360 self.buffer = BytesIO()
361 361 self.readfd, self.writefd = os.pipe()
362 362 self.buffer_lock = Lock()
363 363 self.stop = Event()
364 364
365 365 def run(self):
366 366 self.started = True
367 367
368 368 while not self.stop.is_set():
369 369 chunk = os.read(self.readfd, 1024)
370 370
371 371 with self.buffer_lock:
372 372 self.buffer.write(chunk)
373 373
374 374 os.close(self.readfd)
375 375 os.close(self.writefd)
376 376
377 377 def reset_buffer(self):
378 378 with self.buffer_lock:
379 379 self.buffer.truncate(0)
380 380 self.buffer.seek(0)
381 381
382 382 def get_buffer(self):
383 383 with self.buffer_lock:
384 384 return self.buffer.getvalue()
385 385
386 386 def ensure_started(self):
387 387 if not self.started:
388 388 self.start()
389 389
390 390 def halt(self):
391 391 """Safely stop the thread."""
392 392 if not self.started:
393 393 return
394 394
395 395 self.stop.set()
396 396 os.write(self.writefd, b'wake up') # Ensure we're not locked in a read()
397 397 self.join()
398 398
399 399 class SubprocessStreamCapturePlugin(Plugin):
400 400 name='subprocstreams'
401 401 def __init__(self):
402 402 Plugin.__init__(self)
403 403 self.stream_capturer = StreamCapturer()
404 404 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
405 405 # This is ugly, but distant parts of the test machinery need to be able
406 406 # to redirect streams, so we make the object globally accessible.
407 407 nose.iptest_stdstreams_fileno = self.get_write_fileno
408 408
409 409 def get_write_fileno(self):
410 410 if self.destination == 'capture':
411 411 self.stream_capturer.ensure_started()
412 412 return self.stream_capturer.writefd
413 413 elif self.destination == 'discard':
414 414 return os.open(os.devnull, os.O_WRONLY)
415 415 else:
416 416 return sys.__stdout__.fileno()
417 417
418 418 def configure(self, options, config):
419 419 Plugin.configure(self, options, config)
420 420 # Override nose trying to disable plugin.
421 421 if self.destination == 'capture':
422 422 self.enabled = True
423 423
424 424 def startTest(self, test):
425 425 # Reset log capture
426 426 self.stream_capturer.reset_buffer()
427 427
428 428 def formatFailure(self, test, err):
429 429 # Show output
430 430 ec, ev, tb = err
431 431 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
432 432 if captured.strip():
433 433 ev = safe_str(ev)
434 434 out = [ev, '>> begin captured subprocess output <<',
435 435 captured,
436 436 '>> end captured subprocess output <<']
437 437 return ec, '\n'.join(out), tb
438 438
439 439 return err
440 440
441 441 formatError = formatFailure
442 442
443 443 def finalize(self, result):
444 444 self.stream_capturer.halt()
445 445
446 446
447 447 def run_iptest():
448 448 """Run the IPython test suite using nose.
449 449
450 450 This function is called when this script is **not** called with the form
451 451 `iptest all`. It simply calls nose with appropriate command line flags
452 452 and accepts all of the standard nose arguments.
453 453 """
454 454 # Apply our monkeypatch to Xunit
455 455 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
456 456 monkeypatch_xunit()
457 457
458 458 warnings.filterwarnings('ignore',
459 459 'This will be removed soon. Use IPython.testing.util instead')
460 460
461 461 arg1 = sys.argv[1]
462 462 if arg1 in test_sections:
463 463 section = test_sections[arg1]
464 464 sys.argv[1:2] = section.includes
465 465 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
466 466 section = test_sections[arg1[8:]]
467 467 sys.argv[1:2] = section.includes
468 468 else:
469 469 section = TestSection(arg1, includes=[arg1])
470 470
471 471
472 472 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
473 473
474 474 '--with-ipdoctest',
475 475 '--ipdoctest-tests','--ipdoctest-extension=txt',
476 476
477 477 # We add --exe because of setuptools' imbecility (it
478 478 # blindly does chmod +x on ALL files). Nose does the
479 479 # right thing and it tries to avoid executables,
480 480 # setuptools unfortunately forces our hand here. This
481 481 # has been discussed on the distutils list and the
482 482 # setuptools devs refuse to fix this problem!
483 483 '--exe',
484 484 ]
485 485 if '-a' not in argv and '-A' not in argv:
486 486 argv = argv + ['-a', '!crash']
487 487
488 488 if nose.__version__ >= '0.11':
489 489 # I don't fully understand why we need this one, but depending on what
490 490 # directory the test suite is run from, if we don't give it, 0 tests
491 491 # get run. Specifically, if the test suite is run from the source dir
492 492 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
493 493 # even if the same call done in this directory works fine). It appears
494 494 # that if the requested package is in the current dir, nose bails early
495 495 # by default. Since it's otherwise harmless, leave it in by default
496 496 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
497 497 argv.append('--traverse-namespace')
498 498
499 499 # use our plugin for doctesting. It will remove the standard doctest plugin
500 500 # if it finds it enabled
501 501 plugins = [ExclusionPlugin(section.excludes), IPythonDoctest(), KnownFailure(),
502 502 SubprocessStreamCapturePlugin() ]
503 503
504 504 # Use working directory set by parent process (see iptestcontroller)
505 505 if 'IPTEST_WORKING_DIR' in os.environ:
506 506 os.chdir(os.environ['IPTEST_WORKING_DIR'])
507 507
508 508 # We need a global ipython running in this process, but the special
509 509 # in-process group spawns its own IPython kernels, so for *that* group we
510 510 # must avoid also opening the global one (otherwise there's a conflict of
511 511 # singletons). Ultimately the solution to this problem is to refactor our
512 512 # assumptions about what needs to be a singleton and what doesn't (app
513 513 # objects should, individual shells shouldn't). But for now, this
514 514 # workaround allows the test suite for the inprocess module to complete.
515 515 if 'kernel.inprocess' not in section.name:
516 516 from IPython.testing import globalipapp
517 517 globalipapp.start_ipython()
518 518
519 519 # Now nose can run
520 520 TestProgram(argv=argv, addplugins=plugins)
521 521
522 522 if __name__ == '__main__':
523 523 run_iptest()
524 524
@@ -1,569 +1,592 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Process Controller
3 3
4 4 This module runs one or more subprocesses which will actually run the IPython
5 5 test suite.
6 6
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2009-2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19 from __future__ import print_function
20 20
21 21 import argparse
22 import json
22 23 import multiprocessing.pool
23 from multiprocessing import Process, Queue
24 24 import os
25 25 import shutil
26 26 import signal
27 27 import sys
28 28 import subprocess
29 29 import time
30 30
31 from .iptest import have, test_group_names as py_test_group_names, test_sections
31 from .iptest import have, test_group_names as py_test_group_names, test_sections, StreamCapturer
32 32 from IPython.utils.path import compress_user
33 33 from IPython.utils.py3compat import bytes_to_str
34 34 from IPython.utils.sysinfo import get_sys_info
35 35 from IPython.utils.tempdir import TemporaryDirectory
36 36
37 37
38 38 class TestController(object):
39 39 """Run tests in a subprocess
40 40 """
41 41 #: str, IPython test suite to be executed.
42 42 section = None
43 43 #: list, command line arguments to be executed
44 44 cmd = None
45 45 #: dict, extra environment variables to set for the subprocess
46 46 env = None
47 47 #: list, TemporaryDirectory instances to clear up when the process finishes
48 48 dirs = None
49 49 #: subprocess.Popen instance
50 50 process = None
51 51 #: str, process stdout+stderr
52 52 stdout = None
53 53
54 54 def __init__(self):
55 55 self.cmd = []
56 56 self.env = {}
57 57 self.dirs = []
58 58
59 59 def setup(self):
60 60 """Create temporary directories etc.
61 61
62 62 This is only called when we know the test group will be run. Things
63 63 created here may be cleaned up by self.cleanup().
64 64 """
65 65 pass
66 66
67 67 def launch(self, buffer_output=False):
68 68 # print('*** ENV:', self.env) # dbg
69 69 # print('*** CMD:', self.cmd) # dbg
70 70 env = os.environ.copy()
71 71 env.update(self.env)
72 72 output = subprocess.PIPE if buffer_output else None
73 73 stdout = subprocess.STDOUT if buffer_output else None
74 74 self.process = subprocess.Popen(self.cmd, stdout=output,
75 75 stderr=stdout, env=env)
76 76
77 77 def wait(self):
78 78 self.stdout, _ = self.process.communicate()
79 79 return self.process.returncode
80 80
81 81 def print_extra_info(self):
82 82 """Print extra information about this test run.
83 83
84 84 If we're running in parallel and showing the concise view, this is only
85 85 called if the test group fails. Otherwise, it's called before the test
86 86 group is started.
87 87
88 88 The base implementation does nothing, but it can be overridden by
89 89 subclasses.
90 90 """
91 91 return
92 92
93 93 def cleanup_process(self):
94 94 """Cleanup on exit by killing any leftover processes."""
95 95 subp = self.process
96 96 if subp is None or (subp.poll() is not None):
97 97 return # Process doesn't exist, or is already dead.
98 98
99 99 try:
100 100 print('Cleaning up stale PID: %d' % subp.pid)
101 101 subp.kill()
102 102 except: # (OSError, WindowsError) ?
103 103 # This is just a best effort, if we fail or the process was
104 104 # really gone, ignore it.
105 105 pass
106 106 else:
107 107 for i in range(10):
108 108 if subp.poll() is None:
109 109 time.sleep(0.1)
110 110 else:
111 111 break
112 112
113 113 if subp.poll() is None:
114 114 # The process did not die...
115 115 print('... failed. Manual cleanup may be required.')
116 116
117 117 def cleanup(self):
118 118 "Kill process if it's still alive, and clean up temporary directories"
119 119 self.cleanup_process()
120 120 for td in self.dirs:
121 121 td.cleanup()
122 122
123 123 __del__ = cleanup
124 124
125 125 class PyTestController(TestController):
126 126 """Run Python tests using IPython.testing.iptest"""
127 127 #: str, Python command to execute in subprocess
128 128 pycmd = None
129 129
130 def __init__(self, section):
130 def __init__(self, section, options):
131 131 """Create new test runner."""
132 132 TestController.__init__(self)
133 133 self.section = section
134 134 # pycmd is put into cmd[2] in PyTestController.launch()
135 135 self.cmd = [sys.executable, '-c', None, section]
136 136 self.pycmd = "from IPython.testing.iptest import run_iptest; run_iptest()"
137 self.options = options
137 138
138 139 def setup(self):
139 140 ipydir = TemporaryDirectory()
140 141 self.dirs.append(ipydir)
141 142 self.env['IPYTHONDIR'] = ipydir.name
142 143 self.workingdir = workingdir = TemporaryDirectory()
143 144 self.dirs.append(workingdir)
144 145 self.env['IPTEST_WORKING_DIR'] = workingdir.name
145 146 # This means we won't get odd effects from our own matplotlib config
146 147 self.env['MPLCONFIGDIR'] = workingdir.name
147 148
149 # From options:
150 if self.options.xunit:
151 self.add_xunit()
152 if self.options.coverage:
153 self.add_coverage()
154 self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams
155 self.cmd.extend(self.options.extra_args)
156
148 157 @property
149 158 def will_run(self):
150 159 try:
151 160 return test_sections[self.section].will_run
152 161 except KeyError:
153 162 return True
154 163
155 164 def add_xunit(self):
156 165 xunit_file = os.path.abspath(self.section + '.xunit.xml')
157 166 self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file])
158 167
159 168 def add_coverage(self):
160 169 try:
161 170 sources = test_sections[self.section].includes
162 171 except KeyError:
163 172 sources = ['IPython']
164 173
165 174 coverage_rc = ("[run]\n"
166 175 "data_file = {data_file}\n"
167 176 "source =\n"
168 177 " {source}\n"
169 178 ).format(data_file=os.path.abspath('.coverage.'+self.section),
170 179 source="\n ".join(sources))
171 180 config_file = os.path.join(self.workingdir.name, '.coveragerc')
172 181 with open(config_file, 'w') as f:
173 182 f.write(coverage_rc)
174 183
175 184 self.env['COVERAGE_PROCESS_START'] = config_file
176 185 self.pycmd = "import coverage; coverage.process_startup(); " + self.pycmd
177 186
178 187 def launch(self, buffer_output=False):
179 188 self.cmd[2] = self.pycmd
180 189 super(PyTestController, self).launch(buffer_output=buffer_output)
181 190
182 191 js_prefix = 'js/'
183 192
184 193 def get_js_test_dir():
185 194 import IPython.html.tests as t
186 195 return os.path.join(os.path.dirname(t.__file__), '')
187 196
188 197 def all_js_groups():
189 198 import glob
190 199 test_dir = get_js_test_dir()
191 200 all_subdirs = glob.glob(test_dir + '*/')
192 201 return [js_prefix+os.path.relpath(x, test_dir) for x in all_subdirs if os.path.relpath(x, test_dir) != '__pycache__']
193 202
194 203 class JSController(TestController):
195 204 """Run CasperJS tests """
196 205 def __init__(self, section):
197 206 """Create new test runner."""
198 207 TestController.__init__(self)
199 208 self.section = section
200 209 js_test_dir = get_js_test_dir()
201 210 includes = '--includes=' + os.path.join(js_test_dir,'util.js')
202 211 test_cases = os.path.join(js_test_dir, self.section[len(js_prefix):])
203 212 self.cmd = ['casperjs', 'test', includes, test_cases]
204 213
205 214 def setup(self):
206 215 self.ipydir = TemporaryDirectory()
207 216 self.nbdir = TemporaryDirectory()
208 217 self.dirs.append(self.ipydir)
209 218 self.dirs.append(self.nbdir)
210 219 os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir1', u'sub ∂ir 1a')))
211 220 os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir2', u'sub ∂ir 1b')))
212 221
213 222 # start the ipython notebook, so we get the port number
223 self.server_port = 0
214 224 self._init_server()
215 self.cmd.append('--port=%s' % self.server_port)
216
225 if self.server_port:
226 self.cmd.append("--port=%i" % self.server_port)
227 else:
228 # don't launch tests if the server didn't start
229 self.cmd = [sys.executable, '-c', 'raise SystemExit(1)']
230
217 231 def print_extra_info(self):
218 232 print("Running tests with notebook directory %r" % self.nbdir.name)
219 233
220 234 @property
221 235 def will_run(self):
222 return all(have[a] for a in ['zmq', 'tornado', 'jinja2', 'casperjs'])
236 return all(have[a] for a in ['zmq', 'tornado', 'jinja2', 'casperjs', 'sqlite3'])
223 237
224 238 def _init_server(self):
225 239 "Start the notebook server in a separate process"
226 self.queue = q = Queue()
227 self.server = Process(target=run_webapp, args=(q, self.ipydir.name, self.nbdir.name))
228 self.server.start()
229 self.server_port = q.get()
240 self.server_command = command = [sys.executable,
241 '-m', 'IPython.html',
242 '--no-browser',
243 '--ipython-dir', self.ipydir.name,
244 '--notebook-dir', self.nbdir.name,
245 ]
246 # ipc doesn't work on Windows, and darwin has crazy-long temp paths,
247 # which run afoul of ipc's maximum path length.
248 if sys.platform.startswith('linux'):
249 command.append('--KernelManager.transport=ipc')
250 self.stream_capturer = c = StreamCapturer()
251 c.start()
252 self.server = subprocess.Popen(command, stdout=c.writefd, stderr=subprocess.STDOUT)
253 self.server_info_file = os.path.join(self.ipydir.name,
254 'profile_default', 'security', 'nbserver-%i.json' % self.server.pid
255 )
256 self._wait_for_server()
257
258 def _wait_for_server(self):
259 """Wait 30 seconds for the notebook server to start"""
260 for i in range(300):
261 if self.server.poll() is not None:
262 return self._failed_to_start()
263 if os.path.exists(self.server_info_file):
264 self._load_server_info()
265 return
266 time.sleep(0.1)
267 print("Notebook server-info file never arrived: %s" % self.server_info_file,
268 file=sys.stderr
269 )
270
271 def _failed_to_start(self):
272 """Notebook server exited prematurely"""
273 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
274 print("Notebook failed to start: ", file=sys.stderr)
275 print(self.server_command)
276 print(captured, file=sys.stderr)
277
278 def _load_server_info(self):
279 """Notebook server started, load connection info from JSON"""
280 with open(self.server_info_file) as f:
281 info = json.load(f)
282 self.server_port = info['port']
230 283
231 284 def cleanup(self):
232 self.server.terminate()
233 self.server.join()
285 try:
286 self.server.terminate()
287 except OSError:
288 # already dead
289 pass
290 self.server.wait()
291 self.stream_capturer.halt()
234 292 TestController.cleanup(self)
235 293
236 def run_webapp(q, ipydir, nbdir, loglevel=0):
237 """start the IPython Notebook, and pass port back to the queue"""
238 import os
239 import IPython.html.notebookapp as nbapp
240 import sys
241 sys.stderr = open(os.devnull, 'w')
242 server = nbapp.NotebookApp()
243 args = ['--no-browser']
244 args.extend(['--ipython-dir', ipydir,
245 '--notebook-dir', nbdir,
246 '--log-level', str(loglevel),
247 ])
248 # ipc doesn't work on Windows, and darwin has crazy-long temp paths,
249 # which run afoul of ipc's maximum path length.
250 if sys.platform.startswith('linux'):
251 args.append('--KernelManager.transport=ipc')
252 server.initialize(args)
253 # communicate the port number to the parent process
254 q.put(server.port)
255 server.start()
256 294
257 295 def prepare_controllers(options):
258 296 """Returns two lists of TestController instances, those to run, and those
259 297 not to run."""
260 298 testgroups = options.testgroups
261 299
262 300 if testgroups:
263 301 py_testgroups = [g for g in testgroups if (g in py_test_group_names) \
264 302 or g.startswith('IPython.')]
265 303 if 'js' in testgroups:
266 304 js_testgroups = all_js_groups()
267 305 else:
268 306 js_testgroups = [g for g in testgroups if g not in py_testgroups]
269 307 else:
270 308 py_testgroups = py_test_group_names
271 309 js_testgroups = all_js_groups()
272 310 if not options.all:
273 311 test_sections['parallel'].enabled = False
274 312
275 313 c_js = [JSController(name) for name in js_testgroups]
276 c_py = [PyTestController(name) for name in py_testgroups]
277
278 configure_py_controllers(c_py, xunit=options.xunit,
279 coverage=options.coverage, subproc_streams=options.subproc_streams,
280 extra_args=options.extra_args)
314 c_py = [PyTestController(name, options) for name in py_testgroups]
281 315
282 316 controllers = c_py + c_js
283 317 to_run = [c for c in controllers if c.will_run]
284 318 not_run = [c for c in controllers if not c.will_run]
285 319 return to_run, not_run
286 320
287 def configure_py_controllers(controllers, xunit=False, coverage=False,
288 subproc_streams='capture', extra_args=()):
289 """Apply options for a collection of TestController objects."""
290 for controller in controllers:
291 if xunit:
292 controller.add_xunit()
293 if coverage:
294 controller.add_coverage()
295 controller.env['IPTEST_SUBPROC_STREAMS'] = subproc_streams
296 controller.cmd.extend(extra_args)
297
298 321 def do_run(controller, buffer_output=True):
299 322 """Setup and run a test controller.
300 323
301 324 If buffer_output is True, no output is displayed, to avoid it appearing
302 325 interleaved. In this case, the caller is responsible for displaying test
303 326 output on failure.
304 327
305 328 Returns
306 329 -------
307 330 controller : TestController
308 331 The same controller as passed in, as a convenience for using map() type
309 332 APIs.
310 333 exitcode : int
311 334 The exit code of the test subprocess. Non-zero indicates failure.
312 335 """
313 336 try:
314 337 try:
315 338 controller.setup()
316 339 if not buffer_output:
317 340 controller.print_extra_info()
318 341 controller.launch(buffer_output=buffer_output)
319 342 except Exception:
320 343 import traceback
321 344 traceback.print_exc()
322 345 return controller, 1 # signal failure
323 346
324 347 exitcode = controller.wait()
325 348 return controller, exitcode
326 349
327 350 except KeyboardInterrupt:
328 351 return controller, -signal.SIGINT
329 352 finally:
330 353 controller.cleanup()
331 354
332 355 def report():
333 356 """Return a string with a summary report of test-related variables."""
334 357 inf = get_sys_info()
335 358 out = []
336 359 def _add(name, value):
337 360 out.append((name, value))
338 361
339 362 _add('IPython version', inf['ipython_version'])
340 363 _add('IPython commit', "{} ({})".format(inf['commit_hash'], inf['commit_source']))
341 364 _add('IPython package', compress_user(inf['ipython_path']))
342 365 _add('Python version', inf['sys_version'].replace('\n',''))
343 366 _add('sys.executable', compress_user(inf['sys_executable']))
344 367 _add('Platform', inf['platform'])
345 368
346 369 width = max(len(n) for (n,v) in out)
347 370 out = ["{:<{width}}: {}\n".format(n, v, width=width) for (n,v) in out]
348 371
349 372 avail = []
350 373 not_avail = []
351 374
352 375 for k, is_avail in have.items():
353 376 if is_avail:
354 377 avail.append(k)
355 378 else:
356 379 not_avail.append(k)
357 380
358 381 if avail:
359 382 out.append('\nTools and libraries available at test time:\n')
360 383 avail.sort()
361 384 out.append(' ' + ' '.join(avail)+'\n')
362 385
363 386 if not_avail:
364 387 out.append('\nTools and libraries NOT available at test time:\n')
365 388 not_avail.sort()
366 389 out.append(' ' + ' '.join(not_avail)+'\n')
367 390
368 391 return ''.join(out)
369 392
370 393 def run_iptestall(options):
371 394 """Run the entire IPython test suite by calling nose and trial.
372 395
373 396 This function constructs :class:`IPTester` instances for all IPython
374 397 modules and package and then runs each of them. This causes the modules
375 398 and packages of IPython to be tested each in their own subprocess using
376 399 nose.
377 400
378 401 Parameters
379 402 ----------
380 403
381 404 All parameters are passed as attributes of the options object.
382 405
383 406 testgroups : list of str
384 407 Run only these sections of the test suite. If empty, run all the available
385 408 sections.
386 409
387 410 fast : int or None
388 411 Run the test suite in parallel, using n simultaneous processes. If None
389 412 is passed, one process is used per CPU core. Default 1 (i.e. sequential)
390 413
391 414 inc_slow : bool
392 415 Include slow tests, like IPython.parallel. By default, these tests aren't
393 416 run.
394 417
395 418 xunit : bool
396 419 Produce Xunit XML output. This is written to multiple foo.xunit.xml files.
397 420
398 421 coverage : bool or str
399 422 Measure code coverage from tests. True will store the raw coverage data,
400 423 or pass 'html' or 'xml' to get reports.
401 424
402 425 extra_args : list
403 426 Extra arguments to pass to the test subprocesses, e.g. '-v'
404 427 """
405 428 to_run, not_run = prepare_controllers(options)
406 429
407 430 def justify(ltext, rtext, width=70, fill='-'):
408 431 ltext += ' '
409 432 rtext = (' ' + rtext).rjust(width - len(ltext), fill)
410 433 return ltext + rtext
411 434
412 435 # Run all test runners, tracking execution time
413 436 failed = []
414 437 t_start = time.time()
415 438
416 439 print()
417 440 if options.fast == 1:
418 441 # This actually means sequential, i.e. with 1 job
419 442 for controller in to_run:
420 443 print('Test group:', controller.section)
421 444 sys.stdout.flush() # Show in correct order when output is piped
422 445 controller, res = do_run(controller, buffer_output=False)
423 446 if res:
424 447 failed.append(controller)
425 448 if res == -signal.SIGINT:
426 449 print("Interrupted")
427 450 break
428 451 print()
429 452
430 453 else:
431 454 # Run tests concurrently
432 455 try:
433 456 pool = multiprocessing.pool.ThreadPool(options.fast)
434 457 for (controller, res) in pool.imap_unordered(do_run, to_run):
435 458 res_string = 'OK' if res == 0 else 'FAILED'
436 459 print(justify('Test group: ' + controller.section, res_string))
437 460 if res:
438 461 controller.print_extra_info()
439 462 print(bytes_to_str(controller.stdout))
440 463 failed.append(controller)
441 464 if res == -signal.SIGINT:
442 465 print("Interrupted")
443 466 break
444 467 except KeyboardInterrupt:
445 468 return
446 469
447 470 for controller in not_run:
448 471 print(justify('Test group: ' + controller.section, 'NOT RUN'))
449 472
450 473 t_end = time.time()
451 474 t_tests = t_end - t_start
452 475 nrunners = len(to_run)
453 476 nfail = len(failed)
454 477 # summarize results
455 478 print('_'*70)
456 479 print('Test suite completed for system with the following information:')
457 480 print(report())
458 481 took = "Took %.3fs." % t_tests
459 482 print('Status: ', end='')
460 483 if not failed:
461 484 print('OK (%d test groups).' % nrunners, took)
462 485 else:
463 486 # If anything went wrong, point out what command to rerun manually to
464 487 # see the actual errors and individual summary
465 488 failed_sections = [c.section for c in failed]
466 489 print('ERROR - {} out of {} test groups failed ({}).'.format(nfail,
467 490 nrunners, ', '.join(failed_sections)), took)
468 491 print()
469 492 print('You may wish to rerun these, with:')
470 493 print(' iptest', *failed_sections)
471 494 print()
472 495
473 496 if options.coverage:
474 497 from coverage import coverage
475 498 cov = coverage(data_file='.coverage')
476 499 cov.combine()
477 500 cov.save()
478 501
479 502 # Coverage HTML report
480 503 if options.coverage == 'html':
481 504 html_dir = 'ipy_htmlcov'
482 505 shutil.rmtree(html_dir, ignore_errors=True)
483 506 print("Writing HTML coverage report to %s/ ... " % html_dir, end="")
484 507 sys.stdout.flush()
485 508
486 509 # Custom HTML reporter to clean up module names.
487 510 from coverage.html import HtmlReporter
488 511 class CustomHtmlReporter(HtmlReporter):
489 512 def find_code_units(self, morfs):
490 513 super(CustomHtmlReporter, self).find_code_units(morfs)
491 514 for cu in self.code_units:
492 515 nameparts = cu.name.split(os.sep)
493 516 if 'IPython' not in nameparts:
494 517 continue
495 518 ix = nameparts.index('IPython')
496 519 cu.name = '.'.join(nameparts[ix:])
497 520
498 521 # Reimplement the html_report method with our custom reporter
499 522 cov._harvest_data()
500 523 cov.config.from_args(omit='*{0}tests{0}*'.format(os.sep), html_dir=html_dir,
501 524 html_title='IPython test coverage',
502 525 )
503 526 reporter = CustomHtmlReporter(cov, cov.config)
504 527 reporter.report(None)
505 528 print('done.')
506 529
507 530 # Coverage XML report
508 531 elif options.coverage == 'xml':
509 532 cov.xml_report(outfile='ipy_coverage.xml')
510 533
511 534 if failed:
512 535 # Ensure that our exit code indicates failure
513 536 sys.exit(1)
514 537
515 538 argparser = argparse.ArgumentParser(description='Run IPython test suite')
516 539 argparser.add_argument('testgroups', nargs='*',
517 540 help='Run specified groups of tests. If omitted, run '
518 541 'all tests.')
519 542 argparser.add_argument('--all', action='store_true',
520 543 help='Include slow tests not run by default.')
521 544 argparser.add_argument('-j', '--fast', nargs='?', const=None, default=1, type=int,
522 545 help='Run test sections in parallel. This starts as many '
523 546 'processes as you have cores, or you can specify a number.')
524 547 argparser.add_argument('--xunit', action='store_true',
525 548 help='Produce Xunit XML results')
526 549 argparser.add_argument('--coverage', nargs='?', const=True, default=False,
527 550 help="Measure test coverage. Specify 'html' or "
528 551 "'xml' to get reports.")
529 552 argparser.add_argument('--subproc-streams', default='capture',
530 553 help="What to do with stdout/stderr from subprocesses. "
531 554 "'capture' (default), 'show' and 'discard' are the options.")
532 555
533 556 def default_options():
534 557 """Get an argparse Namespace object with the default arguments, to pass to
535 558 :func:`run_iptestall`.
536 559 """
537 560 options = argparser.parse_args([])
538 561 options.extra_args = []
539 562 return options
540 563
541 564 def main():
542 565 # iptest doesn't work correctly if the working directory is the
543 566 # root of the IPython source tree. Tell the user to avoid
544 567 # frustration.
545 568 if os.path.exists(os.path.join(os.getcwd(),
546 569 'IPython', 'testing', '__main__.py')):
547 570 print("Don't run iptest from the IPython source directory",
548 571 file=sys.stderr)
549 572 sys.exit(1)
550 573 # Arguments after -- should be passed through to nose. Argparse treats
551 574 # everything after -- as regular positional arguments, so we separate them
552 575 # first.
553 576 try:
554 577 ix = sys.argv.index('--')
555 578 except ValueError:
556 579 to_parse = sys.argv[1:]
557 580 extra_args = []
558 581 else:
559 582 to_parse = sys.argv[1:ix]
560 583 extra_args = sys.argv[ix+1:]
561 584
562 585 options = argparser.parse_args(to_parse)
563 586 options.extra_args = extra_args
564 587
565 588 run_iptestall(options)
566 589
567 590
568 591 if __name__ == '__main__':
569 592 main()
@@ -1,1163 +1,1093 b''
1 1 =================
2 2 IPython reference
3 3 =================
4 4
5 5 .. _command_line_options:
6 6
7 7 Command-line usage
8 8 ==================
9 9
10 10 You start IPython with the command::
11 11
12 12 $ ipython [options] files
13 13
14 14 If invoked with no options, it executes all the files listed in sequence
15 15 and drops you into the interpreter while still acknowledging any options
16 16 you may have set in your ipython_config.py. This behavior is different from
17 17 standard Python, which when called as python -i will only execute one
18 18 file and ignore your configuration setup.
19 19
20 20 Please note that some of the configuration options are not available at
21 21 the command line, simply because they are not practical here. Look into
22 22 your configuration files for details on those. There are separate configuration
23 23 files for each profile, and the files look like :file:`ipython_config.py` or
24 24 :file:`ipython_config_{frontendname}.py`. Profile directories look like
25 25 :file:`profile_{profilename}` and are typically installed in the :envvar:`IPYTHONDIR` directory,
26 26 which defaults to :file:`$HOME/.ipython`. For Windows users, :envvar:`HOME`
27 27 resolves to :file:`C:\\Users\\{YourUserName}` in most instances.
28 28
29
30 Eventloop integration
31 ---------------------
32
33 Previously IPython had command line options for controlling GUI event loop
34 integration (-gthread, -qthread, -q4thread, -wthread, -pylab). As of IPython
35 version 0.11, these have been removed. Please see the new ``%gui``
36 magic command or :ref:`this section <gui_support>` for details on the new
37 interface, or specify the gui at the commandline::
38
39 $ ipython --gui=qt
40
41
42 29 Command-line Options
43 30 --------------------
44 31
45 32 To see the options IPython accepts, use ``ipython --help`` (and you probably
46 33 should run the output through a pager such as ``ipython --help | less`` for
47 34 more convenient reading). This shows all the options that have a single-word
48 35 alias to control them, but IPython lets you configure all of its objects from
49 36 the command-line by passing the full class name and a corresponding value; type
50 37 ``ipython --help-all`` to see this full list. For example::
51 38
52 39 ipython --matplotlib qt
53 40
54 41 is equivalent to::
55 42
56 43 ipython --TerminalIPythonApp.matplotlib='qt'
57 44
58 45 Note that in the second form, you *must* use the equal sign, as the expression
59 46 is evaluated as an actual Python assignment. While in the above example the
60 47 short form is more convenient, only the most common options have a short form,
61 48 while any configurable variable in IPython can be set at the command-line by
62 49 using the long form. This long form is the same syntax used in the
63 50 configuration files, if you want to set these options permanently.
64 51
65 52
66 53 Interactive use
67 54 ===============
68 55
69 56 IPython is meant to work as a drop-in replacement for the standard interactive
70 57 interpreter. As such, any code which is valid python should execute normally
71 58 under IPython (cases where this is not true should be reported as bugs). It
72 59 does, however, offer many features which are not available at a standard python
73 60 prompt. What follows is a list of these.
74 61
75 62
76 63 Caution for Windows users
77 64 -------------------------
78 65
79 66 Windows, unfortunately, uses the '\\' character as a path separator. This is a
80 67 terrible choice, because '\\' also represents the escape character in most
81 68 modern programming languages, including Python. For this reason, using '/'
82 69 character is recommended if you have problems with ``\``. However, in Windows
83 70 commands '/' flags options, so you can not use it for the root directory. This
84 71 means that paths beginning at the root must be typed in a contrived manner
85 72 like: ``%copy \opt/foo/bar.txt \tmp``
86 73
87 74 .. _magic:
88 75
89 76 Magic command system
90 77 --------------------
91 78
92 79 IPython will treat any line whose first character is a % as a special
93 80 call to a 'magic' function. These allow you to control the behavior of
94 81 IPython itself, plus a lot of system-type features. They are all
95 82 prefixed with a % character, but parameters are given without
96 83 parentheses or quotes.
97 84
98 85 Lines that begin with ``%%`` signal a *cell magic*: they take as arguments not
99 86 only the rest of the current line, but all lines below them as well, in the
100 87 current execution block. Cell magics can in fact make arbitrary modifications
101 88 to the input they receive, which need not even be valid Python code at all.
102 89 They receive the whole block as a single string.
103 90
104 91 As a line magic example, the ``%cd`` magic works just like the OS command of
105 92 the same name::
106 93
107 94 In [8]: %cd
108 95 /home/fperez
109 96
110 97 The following uses the builtin ``timeit`` in cell mode::
111 98
112 99 In [10]: %%timeit x = range(10000)
113 100 ...: min(x)
114 101 ...: max(x)
115 102 ...:
116 103 1000 loops, best of 3: 438 us per loop
117 104
118 105 In this case, ``x = range(10000)`` is called as the line argument, and the
119 106 block with ``min(x)`` and ``max(x)`` is called as the cell body. The
120 107 ``timeit`` magic receives both.
121 108
122 109 If you have 'automagic' enabled (as it by default), you don't need to type in
123 110 the single ``%`` explicitly for line magics; IPython will scan its internal
124 111 list of magic functions and call one if it exists. With automagic on you can
125 112 then just type ``cd mydir`` to go to directory 'mydir'::
126 113
127 114 In [9]: cd mydir
128 115 /home/fperez/mydir
129 116
130 117 Note that cell magics *always* require an explicit ``%%`` prefix, automagic
131 118 calling only works for line magics.
132 119
133 120 The automagic system has the lowest possible precedence in name searches, so
134 defining an identifier with the same name as an existing magic function will
135 shadow it for automagic use. You can still access the shadowed magic function
136 by explicitly using the ``%`` character at the beginning of the line.
137
138 An example (with automagic on) should clarify all this:
121 you can freely use variables with the same names as magic commands. If a magic
122 command is 'shadowed' by a variable, you will need the explicit ``%`` prefix to
123 use it:
139 124
140 125 .. sourcecode:: ipython
141 126
142 127 In [1]: cd ipython # %cd is called by automagic
143 128 /home/fperez/ipython
144 129
145 130 In [2]: cd=1 # now cd is just a variable
146 131
147 132 In [3]: cd .. # and doesn't work as a function anymore
148 133 File "<ipython-input-3-9fedb3aff56c>", line 1
149 134 cd ..
150 135 ^
151 136 SyntaxError: invalid syntax
152 137
153 138
154 139 In [4]: %cd .. # but %cd always works
155 140 /home/fperez
156 141
157 142 In [5]: del cd # if you remove the cd variable, automagic works again
158 143
159 144 In [6]: cd ipython
160 145
161 146 /home/fperez/ipython
162 147
163 148 .. _defining_magics:
164 149
165 150 Defining your own magics
166 151 ++++++++++++++++++++++++
167 152
168 153 There are two main ways to define your own magic functions: from standalone
169 154 functions and by inheriting from a base class provided by IPython:
170 155 :class:`IPython.core.magic.Magics`. Below we show code you can place in a file
171 156 that you load from your configuration, such as any file in the ``startup``
172 157 subdirectory of your default IPython profile.
173 158
174 159 First, let us see the simplest case. The following shows how to create a line
175 160 magic, a cell one and one that works in both modes, using just plain functions:
176 161
177 162 .. sourcecode:: python
178 163
179 164 from IPython.core.magic import (register_line_magic, register_cell_magic,
180 165 register_line_cell_magic)
181 166
182 167 @register_line_magic
183 168 def lmagic(line):
184 169 "my line magic"
185 170 return line
186 171
187 172 @register_cell_magic
188 173 def cmagic(line, cell):
189 174 "my cell magic"
190 175 return line, cell
191 176
192 177 @register_line_cell_magic
193 178 def lcmagic(line, cell=None):
194 179 "Magic that works both as %lcmagic and as %%lcmagic"
195 180 if cell is None:
196 181 print("Called as line magic")
197 182 return line
198 183 else:
199 184 print("Called as cell magic")
200 185 return line, cell
201 186
202 187 # We delete these to avoid name conflicts for automagic to work
203 188 del lmagic, lcmagic
204 189
205 190
206 191 You can also create magics of all three kinds by inheriting from the
207 192 :class:`IPython.core.magic.Magics` class. This lets you create magics that can
208 193 potentially hold state in between calls, and that have full access to the main
209 194 IPython object:
210 195
211 196 .. sourcecode:: python
212 197
213 198 # This code can be put in any Python module, it does not require IPython
214 199 # itself to be running already. It only creates the magics subclass but
215 200 # doesn't instantiate it yet.
216 201 from __future__ import print_function
217 202 from IPython.core.magic import (Magics, magics_class, line_magic,
218 203 cell_magic, line_cell_magic)
219 204
220 205 # The class MUST call this class decorator at creation time
221 206 @magics_class
222 207 class MyMagics(Magics):
223 208
224 209 @line_magic
225 210 def lmagic(self, line):
226 211 "my line magic"
227 212 print("Full access to the main IPython object:", self.shell)
228 213 print("Variables in the user namespace:", list(self.shell.user_ns.keys()))
229 214 return line
230 215
231 216 @cell_magic
232 217 def cmagic(self, line, cell):
233 218 "my cell magic"
234 219 return line, cell
235 220
236 221 @line_cell_magic
237 222 def lcmagic(self, line, cell=None):
238 223 "Magic that works both as %lcmagic and as %%lcmagic"
239 224 if cell is None:
240 225 print("Called as line magic")
241 226 return line
242 227 else:
243 228 print("Called as cell magic")
244 229 return line, cell
245 230
246 231
247 232 # In order to actually use these magics, you must register them with a
248 233 # running IPython. This code must be placed in a file that is loaded once
249 234 # IPython is up and running:
250 235 ip = get_ipython()
251 236 # You can register the class itself without instantiating it. IPython will
252 237 # call the default constructor on it.
253 238 ip.register_magics(MyMagics)
254 239
255 240 If you want to create a class with a different constructor that holds
256 241 additional state, then you should always call the parent constructor and
257 242 instantiate the class yourself before registration:
258 243
259 244 .. sourcecode:: python
260 245
261 246 @magics_class
262 247 class StatefulMagics(Magics):
263 248 "Magics that hold additional state"
264 249
265 250 def __init__(self, shell, data):
266 251 # You must call the parent constructor
267 252 super(StatefulMagics, self).__init__(shell)
268 253 self.data = data
269 254
270 255 # etc...
271 256
272 257 # This class must then be registered with a manually created instance,
273 258 # since its constructor has different arguments from the default:
274 259 ip = get_ipython()
275 260 magics = StatefulMagics(ip, some_data)
276 261 ip.register_magics(magics)
277 262
278 263
279 264 In earlier versions, IPython had an API for the creation of line magics (cell
280 265 magics did not exist at the time) that required you to create functions with a
281 266 method-looking signature and to manually pass both the function and the name.
282 267 While this API is no longer recommended, it remains indefinitely supported for
283 268 backwards compatibility purposes. With the old API, you'd create a magic as
284 269 follows:
285 270
286 271 .. sourcecode:: python
287 272
288 273 def func(self, line):
289 274 print("Line magic called with line:", line)
290 275 print("IPython object:", self.shell)
291 276
292 277 ip = get_ipython()
293 278 # Declare this function as the magic %mycommand
294 279 ip.define_magic('mycommand', func)
295 280
296 281 Type ``%magic`` for more information, including a list of all available magic
297 282 functions at any time and their docstrings. You can also type
298 283 ``%magic_function_name?`` (see :ref:`below <dynamic_object_info>` for
299 284 information on the '?' system) to get information about any particular magic
300 285 function you are interested in.
301 286
302 287 The API documentation for the :mod:`IPython.core.magic` module contains the full
303 288 docstrings of all currently available magic commands.
304 289
305 290
306 291 Access to the standard Python help
307 292 ----------------------------------
308 293
309 294 Simply type ``help()`` to access Python's standard help system. You can
310 295 also type ``help(object)`` for information about a given object, or
311 296 ``help('keyword')`` for information on a keyword. You may need to configure your
312 297 PYTHONDOCS environment variable for this feature to work correctly.
313 298
314 299 .. _dynamic_object_info:
315 300
316 301 Dynamic object information
317 302 --------------------------
318 303
319 304 Typing ``?word`` or ``word?`` prints detailed information about an object. If
320 305 certain strings in the object are too long (e.g. function signatures) they get
321 306 snipped in the center for brevity. This system gives access variable types and
322 307 values, docstrings, function prototypes and other useful information.
323 308
324 309 If the information will not fit in the terminal, it is displayed in a pager
325 310 (``less`` if available, otherwise a basic internal pager).
326 311
327 312 Typing ``??word`` or ``word??`` gives access to the full information, including
328 313 the source code where possible. Long strings are not snipped.
329 314
330 315 The following magic functions are particularly useful for gathering
331 316 information about your working environment. You can get more details by
332 317 typing ``%magic`` or querying them individually (``%function_name?``);
333 318 this is just a summary:
334 319
335 320 * **%pdoc <object>**: Print (or run through a pager if too long) the
336 321 docstring for an object. If the given object is a class, it will
337 322 print both the class and the constructor docstrings.
338 323 * **%pdef <object>**: Print the call signature for any callable
339 324 object. If the object is a class, print the constructor information.
340 325 * **%psource <object>**: Print (or run through a pager if too long)
341 326 the source code for an object.
342 327 * **%pfile <object>**: Show the entire source file where an object was
343 328 defined via a pager, opening it at the line where the object
344 329 definition begins.
345 330 * **%who/%whos**: These functions give information about identifiers
346 331 you have defined interactively (not things you loaded or defined
347 332 in your configuration files). %who just prints a list of
348 333 identifiers and %whos prints a table with some basic details about
349 334 each identifier.
350 335
351 336 Note that the dynamic object information functions (?/??, ``%pdoc``,
352 337 ``%pfile``, ``%pdef``, ``%psource``) work on object attributes, as well as
353 338 directly on variables. For example, after doing ``import os``, you can use
354 339 ``os.path.abspath??``.
355 340
356 341 .. _readline:
357 342
358 343 Readline-based features
359 344 -----------------------
360 345
361 346 These features require the GNU readline library, so they won't work if your
362 347 Python installation lacks readline support. We will first describe the default
363 348 behavior IPython uses, and then how to change it to suit your preferences.
364 349
365 350
366 351 Command line completion
367 352 +++++++++++++++++++++++
368 353
369 354 At any time, hitting TAB will complete any available python commands or
370 355 variable names, and show you a list of the possible completions if
371 356 there's no unambiguous one. It will also complete filenames in the
372 357 current directory if no python names match what you've typed so far.
373 358
374 359
375 360 Search command history
376 361 ++++++++++++++++++++++
377 362
378 363 IPython provides two ways for searching through previous input and thus
379 364 reduce the need for repetitive typing:
380 365
381 1. Start typing, and then use Ctrl-p (previous,up) and Ctrl-n
382 (next,down) to search through only the history items that match
383 what you've typed so far. If you use Ctrl-p/Ctrl-n at a blank
384 prompt, they just behave like normal arrow keys.
385 2. Hit Ctrl-r: opens a search prompt. Begin typing and the system
366 1. Start typing, and then use the up and down arrow keys (or :kbd:`Ctrl-p`
367 and :kbd:`Ctrl-n`) to search through only the history items that match
368 what you've typed so far.
369 2. Hit :kbd:`Ctrl-r`: to open a search prompt. Begin typing and the system
386 370 searches your history for lines that contain what you've typed so
387 371 far, completing as much as it can.
388 372
389
390 Persistent command history across sessions
391 ++++++++++++++++++++++++++++++++++++++++++
392
393 373 IPython will save your input history when it leaves and reload it next
394 374 time you restart it. By default, the history file is named
395 $IPYTHONDIR/profile_<name>/history.sqlite. This allows you to keep
396 separate histories related to various tasks: commands related to
397 numerical work will not be clobbered by a system shell history, for
398 example.
399
375 :file:`.ipython/profile_{name}/history.sqlite`.
400 376
401 377 Autoindent
402 378 ++++++++++
403 379
404 380 IPython can recognize lines ending in ':' and indent the next line,
405 381 while also un-indenting automatically after 'raise' or 'return'.
406 382
407 383 This feature uses the readline library, so it will honor your
408 :file:`~/.inputrc` configuration (or whatever file your INPUTRC variable points
384 :file:`~/.inputrc` configuration (or whatever file your :envvar:`INPUTRC` environment variable points
409 385 to). Adding the following lines to your :file:`.inputrc` file can make
410 386 indenting/unindenting more convenient (M-i indents, M-u unindents)::
411 387
412 388 # if you don't already have a ~/.inputrc file, you need this include:
413 389 $include /etc/inputrc
414 390
415 391 $if Python
416 392 "\M-i": " "
417 393 "\M-u": "\d\d\d\d"
418 394 $endif
419 395
420 396 Note that there are 4 spaces between the quote marks after "M-i" above.
421 397
422 398 .. warning::
423 399
424 400 Setting the above indents will cause problems with unicode text entry in
425 401 the terminal.
426 402
427 403 .. warning::
428 404
429 405 Autoindent is ON by default, but it can cause problems with the pasting of
430 406 multi-line indented code (the pasted code gets re-indented on each line). A
431 407 magic function %autoindent allows you to toggle it on/off at runtime. You
432 408 can also disable it permanently on in your :file:`ipython_config.py` file
433 409 (set TerminalInteractiveShell.autoindent=False).
434 410
435 411 If you want to paste multiple lines in the terminal, it is recommended that
436 412 you use ``%paste``.
437 413
438 414
439 415 Customizing readline behavior
440 416 +++++++++++++++++++++++++++++
441 417
442 418 All these features are based on the GNU readline library, which has an
443 419 extremely customizable interface. Normally, readline is configured via a
444 file which defines the behavior of the library; the details of the
445 syntax for this can be found in the readline documentation available
446 with your system or on the Internet. IPython doesn't read this file (if
447 it exists) directly, but it does support passing to readline valid
448 options via a simple interface. In brief, you can customize readline by
449 setting the following options in your configuration file (note
450 that these options can not be specified at the command line):
451
452 * **readline_parse_and_bind**: this holds a list of strings to be executed
420 :file:`.inputrc` file. IPython respects this, and you can also customise readline
421 by setting the following :doc:`configuration </config/intro>` options:
422
423 * ``InteractiveShell.readline_parse_and_bind``: this holds a list of strings to be executed
453 424 via a readline.parse_and_bind() command. The syntax for valid commands
454 425 of this kind can be found by reading the documentation for the GNU
455 426 readline library, as these commands are of the kind which readline
456 427 accepts in its configuration file.
457 * **readline_remove_delims**: a string of characters to be removed
428 * ``InteractiveShell.readline_remove_delims``: a string of characters to be removed
458 429 from the default word-delimiters list used by readline, so that
459 430 completions may be performed on strings which contain them. Do not
460 431 change the default value unless you know what you're doing.
461 432
462 433 You will find the default values in your configuration file.
463 434
464 435
465 436 Session logging and restoring
466 437 -----------------------------
467 438
468 439 You can log all input from a session either by starting IPython with the
469 440 command line switch ``--logfile=foo.py`` (see :ref:`here <command_line_options>`)
470 441 or by activating the logging at any moment with the magic function %logstart.
471 442
472 443 Log files can later be reloaded by running them as scripts and IPython
473 444 will attempt to 'replay' the log by executing all the lines in it, thus
474 445 restoring the state of a previous session. This feature is not quite
475 446 perfect, but can still be useful in many cases.
476 447
477 448 The log files can also be used as a way to have a permanent record of
478 449 any code you wrote while experimenting. Log files are regular text files
479 450 which you can later open in your favorite text editor to extract code or
480 451 to 'clean them up' before using them to replay a session.
481 452
482 453 The `%logstart` function for activating logging in mid-session is used as
483 454 follows::
484 455
485 456 %logstart [log_name [log_mode]]
486 457
487 458 If no name is given, it defaults to a file named 'ipython_log.py' in your
488 459 current working directory, in 'rotate' mode (see below).
489 460
490 461 '%logstart name' saves to file 'name' in 'backup' mode. It saves your
491 462 history up to that point and then continues logging.
492 463
493 464 %logstart takes a second optional parameter: logging mode. This can be
494 465 one of (note that the modes are given unquoted):
495 466
496 467 * [over:] overwrite existing log_name.
497 468 * [backup:] rename (if exists) to log_name~ and start log_name.
498 469 * [append:] well, that says it.
499 470 * [rotate:] create rotating logs log_name.1~, log_name.2~, etc.
500 471
501 472 The %logoff and %logon functions allow you to temporarily stop and
502 473 resume logging to a file which had previously been started with
503 474 %logstart. They will fail (with an explanation) if you try to use them
504 475 before logging has been started.
505 476
506 477 .. _system_shell_access:
507 478
508 479 System shell access
509 480 -------------------
510 481
511 482 Any input line beginning with a ! character is passed verbatim (minus
512 483 the !, of course) to the underlying operating system. For example,
513 484 typing ``!ls`` will run 'ls' in the current directory.
514 485
515 486 Manual capture of command output
516 487 --------------------------------
517 488
518 489 You can assign the result of a system command to a Python variable with the
519 490 syntax ``myfiles = !ls``. This gets machine readable output from stdout
520 491 (e.g. without colours), and splits on newlines. To explicitly get this sort of
521 492 output without assigning to a variable, use two exclamation marks (``!!ls``) or
522 493 the ``%sx`` magic command.
523 494
524 495 The captured list has some convenience features. ``myfiles.n`` or ``myfiles.s``
525 496 returns a string delimited by newlines or spaces, respectively. ``myfiles.p``
526 497 produces `path objects <http://pypi.python.org/pypi/path.py>`_ from the list items.
527 498 See :ref:`string_lists` for details.
528 499
529 500 IPython also allows you to expand the value of python variables when
530 501 making system calls. Wrap variables or expressions in {braces}::
531 502
532 503 In [1]: pyvar = 'Hello world'
533 504 In [2]: !echo "A python variable: {pyvar}"
534 505 A python variable: Hello world
535 506 In [3]: import math
536 507 In [4]: x = 8
537 508 In [5]: !echo {math.factorial(x)}
538 509 40320
539 510
540 511 For simple cases, you can alternatively prepend $ to a variable name::
541 512
542 513 In [6]: !echo $sys.argv
543 514 [/home/fperez/usr/bin/ipython]
544 515 In [7]: !echo "A system variable: $$HOME" # Use $$ for literal $
545 516 A system variable: /home/fperez
546 517
547 518 System command aliases
548 519 ----------------------
549 520
550 521 The %alias magic function allows you to define magic functions which are in fact
551 522 system shell commands. These aliases can have parameters.
552 523
553 524 ``%alias alias_name cmd`` defines 'alias_name' as an alias for 'cmd'
554 525
555 526 Then, typing ``alias_name params`` will execute the system command 'cmd
556 527 params' (from your underlying operating system).
557 528
558 529 You can also define aliases with parameters using %s specifiers (one per
559 530 parameter). The following example defines the parts function as an
560 531 alias to the command 'echo first %s second %s' where each %s will be
561 532 replaced by a positional parameter to the call to %parts::
562 533
563 534 In [1]: %alias parts echo first %s second %s
564 535 In [2]: parts A B
565 536 first A second B
566 537 In [3]: parts A
567 538 ERROR: Alias <parts> requires 2 arguments, 1 given.
568 539
569 540 If called with no parameters, %alias prints the table of currently
570 541 defined aliases.
571 542
572 543 The %rehashx magic allows you to load your entire $PATH as
573 544 ipython aliases. See its docstring for further details.
574 545
575 546
576 547 .. _dreload:
577 548
578 549 Recursive reload
579 550 ----------------
580 551
581 552 The :mod:`IPython.lib.deepreload` module allows you to recursively reload a
582 553 module: changes made to any of its dependencies will be reloaded without
583 554 having to exit. To start using it, do::
584 555
585 556 from IPython.lib.deepreload import reload as dreload
586 557
587 558
588 559 Verbose and colored exception traceback printouts
589 560 -------------------------------------------------
590 561
591 562 IPython provides the option to see very detailed exception tracebacks,
592 563 which can be especially useful when debugging large programs. You can
593 564 run any Python file with the %run function to benefit from these
594 565 detailed tracebacks. Furthermore, both normal and verbose tracebacks can
595 566 be colored (if your terminal supports it) which makes them much easier
596 567 to parse visually.
597 568
598 See the magic xmode and colors functions for details (just type %magic).
569 See the magic xmode and colors functions for details.
599 570
600 571 These features are basically a terminal version of Ka-Ping Yee's cgitb
601 572 module, now part of the standard Python library.
602 573
603 574
604 575 .. _input_caching:
605 576
606 577 Input caching system
607 578 --------------------
608 579
609 580 IPython offers numbered prompts (In/Out) with input and output caching
610 581 (also referred to as 'input history'). All input is saved and can be
611 582 retrieved as variables (besides the usual arrow key recall), in
612 583 addition to the %rep magic command that brings a history entry
613 584 up for editing on the next command line.
614 585
615 The following GLOBAL variables always exist (so don't overwrite them!):
586 The following variables always exist:
616 587
617 588 * _i, _ii, _iii: store previous, next previous and next-next previous inputs.
618 589 * In, _ih : a list of all inputs; _ih[n] is the input from line n. If you
619 590 overwrite In with a variable of your own, you can remake the assignment to the
620 591 internal list with a simple ``In=_ih``.
621 592
622 593 Additionally, global variables named _i<n> are dynamically created (<n>
623 594 being the prompt counter), so ``_i<n> == _ih[<n>] == In[<n>]``.
624 595
625 For example, what you typed at prompt 14 is available as _i14, _ih[14]
626 and In[14].
596 For example, what you typed at prompt 14 is available as ``_i14``, ``_ih[14]``
597 and ``In[14]``.
627 598
628 599 This allows you to easily cut and paste multi line interactive prompts
629 600 by printing them out: they print like a clean string, without prompt
630 601 characters. You can also manipulate them like regular variables (they
631 are strings), modify or exec them (typing ``exec _i9`` will re-execute the
632 contents of input prompt 9.
602 are strings), modify or exec them.
633 603
634 604 You can also re-execute multiple lines of input easily by using the
635 605 magic %rerun or %macro functions. The macro system also allows you to re-execute
636 606 previous lines which include magic function calls (which require special
637 607 processing). Type %macro? for more details on the macro system.
638 608
639 609 A history function %hist allows you to see any part of your input
640 610 history by printing a range of the _i variables.
641 611
642 612 You can also search ('grep') through your history by typing
643 613 ``%hist -g somestring``. This is handy for searching for URLs, IP addresses,
644 614 etc. You can bring history entries listed by '%hist -g' up for editing
645 615 with the %recall command, or run them immediately with %rerun.
646 616
647 617 .. _output_caching:
648 618
649 619 Output caching system
650 620 ---------------------
651 621
652 622 For output that is returned from actions, a system similar to the input
653 623 cache exists but using _ instead of _i. Only actions that produce a
654 624 result (NOT assignments, for example) are cached. If you are familiar
655 625 with Mathematica, IPython's _ variables behave exactly like
656 626 Mathematica's % variables.
657 627
658 The following GLOBAL variables always exist (so don't overwrite them!):
628 The following variables always exist:
659 629
660 * [_] (a single underscore) : stores previous output, like Python's
630 * [_] (a single underscore): stores previous output, like Python's
661 631 default interpreter.
662 632 * [__] (two underscores): next previous.
663 633 * [___] (three underscores): next-next previous.
664 634
665 635 Additionally, global variables named _<n> are dynamically created (<n>
666 636 being the prompt counter), such that the result of output <n> is always
667 637 available as _<n> (don't use the angle brackets, just the number, e.g.
668 _21).
638 ``_21``).
669 639
670 640 These variables are also stored in a global dictionary (not a
671 641 list, since it only has entries for lines which returned a result)
672 642 available under the names _oh and Out (similar to _ih and In). So the
673 output from line 12 can be obtained as _12, Out[12] or _oh[12]. If you
643 output from line 12 can be obtained as ``_12``, ``Out[12]`` or ``_oh[12]``. If you
674 644 accidentally overwrite the Out variable you can recover it by typing
675 'Out=_oh' at the prompt.
645 ``Out=_oh`` at the prompt.
676 646
677 647 This system obviously can potentially put heavy memory demands on your
678 648 system, since it prevents Python's garbage collector from removing any
679 649 previously computed results. You can control how many results are kept
680 in memory with the option (at the command line or in your configuration
681 file) cache_size. If you set it to 0, the whole system is completely
682 disabled and the prompts revert to the classic '>>>' of normal Python.
683
650 in memory with the configuration option ``InteractiveShell.cache_size``.
651 If you set it to 0, output caching is disabled. You can also use the ``%reset``
652 and ``%xdel`` magics to clear large items from memory.
684 653
685 654 Directory history
686 655 -----------------
687 656
688 657 Your history of visited directories is kept in the global list _dh, and
689 658 the magic %cd command can be used to go to any entry in that list. The
690 659 %dhist command allows you to view this history. Do ``cd -<TAB>`` to
691 660 conveniently view the directory history.
692 661
693 662
694 663 Automatic parentheses and quotes
695 664 --------------------------------
696 665
697 666 These features were adapted from Nathan Gray's LazyPython. They are
698 667 meant to allow less typing for common situations.
699 668
700
701 Automatic parentheses
702 +++++++++++++++++++++
703
704 669 Callable objects (i.e. functions, methods, etc) can be invoked like this
705 670 (notice the commas between the arguments)::
706 671
707 672 In [1]: callable_ob arg1, arg2, arg3
708 673 ------> callable_ob(arg1, arg2, arg3)
709 674
675 .. note::
676 This feature is disabled by default. To enable it, use the ``%autocall``
677 magic command. The commands below with special prefixes will always work,
678 however.
679
710 680 You can force automatic parentheses by using '/' as the first character
711 681 of a line. For example::
712 682
713 683 In [2]: /globals # becomes 'globals()'
714 684
715 685 Note that the '/' MUST be the first character on the line! This won't work::
716 686
717 687 In [3]: print /globals # syntax error
718 688
719 689 In most cases the automatic algorithm should work, so you should rarely
720 690 need to explicitly invoke /. One notable exception is if you are trying
721 691 to call a function with a list of tuples as arguments (the parenthesis
722 692 will confuse IPython)::
723 693
724 694 In [4]: zip (1,2,3),(4,5,6) # won't work
725 695
726 696 but this will work::
727 697
728 698 In [5]: /zip (1,2,3),(4,5,6)
729 699 ------> zip ((1,2,3),(4,5,6))
730 700 Out[5]: [(1, 4), (2, 5), (3, 6)]
731 701
732 702 IPython tells you that it has altered your command line by displaying
733 the new command line preceded by ->. e.g.::
734
735 In [6]: callable list
736 ------> callable(list)
703 the new command line preceded by ``--->``.
737 704
738
739 Automatic quoting
740 +++++++++++++++++
741
742 You can force automatic quoting of a function's arguments by using ','
743 or ';' as the first character of a line. For example::
705 You can force automatic quoting of a function's arguments by using ``,``
706 or ``;`` as the first character of a line. For example::
744 707
745 708 In [1]: ,my_function /home/me # becomes my_function("/home/me")
746 709
747 710 If you use ';' the whole argument is quoted as a single string, while ',' splits
748 711 on whitespace::
749 712
750 713 In [2]: ,my_function a b c # becomes my_function("a","b","c")
751 714
752 715 In [3]: ;my_function a b c # becomes my_function("a b c")
753 716
754 717 Note that the ',' or ';' MUST be the first character on the line! This
755 718 won't work::
756 719
757 720 In [4]: x = ,my_function /home/me # syntax error
758 721
759 722 IPython as your default Python environment
760 723 ==========================================
761 724
762 725 Python honors the environment variable :envvar:`PYTHONSTARTUP` and will
763 726 execute at startup the file referenced by this variable. If you put the
764 727 following code at the end of that file, then IPython will be your working
765 728 environment anytime you start Python::
766 729
767 730 import os, IPython
768 731 os.environ['PYTHONSTARTUP'] = '' # Prevent running this again
769 732 IPython.start_ipython()
770 733 raise SystemExit
771 734
772 735 The ``raise SystemExit`` is needed to exit Python when
773 it finishes, otherwise you'll be back at the normal Python '>>>'
736 it finishes, otherwise you'll be back at the normal Python ``>>>``
774 737 prompt.
775 738
776 739 This is probably useful to developers who manage multiple Python
777 740 versions and don't want to have correspondingly multiple IPython
778 741 versions. Note that in this mode, there is no way to pass IPython any
779 742 command-line options, as those are trapped first by Python itself.
780 743
781 744 .. _Embedding:
782 745
783 746 Embedding IPython
784 747 =================
785 748
786 749 You can start a regular IPython session with
787 750
788 751 .. sourcecode:: python
789 752
790 753 import IPython
791 IPython.start_ipython()
754 IPython.start_ipython(argv=[])
792 755
793 756 at any point in your program. This will load IPython configuration,
794 757 startup files, and everything, just as if it were a normal IPython session.
795 In addition to this,
796 it is possible to embed an IPython instance inside your own Python programs.
758
759 It is also possible to embed an IPython shell in a namespace in your Python code.
797 760 This allows you to evaluate dynamically the state of your code,
798 761 operate with your variables, analyze them, etc. Note however that
799 762 any changes you make to values while in the shell do not propagate back
800 763 to the running code, so it is safe to modify your values because you
801 764 won't break your code in bizarre ways by doing so.
802 765
803 766 .. note::
804 767
805 768 At present, embedding IPython cannot be done from inside IPython.
806 769 Run the code samples below outside IPython.
807 770
808 771 This feature allows you to easily have a fully functional python
809 772 environment for doing object introspection anywhere in your code with a
810 773 simple function call. In some cases a simple print statement is enough,
811 774 but if you need to do more detailed analysis of a code fragment this
812 775 feature can be very valuable.
813 776
814 777 It can also be useful in scientific computing situations where it is
815 778 common to need to do some automatic, computationally intensive part and
816 779 then stop to look at data, plots, etc.
817 780 Opening an IPython instance will give you full access to your data and
818 781 functions, and you can resume program execution once you are done with
819 782 the interactive part (perhaps to stop again later, as many times as
820 783 needed).
821 784
822 785 The following code snippet is the bare minimum you need to include in
823 786 your Python programs for this to work (detailed examples follow later)::
824 787
825 788 from IPython import embed
826 789
827 790 embed() # this call anywhere in your program will start IPython
828 791
829 .. note::
830
831 As of 0.13, you can embed an IPython *kernel*, for use with qtconsole,
832 etc. via ``IPython.embed_kernel()`` instead of ``IPython.embed()``.
833 It should function just the same as regular embed, but you connect
834 an external frontend rather than IPython starting up in the local
835 terminal.
792 You can also embed an IPython *kernel*, for use with qtconsole, etc. via
793 ``IPython.embed_kernel()``. This should function work the same way, but you can
794 connect an external frontend (``ipython qtconsole`` or ``ipython console``),
795 rather than interacting with it in the terminal.
836 796
837 797 You can run embedded instances even in code which is itself being run at
838 798 the IPython interactive prompt with '%run <filename>'. Since it's easy
839 799 to get lost as to where you are (in your top-level IPython or in your
840 800 embedded one), it's a good idea in such cases to set the in/out prompts
841 801 to something different for the embedded instances. The code examples
842 802 below illustrate this.
843 803
844 804 You can also have multiple IPython instances in your program and open
845 805 them separately, for example with different options for data
846 806 presentation. If you close and open the same instance multiple times,
847 807 its prompt counters simply continue from each execution to the next.
848 808
849 809 Please look at the docstrings in the :mod:`~IPython.frontend.terminal.embed`
850 810 module for more details on the use of this system.
851 811
852 812 The following sample file illustrating how to use the embedding
853 813 functionality is provided in the examples directory as example-embed.py.
854 814 It should be fairly self-explanatory:
855 815
856 816 .. literalinclude:: ../../../examples/core/example-embed.py
857 817 :language: python
858 818
859 819 Once you understand how the system functions, you can use the following
860 820 code fragments in your programs which are ready for cut and paste:
861 821
862 822 .. literalinclude:: ../../../examples/core/example-embed-short.py
863 823 :language: python
864 824
865 825 Using the Python debugger (pdb)
866 826 ===============================
867 827
868 828 Running entire programs via pdb
869 829 -------------------------------
870 830
871 831 pdb, the Python debugger, is a powerful interactive debugger which
872 832 allows you to step through code, set breakpoints, watch variables,
873 833 etc. IPython makes it very easy to start any script under the control
874 834 of pdb, regardless of whether you have wrapped it into a 'main()'
875 function or not. For this, simply type '%run -d myscript' at an
876 IPython prompt. See the %run command's documentation (via '%run?' or
877 in Sec. magic_ for more details, including how to control where pdb
878 will stop execution first.
879
880 For more information on the use of the pdb debugger, read the included
881 pdb.doc file (part of the standard Python distribution). On a stock
882 Linux system it is located at /usr/lib/python2.3/pdb.doc, but the
883 easiest way to read it is by using the help() function of the pdb module
884 as follows (in an IPython prompt)::
835 function or not. For this, simply type ``%run -d myscript`` at an
836 IPython prompt. See the %run command's documentation for more details, including
837 how to control where pdb will stop execution first.
885 838
886 In [1]: import pdb
887 In [2]: pdb.help()
839 For more information on the use of the pdb debugger, see :ref:`debugger-commands`
840 in the Python documentation.
888 841
889 This will load the pdb.doc document in a file viewer for you automatically.
890 842
843 Post-mortem debugging
844 ---------------------
891 845
892 Automatic invocation of pdb on exceptions
893 -----------------------------------------
894
895 IPython, if started with the ``--pdb`` option (or if the option is set in
896 your config file) can call the Python pdb debugger every time your code
897 triggers an uncaught exception. This feature
898 can also be toggled at any time with the %pdb magic command. This can be
846 Going into a debugger when an exception occurs can be
899 847 extremely useful in order to find the origin of subtle bugs, because pdb
900 848 opens up at the point in your code which triggered the exception, and
901 849 while your program is at this point 'dead', all the data is still
902 850 available and you can walk up and down the stack frame and understand
903 851 the origin of the problem.
904 852
905 Furthermore, you can use these debugging facilities both with the
906 embedded IPython mode and without IPython at all. For an embedded shell
907 (see sec. Embedding_), simply call the constructor with
908 ``--pdb`` in the argument string and pdb will automatically be called if an
909 uncaught exception is triggered by your code.
853 You can use the ``%debug`` magic after an exception has occurred to start
854 post-mortem debugging. IPython can also call debugger every time your code
855 triggers an uncaught exception. This feature can be toggled with the %pdb magic
856 command, or you can start IPython with the ``--pdb`` option.
910 857
911 For stand-alone use of the feature in your programs which do not use
912 IPython at all, put the following lines toward the top of your 'main'
913 routine::
858 For a post-mortem debugger in your programs outside IPython,
859 put the following lines toward the top of your 'main' routine::
914 860
915 861 import sys
916 862 from IPython.core import ultratb
917 863 sys.excepthook = ultratb.FormattedTB(mode='Verbose',
918 864 color_scheme='Linux', call_pdb=1)
919 865
920 866 The mode keyword can be either 'Verbose' or 'Plain', giving either very
921 867 detailed or normal tracebacks respectively. The color_scheme keyword can
922 868 be one of 'NoColor', 'Linux' (default) or 'LightBG'. These are the same
923 869 options which can be set in IPython with ``--colors`` and ``--xmode``.
924 870
925 871 This will give any of your programs detailed, colored tracebacks with
926 872 automatic invocation of pdb.
927 873
928
929 Extensions for syntax processing
930 ================================
931
932 This isn't for the faint of heart, because the potential for breaking
933 things is quite high. But it can be a very powerful and useful feature.
934 In a nutshell, you can redefine the way IPython processes the user input
935 line to accept new, special extensions to the syntax without needing to
936 change any of IPython's own code.
937
938 In the IPython/extensions directory you will find some examples
939 supplied, which we will briefly describe now. These can be used 'as is'
940 (and both provide very useful functionality), or you can use them as a
941 starting point for writing your own extensions.
942
943 874 .. _pasting_with_prompts:
944 875
945 876 Pasting of code starting with Python or IPython prompts
946 -------------------------------------------------------
877 =======================================================
947 878
948 879 IPython is smart enough to filter out input prompts, be they plain Python ones
949 880 (``>>>`` and ``...``) or IPython ones (``In [N]:`` and ``...:``). You can
950 881 therefore copy and paste from existing interactive sessions without worry.
951 882
952 883 The following is a 'screenshot' of how things work, copying an example from the
953 884 standard Python tutorial::
954 885
955 886 In [1]: >>> # Fibonacci series:
956 887
957 888 In [2]: ... # the sum of two elements defines the next
958 889
959 890 In [3]: ... a, b = 0, 1
960 891
961 892 In [4]: >>> while b < 10:
962 893 ...: ... print(b)
963 894 ...: ... a, b = b, a+b
964 895 ...:
965 896 1
966 897 1
967 898 2
968 899 3
969 900 5
970 901 8
971 902
972 903 And pasting from IPython sessions works equally well::
973 904
974 905 In [1]: In [5]: def f(x):
975 906 ...: ...: "A simple function"
976 907 ...: ...: return x**2
977 908 ...: ...:
978 909
979 910 In [2]: f(3)
980 911 Out[2]: 9
981 912
982 913 .. _gui_support:
983 914
984 915 GUI event loop support
985 916 ======================
986 917
987 918 .. versionadded:: 0.11
988 919 The ``%gui`` magic and :mod:`IPython.lib.inputhook`.
989 920
990 921 IPython has excellent support for working interactively with Graphical User
991 922 Interface (GUI) toolkits, such as wxPython, PyQt4/PySide, PyGTK and Tk. This is
992 923 implemented using Python's builtin ``PyOSInputHook`` hook. This implementation
993 924 is extremely robust compared to our previous thread-based version. The
994 925 advantages of this are:
995 926
996 927 * GUIs can be enabled and disabled dynamically at runtime.
997 928 * The active GUI can be switched dynamically at runtime.
998 929 * In some cases, multiple GUIs can run simultaneously with no problems.
999 930 * There is a developer API in :mod:`IPython.lib.inputhook` for customizing
1000 931 all of these things.
1001 932
1002 933 For users, enabling GUI event loop integration is simple. You simple use the
1003 934 ``%gui`` magic as follows::
1004 935
1005 936 %gui [GUINAME]
1006 937
1007 938 With no arguments, ``%gui`` removes all GUI support. Valid ``GUINAME``
1008 939 arguments are ``wx``, ``qt``, ``gtk`` and ``tk``.
1009 940
1010 941 Thus, to use wxPython interactively and create a running :class:`wx.App`
1011 942 object, do::
1012 943
1013 944 %gui wx
1014 945
946 You can also start IPython with an event loop set up using the :option:`--gui`
947 flag::
948
949 $ ipython --gui=qt
950
1015 951 For information on IPython's matplotlib_ integration (and the ``matplotlib``
1016 952 mode) see :ref:`this section <matplotlib_support>`.
1017 953
1018 954 For developers that want to use IPython's GUI event loop integration in the
1019 955 form of a library, these capabilities are exposed in library form in the
1020 956 :mod:`IPython.lib.inputhook` and :mod:`IPython.lib.guisupport` modules.
1021 957 Interested developers should see the module docstrings for more information,
1022 958 but there are a few points that should be mentioned here.
1023 959
1024 960 First, the ``PyOSInputHook`` approach only works in command line settings
1025 961 where readline is activated. The integration with various eventloops
1026 962 is handled somewhat differently (and more simply) when using the standalone
1027 963 kernel, as in the qtconsole and notebook.
1028 964
1029 965 Second, when using the ``PyOSInputHook`` approach, a GUI application should
1030 966 *not* start its event loop. Instead all of this is handled by the
1031 967 ``PyOSInputHook``. This means that applications that are meant to be used both
1032 968 in IPython and as standalone apps need to have special code to detects how the
1033 969 application is being run. We highly recommend using IPython's support for this.
1034 970 Since the details vary slightly between toolkits, we point you to the various
1035 971 examples in our source directory :file:`examples/lib` that demonstrate
1036 972 these capabilities.
1037 973
1038 974 Third, unlike previous versions of IPython, we no longer "hijack" (replace
1039 975 them with no-ops) the event loops. This is done to allow applications that
1040 976 actually need to run the real event loops to do so. This is often needed to
1041 977 process pending events at critical points.
1042 978
1043 979 Finally, we also have a number of examples in our source directory
1044 980 :file:`examples/lib` that demonstrate these capabilities.
1045 981
1046 982 PyQt and PySide
1047 983 ---------------
1048 984
1049 985 .. attempt at explanation of the complete mess that is Qt support
1050 986
1051 987 When you use ``--gui=qt`` or ``--matplotlib=qt``, IPython can work with either
1052 988 PyQt4 or PySide. There are three options for configuration here, because
1053 989 PyQt4 has two APIs for QString and QVariant - v1, which is the default on
1054 990 Python 2, and the more natural v2, which is the only API supported by PySide.
1055 991 v2 is also the default for PyQt4 on Python 3. IPython's code for the QtConsole
1056 992 uses v2, but you can still use any interface in your code, since the
1057 993 Qt frontend is in a different process.
1058 994
1059 995 The default will be to import PyQt4 without configuration of the APIs, thus
1060 996 matching what most applications would expect. It will fall back of PySide if
1061 997 PyQt4 is unavailable.
1062 998
1063 999 If specified, IPython will respect the environment variable ``QT_API`` used
1064 1000 by ETS. ETS 4.0 also works with both PyQt4 and PySide, but it requires
1065 1001 PyQt4 to use its v2 API. So if ``QT_API=pyside`` PySide will be used,
1066 1002 and if ``QT_API=pyqt`` then PyQt4 will be used *with the v2 API* for
1067 1003 QString and QVariant, so ETS codes like MayaVi will also work with IPython.
1068 1004
1069 1005 If you launch IPython in matplotlib mode with ``ipython --matplotlib=qt``,
1070 1006 then IPython will ask matplotlib which Qt library to use (only if QT_API is
1071 1007 *not set*), via the 'backend.qt4' rcParam. If matplotlib is version 1.0.1 or
1072 1008 older, then IPython will always use PyQt4 without setting the v2 APIs, since
1073 1009 neither v2 PyQt nor PySide work.
1074 1010
1075 1011 .. warning::
1076 1012
1077 1013 Note that this means for ETS 4 to work with PyQt4, ``QT_API`` *must* be set
1078 1014 to work with IPython's qt integration, because otherwise PyQt4 will be
1079 1015 loaded in an incompatible mode.
1080 1016
1081 1017 It also means that you must *not* have ``QT_API`` set if you want to
1082 1018 use ``--gui=qt`` with code that requires PyQt4 API v1.
1083 1019
1084 1020
1085 1021 .. _matplotlib_support:
1086 1022
1087 1023 Plotting with matplotlib
1088 1024 ========================
1089 1025
1090 1026 matplotlib_ provides high quality 2D and 3D plotting for Python. matplotlib_
1091 1027 can produce plots on screen using a variety of GUI toolkits, including Tk,
1092 1028 PyGTK, PyQt4 and wxPython. It also provides a number of commands useful for
1093 1029 scientific computing, all with a syntax compatible with that of the popular
1094 1030 Matlab program.
1095 1031
1096 1032 To start IPython with matplotlib support, use the ``--matplotlib`` switch. If
1097 1033 IPython is already running, you can run the ``%matplotlib`` magic. If no
1098 1034 arguments are given, IPython will automatically detect your choice of
1099 1035 matplotlib backend. You can also request a specific backend with
1100 1036 ``%matplotlib backend``, where ``backend`` must be one of: 'tk', 'qt', 'wx',
1101 1037 'gtk', 'osx'. In the web notebook and Qt console, 'inline' is also a valid
1102 1038 backend value, which produces static figures inlined inside the application
1103 1039 window instead of matplotlib's interactive figures that live in separate
1104 1040 windows.
1105 1041
1106 1042 .. _interactive_demos:
1107 1043
1108 1044 Interactive demos with IPython
1109 1045 ==============================
1110 1046
1111 1047 IPython ships with a basic system for running scripts interactively in
1112 1048 sections, useful when presenting code to audiences. A few tags embedded
1113 1049 in comments (so that the script remains valid Python code) divide a file
1114 1050 into separate blocks, and the demo can be run one block at a time, with
1115 1051 IPython printing (with syntax highlighting) the block before executing
1116 1052 it, and returning to the interactive prompt after each block. The
1117 1053 interactive namespace is updated after each block is run with the
1118 1054 contents of the demo's namespace.
1119 1055
1120 1056 This allows you to show a piece of code, run it and then execute
1121 1057 interactively commands based on the variables just created. Once you
1122 1058 want to continue, you simply execute the next block of the demo. The
1123 1059 following listing shows the markup necessary for dividing a script into
1124 1060 sections for execution as a demo:
1125 1061
1126 1062 .. literalinclude:: ../../../examples/lib/example-demo.py
1127 1063 :language: python
1128 1064
1129 1065 In order to run a file as a demo, you must first make a Demo object out
1130 1066 of it. If the file is named myscript.py, the following code will make a
1131 1067 demo::
1132 1068
1133 1069 from IPython.lib.demo import Demo
1134 1070
1135 1071 mydemo = Demo('myscript.py')
1136 1072
1137 1073 This creates the mydemo object, whose blocks you run one at a time by
1138 simply calling the object with no arguments. If you have autocall active
1139 in IPython (the default), all you need to do is type::
1074 simply calling the object with no arguments. Then call it to run each step
1075 of the demo::
1140 1076
1141 mydemo
1077 mydemo()
1142 1078
1143 and IPython will call it, executing each block. Demo objects can be
1079 Demo objects can be
1144 1080 restarted, you can move forward or back skipping blocks, re-execute the
1145 last block, etc. Simply use the Tab key on a demo object to see its
1146 methods, and call '?' on them to see their docstrings for more usage
1147 details. In addition, the demo module itself contains a comprehensive
1148 docstring, which you can access via::
1149
1150 from IPython.lib import demo
1151
1152 demo?
1081 last block, etc. See the :mod:`IPython.lib.demo` module and the
1082 :class:`~IPython.lib.demo.Demo` class for details.
1153 1083
1154 Limitations: It is important to note that these demos are limited to
1084 Limitations: These demos are limited to
1155 1085 fairly simple uses. In particular, you cannot break up sections within
1156 1086 indented code (loops, if statements, function definitions, etc.)
1157 1087 Supporting something like this would basically require tracking the
1158 1088 internal execution state of the Python interpreter, so only top-level
1159 1089 divisions are allowed. If you want to be able to open an IPython
1160 1090 instance at an arbitrary point in a program, you can use IPython's
1161 embedding facilities, see :func:`IPython.embed` for details.
1091 :ref:`embedding facilities <Embedding>`.
1162 1092
1163 1093 .. include:: ../links.txt
@@ -1,138 +1,137 b''
1 1 #!/usr/bin/env python
2
3 2 """An example of how to embed an IPython shell into a running program.
4 3
5 4 Please see the documentation in the IPython.Shell module for more details.
6 5
7 6 The accompanying file example-embed-short.py has quick code fragments for
8 7 embedding which you can cut and paste in your code once you understand how
9 8 things work.
10 9
11 10 The code in this file is deliberately extra-verbose, meant for learning."""
12 11 from __future__ import print_function
13 12
14 13 # The basics to get you going:
15 14
16 # IPython sets the __IPYTHON__ variable so you can know if you have nested
15 # IPython injects get_ipython into builtins, so you can know if you have nested
17 16 # copies running.
18 17
19 18 # Try running this code both at the command line and from inside IPython (with
20 19 # %run example-embed.py)
21 20 from IPython.config.loader import Config
22 21 try:
23 22 get_ipython
24 23 except NameError:
25 24 nested = 0
26 25 cfg = Config()
27 26 prompt_config = cfg.PromptManager
28 27 prompt_config.in_template = 'In <\\#>: '
29 28 prompt_config.in2_template = ' .\\D.: '
30 29 prompt_config.out_template = 'Out<\\#>: '
31 30 else:
32 31 print("Running nested copies of IPython.")
33 32 print("The prompts for the nested copy have been modified")
34 33 cfg = Config()
35 34 nested = 1
36 35
37 36 # First import the embeddable shell class
38 37 from IPython.terminal.embed import InteractiveShellEmbed
39 38
40 39 # Now create an instance of the embeddable shell. The first argument is a
41 40 # string with options exactly as you would type them if you were starting
42 41 # IPython at the system command line. Any parameters you want to define for
43 42 # configuration can thus be specified here.
44 43 ipshell = InteractiveShellEmbed(config=cfg,
45 44 banner1 = 'Dropping into IPython',
46 45 exit_msg = 'Leaving Interpreter, back to program.')
47 46
48 47 # Make a second instance, you can have as many as you want.
49 48 cfg2 = cfg.copy()
50 49 prompt_config = cfg2.PromptManager
51 50 prompt_config.in_template = 'In2<\\#>: '
52 51 if not nested:
53 52 prompt_config.in_template = 'In2<\\#>: '
54 53 prompt_config.in2_template = ' .\\D.: '
55 54 prompt_config.out_template = 'Out<\\#>: '
56 55 ipshell2 = InteractiveShellEmbed(config=cfg,
57 56 banner1 = 'Second IPython instance.')
58 57
59 58 print('\nHello. This is printed from the main controller program.\n')
60 59
61 60 # You can then call ipshell() anywhere you need it (with an optional
62 61 # message):
63 62 ipshell('***Called from top level. '
64 63 'Hit Ctrl-D to exit interpreter and continue program.\n'
65 64 'Note that if you use %kill_embedded, you can fully deactivate\n'
66 65 'This embedded instance so it will never turn on again')
67 66
68 67 print('\nBack in caller program, moving along...\n')
69 68
70 69 #---------------------------------------------------------------------------
71 70 # More details:
72 71
73 72 # InteractiveShellEmbed instances don't print the standard system banner and
74 73 # messages. The IPython banner (which actually may contain initialization
75 74 # messages) is available as get_ipython().banner in case you want it.
76 75
77 76 # InteractiveShellEmbed instances print the following information everytime they
78 77 # start:
79 78
80 79 # - A global startup banner.
81 80
82 81 # - A call-specific header string, which you can use to indicate where in the
83 82 # execution flow the shell is starting.
84 83
85 84 # They also print an exit message every time they exit.
86 85
87 86 # Both the startup banner and the exit message default to None, and can be set
88 87 # either at the instance constructor or at any other time with the
89 88 # by setting the banner and exit_msg attributes.
90 89
91 90 # The shell instance can be also put in 'dummy' mode globally or on a per-call
92 91 # basis. This gives you fine control for debugging without having to change
93 92 # code all over the place.
94 93
95 94 # The code below illustrates all this.
96 95
97 96
98 97 # This is how the global banner and exit_msg can be reset at any point
99 98 ipshell.banner = 'Entering interpreter - New Banner'
100 99 ipshell.exit_msg = 'Leaving interpreter - New exit_msg'
101 100
102 101 def foo(m):
103 102 s = 'spam'
104 103 ipshell('***In foo(). Try %whos, or print s or m:')
105 104 print('foo says m = ',m)
106 105
107 106 def bar(n):
108 107 s = 'eggs'
109 108 ipshell('***In bar(). Try %whos, or print s or n:')
110 109 print('bar says n = ',n)
111 110
112 111 # Some calls to the above functions which will trigger IPython:
113 112 print('Main program calling foo("eggs")\n')
114 113 foo('eggs')
115 114
116 115 # The shell can be put in 'dummy' mode where calls to it silently return. This
117 116 # allows you, for example, to globally turn off debugging for a program with a
118 117 # single call.
119 118 ipshell.dummy_mode = True
120 119 print('\nTrying to call IPython which is now "dummy":')
121 120 ipshell()
122 121 print('Nothing happened...')
123 122 # The global 'dummy' mode can still be overridden for a single call
124 123 print('\nOverriding dummy mode manually:')
125 124 ipshell(dummy=False)
126 125
127 126 # Reactivate the IPython shell
128 127 ipshell.dummy_mode = False
129 128
130 129 print('You can even have multiple embedded instances:')
131 130 ipshell2()
132 131
133 132 print('\nMain program calling bar("spam")\n')
134 133 bar('spam')
135 134
136 135 print('Main program finished. Bye!')
137 136
138 137 #********************** End of file <example-embed.py> ***********************
@@ -1,44 +1,43 b''
1 1 # -*- coding: utf-8 -*-
2 2 """A simple interactive demo to illustrate the use of IPython's Demo class.
3 3
4 4 Any python script can be run as a demo, but that does little more than showing
5 5 it on-screen, syntax-highlighted in one shot. If you add a little simple
6 6 markup, you can stop at specified intervals and return to the ipython prompt,
7 7 resuming execution later.
8 8
9 9 This is a unicode test, åäö
10 10 """
11 11 from __future__ import print_function
12 12
13 13 print('Hello, welcome to an interactive IPython demo.')
14 14 print('Executing this block should require confirmation before proceeding,')
15 15 print('unless auto_all has been set to true in the demo object')
16 16
17 17 # The mark below defines a block boundary, which is a point where IPython will
18 18 # stop execution and return to the interactive prompt.
19 # Note that in actual interactive execution,
20 19 # <demo> --- stop ---
21 20
22 21 x = 1
23 22 y = 2
24 23
25 24 # <demo> --- stop ---
26 25
27 26 # the mark below makes this block as silent
28 27 # <demo> silent
29 28
30 29 print('This is a silent block, which gets executed but not printed.')
31 30
32 31 # <demo> --- stop ---
33 32 # <demo> auto
34 33 print('This is an automatic block.')
35 34 print('It is executed without asking for confirmation, but printed.')
36 35 z = x+y
37 36
38 37 print('z=',x)
39 38
40 39 # <demo> --- stop ---
41 40 # This is just another normal block.
42 41 print('z is now:', z)
43 42
44 43 print('bye!')
General Comments 0
You need to be logged in to leave comments. Login now