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.

206 lines
6.9 KiB

define([
'base/js/namespace',
'base/js/events',
'notebook/js/codecell',
'notebook/js/textcell',
'jquery'
], function (
Jupyter,
events,
codecell,
textcell,
$
) {
'use strict';
var CodeCell = codecell.CodeCell;
var MarkdownCell = textcell.MarkdownCell;
var mod_name = 'Freeze';
var log_prefix = '[' + mod_name + ']';
// defaults, overridden by server's config
var options = {
readonly_color: '#fffef0',
frozen_color: '#f0feff'
};
function patch_MarkdownCell_unrender () {
console.log('[Freeze] patching MarkdownCell.prototype.unrender');
var old_unrender = MarkdownCell.prototype.unrender;
MarkdownCell.prototype.unrender = function () {
// console.log('[Freeze] patched unrender applied');
if (this.metadata.run_control === undefined ||
!this.metadata.run_control.frozen
) {
old_unrender.apply(this, arguments);
}
};
}
function patch_CodeCell_execute () {
console.log('[Freeze] patching CodeCell.prototype.execute');
var old_execute = CodeCell.prototype.execute;
CodeCell.prototype.execute = function () {
if (this.metadata.run_control === undefined ||
!this.metadata.run_control.frozen
) {
old_execute.apply(this, arguments);
}
};
}
// Migrate old metadata format to new notebook-defined metadata.editable
function migrate_state (cell) {
if (cell.metadata.run_control !== undefined) {
if (cell instanceof CodeCell || cell instanceof MarkdownCell) {
if (cell.metadata.run_control.read_only === true) {
cell.metadata.editable = false;
}
}
else {
// remove metadata irrelevant to non-code/markdown cells
delete cell.metadata.run_control.frozen;
}
// remove old key replaced by metadata.editable
delete cell.metadata.run_control.read_only;
// remove whole object if it's now empty
if (Object.keys(cell.metadata.run_control).length === 0) {
delete cell.metadata.run_control;
}
}
}
function get_state (cell) {
if (cell.metadata.editable === false && (cell instanceof CodeCell || cell instanceof MarkdownCell)) {
if (cell.metadata.run_control !== undefined && cell.metadata.run_control.frozen) {
return 'frozen';
}
return 'readonly';
}
return 'normal';
}
function set_state(cell, state) {
if (!(cell instanceof CodeCell || cell instanceof MarkdownCell)) {
return;
}
state = state || 'normal';
var bg;
switch (state) {
case 'normal':
cell.metadata.editable = true;
cell.metadata.deletable = true;
if (cell.metadata.run_control !== undefined) {
delete cell.metadata.run_control.frozen;
}
bg = "";
break;
case 'read_only':
case 'readonly':
cell.metadata.editable = false;
cell.metadata.deletable = false;
if (cell.metadata.run_control !== undefined) {
delete cell.metadata.run_control.frozen;
}
bg = options.readonly_color;
break;
case 'frozen':
cell.metadata.editable = false;
cell.metadata.deletable = false;
$.extend(true, cell.metadata, {run_control: {frozen: true}});
bg = options.frozen_color;
break;
}
// remove whole object if it's now empty
if (cell.metadata.run_control !== undefined && Object.keys(cell.metadata.run_control).length === 0) {
delete cell.metadata.run_control;
}
cell.code_mirror.setOption('readOnly', !cell.metadata.editable);
var prompt = cell.element.find('div.input_area');
prompt.css("background-color", bg);
}
function set_state_selected (state) {
var cells = Jupyter.notebook.get_selected_cells();
for (var i = 0; i < cells.length; i++) {
set_state(cells[i], state);
}
}
function button_callback(state) {
set_state_selected(state);
var dirty_state = {value: true};
events.trigger("set_dirty.Notebook", dirty_state);
}
function make_normal_selected () {
button_callback('normal');
}
function make_read_only_selected () {
button_callback('read_only');
}
function make_frozen_selected () {
button_callback('frozen');
}
function initialize_states () {
var cells = Jupyter.notebook.get_cells();
for (var i = 0; i < cells.length; i++) {
var cell = cells[i];
migrate_state(cell);
var state = get_state(cell);
set_state(cell, state);
}
}
function load_extension () {
Jupyter.toolbar.add_buttons_group([
Jupyter.keyboard_manager.actions.register ({
help : 'lift restrictions from selected cells',
icon : 'fa-unlock-alt',
handler : make_normal_selected
}, 'make-cells-normal', mod_name),
Jupyter.keyboard_manager.actions.register({
help : 'make selected cells read-only',
icon: 'fa-lock',
handler : make_read_only_selected
}, 'make-cells-read-only', mod_name),
Jupyter.keyboard_manager.actions.register({
help : 'freeze selected cells',
icon : 'fa-asterisk',
handler : make_frozen_selected
}, 'freeze-cells', mod_name)
]);
patch_CodeCell_execute();
patch_MarkdownCell_unrender();
Jupyter.notebook.config.loaded.then(function on_config_loaded () {
$.extend(true, options, Jupyter.notebook.config.data[mod_name]);
}, function on_config_load_error (reason) {
console.warn(log_prefix, 'Using defaults after error loading config:', reason);
}).then(function do_stuff_with_config () {
events.on("notebook_loaded.Notebook", initialize_states);
if (Jupyter.notebook !== undefined && Jupyter.notebook._fully_loaded) {
// notebook already loaded, so we missed the event, so update all
initialize_states();
}
}).catch(function on_error (reason) {
console.error(log_prefix, 'Error:', reason);
});
}
return {
get_state : get_state,
set_state : set_state,
load_jupyter_extension : load_extension,
load_ipython_extension : load_extension
};
});