init.js
349 lines
| 13.6 KiB
| application/javascript
|
JavascriptLexer
Jonathan Frederic
|
r14470 | //---------------------------------------------------------------------------- | ||
// Copyright (C) 2013 The IPython Development Team | ||||
// | ||||
// Distributed under the terms of the BSD License. The full license is in | ||||
// the file COPYING, distributed as part of this software. | ||||
//---------------------------------------------------------------------------- | ||||
//============================================================================ | ||||
Jonathan Frederic
|
r14546 | // Basic Widgets | ||
Jonathan Frederic
|
r14470 | //============================================================================ | ||
Jonathan Frederic
|
r14546 | <<<<<<< HEAD | ||
Jonathan Frederic
|
r14470 | /** | ||
* @module IPython | ||||
* @namespace IPython | ||||
**/ | ||||
Jonathan Frederic
|
r14471 | define(["notebook/js/widgetmanager", | ||
Jonathan Frederic
|
r14483 | "underscore", | ||
"backbone"], | ||||
Jonathan Frederic
|
r14470 | function(widget_manager, underscore, backbone){ | ||
//-------------------------------------------------------------------- | ||||
// WidgetModel class | ||||
//-------------------------------------------------------------------- | ||||
var WidgetModel = Backbone.Model.extend({ | ||||
Jonathan Frederic
|
r14512 | constructor: function (widget_manager, model_id, comm) { | ||
Jonathan Frederic
|
r14470 | this.widget_manager = widget_manager; | ||
this.pending_msgs = 0; | ||||
this.msg_throttle = 3; | ||||
this.msg_buffer = null; | ||||
Jonathan Frederic
|
r14512 | this.id = model_id; | ||
Jason Grout
|
r14501 | this.views = []; | ||
Jonathan Frederic
|
r14470 | |||
if (comm !== undefined) { | ||||
// Remember comm associated with the model. | ||||
this.comm = comm; | ||||
comm.model = this; | ||||
// Hook comm messages up to model. | ||||
comm.on_close($.proxy(this._handle_comm_closed, this)); | ||||
comm.on_msg($.proxy(this._handle_comm_msg, this)); | ||||
} | ||||
return Backbone.Model.apply(this); | ||||
}, | ||||
Jason Grout
|
r14486 | send: function (content, callbacks) { | ||
if (this.comm !== undefined) { | ||||
Jonathan Frederic
|
r14470 | var data = {method: 'custom', custom_content: content}; | ||
this.comm.send(data, callbacks); | ||||
} | ||||
}, | ||||
// Handle when a widget is closed. | ||||
_handle_comm_closed: function (msg) { | ||||
Jason Grout
|
r14501 | this.trigger('comm:close'); | ||
Jason Grout
|
r14490 | delete this.comm.model; // Delete ref so GC will collect widget model. | ||
delete this.comm; | ||||
Jonathan Frederic
|
r14512 | delete this.model_id; // Delete id from model so widget manager cleans up. | ||
Jason Grout
|
r14501 | // TODO: Handle deletion, like this.destroy(), and delete views, etc. | ||
Jonathan Frederic
|
r14470 | }, | ||
Jason Grout
|
r14486 | // Handle incoming comm msg. | ||
Jonathan Frederic
|
r14470 | _handle_comm_msg: function (msg) { | ||
var method = msg.content.data.method; | ||||
switch (method) { | ||||
case 'update': | ||||
Jonathan Frederic
|
r14474 | this.apply_update(msg.content.data.state); | ||
Jonathan Frederic
|
r14470 | break; | ||
case 'custom': | ||||
Jason Grout
|
r14486 | this.trigger('msg:custom', msg.content.data.custom_content); | ||
Jonathan Frederic
|
r14470 | break; | ||
Jason Grout
|
r14501 | default: | ||
// pass on to widget manager | ||||
this.widget_manager.handle_msg(msg, this); | ||||
Jonathan Frederic
|
r14470 | } | ||
}, | ||||
// Handle when a widget is updated via the python side. | ||||
Jonathan Frederic
|
r14474 | apply_update: function (state) { | ||
Jonathan Frederic
|
r14470 | this.updating = true; | ||
try { | ||||
for (var key in state) { | ||||
if (state.hasOwnProperty(key)) { | ||||
Jason Grout
|
r14486 | this.set(key, state[key]); | ||
Jonathan Frederic
|
r14470 | } | ||
} | ||||
Jason Grout
|
r14501 | //TODO: are there callbacks that make sense in this case? If so, attach them here as an option | ||
Jonathan Frederic
|
r14470 | this.save(); | ||
} finally { | ||||
this.updating = false; | ||||
} | ||||
}, | ||||
Jason Grout
|
r14486 | _handle_status: function (msg, callbacks) { | ||
Jonathan Frederic
|
r14470 | //execution_state : ('busy', 'idle', 'starting') | ||
Jason Grout
|
r14501 | if (this.comm !== undefined && msg.content.execution_state ==='idle') { | ||
// Send buffer if this message caused another message to be | ||||
// throttled. | ||||
if (this.msg_buffer !== null && | ||||
this.msg_throttle === this.pending_msgs) { | ||||
var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer}; | ||||
this.comm.send(data, callbacks); | ||||
this.msg_buffer = null; | ||||
} else { | ||||
// Only decrease the pending message count if the buffer | ||||
// doesn't get flushed (sent). | ||||
--this.pending_msgs; | ||||
Jonathan Frederic
|
r14470 | } | ||
} | ||||
}, | ||||
// Custom syncronization logic. | ||||
_handle_sync: function (method, options) { | ||||
var model_json = this.toJSON(); | ||||
var attr; | ||||
// Only send updated state if the state hasn't been changed | ||||
// during an update. | ||||
Jason Grout
|
r14486 | if (this.comm !== undefined) { | ||
Jonathan Frederic
|
r14470 | if (!this.updating) { | ||
if (this.pending_msgs >= this.msg_throttle) { | ||||
// The throttle has been exceeded, buffer the current msg so | ||||
// it can be sent once the kernel has finished processing | ||||
// some of the existing messages. | ||||
if (method=='patch') { | ||||
if (this.msg_buffer === null) { | ||||
this.msg_buffer = $.extend({}, model_json); // Copy | ||||
} | ||||
for (attr in options.attrs) { | ||||
this.msg_buffer[attr] = options.attrs[attr]; | ||||
} | ||||
} else { | ||||
this.msg_buffer = $.extend({}, model_json); // Copy | ||||
} | ||||
} else { | ||||
// We haven't exceeded the throttle, send the message like | ||||
// normal. If this is a patch operation, just send the | ||||
// changes. | ||||
var send_json = model_json; | ||||
if (method =='patch') { | ||||
send_json = {}; | ||||
for (attr in options.attrs) { | ||||
send_json[attr] = options.attrs[attr]; | ||||
} | ||||
} | ||||
Jonathan Frederic
|
r14543 | var data = {method: 'backbone', sync_data: send_json}; | ||
Jason Grout
|
r14501 | this.comm.send(data, options.callbacks); | ||
Jonathan Frederic
|
r14470 | this.pending_msgs++; | ||
} | ||||
} | ||||
} | ||||
// Since the comm is a one-way communication, assume the message | ||||
// arrived. | ||||
return model_json; | ||||
}, | ||||
Jason Grout
|
r14491 | }); | ||
//-------------------------------------------------------------------- | ||||
// WidgetView class | ||||
//-------------------------------------------------------------------- | ||||
var BaseWidgetView = Backbone.View.extend({ | ||||
initialize: function(options) { | ||||
this.model.on('change',this.update,this); | ||||
Jason Grout
|
r14501 | this.widget_manager = options.widget_manager; | ||
this.comm_manager = options.widget_manager.comm_manager; | ||||
Jason Grout
|
r14528 | this.options = options.options; | ||
Jason Grout
|
r14501 | this.child_views = []; | ||
Jason Grout
|
r14528 | this.model.views.push(this); | ||
Jason Grout
|
r14491 | }, | ||
Jason Grout
|
r14501 | update: function(){ | ||
// update view to be consistent with this.model | ||||
// triggered on model change | ||||
}, | ||||
Jason Grout
|
r14491 | |||
Jason Grout
|
r14528 | <<<<<<< HEAD | ||
Jonathan Frederic
|
r14532 | <<<<<<< HEAD | ||
Jonathan Frederic
|
r14512 | child_view: function(model_id, view_name) { | ||
Jonathan Frederic
|
r14532 | ======= | ||
child_view: function(model_id, view_name, options) { | ||||
Jonathan Frederic
|
r14533 | <<<<<<< HEAD | ||
Jonathan Frederic
|
r14532 | >>>>>>> s/comm_id/model_id (left over from before) | ||
Jason Grout
|
r14501 | // create and return a child view, given a comm id for a model and (optionally) a view name | ||
Jonathan Frederic
|
r14533 | ======= | ||
// create and return a child view, given a model id for a model and (optionally) a view name | ||||
>>>>>>> Updated comm id comments in view to model id | ||||
Jason Grout
|
r14501 | // if the view name is not given, it defaults to the model's default view attribute | ||
Jonathan Frederic
|
r14512 | var child_model = this.widget_manager.get_model(model_id); | ||
Jonathan Frederic
|
r14534 | <<<<<<< HEAD | ||
Jason Grout
|
r14501 | var child_view = this.widget_manager.create_view(child_model, view_name, this.cell); | ||
Jonathan Frederic
|
r14534 | ======= | ||
var child_view = this.widget_manager.create_view(child_model, view_name, options); | ||||
>>>>>>> Completely remove cell from model and view. | ||||
Jonathan Frederic
|
r14512 | this.child_views[model_id] = child_view; | ||
Jason Grout
|
r14528 | ======= | ||
child_view: function(comm_id, view_name, options) { | ||||
// create and return a child view, given a comm id for a model and (optionally) a view name | ||||
// if the view name is not given, it defaults to the model's default view attribute | ||||
var child_model = this.comm_manager.comms[comm_id].model; | ||||
var child_view = this.widget_manager.create_view(child_model, view_name, this.cell, options); | ||||
this.child_views[comm_id] = child_view; | ||||
>>>>>>> Add widget view options in creating child views | ||||
Jason Grout
|
r14501 | return child_view; | ||
}, | ||||
Jason Grout
|
r14503 | |||
update_child_views: function(old_list, new_list) { | ||||
Jonathan Frederic
|
r14533 | // this function takes an old list and new list of model ids | ||
Jason Grout
|
r14503 | // views in child_views that correspond to deleted ids are deleted | ||
// views corresponding to added ids are added child_views | ||||
// delete old views | ||||
_.each(_.difference(old_list, new_list), function(element, index, list) { | ||||
var view = this.child_views[element]; | ||||
delete this.child_views[element]; | ||||
view.remove(); | ||||
}, this); | ||||
// add new views | ||||
_.each(_.difference(new_list, old_list), function(element, index, list) { | ||||
// this function adds the view to the child_views dictionary | ||||
this.child_view(element); | ||||
}, this); | ||||
}, | ||||
Jonathan Frederic
|
r14544 | callbacks: function(){ | ||
return this.widget_manager.callbacks(this); | ||||
} | ||||
Jason Grout
|
r14491 | |||
Jason Grout
|
r14501 | render: function(){ | ||
// render the view. By default, this is only called the first time the view is created | ||||
}, | ||||
Jason Grout
|
r14491 | send: function (content) { | ||
Jonathan Frederic
|
r14544 | this.model.send(content, this.callbacks()); | ||
Jason Grout
|
r14491 | }, | ||
touch: function () { | ||||
Jonathan Frederic
|
r14544 | this.model.save(this.model.changedAttributes(), {patch: true, callbacks: this.callbacks()}); | ||
Jonathan Frederic
|
r14470 | }, | ||
}); | ||||
Jason Grout
|
r14486 | var WidgetView = BaseWidgetView.extend({ | ||
initialize: function (options) { | ||||
Jason Grout
|
r14501 | // TODO: make changes more granular (e.g., trigger on visible:change) | ||
this.model.on('change', this.update, this); | ||||
Jason Grout
|
r14502 | this.model.on('msg:custom', this.on_msg, this); | ||
Jason Grout
|
r14501 | BaseWidgetView.prototype.initialize.apply(this, arguments); | ||
Jonathan Frederic
|
r14470 | }, | ||
Jason Grout
|
r14502 | |||
on_msg: function(msg) { | ||||
switch(msg.msg_type) { | ||||
case 'add_class': | ||||
this.add_class(msg.selector, msg.class_list); | ||||
break; | ||||
case 'remove_class': | ||||
this.remove_class(msg.selector, msg.class_list); | ||||
break; | ||||
} | ||||
}, | ||||
Jonathan Frederic
|
r14470 | add_class: function (selector, class_list) { | ||
var elements = this._get_selector_element(selector); | ||||
if (elements.length > 0) { | ||||
elements.addClass(class_list); | ||||
} | ||||
}, | ||||
remove_class: function (selector, class_list) { | ||||
var elements = this._get_selector_element(selector); | ||||
if (elements.length > 0) { | ||||
elements.removeClass(class_list); | ||||
} | ||||
}, | ||||
Jonathan Frederic
|
r14506 | |||
Jason Grout
|
r14497 | update: function () { | ||
Jason Grout
|
r14501 | // the very first update seems to happen before the element is finished rendering | ||
// so we use setTimeout to give the element time to render | ||||
var e = this.$el; | ||||
var visible = this.model.get('visible'); | ||||
setTimeout(function() {e.toggle(visible)},0); | ||||
Jason Grout
|
r14502 | var css = this.model.get('_css'); | ||
Jason Grout
|
r14501 | if (css === undefined) {return;} | ||
Jason Grout
|
r14492 | for (var selector in css) { | ||
if (css.hasOwnProperty(selector)) { | ||||
// Apply the css traits to all elements that match the selector. | ||||
var elements = this._get_selector_element(selector); | ||||
if (elements.length > 0) { | ||||
var css_traits = css[selector]; | ||||
for (var css_key in css_traits) { | ||||
if (css_traits.hasOwnProperty(css_key)) { | ||||
elements.css(css_key, css_traits[css_key]); | ||||
Jonathan Frederic
|
r14470 | } | ||
} | ||||
} | ||||
} | ||||
} | ||||
}, | ||||
_get_selector_element: function (selector) { | ||||
// Get the elements via the css selector. If the selector is | ||||
// blank, apply the style to the $el_to_style element. If | ||||
// the $el_to_style element is not defined, use apply the | ||||
// style to the view's element. | ||||
Jason Grout
|
r14492 | var elements; | ||
Jonathan Frederic
|
r14470 | if (selector === undefined || selector === null || selector === '') { | ||
if (this.$el_to_style === undefined) { | ||||
elements = this.$el; | ||||
} else { | ||||
elements = this.$el_to_style; | ||||
} | ||||
Jason Grout
|
r14492 | } else { | ||
Jason Grout
|
r14501 | elements = this.$el.find(selector); | ||
} | ||||
Jonathan Frederic
|
r14470 | return elements; | ||
}, | ||||
}); | ||||
IPython.WidgetModel = WidgetModel; | ||||
IPython.WidgetView = WidgetView; | ||||
Jason Grout
|
r14501 | IPython.BaseWidgetView = BaseWidgetView; | ||
Jonathan Frederic
|
r14472 | |||
return widget_manager; | ||||
Jason Grout
|
r14486 | }); | ||
Jonathan Frederic
|
r14546 | ======= | ||
define([ | ||||
"notebook/js/widgets/widget_bool", | ||||
"notebook/js/widgets/widget_button", | ||||
"notebook/js/widgets/widget_container", | ||||
"notebook/js/widgets/widget_float", | ||||
"notebook/js/widgets/widget_float_range", | ||||
"notebook/js/widgets/widget_image", | ||||
"notebook/js/widgets/widget_int", | ||||
"notebook/js/widgets/widget_int_range", | ||||
"notebook/js/widgets/widget_multicontainer", | ||||
"notebook/js/widgets/widget_selection", | ||||
"notebook/js/widgets/widget_string", | ||||
], function(){ return true; }); | ||||
>>>>>>> renamed: basic_widgets.js -> init.js | ||||