diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py
index d306b950f1ee..63698180788a 100644
--- a/lib/matplotlib/__init__.py
+++ b/lib/matplotlib/__init__.py
@@ -1454,6 +1454,17 @@ def tk_window_focus():
except (KeyError, ValueError):
pass
+
+# Jupyter extension paths
+def _jupyter_nbextension_paths():
+ return [{
+ 'section': 'notebook',
+ 'src': 'backends/web_backend/js',
+ 'dest': 'matplotlib',
+ 'require': 'matplotlib/extension'
+ }]
+
+
default_test_modules = [
'matplotlib.tests.test_agg',
'matplotlib.tests.test_animation',
diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py
index 6c72f16020ae..fe7310155336 100644
--- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -2161,7 +2161,8 @@ def print_figure(self, filename, dpi=None, facecolor=None, edgecolor=None,
origfacecolor = self.figure.get_facecolor()
origedgecolor = self.figure.get_edgecolor()
- self.figure.dpi = dpi
+ if dpi != 'figure':
+ self.figure.dpi = dpi
self.figure.set_facecolor(facecolor)
self.figure.set_edgecolor(edgecolor)
diff --git a/lib/matplotlib/backends/backend_nbagg.py b/lib/matplotlib/backends/backend_nbagg.py
index 3fcca314124d..972bc8929975 100644
--- a/lib/matplotlib/backends/backend_nbagg.py
+++ b/lib/matplotlib/backends/backend_nbagg.py
@@ -3,23 +3,27 @@
# lib/matplotlib/backends/web_backend/nbagg_uat.ipynb to help verify
# that changes made maintain expected behaviour.
-import datetime
from base64 import b64encode
import json
import io
+from tempfile import mkdtemp
+import shutil
import os
from matplotlib.externals import six
from uuid import uuid4 as uuid
-import tornado.ioloop
-
-from IPython.display import display, Javascript, HTML
+from IPython.display import display, HTML
+from IPython import version_info
try:
# Jupyter/IPython 4.x or later
- from ipykernel.comm import Comm
+ from ipywidgets import DOMWidget
+ from traitlets import Unicode, Bool, Float, List, Any
+ from notebook.nbextensions import install_nbextension, check_nbextension
except ImportError:
# Jupyter/IPython 3.x or earlier
- from IPython.kernel.comm import Comm
+ from IPython.html.widgets import DOMWidget
+ from IPython.utils.traitlets import Unicode, Bool, Float, List, Any
+ from IPython.html.nbextensions import install_nbextension
from matplotlib import rcParams
from matplotlib.figure import Figure
@@ -33,6 +37,7 @@
class Show(ShowBase):
+
def __call__(self, block=None):
from matplotlib._pylab_helpers import Gcf
@@ -98,6 +103,7 @@ def connection_info():
'zoom_to_rect': 'fa fa-square-o icon-check-empty',
'move': 'fa fa-arrows icon-move',
'download': 'fa fa-floppy-o icon-save',
+ 'export': 'fa fa-file-picture-o icon-picture',
None: None
}
@@ -109,84 +115,74 @@ class NavigationIPy(NavigationToolbar2WebAgg):
_FONT_AWESOME_CLASSES[image_file], name_of_method)
for text, tooltip_text, image_file, name_of_method
in (NavigationToolbar2.toolitems +
- (('Download', 'Download plot', 'download', 'download'),))
+ (('Download', 'Download plot', 'download', 'download'),
+ ('Export', 'Export plot', 'export', 'export')))
if image_file in _FONT_AWESOME_CLASSES]
+ def export(self):
+ buf = io.BytesIO()
+ self.canvas.figure.savefig(buf, format='png', dpi='figure')
+ data = "
"
+ data = data.format(b64encode(buf.getvalue()).decode('utf-8'))
+ display(HTML(data))
+
+
+class FigureCanvasNbAgg(DOMWidget, FigureCanvasWebAggCore):
+ _view_module = Unicode("matplotlib", sync=True)
+ _view_name = Unicode('MPLCanvasView', sync=True)
+ _toolbar_items = List(sync=True)
+ _closed = Bool(True)
+ _id = Unicode('', sync=True)
+
+ # Must declare the superclass private members.
+ _png_is_old = Bool()
+ _force_full = Bool()
+ _current_image_mode = Unicode()
+ _dpi_ratio = Float(1.0)
+ _is_idle_drawing = Bool()
+ _is_saving = Bool()
+ _button = Any()
+ _key = Any()
+ _lastx = Any()
+ _lasty = Any()
+ _is_idle_drawing = Bool()
+
+ def __init__(self, figure, *args, **kwargs):
+ super(FigureCanvasWebAggCore, self).__init__(figure, *args, **kwargs)
+ super(DOMWidget, self).__init__(*args, **kwargs)
+ self._uid = uuid().hex
+ self.on_msg(self._handle_message)
+
+ def _handle_message(self, object, message, buffers):
+ # The 'supports_binary' message is relevant to the
+ # websocket itself. The other messages get passed along
+ # to matplotlib as-is.
-class FigureManagerNbAgg(FigureManagerWebAgg):
- ToolbarCls = NavigationIPy
-
- def __init__(self, canvas, num):
- self._shown = False
- FigureManagerWebAgg.__init__(self, canvas, num)
-
- def display_js(self):
- # XXX How to do this just once? It has to deal with multiple
- # browser instances using the same kernel (require.js - but the
- # file isn't static?).
- display(Javascript(FigureManagerNbAgg.get_javascript()))
-
- def show(self):
- if not self._shown:
- self.display_js()
- self._create_comm()
- else:
- self.canvas.draw_idle()
- self._shown = True
-
- def reshow(self):
- """
- A special method to re-show the figure in the notebook.
-
- """
- self._shown = False
- self.show()
-
- @property
- def connected(self):
- return bool(self.web_sockets)
-
- @classmethod
- def get_javascript(cls, stream=None):
- if stream is None:
- output = io.StringIO()
+ # Every message has a "type" and a "figure_id".
+ message = json.loads(message)
+ if message['type'] == 'closing':
+ self._closed = True
+ elif message['type'] == 'supports_binary':
+ self.supports_binary = message['value']
+ elif message['type'] == 'initialized':
+ _, _, w, h = self.figure.bbox.bounds
+ self.manager.resize(w, h)
+ self.send_json('refresh')
else:
- output = stream
- super(FigureManagerNbAgg, cls).get_javascript(stream=output)
- with io.open(os.path.join(
- os.path.dirname(__file__),
- "web_backend",
- "nbagg_mpl.js"), encoding='utf8') as fd:
- output.write(fd.read())
- if stream is None:
- return output.getvalue()
-
- def _create_comm(self):
- comm = CommSocket(self)
- self.add_web_socket(comm)
- return comm
-
- def destroy(self):
- self._send_event('close')
- # need to copy comms as callbacks will modify this list
- for comm in list(self.web_sockets):
- comm.on_close()
- self.clearup_closed()
-
- def clearup_closed(self):
- """Clear up any closed Comms."""
- self.web_sockets = set([socket for socket in self.web_sockets
- if socket.is_open()])
-
- if len(self.web_sockets) == 0:
- self.canvas.close_event()
+ self.manager.handle_json(message)
- def remove_comm(self, comm_id):
- self.web_sockets = set([socket for socket in self.web_sockets
- if not socket.comm.comm_id == comm_id])
+ def send_json(self, content):
+ self.send({'data': json.dumps(content)})
+ def send_binary(self, blob):
+ # The comm is ascii, so we always send the image in base64
+ # encoded data URL form.
+ data = b64encode(blob)
+ if six.PY3:
+ data = data.decode('ascii')
+ data_uri = "data:image/png;base64,{0}".format(data)
+ self.send({'data': data_uri})
-class FigureCanvasNbAgg(FigureCanvasWebAggCore):
def new_timer(self, *args, **kwargs):
return TimerTornado(*args, **kwargs)
@@ -197,6 +193,31 @@ def stop_event_loop(self):
FigureCanvasBase.stop_event_loop_default(self)
+class FigureManagerNbAgg(FigureManagerWebAgg):
+ ToolbarCls = NavigationIPy
+
+ def __init__(self, canvas, num):
+ FigureManagerWebAgg.__init__(self, canvas, num)
+ toolitems = []
+ for name, tooltip, image, method in self.ToolbarCls.toolitems:
+ if name is None:
+ toolitems.append(['', '', '', ''])
+ else:
+ toolitems.append([name, tooltip, image, method])
+ canvas._toolbar_items = toolitems
+ self.web_sockets = [self.canvas]
+
+ def show(self):
+ if self.canvas._closed:
+ self.canvas._closed = False
+ display(self.canvas)
+ else:
+ self.canvas.draw_idle()
+
+ def destroy(self):
+ self._send_event('close')
+
+
def new_figure_manager(num, *args, **kwargs):
"""
Create a new figure manager instance
@@ -229,76 +250,46 @@ def closer(event):
return manager
-class CommSocket(object):
+def nbinstall(overwrite=False, user=True):
"""
- Manages the Comm connection between IPython and the browser (client).
-
- Comms are 2 way, with the CommSocket being able to publish a message
- via the send_json method, and handle a message with on_message. On the
- JS side figure.send_message and figure.ws.onmessage do the sending and
- receiving respectively.
-
+ Copies javascript dependencies to the '/nbextensions' folder in
+ your IPython directory.
+
+ Parameters
+ ----------
+
+ overwrite : bool
+ If True, always install the files, regardless of what mayĆ already be
+ installed. Defaults to False.
+ user : bool
+ Whether to install to the user's .ipython/nbextensions directory.
+ Otherwise do a system-wide install
+ (e.g. /usr/local/share/jupyter/nbextensions). Defaults to False.
"""
- def __init__(self, manager):
- self.supports_binary = None
- self.manager = manager
- self.uuid = str(uuid())
- # Publish an output area with a unique ID. The javascript can then
- # hook into this area.
- display(HTML("
" % self.uuid))
- try:
- self.comm = Comm('matplotlib', data={'id': self.uuid})
- except AttributeError:
- raise RuntimeError('Unable to create an IPython notebook Comm '
- 'instance. Are you in the IPython notebook?')
- self.comm.on_msg(self.on_message)
-
- manager = self.manager
- self._ext_close = False
-
- def _on_close(close_message):
- self._ext_close = True
- manager.remove_comm(close_message['content']['comm_id'])
- manager.clearup_closed()
-
- self.comm.on_close(_on_close)
-
- def is_open(self):
- return not (self._ext_close or self.comm._closed)
-
- def on_close(self):
- # When the socket is closed, deregister the websocket with
- # the FigureManager.
- if self.is_open():
- try:
- self.comm.close()
- except KeyError:
- # apparently already cleaned it up?
- pass
-
- def send_json(self, content):
- self.comm.send({'data': json.dumps(content)})
-
- def send_binary(self, blob):
- # The comm is ascii, so we always send the image in base64
- # encoded data URL form.
- data = b64encode(blob)
- if six.PY3:
- data = data.decode('ascii')
- data_uri = "data:image/png;base64,{0}".format(data)
- self.comm.send({'data': data_uri})
-
- def on_message(self, message):
- # The 'supports_binary' message is relevant to the
- # websocket itself. The other messages get passed along
- # to matplotlib as-is.
-
- # Every message has a "type" and a "figure_id".
- message = json.loads(message['content']['data'])
- if message['type'] == 'closing':
- self.on_close()
- self.manager.clearup_closed()
- elif message['type'] == 'supports_binary':
- self.supports_binary = message['value']
- else:
- self.manager.handle_json(message)
+ if (check_nbextension('matplotlib') or
+ check_nbextension('matplotlib', True)):
+ return
+
+ # Make a temporary directory so we can wrap mpl.js in a requirejs define().
+ tempdir = mkdtemp()
+ path = os.path.join(os.path.dirname(__file__), "web_backend")
+ shutil.copy2(os.path.join(path, "nbagg_mpl.js"), tempdir)
+
+ with open(os.path.join(path, 'mpl.js')) as fid:
+ contents = fid.read()
+
+ with open(os.path.join(tempdir, 'mpl.js'), 'w') as fid:
+ fid.write('define(["jquery"], function($) {\n')
+ fid.write(contents)
+ fid.write('\nreturn mpl;\n});')
+
+ install_nbextension(
+ tempdir,
+ overwrite=overwrite,
+ symlink=False,
+ destination='matplotlib',
+ verbose=0,
+ **({'user': user} if version_info >= (3, 0, 0, '') else {})
+ )
+
+#nbinstall()
diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py
index 8dba7c3e8647..9f93b2bb1bc8 100644
--- a/lib/matplotlib/backends/backend_webagg_core.py
+++ b/lib/matplotlib/backends/backend_webagg_core.py
@@ -18,7 +18,7 @@
import io
import json
import os
-import time
+import datetime
import warnings
import numpy as np
@@ -501,6 +501,7 @@ def get_javascript(cls, stream=None):
with io.open(os.path.join(
os.path.dirname(__file__),
"web_backend",
+ "js",
"mpl.js"), encoding='utf8') as fd:
output.write(fd.read())
@@ -530,7 +531,7 @@ def get_javascript(cls, stream=None):
@classmethod
def get_static_file_path(cls):
- return os.path.join(os.path.dirname(__file__), 'web_backend')
+ return os.path.join(os.path.dirname(__file__), 'web_backend', 'js')
def _send_event(self, event_type, **kwargs):
payload = {'type': event_type}
diff --git a/lib/matplotlib/backends/web_backend/js/extension.js b/lib/matplotlib/backends/web_backend/js/extension.js
new file mode 100644
index 000000000000..be7ea701550c
--- /dev/null
+++ b/lib/matplotlib/backends/web_backend/js/extension.js
@@ -0,0 +1,18 @@
+
+define([], function() {
+ if (window.require) {
+ window.require.config({
+ map: {
+ "*" : {
+ "matplotlib": "nbextensions/matplotlib/nbagg_mpl",
+ "jupyter-js-widgets": "nbextensions/jupyter-js-widgets/extension"
+ }
+ }
+ });
+ }
+
+ // Export the required load_ipython_extention
+ return {
+ load_ipython_extension: function() {}
+ };
+});
diff --git a/lib/matplotlib/backends/web_backend/mpl.js b/lib/matplotlib/backends/web_backend/js/mpl.js
similarity index 96%
rename from lib/matplotlib/backends/web_backend/mpl.js
rename to lib/matplotlib/backends/web_backend/js/mpl.js
index cecebd8e0201..d9949453ea5c 100644
--- a/lib/matplotlib/backends/web_backend/mpl.js
+++ b/lib/matplotlib/backends/web_backend/js/mpl.js
@@ -1,4 +1,16 @@
/* Put everything inside the global mpl namespace */
+
+// Universal Module Definition
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(['jquery'], factory);
+ } else {
+ // Browser globals (root is window)
+ root.returnExports = factory(root.jQuery);
+ }
+}(this, function ($) {
+
window.mpl = {};
@@ -133,11 +145,11 @@ mpl.figure.prototype._init_canvas = function() {
this.context = canvas[0].getContext("2d");
var backingStore = this.context.backingStorePixelRatio ||
- this.context.webkitBackingStorePixelRatio ||
- this.context.mozBackingStorePixelRatio ||
- this.context.msBackingStorePixelRatio ||
- this.context.oBackingStorePixelRatio ||
- this.context.backingStorePixelRatio || 1;
+ this.context.webkitBackingStorePixelRatio ||
+ this.context.mozBackingStorePixelRatio ||
+ this.context.msBackingStorePixelRatio ||
+ this.context.oBackingStorePixelRatio ||
+ this.context.backingStorePixelRatio || 1;
mpl.ratio = (window.devicePixelRatio || 1) / backingStore;
@@ -552,3 +564,7 @@ mpl.figure.prototype.toolbar_button_onclick = function(name) {
mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {
this.message.textContent = tooltip;
};
+
+return mpl;
+
+}));
diff --git a/lib/matplotlib/backends/web_backend/mpl_tornado.js b/lib/matplotlib/backends/web_backend/js/mpl_tornado.js
similarity index 100%
rename from lib/matplotlib/backends/web_backend/mpl_tornado.js
rename to lib/matplotlib/backends/web_backend/js/mpl_tornado.js
diff --git a/lib/matplotlib/backends/web_backend/js/nbagg_mpl.js b/lib/matplotlib/backends/web_backend/js/nbagg_mpl.js
new file mode 100644
index 000000000000..0718434c75e8
--- /dev/null
+++ b/lib/matplotlib/backends/web_backend/js/nbagg_mpl.js
@@ -0,0 +1,167 @@
+
+
+define(['jupyter-js-widgets', '/nbextensions/matplotlib/mpl.js'], function(widgets, mpl) {
+
+ var MPLCanvasView = widgets.WidgetView.extend({
+
+ render: function() {
+ var that = this;
+
+ var id = this.model.get('_id');
+
+ var element = this.$el;
+
+ this.ws_proxy = this.comm_websocket_adapter(this.model.comm);
+
+ function ondownload(figure, format) {
+ window.open(figure.imageObj.src);
+ }
+
+ mpl.toolbar_items = this.model.get('_toolbar_items')
+
+ var fig = new mpl.figure(id, this.ws_proxy,
+ ondownload,
+ element.get(0));
+
+ // 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.
+ this.ws_proxy.onopen();
+
+ fig.parent_element = element.get(0);
+
+ // subscribe to incoming messages from the MPLCanvasWidget
+ this.model.on('msg:custom', this.ws_proxy.onmessage, this);
+
+ this.send(JSON.stringify({ type: 'initialized' }));
+ },
+
+ 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 = {};
+ var that = this;
+
+ ws.close = function() {
+ comm.close()
+ };
+ ws.send = function(m) {
+ that.send(m);
+ };
+ return ws;
+ }
+
+ });
+
+ mpl.figure.prototype.handle_close = function(fig, msg) {
+ var width = fig.canvas.width/mpl.ratio
+ fig.root.unbind('remove')
+
+ // Re-enable the keyboard manager in IPython - without this line, in FF,
+ // the notebook keyboard shortcuts fail.
+ IPython.keyboard_manager.enable()
+ fig.close_ws(fig, msg);
+ }
+
+ mpl.figure.prototype.close_ws = function(fig, msg){
+ fig.send_message('closing', msg);
+ // fig.ws.close()
+ }
+
+ mpl.figure.prototype.updated_canvas_event = function() {
+ // Tell IPython that the notebook contents must change.
+ IPython.notebook.set_dirty(true);
+ this.send_message("ack", {});
+ }
+
+ mpl.figure.prototype._init_toolbar = function() {
+ var fig = this;
+
+ var nav_element = $('')
+ nav_element.attr('style', 'width: 100%');
+ this.root.append(nav_element);
+
+ // Define a callback function for later on.
+ function toolbar_event(event) {
+ return fig.toolbar_button_onclick(event['data']);
+ }
+ function toolbar_mouse_event(event) {
+ return fig.toolbar_button_onmouseover(event['data']);
+ }
+
+ 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) { continue; };
+
+ var button = $('');
+ button.click(method_name, toolbar_event);
+ button.mouseover(tooltip, toolbar_mouse_event);
+ nav_element.append(button);
+ }
+
+ // Add the status bar.
+ var status_bar = $('');
+ nav_element.append(status_bar);
+ this.message = status_bar[0];
+
+ // Add the close button to the window.
+ var buttongrp = $('');
+ var button = $('');
+ button.click(function (evt) { fig.handle_close(fig, {}); } );
+ button.mouseover('Stop Interaction', toolbar_mouse_event);
+ buttongrp.append(button);
+ var titlebar = this.root.find($('.ui-dialog-titlebar'));
+ titlebar.prepend(buttongrp);
+ }
+
+ mpl.figure.prototype._root_extra_style = function(el){
+ var fig = this
+ el.on("remove", function(){
+ fig.close_ws(fig, {});
+ });
+ }
+
+ mpl.figure.prototype._canvas_extra_style = function(el){
+ // this is important to make the div 'focusable
+ el.attr('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();
+ event.shiftKey = false;
+ // Send a "J" for go to next cell
+ event.which = 74;
+ event.keyCode = 74;
+ manager.command_mode();
+ manager.handle_keydown(event);
+ }
+ }
+
+ mpl.figure.prototype.handle_save = function(fig, msg) {
+ fig.ondownload(fig, null);
+ }
+
+ return {MPLCanvasView: MPLCanvasView}
+});
diff --git a/lib/matplotlib/backends/web_backend/nbagg_mpl.js b/lib/matplotlib/backends/web_backend/nbagg_mpl.js
deleted file mode 100644
index 9471f5340d51..000000000000
--- a/lib/matplotlib/backends/web_backend/nbagg_mpl.js
+++ /dev/null
@@ -1,211 +0,0 @@
-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.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);
- // Pass the mpl event to the overriden (by mpl) onmessage function.
- ws.onmessage(msg['content']['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 = $("#" + id);
- var ws_proxy = comm_websocket_adapter(comm)
-
- function ondownload(figure, format) {
- window.open(figure.imageObj.src);
- }
-
- var fig = new mpl.figure(id, ws_proxy,
- ondownload,
- element.get(0));
-
- // 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.get(0);
- fig.cell_info = mpl.find_output_cell("");
- if (!fig.cell_info) {
- console.error("Failed to find cell for figure", id, fig);
- return;
- }
-
- var output_index = fig.cell_info[2]
- var cell = fig.cell_info[0];
-
-};
-
-mpl.figure.prototype.handle_close = function(fig, msg) {
- var width = fig.canvas.width/mpl.ratio
- fig.root.unbind('remove')
-
- // 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).html('
');
- 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/mpl.ratio
- var dataURL = this.canvas.toDataURL();
- this.cell_info[1]['text/html'] = '
';
-}
-
-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 nav_element = $('')
- nav_element.attr('style', 'width: 100%');
- this.root.append(nav_element);
-
- // Define a callback function for later on.
- function toolbar_event(event) {
- return fig.toolbar_button_onclick(event['data']);
- }
- function toolbar_mouse_event(event) {
- return fig.toolbar_button_onmouseover(event['data']);
- }
-
- 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) { continue; };
-
- var button = $('');
- button.click(method_name, toolbar_event);
- button.mouseover(tooltip, toolbar_mouse_event);
- nav_element.append(button);
- }
-
- // Add the status bar.
- var status_bar = $('');
- nav_element.append(status_bar);
- this.message = status_bar[0];
-
- // Add the close button to the window.
- var buttongrp = $('');
- var button = $('');
- button.click(function (evt) { fig.handle_close(fig, {}); } );
- button.mouseover('Stop Interaction', toolbar_mouse_event);
- buttongrp.append(button);
- var titlebar = this.root.find($('.ui-dialog-titlebar'));
- titlebar.prepend(buttongrp);
-}
-
-mpl.figure.prototype._root_extra_style = function(el){
- var fig = this
- el.on("remove", function(){
- fig.close_ws(fig, {});
- });
-}
-
-mpl.figure.prototype._canvas_extra_style = function(el){
- // this is important to make the div 'focusable
- el.attr('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();
- event.shiftKey = false;
- // Send a "J" for go to next cell
- event.which = 74;
- event.keyCode = 74;
- manager.command_mode();
- manager.handle_keydown(event);
- }
-}
-
-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= 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);
-}
diff --git a/setup.py b/setup.py
index 2570f157152b..754639ad5ec5 100644
--- a/setup.py
+++ b/setup.py
@@ -290,6 +290,14 @@ def run(self):
ext_modules=ext_modules,
package_dir=package_dir,
package_data=package_data,
+ include_package_data=True,
+ data_files=[
+ ('share/jupyter/nbextensions/matplotlib', [
+ 'lib/matplotlib/backends/web_backend/js/extension.js',
+ 'lib/matplotlib/backends/web_backend/js/nbagg_mpl.js',
+ 'lib/matplotlib/backends/web_backend/js/mpl.js',
+ ]),
+ ],
classifiers=classifiers,
download_url="http://matplotlib.org/users/installing.html",
diff --git a/setupext.py b/setupext.py
index 6e6705e8bd33..95644c79bb28 100755
--- a/setupext.py
+++ b/setupext.py
@@ -676,6 +676,7 @@ def get_package_data(self):
'backends/web_backend/jquery/css/themes/base/*.min.css',
'backends/web_backend/jquery/css/themes/base/images/*',
'backends/web_backend/css/*.*',
+ 'backends/web_backend/js/*.js',
'backends/Matplotlib.nib/*',
'mpl-data/stylelib/*.mplstyle',
]}