281 lines
9.4 KiB
JavaScript
281 lines
9.4 KiB
JavaScript
![]() |
/* global mpl */
|
||
|
|
||
|
var comm_websocket_adapter = function (comm) {
|
||
|
// Create a "websocket"-like object which calls the given IPython comm
|
||
|
// object with the appropriate methods. Currently this is a non binary
|
||
|
// socket, so there is still some room for performance tuning.
|
||
|
var ws = {};
|
||
|
|
||
|
ws.binaryType = comm.kernel.ws.binaryType;
|
||
|
ws.readyState = comm.kernel.ws.readyState;
|
||
|
function updateReadyState(_event) {
|
||
|
if (comm.kernel.ws) {
|
||
|
ws.readyState = comm.kernel.ws.readyState;
|
||
|
} else {
|
||
|
ws.readyState = 3; // Closed state.
|
||
|
}
|
||
|
}
|
||
|
comm.kernel.ws.addEventListener('open', updateReadyState);
|
||
|
comm.kernel.ws.addEventListener('close', updateReadyState);
|
||
|
comm.kernel.ws.addEventListener('error', updateReadyState);
|
||
|
|
||
|
ws.close = function () {
|
||
|
comm.close();
|
||
|
};
|
||
|
ws.send = function (m) {
|
||
|
//console.log('sending', m);
|
||
|
comm.send(m);
|
||
|
};
|
||
|
// Register the callback with on_msg.
|
||
|
comm.on_msg(function (msg) {
|
||
|
//console.log('receiving', msg['content']['data'], msg);
|
||
|
var data = msg['content']['data'];
|
||
|
if (data['blob'] !== undefined) {
|
||
|
data = {
|
||
|
data: new Blob(msg['buffers'], { type: data['blob'] }),
|
||
|
};
|
||
|
}
|
||
|
// Pass the mpl event to the overridden (by mpl) onmessage function.
|
||
|
ws.onmessage(data);
|
||
|
});
|
||
|
return ws;
|
||
|
};
|
||
|
|
||
|
mpl.mpl_figure_comm = function (comm, msg) {
|
||
|
// This is the function which gets called when the mpl process
|
||
|
// starts-up an IPython Comm through the "matplotlib" channel.
|
||
|
|
||
|
var id = msg.content.data.id;
|
||
|
// Get hold of the div created by the display call when the Comm
|
||
|
// socket was opened in Python.
|
||
|
var element = document.getElementById(id);
|
||
|
var ws_proxy = comm_websocket_adapter(comm);
|
||
|
|
||
|
function ondownload(figure, _format) {
|
||
|
window.open(figure.canvas.toDataURL());
|
||
|
}
|
||
|
|
||
|
var fig = new mpl.figure(id, ws_proxy, ondownload, element);
|
||
|
|
||
|
// Call onopen now - mpl needs it, as it is assuming we've passed it a real
|
||
|
// web socket which is closed, not our websocket->open comm proxy.
|
||
|
ws_proxy.onopen();
|
||
|
|
||
|
fig.parent_element = element;
|
||
|
fig.cell_info = mpl.find_output_cell("<div id='" + id + "'></div>");
|
||
|
if (!fig.cell_info) {
|
||
|
console.error('Failed to find cell for figure', id, fig);
|
||
|
return;
|
||
|
}
|
||
|
fig.cell_info[0].output_area.element.on(
|
||
|
'cleared',
|
||
|
{ fig: fig },
|
||
|
fig._remove_fig_handler
|
||
|
);
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype.handle_close = function (fig, msg) {
|
||
|
var width = fig.canvas.width / fig.ratio;
|
||
|
fig.cell_info[0].output_area.element.off(
|
||
|
'cleared',
|
||
|
fig._remove_fig_handler
|
||
|
);
|
||
|
fig.resizeObserverInstance.unobserve(fig.canvas_div);
|
||
|
|
||
|
// Update the output cell to use the data from the current canvas.
|
||
|
fig.push_to_output();
|
||
|
var dataURL = fig.canvas.toDataURL();
|
||
|
// Re-enable the keyboard manager in IPython - without this line, in FF,
|
||
|
// the notebook keyboard shortcuts fail.
|
||
|
IPython.keyboard_manager.enable();
|
||
|
fig.parent_element.innerHTML =
|
||
|
'<img src="' + dataURL + '" width="' + width + '">';
|
||
|
fig.close_ws(fig, msg);
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype.close_ws = function (fig, msg) {
|
||
|
fig.send_message('closing', msg);
|
||
|
// fig.ws.close()
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype.push_to_output = function (_remove_interactive) {
|
||
|
// Turn the data on the canvas into data in the output cell.
|
||
|
var width = this.canvas.width / this.ratio;
|
||
|
var dataURL = this.canvas.toDataURL();
|
||
|
this.cell_info[1]['text/html'] =
|
||
|
'<img src="' + dataURL + '" width="' + width + '">';
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype.updated_canvas_event = function () {
|
||
|
// Tell IPython that the notebook contents must change.
|
||
|
IPython.notebook.set_dirty(true);
|
||
|
this.send_message('ack', {});
|
||
|
var fig = this;
|
||
|
// Wait a second, then push the new image to the DOM so
|
||
|
// that it is saved nicely (might be nice to debounce this).
|
||
|
setTimeout(function () {
|
||
|
fig.push_to_output();
|
||
|
}, 1000);
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype._init_toolbar = function () {
|
||
|
var fig = this;
|
||
|
|
||
|
var toolbar = document.createElement('div');
|
||
|
toolbar.classList = 'btn-toolbar';
|
||
|
this.root.appendChild(toolbar);
|
||
|
|
||
|
function on_click_closure(name) {
|
||
|
return function (_event) {
|
||
|
return fig.toolbar_button_onclick(name);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function on_mouseover_closure(tooltip) {
|
||
|
return function (event) {
|
||
|
if (!event.currentTarget.disabled) {
|
||
|
return fig.toolbar_button_onmouseover(tooltip);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
fig.buttons = {};
|
||
|
var buttonGroup = document.createElement('div');
|
||
|
buttonGroup.classList = 'btn-group';
|
||
|
var button;
|
||
|
for (var toolbar_ind in mpl.toolbar_items) {
|
||
|
var name = mpl.toolbar_items[toolbar_ind][0];
|
||
|
var tooltip = mpl.toolbar_items[toolbar_ind][1];
|
||
|
var image = mpl.toolbar_items[toolbar_ind][2];
|
||
|
var method_name = mpl.toolbar_items[toolbar_ind][3];
|
||
|
|
||
|
if (!name) {
|
||
|
/* Instead of a spacer, we start a new button group. */
|
||
|
if (buttonGroup.hasChildNodes()) {
|
||
|
toolbar.appendChild(buttonGroup);
|
||
|
}
|
||
|
buttonGroup = document.createElement('div');
|
||
|
buttonGroup.classList = 'btn-group';
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
button = fig.buttons[name] = document.createElement('button');
|
||
|
button.classList = 'btn btn-default';
|
||
|
button.href = '#';
|
||
|
button.title = name;
|
||
|
button.innerHTML = '<i class="fa ' + image + ' fa-lg"></i>';
|
||
|
button.addEventListener('click', on_click_closure(method_name));
|
||
|
button.addEventListener('mouseover', on_mouseover_closure(tooltip));
|
||
|
buttonGroup.appendChild(button);
|
||
|
}
|
||
|
|
||
|
if (buttonGroup.hasChildNodes()) {
|
||
|
toolbar.appendChild(buttonGroup);
|
||
|
}
|
||
|
|
||
|
// Add the status bar.
|
||
|
var status_bar = document.createElement('span');
|
||
|
status_bar.classList = 'mpl-message pull-right';
|
||
|
toolbar.appendChild(status_bar);
|
||
|
this.message = status_bar;
|
||
|
|
||
|
// Add the close button to the window.
|
||
|
var buttongrp = document.createElement('div');
|
||
|
buttongrp.classList = 'btn-group inline pull-right';
|
||
|
button = document.createElement('button');
|
||
|
button.classList = 'btn btn-mini btn-primary';
|
||
|
button.href = '#';
|
||
|
button.title = 'Stop Interaction';
|
||
|
button.innerHTML = '<i class="fa fa-power-off icon-remove icon-large"></i>';
|
||
|
button.addEventListener('click', function (_evt) {
|
||
|
fig.handle_close(fig, {});
|
||
|
});
|
||
|
button.addEventListener(
|
||
|
'mouseover',
|
||
|
on_mouseover_closure('Stop Interaction')
|
||
|
);
|
||
|
buttongrp.appendChild(button);
|
||
|
var titlebar = this.root.querySelector('.ui-dialog-titlebar');
|
||
|
titlebar.insertBefore(buttongrp, titlebar.firstChild);
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype._remove_fig_handler = function (event) {
|
||
|
var fig = event.data.fig;
|
||
|
if (event.target !== this) {
|
||
|
// Ignore bubbled events from children.
|
||
|
return;
|
||
|
}
|
||
|
fig.close_ws(fig, {});
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype._root_extra_style = function (el) {
|
||
|
el.style.boxSizing = 'content-box'; // override notebook setting of border-box.
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype._canvas_extra_style = function (el) {
|
||
|
// this is important to make the div 'focusable
|
||
|
el.setAttribute('tabindex', 0);
|
||
|
// reach out to IPython and tell the keyboard manager to turn it's self
|
||
|
// off when our div gets focus
|
||
|
|
||
|
// location in version 3
|
||
|
if (IPython.notebook.keyboard_manager) {
|
||
|
IPython.notebook.keyboard_manager.register_events(el);
|
||
|
} else {
|
||
|
// location in version 2
|
||
|
IPython.keyboard_manager.register_events(el);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype._key_event_extra = function (event, _name) {
|
||
|
var manager = IPython.notebook.keyboard_manager;
|
||
|
if (!manager) {
|
||
|
manager = IPython.keyboard_manager;
|
||
|
}
|
||
|
|
||
|
// Check for shift+enter
|
||
|
if (event.shiftKey && event.which === 13) {
|
||
|
this.canvas_div.blur();
|
||
|
// select the cell after this one
|
||
|
var index = IPython.notebook.find_cell_index(this.cell_info[0]);
|
||
|
IPython.notebook.select(index + 1);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
mpl.figure.prototype.handle_save = function (fig, _msg) {
|
||
|
fig.ondownload(fig, null);
|
||
|
};
|
||
|
|
||
|
mpl.find_output_cell = function (html_output) {
|
||
|
// Return the cell and output element which can be found *uniquely* in the notebook.
|
||
|
// Note - this is a bit hacky, but it is done because the "notebook_saving.Notebook"
|
||
|
// IPython event is triggered only after the cells have been serialised, which for
|
||
|
// our purposes (turning an active figure into a static one), is too late.
|
||
|
var cells = IPython.notebook.get_cells();
|
||
|
var ncells = cells.length;
|
||
|
for (var i = 0; i < ncells; i++) {
|
||
|
var cell = cells[i];
|
||
|
if (cell.cell_type === 'code') {
|
||
|
for (var j = 0; j < cell.output_area.outputs.length; j++) {
|
||
|
var data = cell.output_area.outputs[j];
|
||
|
if (data.data) {
|
||
|
// IPython >= 3 moved mimebundle to data attribute of output
|
||
|
data = data.data;
|
||
|
}
|
||
|
if (data['text/html'] === html_output) {
|
||
|
return [cell, data, j];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Register the function which deals with the matplotlib target/channel.
|
||
|
// The kernel may be null if the page has been refreshed.
|
||
|
if (IPython.notebook.kernel !== null) {
|
||
|
IPython.notebook.kernel.comm_manager.register_target(
|
||
|
'matplotlib',
|
||
|
mpl.mpl_figure_comm
|
||
|
);
|
||
|
}
|