##// END OF EJS Templates
don't use fixed header...
don't use fixed header instead, use static header and flex box to accomplish the same thing. fixes various issues related to scrolling, anchors, etc.

File last commit:

r19800:1ea57261
r20050:a5cb9bb0
Show More
manager.js
483 lines | 19.4 KiB | application/javascript | JavascriptLexer
Jonathan Frederic
Almost done!...
r17198 // Copyright (c) IPython Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"underscore",
Jonathan Frederic
Done with major changes,...
r17199 "backbone",
Tarun Gaba
Added jquery in define
r17273 "jquery",
Jonathan Frederic
Initial stab at adding promises to the widget framework.
r18882 "base/js/utils",
Jonathan Frederic
Use rsvp.js for Promises
r18897 "base/js/namespace",
Jonathan Frederic
Persistence API,...
r19350 "services/kernels/comm"
], function (_, Backbone, $, utils, IPython, comm) {
Thomas Kluyver
Clean up some JS code
r18146 "use strict";
Jonathan Frederic
Done with major changes,...
r17199 //--------------------------------------------------------------------
// WidgetManager class
//--------------------------------------------------------------------
Jonathan Frederic
MWE,...
r17200 var WidgetManager = function (comm_manager, notebook) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* Public constructor
*/
Jonathan Frederic
Done with major changes,...
r17199 WidgetManager._managers.push(this);
// Attach a comm manager to the
Jonathan Frederic
MWE,...
r17200 this.keyboard_manager = notebook.keyboard_manager;
Jonathan Frederic
Done with major changes,...
r17199 this.notebook = notebook;
this.comm_manager = comm_manager;
Jonathan Frederic
Persistence API,...
r19350 this.comm_target_name = 'ipython.widget';
Jonathan Frederic
Add convenience API
r19352 this._models = {}; /* Dictionary of model ids and model instance promises */
Jonathan Frederic
Done with major changes,...
r17199
Jonathan Frederic
Separate widget model name from com target name.
r18264 // Register with the comm manager.
Jonathan Frederic
Persistence API,...
r19350 this.comm_manager.register_target(this.comm_target_name, $.proxy(this._handle_comm_open, this));
Jonathan Frederic
Add convenience API
r19352
// Load the initial state of the widget manager if a load callback was
// registered.
Jonathan Frederic
Document set_state_callbacks and,...
r19362 var that = this;
Jonathan Frederic
Add convenience API
r19352 if (WidgetManager._load_callback) {
Jonathan Frederic
Document set_state_callbacks and,...
r19362 Promise.resolve(WidgetManager._load_callback.call(this)).then(function(state) {
that.set_state(state);
}).catch(utils.reject('Error loading widget manager state', true));
Jonathan Frederic
Add convenience API
r19352 }
// Setup state saving code.
Jonathan Frederic
Add an event that fires before the notebook saves
r19359 this.notebook.events.on('before_save.Notebook', function() {
Jonathan Frederic
Add convenience API
r19352 var save_callback = WidgetManager._save_callback;
var options = WidgetManager._get_state_options;
if (save_callback) {
that.get_state(options).then(function(state) {
save_callback.call(that, state);
Jonathan Frederic
Log save state callback failures
r19357 }).catch(utils.reject('Could not call widget save state callback.', true));
Jonathan Frederic
Add convenience API
r19352 }
});
Jonathan Frederic
Done with major changes,...
r17199 };
//--------------------------------------------------------------------
// Class level
//--------------------------------------------------------------------
WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
WidgetManager._managers = []; /* List of widget managers */
Jonathan Frederic
Add convenience API
r19352 WidgetManager._load_callback = null;
WidgetManager._save_callback = null;
Jonathan Frederic
Done with major changes,...
r17199
WidgetManager.register_widget_model = function (model_name, model_type) {
Jonathan Frederic
Updated comments to new style.
r19364 /**
* Registers a widget model by name.
*/
Jonathan Frederic
Done with major changes,...
r17199 WidgetManager._model_types[model_name] = model_type;
};
WidgetManager.register_widget_view = function (view_name, view_type) {
Jonathan Frederic
Updated comments to new style.
r19364 /**
* Registers a widget view by name.
*/
Jonathan Frederic
Done with major changes,...
r17199 WidgetManager._view_types[view_name] = view_type;
};
Jonathan Frederic
Add convenience API
r19352 WidgetManager.set_state_callbacks = function (load_callback, save_callback, options) {
Jonathan Frederic
Updated comments to new style.
r19364 /**
* Registers callbacks for widget state persistence.
*
* Parameters
* ----------
* load_callback: function()
* function that is called when the widget manager state should be
* loaded. This function should return a promise for the widget
* manager state. An empty state is an empty dictionary `{}`.
* save_callback: function(state as dictionary)
* function that is called when the notebook is saved or autosaved.
* The current state of the widget manager is passed in as the first
* argument.
*/
Jonathan Frederic
Add convenience API
r19352 WidgetManager._load_callback = load_callback;
WidgetManager._save_callback = save_callback;
WidgetManager._get_state_options = options;
// Use the load callback to immediately load widget states.
WidgetManager._managers.forEach(function(manager) {
if (load_callback) {
Jonathan Frederic
Document set_state_callbacks and,...
r19362 Promise.resolve(load_callback.call(manager)).then(function(state) {
manager.set_state(state);
}).catch(utils.reject('Error loading widget manager state', true));
Jonathan Frederic
Add convenience API
r19352 }
});
};
Jonathan Frederic
Make widgets persist across page refresh
r19430 // Use local storage to persist widgets across page refresh by default.
Jonathan Frederic
Associate persistence with URL
r19771 // LocalStorage is per domain, so we need to explicitly set the URL
// that the widgets are associated with so they don't show on other
// pages hosted by the noteboook server.
var url = [window.location.protocol, '//', window.location.host, window.location.pathname].join('');
Min RK
store widget state in single key...
r19800 var key = 'widgets:' + url;
Jonathan Frederic
Make widgets persist across page refresh
r19430 WidgetManager.set_state_callbacks(function() {
Min RK
store widget state in single key...
r19800 if (localStorage[key]) {
return JSON.parse(localStorage[key]);
Jonathan Frederic
Associate persistence with URL
r19771 }
return {};
Jonathan Frederic
Make widgets persist across page refresh
r19430 }, function(state) {
Min RK
store widget state in single key...
r19800 localStorage[key] = JSON.stringify(state);
Jonathan Frederic
Make widgets persist across page refresh
r19430 });
Jonathan Frederic
Done with major changes,...
r17199 //--------------------------------------------------------------------
// Instance level
//--------------------------------------------------------------------
WidgetManager.prototype.display_view = function(msg, model) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* Displays a view for a particular model.
*/
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 var cell = this.get_msg_cell(msg.parent_header.msg_id);
if (cell === null) {
return Promise.reject(new Error("Could not determine where the display" +
" message was from. Widget will not be displayed"));
} else {
return this.display_view_in_cell(cell, model)
Jonathan Frederic
Rebase fixes
r19361 .catch(utils.reject('Could not display view', true));
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 }
Jonathan Frederic
Persistence API,...
r19350 };
WidgetManager.prototype.display_view_in_cell = function(cell, model) {
// Displays a view in a cell.
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 if (cell.display_widget_view) {
var that = this;
return cell.display_widget_view(this.create_view(model, {
cell: cell,
// Only set cell_index when view is displayed as directly.
cell_index: that.notebook.find_cell_index(cell),
})).then(function(view) {
that._handle_display_view(view);
view.trigger('displayed');
Jonathan Frederic
Another widget promise bug fix
r19420 return view;
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 }).catch(utils.reject('Could not create or display view', true));
} else {
return Promise.reject(new Error('Cell does not have a `display_widget_view` method'));
}
Jonathan Frederic
Done with major changes,...
r17199 };
WidgetManager.prototype._handle_display_view = function (view) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* Have the IPython keyboard manager disable its event
* handling so the widget can capture keyboard input.
* Note, this is only done on the outer most widgets.
*/
Jonathan Frederic
Done with major changes,...
r17199 if (this.keyboard_manager) {
this.keyboard_manager.register_events(view.$el);
Jonathan Frederic
Initial stab at adding promises to the widget framework.
r18882 if (view.additional_elements) {
for (var i = 0; i < view.additional_elements.length; i++) {
Jonathan Frederic
Add a WrappedError class
r18895 this.keyboard_manager.register_events(view.additional_elements[i]);
Jonathan Frederic
Initial stab at adding promises to the widget framework.
r18882 }
}
Jonathan Frederic
Done with major changes,...
r17199 }
};
Thomas Kluyver
Allow widget views to be loaded from require modules...
r18142
Thomas Kluyver
Clean up some JS code
r18146 WidgetManager.prototype.create_view = function(model, options) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* Creates a promise for a view of a given model
*
* Make sure the view creation is not out of order with
* any state updates.
*/
Jonathan Frederic
Move the display Promise into a lower level method,...
r18905 model.state_change = model.state_change.then(function() {
Jonathan Frederic
Current state with lots and lots of debugging junk
r18906
Jonathan Frederic
Move the display Promise into a lower level method,...
r18905 return utils.load_class(model.get('_view_name'), model.get('_view_module'),
Jonathan Frederic
Make display also pend on set_state.
r18894 WidgetManager._view_types).then(function(ViewType) {
// If a view is passed into the method, use that view's cell as
// the cell for the view that is created.
options = options || {};
if (options.parent !== undefined) {
options.cell = options.parent.options.cell;
}
// Create and render the view...
var parameters = {model: model, options: options};
var view = new ViewType(parameters);
view.listenTo(model, 'destroy', view.remove);
Jason Grout
Wait for any promises returned by a view's render method before considering the view created...
r19158 return Promise.resolve(view.render()).then(function() {return view;});
Jason Grout
Catch errors after our then()s, instead of in parallel with them (this missing exceptions)...
r19080 }).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true));
Jonathan Frederic
Move the display Promise into a lower level method,...
r18905 });
Jonathan Frederic
Added changes discussed with @jasongrout
r19365 var id = utils.uuid();
model.views[id] = model.state_change;
model.state_change.then(function(view) {
Jonathan Frederic
Use once instead of on
r19371 view.once('remove', function() {
Jonathan Frederic
Added changes discussed with @jasongrout
r19365 delete view.model.views[id];
}, this);
});
Jonathan Frederic
Move the display Promise into a lower level method,...
r18905 return model.state_change;
Jonathan Frederic
Done with major changes,...
r17199 };
WidgetManager.prototype.get_msg_cell = function (msg_id) {
var cell = null;
// First, check to see if the msg was triggered by cell execution.
if (this.notebook) {
cell = this.notebook.get_msg_cell(msg_id);
}
if (cell !== null) {
return cell;
}
// Second, check to see if a get_cell callback was defined
// for the message. get_cell callbacks are registered for
// widget messages, so this block is actually checking to see if the
// message was triggered by a widget.
var kernel = this.comm_manager.kernel;
if (kernel) {
var callbacks = kernel.get_callbacks_for_msg(msg_id);
if (callbacks && callbacks.iopub &&
callbacks.iopub.get_cell !== undefined) {
return callbacks.iopub.get_cell();
Jonathan Frederic
Completely remove cell from model and view.
r14534 }
Jonathan Frederic
Done with major changes,...
r17199 }
// Not triggered by a cell or widget (no get_cell callback
// exists).
return null;
};
WidgetManager.prototype.callbacks = function (view) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* callback handlers specific a view
*/
Jonathan Frederic
Done with major changes,...
r17199 var callbacks = {};
if (view && view.options.cell) {
// Try to get output handlers
var cell = view.options.cell;
var handle_output = null;
var handle_clear_output = null;
if (cell.output_area) {
handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
Jonathan Frederic
Removed comm dependency of widget model and view
r14469 }
Jonathan Frederic
Rebase fixes
r18517 // Create callback dictionary using what is known
Brian E. Granger
Remove model from WidgetManager._model on comm:close.
r16652 var that = this;
Jonathan Frederic
Done with major changes,...
r17199 callbacks = {
iopub : {
output : handle_output,
clear_output : handle_clear_output,
// Special function only registered by widget messages.
// Allows us to get the cell for a message so we know
// where to add widgets if the code requires it.
get_cell : function () {
return cell;
},
},
};
}
return callbacks;
};
WidgetManager.prototype.get_model = function (model_id) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* Get a promise for a model by model id.
*/
Jonathan Frederic
Bug fixes
r18898 return this._models[model_id];
Jonathan Frederic
Done with major changes,...
r17199 };
WidgetManager.prototype._handle_comm_open = function (comm, msg) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* Handle when a comm is opened.
*/
Jonathan Frederic
Remove message promise.
r18916 return this.create_model({
Jonathan Frederic
Rebase fixes
r18517 model_name: msg.content.data.model_name,
model_module: msg.content.data.model_module,
Jason Grout
Catch errors after our then()s, instead of in parallel with them (this missing exceptions)...
r19080 comm: comm}).catch(utils.reject("Couldn't create a model.", true));
Jonathan Frederic
Enable widget instanciation from front-end.
r18506 };
Jonathan Frederic
Clarified API for the create_model function,...
r18512 WidgetManager.prototype.create_model = function (options) {
Jonathan Frederic
Ran function comment conversion tool
r19176 /**
* Create and return a promise for a new widget model
*
* Minimally, one must provide the model_name and widget_class
* parameters to create a model from Javascript.
*
* Example
* --------
* JS:
* IPython.notebook.kernel.widget_manager.create_model({
* model_name: 'WidgetModel',
* widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
* .then(function(model) { console.log('Create success!', model); },
* $.proxy(console.error, console));
*
* Parameters
* ----------
* options: dictionary
* Dictionary of options with the following contents:
* model_name: string
* Target name of the widget model to create.
* model_module: (optional) string
* Module name of the widget model to create.
* widget_class: (optional) string
* Target name of the widget in the back-end.
* comm: (optional) Comm
*
* Create a comm if it wasn't provided.
*/
Jonathan Frederic
Add Promise support to models.
r18884 var comm = options.comm;
if (!comm) {
comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
}
var that = this;
var model_id = comm.comm_id;
Jonathan Frederic
Add a WrappedError class
r18895 var model_promise = utils.load_class(options.model_name, options.model_module, WidgetManager._model_types)
Jason Grout
Simplify code by using Promises in a better way; try_load -> load
r18887 .then(function(ModelType) {
var widget_model = new ModelType(that, model_id, comm);
widget_model.once('comm:close', function () {
delete that._models[model_id];
});
Jonathan Frederic
Persistence API,...
r19350 widget_model.name = options.model_name;
widget_model.module = options.model_module;
Jason Grout
Simplify code by using Promises in a better way; try_load -> load
r18887 return widget_model;
Jonathan Frederic
Add a WrappedError class
r18895
Jason Grout
Add some error handling for creating views and models
r18889 }, function(error) {
delete that._models[model_id];
Jonathan Frederic
Add a WrappedError class
r18895 var wrapped_error = new utils.WrappedError("Couldn't create model", error);
Jonathan Frederic
use es6
r18907 return Promise.reject(wrapped_error);
Jason Grout
Simplify code by using Promises in a better way; try_load -> load
r18887 });
this._models[model_id] = model_promise;
return model_promise;
Jonathan Frederic
Done with major changes,...
r17199 };
Jonathan Frederic
Almost done!...
r17198
Jonathan Frederic
Persistence API,...
r19350 WidgetManager.prototype.get_state = function(options) {
Jonathan Frederic
Updated comments to new style.
r19364 /**
* Asynchronously get the state of the widget manager.
*
* This includes all of the widget models and the cells that they are
* displayed in.
*
* Parameters
* ----------
* options: dictionary
* Dictionary of options with the following contents:
* only_displayed: (optional) boolean=false
* Only return models with one or more displayed views.
* not_live: (optional) boolean=false
* Include models that have comms with severed connections.
*
* Returns
* -------
* Promise for a state dictionary
*/
Jonathan Frederic
Promises...
r19351 var that = this;
return utils.resolve_promises_dict(this._models).then(function(models) {
Jonathan Frederic
Persistence API,...
r19350 var state = {};
Jonathan Frederic
Fix persistence API,...
r19428
var model_promises = [];
Jonathan Frederic
Persistence API,...
r19350 for (var model_id in models) {
if (models.hasOwnProperty(model_id)) {
var model = models[model_id];
// If the model has one or more views defined for it,
// consider it displayed.
var displayed_flag = !(options && options.only_displayed) || Object.keys(model.views).length > 0;
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 var live_flag = (options && options.not_live) || model.comm_live;
if (displayed_flag && live_flag) {
Jonathan Frederic
Promises...
r19351 state[model_id] = {
Jonathan Frederic
Persistence API,...
r19350 model_name: model.name,
model_module: model.module,
Jonathan Frederic
Promises...
r19351 state: model.get_state(),
Jonathan Frederic
Persistence API,...
r19350 views: [],
};
// Get the views that are displayed *now*.
Jonathan Frederic
Fix persistence API,...
r19428 model_promises.push(utils.resolve_promises_dict(model.views).then(function(model_views) {
for (var id in model_views) {
if (model_views.hasOwnProperty(id)) {
var view = model_views[id];
if (view.options.cell_index) {
state[model_id].views.push(view.options.cell_index);
}
Jonathan Frederic
Fix, only return views that are direct children of the cell
r19358 }
Jonathan Frederic
Persistence API,...
r19350 }
Jonathan Frederic
Fix persistence API,...
r19428 }));
Jonathan Frederic
Persistence API,...
r19350 }
}
}
Jonathan Frederic
Fix persistence API,...
r19428 return Promise.all(model_promises).then(function() { return state; });
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 }).catch(utils.reject('Could not get state of widget manager', true));
Jonathan Frederic
Persistence API,...
r19350 };
WidgetManager.prototype.set_state = function(state) {
Jonathan Frederic
Updated comments to new style.
r19364 /**
* Set the notebook's state.
*
* Reconstructs all of the widget models and attempts to redisplay the
* widgets in the appropriate cells by cell index.
*/
Jonathan Frederic
Persistence API,...
r19350 // Get the kernel when it's available.
var that = this;
Jonathan Frederic
Add convenience API
r19352 return this._get_connected_kernel().then(function(kernel) {
Jonathan Frederic
Persistence API,...
r19350
Jonathan Frederic
Promises...
r19351 // Recreate all the widget models for the given state and
// display the views.
that.all_views = [];
var model_ids = Object.keys(state);
for (var i = 0; i < model_ids.length; i++) {
var model_id = model_ids[i];
Jonathan Frederic
Persistence API,...
r19350 // Recreate a comm using the widget's model id (model_id == comm_id).
Jonathan Frederic
Promises...
r19351 var new_comm = new comm.Comm(kernel.widget_manager.comm_target_name, model_id);
Jonathan Frederic
Persistence API,...
r19350 kernel.comm_manager.register_comm(new_comm);
// Create the model using the recreated comm. When the model is
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 // created we don't know yet if the comm is valid so set_comm_live
Jonathan Frederic
Persistence API,...
r19350 // false. Once we receive the first state push from the back-end
// we know the comm is alive.
Jonathan Frederic
Promises...
r19351 var views = kernel.widget_manager.create_model({
Jonathan Frederic
Persistence API,...
r19350 comm: new_comm,
Jonathan Frederic
Promises...
r19351 model_name: state[model_id].model_name,
model_module: state[model_id].model_module})
.then(function(model) {
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 model.set_comm_live(false);
Jonathan Frederic
Promises...
r19351 var view_promise = Promise.resolve().then(function() {
return model.set_state(state[model.id].state);
}).then(function() {
model.request_state().then(function() {
Jonathan Frederic
Address @jasongrout 's review comments, take 2
r19360 model.set_comm_live(true);
Jonathan Frederic
Persistence API,...
r19350 });
Jonathan Frederic
Promises...
r19351
// Display the views of the model.
var views = [];
var model_views = state[model.id].views;
for (var j=0; j<model_views.length; j++) {
var cell_index = model_views[j];
var cell = that.notebook.get_cell(cell_index);
views.push(that.display_view_in_cell(cell, model));
}
return Promise.all(views);
Jonathan Frederic
Persistence API,...
r19350 });
Jonathan Frederic
Promises...
r19351 return view_promise;
});
that.all_views.push(views);
Jonathan Frederic
Persistence API,...
r19350 }
Jonathan Frederic
Promises...
r19351 return Promise.all(that.all_views);
Jonathan Frederic
Add convenience API
r19352 }).catch(utils.reject('Could not set widget manager state.', true));
};
WidgetManager.prototype._get_connected_kernel = function() {
Jonathan Frederic
Updated comments to new style.
r19364 /**
* Gets a promise for a connected kernel
*/
Jonathan Frederic
Add convenience API
r19352 var that = this;
return new Promise(function(resolve, reject) {
if (that.comm_manager &&
that.comm_manager.kernel &&
that.comm_manager.kernel.is_connected()) {
Jonathan Frederic
Persistence API,...
r19350
Jonathan Frederic
Add convenience API
r19352 resolve(that.comm_manager.kernel);
} else {
that.notebook.events.on('kernel_connected.Kernel', function(event, data) {
resolve(data.kernel);
});
}
Jonathan Frederic
Persistence API,...
r19350 });
};
Jonathan Frederic
Initial stab at adding promises to the widget framework.
r18882 // Backwards compatibility.
Jonathan Frederic
Fix all the tests
r17216 IPython.WidgetManager = WidgetManager;
Jonathan Frederic
Fix all the bugs!
r17203 return {'WidgetManager': WidgetManager};
Jonathan Frederic
Done with major changes,...
r17199 });