You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
8.1 KiB

// Copyright (c) IPython-Contrib Team.
// Distributed under the terms of the Modified BSD License.
define([
'base/js/namespace',
'jquery',
'require',
'notebook/js/outputarea',
], function (
Jupyter,
$,
requirejs,
outputarea
) {
"use strict";
var mod_name = 'skip-traceback';
var log_prefix = '[' + mod_name + ']';
var cfg = {
enable: true,
use_toolbar_button: false,
button_icon: 'fa-warning',
animation_duration: 100,
show_copy_buttons: true,
};
// this will be filled as they're registered
var actions = {};
var apply_patches = function () {
outputarea.OutputArea.prototype.append_error = function (json) {
// firts part is just a copy of the original, but keeping a reference to inserted_text
var inserted_text;
var tb = json.traceback;
if (tb !== undefined && tb.length > 0) {
var s = '';
var len = tb.length;
for (var i = 0; i < len; i++) {
s = s + tb[i] + '\n';
}
s = s + '\n';
var toinsert = this.create_output_area();
var append_text = outputarea.OutputArea.append_map['text/plain'];
if (append_text) {
inserted_text = append_text.apply(this, [s, {}, toinsert]).addClass('output_error');
}
this._safe_append(toinsert);
// now we add our header at the top of the inserted_text
if (!inserted_text) {
return;
}
var copy_btn = $('<i class="fa fa-fw fa-copy" title="Copy full traceback to clipboard"/>')
.on('click', function (evt) {
// prevent event bubbling up to collapse/uncollapse traceback
evt.stopPropagation();
var $copy_btn = $(this)
.tooltip({track: false, trigger: 'manual', placement: 'bottom'});
// create temporary off-screen textarea for copying text
var $copy_txt_area = $('<textarea>')
.css({position: 'absolute', left: '-10000px', top: '-10000px'})
.appendTo('body');
// remember this for later
var was_focussed = document.activeElement;
var msg = 'Failed to copy traceback to clipboard';
try {
$copy_txt_area[0].value = $copy_btn.closest('.skip-traceback-summary').siblings().text();
$copy_txt_area[0].select();
var successful = document.execCommand('copy');
if (successful) {
msg = 'Copied traceback to clipboard!';
console.log(log_prefix, msg);
}
else {
console.warn(log_prefix, msg);
}
}
catch (err) {
console.warn(log_prefix, msg + ':', err);
}
finally {
$copy_txt_area.remove();
was_focussed.focus();
// this tooltip bit relies on jqueryui tooltip, but
// it may have been overwritten by bootstrap tooltip (if loaded).
try {
$copy_btn
.tooltip('option', 'content', msg)
.tooltip('open');
setTimeout(function () {
$copy_btn.tooltip('disable');
}, 1000);
}
catch (err) {
console.warn(log_prefix, err);
}
}
});
var sum = $('<pre/>')
.addClass('skip-traceback-summary')
.css('cursor', 'pointer')
.text(': ' + json.evalue + ' ')
.prepend($('<span class=ansired/>').text(json.ename));
if (cfg.show_copy_buttons) {
sum.prepend(' ').prepend(copy_btn);
}
sum
.append('<i class="fa fa-caret-right" title="Expand traceback"/>')
.append('\n')
.on('click', function (evt) {
var summary = $(this);
var icon = summary.find('.fa-caret-right,.fa-caret-down');
var show = icon.hasClass('fa-caret-right');
icon
.toggleClass('fa-caret-down', show)
.toggleClass('fa-caret-right', !show)
.attr('title', show ? 'Collapse traceback' : 'Expand traceback');
summary.siblings()[show ? 'slideDown' : 'slideUp'](cfg.animation_duration || 100);
})
.prependTo(inserted_text);
if (cfg.enable) {
sum.siblings().css('display', 'none');
}
else {
sum.css('display', 'none');
}
}
};
};
var toggle_traceback = function (set_on) {
if (set_on === undefined) {
set_on = !cfg.enable;
}
// update config
if (set_on !== cfg.enable) {
cfg.enable = set_on;
var conf_update = {};
conf_update[mod_name] = {enable: set_on};
Jupyter.notebook.config.update(conf_update);
console.log(log_prefix, 'toggled', set_on ? 'on' : 'off');
}
// update button looks
$('#toggle_traceback_btns > .btn').toggleClass('active', set_on).blur();
// update existing OutputAreas
$('.cell .output_area .output_error .skip-traceback-summary').each(function (idx, el) {
var $summary = $(el);
$summary.css('display', set_on ? '' : 'none');
if ($summary.find('.fa').hasClass(set_on ? 'fa-caret-down' : 'fa-caret-right')) {
$summary.click();
}
});
};
var register_new_actions = function () {
actions.toggle = {
help : 'Toggle Hiding Traceback',
help_index: 'zz',
icon : cfg.button_icon || 'fa-warning',
handler : function (env) { toggle_traceback(); },
};
actions.toggle.name = Jupyter.keyboard_manager.actions.register(
actions.toggle, 'toggle', mod_name);
};
var add_toolbar_button = function () {
if (cfg.use_toolbar_button) {
Jupyter.toolbar.add_buttons_group([actions.toggle.name], 'toggle_traceback_btns');
}
};
var load_ipython_extension = function () {
apply_patches();
Jupyter.notebook.config.loaded
.then(function () {
$.extend(true, cfg, Jupyter.notebook.config.data[mod_name]);
register_new_actions();
add_toolbar_button();
toggle_traceback(cfg.enable);
// update any OutputArea created before our patch)
$('.cell .output_area .output_error').each(function (idx, el) {
var $el = $(el);
if ($el.children('.skip-traceback-summary').length < 1) {
var oa = $el.closest('.cell').data('cell').output_area;
var json_outputs = oa.toJSON();
// clear; do not wait, ignore queue
oa.clear_output(false, true);
oa.fromJSON(json_outputs);
}
});
})
.catch(function (reason) {
console.error(log_prefix, 'Error loading:', reason);
});
};
return {
load_ipython_extension : load_ipython_extension
};
});