From f58ca8b9cc3b0ee65ab44671710197424fc96ce9 2014-01-16 10:56:01
From: Jonathan Frederic <jdfreder@calpoly.edu>
Date: 2014-01-16 10:56:01
Subject: [PATCH] LOTS OF WIDGET CHANGES
Moved model-like code out of manager.
Added parent/child API.
Throttling now occurs on a model by model level.
View/cell association is fixed for the most part, but there is still
     one assumption being made in handle_com_msg.

---

diff --git a/IPython/html/static/notebook/js/widget.js b/IPython/html/static/notebook/js/widget.js
index 0f24fed..a3d7633 100644
--- a/IPython/html/static/notebook/js/widget.js
+++ b/IPython/html/static/notebook/js/widget.js
@@ -20,8 +20,8 @@
 // Use require.js 'define' method so that require.js is intelligent enough to
 // syncronously load everything within this file when it is being 'required' 
 // elsewhere.
-define(["static/components/underscore/underscore-min.js",
-         "static/components/backbone/backbone-min.js",
+define(["../../components/underscore/underscore-min.js",
+         "../../components/backbone/backbone-min.js",
         ], function(){
 
     // Only run once on a notebook.
@@ -31,15 +31,236 @@ define(["static/components/underscore/underscore-min.js",
         // WidgetModel class
         //--------------------------------------------------------------------
         var WidgetModel = Backbone.Model.extend({
-            apply: function(sender) {
+            constructor: function(comm_manager, comm, widget_view_types) {
+                this.comm_manager = comm_manager;
+                this.widget_view_types = widget_view_types;
+                this.pending_msgs = 0;
+                this.msg_throttle = 3;
+                this.msg_buffer = {};
+                this.views = {};
+
+                // 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);
+            },
+
+
+            update_other_views: function(caller) {
+                this.last_modified_view = caller;
                 this.save(this.changedAttributes(), {patch: true});
 
-                for (var index in this.views) {
-                    var view = this.views[index];
-                    if (view !== sender) {
+                for (var cell_index in this.views) {
+                    var view = this.views[cell_index];
+                    if (view !== caller) {
                         view.refresh();    
                     }
                 }
+            },
+
+
+            handle_status: function (output_area, msg) {
+                //execution_state : ('busy', 'idle', 'starting')
+                if (msg.content.execution_state=='idle') {
+                    
+                    // Send buffer if this message caused another message to be
+                    // throttled.
+                    if (this.msg_throttle == this.pending_msgs && 
+                        this.msg_buffer.length > 0) {
+                        
+                        var output_area = this._get_msg_output_area(msg);
+                        var callbacks = this._make_callbacks(output_area);
+                        var data = {sync_method: 'patch', sync_data: this.msg_buffer};
+                        comm.send(data, callbacks);   
+                        this.msg_buffer = {};
+                    } else {
+
+                        // Only decrease the pending message count if the buffer
+                        // doesn't get flushed (sent).
+                        --this.pending_msgs;
+                    }
+                }
+            },
+
+
+            // Custom syncronization logic.
+            handle_sync: function (method, options) {
+                var model_json = this.toJSON();
+
+                // Only send updated state if the state hasn't been changed 
+                // during an update.
+                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') {
+                            for (var 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 (var attr in options.attrs) {
+                                send_json[attr] = options.attrs[attr];
+                            }
+                        }
+
+                        var data = {sync_method: method, sync_data: send_json};
+                        var output_area = this._get_view_output_area(this.last_modified_view);
+                        var callbacks = this._make_callbacks();
+                        this.comm.send(data, callbacks);    
+                        this.pending_msgs++;
+                    }
+                }
+                
+                // Since the comm is a one-way communication, assume the message 
+                // arrived.
+                return model_json;
+            },
+
+
+            // Handle incomming comm msg.
+            handle_comm_msg: function (comm, msg) {
+                var method = msg.content.data.method;
+                switch (method){
+                    case 'display':
+
+////////////////////////// TODO: Get cell index via currently executing cell.
+                        var cell_index = IPython.notebook.get_selected_index()-1;
+
+                        this.display_view(msg.content.data.view_name, 
+                            msg.content.data.parent,
+                            cell_index);
+                        break;
+                    case 'update':
+                        this.handle_update(msg.content.data.state);
+                        break;
+                }
+            }
+
+
+            // Handle when a widget is updated via the python side.
+            handle_update: function (state) {
+                this.updating = true;
+                try {
+                    for (var key in state) {
+                        if (state.hasOwnProperty(key)) {
+                            if (key == "_css"){
+                                this.css = state[key];
+                            } else {
+                                this.set(key, state[key]); 
+                            }
+                        }
+                    }
+                    this.id = this.comm.comm_id;
+                    this.save();
+                } finally {
+                    this.updating = false;
+                }
+            }
+
+
+            // Handle when a widget is closed.
+            handle_comm_closed: function (msg) {
+                for (var cell_index in this.views) {
+                    var view = this.views[cell_index];
+                    view.remove();
+                }
+            }
+
+
+            // Create view that represents the model.
+            display_view = function (view_name, parent_comm_id, cell_index) {
+                var view = new this.widget_view_types[view_name]({model: this});
+                view.render();
+                this.views[cell_index] = view;
+                view.cell_index = cell_index;
+
+                // Handle when the view element is remove from the page.
+                var that = this;
+                view.$el.on("remove", function(){ 
+                    var index = that.views.indexOf(view);
+                    if (index > -1) {
+                        that.views.splice(index, 1);
+                    }
+                    view.remove(); // Clean-up view 
+
+                    // Close the comm if there are no views left.
+                    if (that.views.length()==0) {
+                        that.comm.close();     
+                    }
+                });
+
+                var display_child = null;
+                if (parent_comm_id != undefined) {
+                    var parent_comm = this.comm_manager.comms[parent_comm_id];
+                    var parent_model = parent_comm.model;
+                    var parent_view = parent_model.views[cell_id];
+                    if (parent_view.display_child != undefined) {
+                        display_child = parent_view.display_child;
+                    }
+                }
+
+                if (display_child != null) {
+                    display_child(view.$el);
+                } else {
+                    // No parent view is defined or exists.  Add the view's 
+                    // element to cell's widget div.
+                    var cell = IPython.notebook.get_cell(cell_index);
+                    cell.element.find('.widget_area').find('.widget_subarea')
+                        .append(view.$el)
+                        .parent().show(); // Show the widget_area (parent of widget_subarea)
+                
+                }
+
+                // Update the view based on the model contents.
+                view.refresh();
+            }
+
+
+            // Build a callback dict.
+            _make_callbacks: function (output_area) {
+                var callbacks = {};
+                if (output_area != null) {
+                    var that = this;
+                    callbacks = {
+                        iopub : {
+                            output : $.proxy(output_area.handle_output, output_area),
+                            clear_output : $.proxy(output_area.handle_clear_output, output_area),
+                            status : function(msg){
+                                that.handle_status(output_area, msg);
+                            },
+                        },
+                    };
+                }
+                return callbacks;
+            },
+
+
+            // Get the cell output area corresponding to the view.
+            _get_view_output_area : function (view) {
+                return this._get_cell_output_area(view.cell_index);
+            }
+
+
+            // Get the cell output area corresponding to the cell id.
+            _get_cell_output_area : function (cell_id) {
+                var cell = IPython.notebook.get_cell(cell_id)
+                return cell.output_area;
             }
         });
 
@@ -53,8 +274,8 @@ define(["static/components/underscore/underscore-min.js",
                 this.model.on('change',this.refresh,this);
             },
             
-            refresh: function() {
-                this.update();
+            update: function() {
+                var results = Backbone.Model.prototype.update.call(this);
                 
                 if (this.model.css != undefined) {
                     for (var selector in this.model.css) {
@@ -79,6 +300,7 @@ define(["static/components/underscore/underscore-min.js",
                         }
                     }
                 }
+                return results;
             },
         });
 
@@ -86,240 +308,43 @@ define(["static/components/underscore/underscore-min.js",
         //--------------------------------------------------------------------
         // WidgetManager class
         //--------------------------------------------------------------------
-        // Public constructor
         var WidgetManager = function(comm_manager){
             this.comm_manager = comm_manager;
             this.widget_model_types = {};
             this.widget_view_types = {};
-            this.model_widget_views = {};
-            this.pending_msgs = 0;
-            this.msg_throttle = 3;
-            this.msg_buffer = {};
             
             var that = this;
             Backbone.sync = function(method, model, options, error) {
-                var result = that.handle_sync(method, model, options);
+                var result = model.handle_sync(method, options);
                 if (options.success) {
                   options.success(result);
                 }
             }; 
         }
 
-        // Register a widget model type.
+
         WidgetManager.prototype.register_widget_model = function (widget_model_name, widget_model_type) {
-            
             // Register the widget with the comm manager.  Make sure to pass this object's context
             // in so `this` works in the call back.
             this.comm_manager.register_target(widget_model_name, $.proxy(this.handle_com_open, this));
-            
-            // Register the types of the model and view correspong to this widget type.  Later
-            // the widget manager will initialize these when the comm is opened.
             this.widget_model_types[widget_model_name] = widget_model_type;
         }
 
-        // Register a widget view type.
+
         WidgetManager.prototype.register_widget_view = function (widget_view_name, widget_view_type) {
             this.widget_view_types[widget_view_name] = widget_view_type;
         }
 
-        // Handle when a comm is opened.
+
         WidgetManager.prototype.handle_com_open = function (comm, msg) {
             var widget_type_name = msg.content.target_name;
-            
-            // Create the corresponding widget model.
-            var widget_model = new this.widget_model_types[widget_type_name];
-
-            // Remember comm associated with the model.
-            widget_model.comm = comm;
-            comm.model = widget_model;
-
-            // Create an array to remember the views associated with the model.
-            widget_model.views = [];
-
-            // Add a handle to delete the control when the comm is closed.
-            var that = this;
-            var handle_close = function(msg) {
-                that.handle_comm_closed(comm, msg);
-            }
-            comm.on_close(handle_close);
-
-            // Handle incomming messages.
-            var handle_msg = function(msg) {
-                that.handle_comm_msg(comm, msg);
-            }
-            comm.on_msg(handle_msg);
+            var widget_model = new this.widget_model_types[widget_type_name](this.comm_manager, comm, view_types);
         }
 
-        // Create view that represents the model.
-        WidgetManager.prototype.show_view = function (widget_area, widget_model, widget_view_name) {
-            var widget_view = new this.widget_view_types[widget_view_name]({model: widget_model});
-            widget_view.render();
-            widget_model.views.push(widget_view);
-
-            // Handle when the view element is remove from the page.
-            widget_view.$el.on("remove", function(){ 
-                var index = widget_model.views.indexOf(widget_view);
-                if (index > -1) {
-                    widget_model.views.splice(index, 1);
-                }
-                widget_view.remove(); // Clean-up view 
-
-                // Close the comm if there are no views left.
-                if (widget_model.views.length()==0) {
-                    widget_model.comm.close();     
-                }
-            });
-
-            // Add the view's element to cell's widget div.
-            widget_area
-                .append(widget_view.$el)
-                .parent().show(); // Show the widget_area (parent of widget_subarea)
-
-            // Update the view based on the model contents.
-            widget_view.refresh();
-        }
-
-        // Handle incomming comm msg.
-        WidgetManager.prototype.handle_comm_msg = function (comm, msg) {
-            // Different logic for different methods.
-            var method = msg.content.data.method;
-            switch (method){
-                case 'show':
-
-                    // TODO: Get cell from registered output handler.
-                    var cell = IPython.notebook.get_cell(IPython.notebook.get_selected_index()-1);
-                    var widget_subarea = cell.element.find('.widget_area').find('.widget_subarea');
-
-                    if (msg.content.data.parent != undefined) {
-                        var find_results = widget_subarea.find("." + msg.content.data.parent);
-                        if (find_results.length > 0) {
-                            widget_subarea = find_results;
-                        }
-                    }
-
-                    this.show_view(widget_subarea, comm.model, msg.content.data.view_name);
-                    break;
-                case 'update':
-                    this.handle_update(comm, msg.content.data.state);
-                    break;
-            }
-        }
-
-        // Handle when a widget is updated via the python side.
-        WidgetManager.prototype.handle_update = function (comm, state) {
-            this.updating = true;
-            for (var key in state) {
-                if (state.hasOwnProperty(key)) {
-                    if (key == "_css"){
-                        comm.model.css = state[key];
-                    } else {
-                        comm.model.set(key, state[key]); 
-                    }
-                }
-            }
-            comm.model.id = comm.comm_id;
-            comm.model.save();
-            this.updating = false;
-        }
-
-        // Handle when a widget is closed.
-        WidgetManager.prototype.handle_comm_closed = function (comm, msg) {
-            for (var view_index in comm.model.views) {
-                var view = comm.model.views[view_index];
-                view.remove();
-            }
-        }
-
-        // Handle when a msg status changes in the kernel.
-        WidgetManager.prototype.handle_status = function (msg) {
-            //execution_state : ('busy', 'idle', 'starting')
-            if (msg.content.execution_state=='idle') {
-                // Send buffer if this message caused another message to be
-                // throttled.
-                if (this.msg_throttle == --this.pending_msgs && 
-                    this.msg_buffer.length > 0) {
-                    var outputarea = this._get_msg_outputarea(msg);
-                    var callbacks = this._make_callbacks(outputarea);
-                    var data = {sync_method: 'patch', sync_data: this.msg_buffer};
-                    comm.send(data, callbacks);   
-                    this.pending_msgs++;
-                    this.msg_buffer = {};
-                }
-            }
-        }
-
-        // Get the cell output area corresponding to the comm.
-        WidgetManager.prototype._get_comm_outputarea = function (comm) {
-            // TODO: get element from comm instead of guessing
-            var cell = IPython.notebook.get_cell(IPython.notebook.get_selected_index())
-            return cell.output_area;
-        }
-
-        // Get the cell output area corresponding to the msg_id.
-        WidgetManager.prototype._get_msg_outputarea = function (msg) {
-            // TODO: get element from msg_id instead of guessing
-            // msg.parent_header.msg_id
-            var cell = IPython.notebook.get_cell(IPython.notebook.get_selected_index())
-            return cell.output_area;
-        }
-
-        // Build a callback dict.
-        WidgetManager.prototype._make_callbacks = function (outputarea) {
-            var callbacks = {};
-            if (outputarea != null) {
-                callbacks = {
-                    iopub : {
-                    status : $.proxy(this.handle_status, this),
-                    output : $.proxy(outputarea.handle_output, outputarea),
-                    clear_output : $.proxy(outputarea.handle_clear_output, outputarea)}
-                };
-            }
-            return callbacks;
-        }
-
-        // Send widget state to python backend.
-        WidgetManager.prototype.handle_sync = function (method, model, options) {
-            var model_json = model.toJSON();
-
-            // Only send updated state if the state hasn't been changed during an update.
-            if (!this.updating) {
-                // Create a callback for the output if the widget has an output area associate with it.
-                var callbacks = this._make_callbacks(this._get_comm_outputarea(comm));
-                var comm = model.comm;
-
-                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') {
-                        for (var 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 (var attr in options.attrs) {
-                            send_json[attr] = options.attrs[attr];
-                        }
-                    }
-                    var data = {sync_method: method, sync_data: send_json};
-                    comm.send(data, callbacks);    
-                    this.pending_msgs++;
-                }
-            }
-            
-            // Since the comm is a one-way communication, assume the message 
-            // arrived.
-            return model_json;
-        }
 
+        //--------------------------------------------------------------------
+        // Init code
+        //--------------------------------------------------------------------
         IPython.WidgetManager = WidgetManager;
         IPython.WidgetModel = WidgetModel;
         IPython.WidgetView = WidgetView;
diff --git a/IPython/html/static/notebook/js/widgets/bool.js b/IPython/html/static/notebook/js/widgets/bool.js
index 104ec7c..d121631 100644
--- a/IPython/html/static/notebook/js/widgets/bool.js
+++ b/IPython/html/static/notebook/js/widgets/bool.js
@@ -1,5 +1,5 @@
 
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     
     var BoolWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('BoolWidgetModel', BoolWidgetModel);
@@ -9,8 +9,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
 
             var $label = $('<label />')
                 .addClass('checkbox')
@@ -39,7 +38,7 @@ require(["notebook/js/widget"], function(){
         handleChanged: function(e) { 
             this.user_invoked_update = true;
             this.model.set('value', $(e.target).prop('checked'));
-            this.model.apply(this);
+            this.model.update_other_views(this);
             this.user_invoked_update = false;
         },
     });
@@ -51,8 +50,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
 
             this.$button = $('<button />')
                 .addClass('btn')
@@ -82,7 +80,7 @@ require(["notebook/js/widget"], function(){
         handleClick: function(e) { 
             this.user_invoked_update = true;
             this.model.set('value', ! $(e.target).hasClass('active'));
-            this.model.apply(this);
+            this.model.update_other_views(this);
             this.user_invoked_update = false;
         },
     });
diff --git a/IPython/html/static/notebook/js/widgets/button.js b/IPython/html/static/notebook/js/widgets/button.js
index 12818f7..6709a7c 100644
--- a/IPython/html/static/notebook/js/widgets/button.js
+++ b/IPython/html/static/notebook/js/widgets/button.js
@@ -1,5 +1,5 @@
 
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     
     var ButtonWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('ButtonWidgetModel', ButtonWidgetModel);
@@ -13,7 +13,7 @@ require(["notebook/js/widget"], function(){
                 .addClass('btn')
                 .click(function() {
                     that.model.set('clicks', that.model.get('clicks') + 1);
-                    that.model.apply(that);
+                    that.model.update_other_views(that);
                 });
 
             this.update(); // Set defaults.
diff --git a/IPython/html/static/notebook/js/widgets/container.js b/IPython/html/static/notebook/js/widgets/container.js
index 7617f1f..e2a3ae4 100644
--- a/IPython/html/static/notebook/js/widgets/container.js
+++ b/IPython/html/static/notebook/js/widgets/container.js
@@ -1,4 +1,4 @@
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     var ContainerModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('ContainerWidgetModel', ContainerModel);
 
@@ -6,8 +6,7 @@ require(["notebook/js/widget"], function(){
         
         render : function(){
             this.$el = $('<div />')
-                .addClass('widget_container')
-                .addClass(this.model.comm.comm_id);
+                .addClass('widget_container');
         },
         
         update : function(){
@@ -24,6 +23,10 @@ require(["notebook/js/widget"], function(){
                 }    
             }
         },
+
+        display_child : function($element) {
+            this.$el.append($element);
+        },
     });
 
     IPython.notebook.widget_manager.register_widget_view('ContainerView', ContainerView);
diff --git a/IPython/html/static/notebook/js/widgets/float.js b/IPython/html/static/notebook/js/widgets/float.js
index a793d2a..349b68f 100644
--- a/IPython/html/static/notebook/js/widgets/float.js
+++ b/IPython/html/static/notebook/js/widgets/float.js
@@ -1,4 +1,4 @@
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     var FloatWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('FloatWidgetModel', FloatWidgetModel);
 });
\ No newline at end of file
diff --git a/IPython/html/static/notebook/js/widgets/float_range.js b/IPython/html/static/notebook/js/widgets/float_range.js
index 479b579..bcbfc45 100644
--- a/IPython/html/static/notebook/js/widgets/float_range.js
+++ b/IPython/html/static/notebook/js/widgets/float_range.js
@@ -1,4 +1,4 @@
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     var FloatRangeWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('FloatRangeWidgetModel', FloatRangeWidgetModel);
 
@@ -7,8 +7,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
             this.$slider = $('<div />')
                 .slider({})
                 .addClass('slider');
@@ -41,7 +40,7 @@ require(["notebook/js/widget"], function(){
         events: { "slide" : "handleSliderChange" }, 
         handleSliderChange: function(e, ui) { 
             this.model.set('value', ui.value); 
-            this.model.apply(this);
+            this.model.update_other_views(this);
         },
     });
 
@@ -53,8 +52,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
             this.$textbox = $('<input type="text" />')
                 .addClass('input')
                 .appendTo(this.$el);
@@ -106,7 +104,7 @@ require(["notebook/js/widget"], function(){
                 if (numericalValue != this.model.get('value')) {
                     this.changing = true;
                     this.model.set('value', numericalValue);
-                    this.model.apply(this);
+                    this.model.update_other_views(this);
                     this.changing = false;
                 }
             }
diff --git a/IPython/html/static/notebook/js/widgets/int.js b/IPython/html/static/notebook/js/widgets/int.js
index 4595862..0cb7d8f 100644
--- a/IPython/html/static/notebook/js/widgets/int.js
+++ b/IPython/html/static/notebook/js/widgets/int.js
@@ -1,4 +1,4 @@
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     var IntWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('IntWidgetModel', IntWidgetModel);
 });
\ No newline at end of file
diff --git a/IPython/html/static/notebook/js/widgets/int_range.js b/IPython/html/static/notebook/js/widgets/int_range.js
index a312964..e70768e 100644
--- a/IPython/html/static/notebook/js/widgets/int_range.js
+++ b/IPython/html/static/notebook/js/widgets/int_range.js
@@ -1,4 +1,4 @@
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     var IntRangeWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('IntRangeWidgetModel', IntRangeWidgetModel);
 
@@ -41,7 +41,7 @@ require(["notebook/js/widget"], function(){
         events: { "slide" : "handleSliderChange" }, 
         handleSliderChange: function(e, ui) { 
             this.model.set('value', ~~ui.value); // Double bit-wise not to truncate decimel
-            this.model.apply(this);
+            this.model.update_other_views(this);
         },
     });
 
@@ -105,7 +105,7 @@ require(["notebook/js/widget"], function(){
                 if (numericalValue != this.model.get('value')) {
                     this.changing = true;
                     this.model.set('value', numericalValue);
-                    this.model.apply(this);
+                    this.model.update_other_views(this);
                     this.changing = false;
                 }
             }
diff --git a/IPython/html/static/notebook/js/widgets/selection.js b/IPython/html/static/notebook/js/widgets/selection.js
index f151661..4caefde 100644
--- a/IPython/html/static/notebook/js/widgets/selection.js
+++ b/IPython/html/static/notebook/js/widgets/selection.js
@@ -1,4 +1,4 @@
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     var SelectionWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('SelectionWidgetModel', SelectionWidgetModel);
 
@@ -8,8 +8,7 @@ require(["notebook/js/widget"], function(){
         render : function(){
             
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
             this.$buttongroup = $('<div />')
                                 .addClass('widget_item')
                                 .addClass('btn-group')
@@ -44,7 +43,7 @@ require(["notebook/js/widget"], function(){
                     .html(items[index])
                     .on('click', function(e){
                         that.model.set('value', $(e.target).html(), this );
-                        that.model.apply(that);
+                        that.model.update_other_views(that);
                     })
                 
                 this.$droplist.append($('<li />').append(item_button))
@@ -72,8 +71,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
             this.update();
         },
         
@@ -99,7 +97,7 @@ require(["notebook/js/widget"], function(){
                         .prependTo($label)
                         .on('click', function(e){
                             that.model.set('value', $(e.target).val(), this);
-                            that.model.apply();
+                            that.model.update_other_views();
                         });
                 }
                 
@@ -137,8 +135,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
             this.$buttongroup = $('<div />')
                 .addClass('btn-group')
                 .attr('data-toggle', 'buttons-radio')
@@ -164,7 +161,7 @@ require(["notebook/js/widget"], function(){
                         .appendTo(this.$buttongroup)
                         .on('click', function(e){
                             that.model.set('value', $(e.target).html(), this);
-                            that.model.apply();
+                            that.model.update_other_views();
                         });
                 }
                 
diff --git a/IPython/html/static/notebook/js/widgets/string.js b/IPython/html/static/notebook/js/widgets/string.js
index fc87878..280c696 100644
--- a/IPython/html/static/notebook/js/widgets/string.js
+++ b/IPython/html/static/notebook/js/widgets/string.js
@@ -1,4 +1,4 @@
-require(["notebook/js/widget"], function(){
+require(["../static/notebook/js/widget"], function(){
     var StringWidgetModel = IPython.WidgetModel.extend({});
     IPython.notebook.widget_manager.register_widget_model('StringWidgetModel', StringWidgetModel);
 
@@ -6,8 +6,7 @@ require(["notebook/js/widget"], function(){
       
         // Called when view is rendered.
         render : function(){
-            this.$el = $('<div />')
-                .addClass(this.model.comm.comm_id);
+            this.$el = $('<div />');
             this.update(); // Set defaults.
         },
         
@@ -26,8 +25,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
             this.$textbox = $('<textarea />')
                 .attr('rows', 5)
                 .appendTo(this.$el);
@@ -50,7 +48,7 @@ require(["notebook/js/widget"], function(){
         handleChanging: function(e) { 
             this.user_invoked_update = true;
             this.model.set('value', e.target.value);
-            this.model.apply(this);
+            this.model.update_other_views(this);
             this.user_invoked_update = false;
         },
     });
@@ -62,8 +60,7 @@ require(["notebook/js/widget"], function(){
         // Called when view is rendered.
         render : function(){
             this.$el
-                .html('')
-                .addClass(this.model.comm.comm_id);
+                .html('');
             this.$textbox = $('<input type="text" />')
                 .addClass('input')
                 .appendTo(this.$el);
@@ -86,7 +83,7 @@ require(["notebook/js/widget"], function(){
         handleChanging: function(e) { 
             this.user_invoked_update = true;
             this.model.set('value', e.target.value);
-            this.model.apply(this);
+            this.model.update_other_views(this);
             this.user_invoked_update = false;
         },
     });
diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py
index 08e03c4..a822ffc 100644
--- a/IPython/html/widgets/widget.py
+++ b/IPython/html/widgets/widget.py
@@ -18,7 +18,7 @@ def init_widget_js():
         name = filename.rsplit('.', 1)[0]
         if not (name == 'widget' or name == '__init__') and name.startswith('widget_'):
             # Remove 'widget_' from the start of the name before compiling the path.
-            js_path = 'static/notebook/js/widgets/%s.js' % name[7:]
+            js_path = '../static/notebook/js/widgets/%s.js' % name[7:]
             display(Javascript(data='$.getScript("%s");' % js_path))  
 
 
@@ -141,13 +141,13 @@ class Widget(LoggingConfigurable):
             
         # Show view.
         if self.parent is None:
-            self.comm.send({"method": "show", "view_name": view_name})
+            self.comm.send({"method": "display", "view_name": view_name})
         else:
-            self.comm.send({"method": "show", 
+            self.comm.send({"method": "display", 
                             "view_name": view_name,
                             "parent": self.parent.comm.comm_id})
         
-        # Now show children if any.
+        # Now display children if any.
         for child in self.children:
             child._repr_widget_()
         return None