Show More
@@ -0,0 +1,229 b'' | |||||
|
1 | //---------------------------------------------------------------------------- | |||
|
2 | // Copyright (C) 2013 The IPython Development Team | |||
|
3 | // | |||
|
4 | // Distributed under the terms of the BSD License. The full license is in | |||
|
5 | // the file COPYING, distributed as part of this software. | |||
|
6 | //---------------------------------------------------------------------------- | |||
|
7 | ||||
|
8 | //============================================================================ | |||
|
9 | // WidgetModel, WidgetView, and WidgetManager | |||
|
10 | //============================================================================ | |||
|
11 | /** | |||
|
12 | * Base Widget classes | |||
|
13 | * @module IPython | |||
|
14 | * @namespace IPython | |||
|
15 | * @submodule widget | |||
|
16 | */ | |||
|
17 | ||||
|
18 | ||||
|
19 | // require(['components/underscore/underscore-min.js', | |||
|
20 | // 'components/backbone/backbone-min.js'], | |||
|
21 | ||||
|
22 | var IPython = function (IPython) { | |||
|
23 | "use strict"; | |||
|
24 | ||||
|
25 | //----------------------------------------------------------------------- | |||
|
26 | // WidgetModel class | |||
|
27 | //----------------------------------------------------------------------- | |||
|
28 | var WidgetModel = Backbone.Model.extend({}); | |||
|
29 | ||||
|
30 | ||||
|
31 | //----------------------------------------------------------------------- | |||
|
32 | // WidgetView class | |||
|
33 | //----------------------------------------------------------------------- | |||
|
34 | var WidgetView = Backbone.View.extend({ | |||
|
35 | ||||
|
36 | initialize: function() { | |||
|
37 | this.model.on('change',this.refresh,this); | |||
|
38 | }, | |||
|
39 | ||||
|
40 | refresh: function() { | |||
|
41 | this.update(); | |||
|
42 | ||||
|
43 | if (this.model.css != undefined) { | |||
|
44 | for (var selector in this.model.css) { | |||
|
45 | if (this.model.css.hasOwnProperty(selector)) { | |||
|
46 | ||||
|
47 | // Get the elements via the css selector. If the selector is | |||
|
48 | // blank, assume the current element is the target. | |||
|
49 | var elements = this.$el.find(selector); | |||
|
50 | if (selector=='') { | |||
|
51 | elements = this.$el; | |||
|
52 | } | |||
|
53 | ||||
|
54 | // Apply the css traits to all elements that match the selector. | |||
|
55 | if (elements.length>0){ | |||
|
56 | var css_traits = this.model.css[selector]; | |||
|
57 | for (var css_key in css_traits) { | |||
|
58 | if (css_traits.hasOwnProperty(css_key)) { | |||
|
59 | elements.css(css_key, css_traits[css_key]); | |||
|
60 | } | |||
|
61 | } | |||
|
62 | } | |||
|
63 | } | |||
|
64 | } | |||
|
65 | } | |||
|
66 | }, | |||
|
67 | }); | |||
|
68 | ||||
|
69 | ||||
|
70 | //----------------------------------------------------------------------- | |||
|
71 | // WidgetManager class | |||
|
72 | //----------------------------------------------------------------------- | |||
|
73 | // Public constructor | |||
|
74 | var WidgetManager = function(comm_manager){ | |||
|
75 | this.comm_manager = comm_manager; | |||
|
76 | this.widget_model_types = {}; | |||
|
77 | this.widget_view_types = {}; | |||
|
78 | this.model_widget_views = {}; | |||
|
79 | ||||
|
80 | var that = this; | |||
|
81 | Backbone.sync = function(method, model, options, error) { | |||
|
82 | var result = that.send_sync(method, model); | |||
|
83 | if (options.success) { | |||
|
84 | options.success(result); | |||
|
85 | } | |||
|
86 | }; | |||
|
87 | } | |||
|
88 | ||||
|
89 | // Register a widget model type. | |||
|
90 | WidgetManager.prototype.register_widget_model = function (widget_model_name, widget_model_type) { | |||
|
91 | ||||
|
92 | // Register the widget with the comm manager. Make sure to pass this object's context | |||
|
93 | // in so `this` works in the call back. | |||
|
94 | this.comm_manager.register_target(widget_model_name, $.proxy(this.handle_com_open, this)); | |||
|
95 | ||||
|
96 | // Register the types of the model and view correspong to this widget type. Later | |||
|
97 | // the widget manager will initialize these when the comm is opened. | |||
|
98 | this.widget_model_types[widget_model_name] = widget_model_type; | |||
|
99 | } | |||
|
100 | ||||
|
101 | // Register a widget view type. | |||
|
102 | WidgetManager.prototype.register_widget_view = function (widget_view_name, widget_view_type) { | |||
|
103 | this.widget_view_types[widget_view_name] = widget_view_type; | |||
|
104 | } | |||
|
105 | ||||
|
106 | // Handle when a comm is opened. | |||
|
107 | WidgetManager.prototype.handle_com_open = function (comm, msg) { | |||
|
108 | var widget_type_name = msg.content.target_name; | |||
|
109 | ||||
|
110 | // Create the corresponding widget model. | |||
|
111 | var widget_model = new this.widget_model_types[widget_type_name]; | |||
|
112 | ||||
|
113 | // Remember comm associated with the model. | |||
|
114 | widget_model.comm = comm; | |||
|
115 | comm.model = widget_model; | |||
|
116 | ||||
|
117 | // Create an array to remember the views associated with the model. | |||
|
118 | widget_model.views = []; | |||
|
119 | ||||
|
120 | // Add a handle to delete the control when the comm is closed. | |||
|
121 | var that = this; | |||
|
122 | var handle_close = function(msg) { | |||
|
123 | that.handle_comm_closed(comm, msg); | |||
|
124 | } | |||
|
125 | comm.on_close(handle_close); | |||
|
126 | ||||
|
127 | // Handle incomming messages. | |||
|
128 | var handle_msg = function(msg) { | |||
|
129 | that.handle_comm_msg(comm, msg); | |||
|
130 | } | |||
|
131 | comm.on_msg(handle_msg); | |||
|
132 | } | |||
|
133 | ||||
|
134 | // Create view that represents the model. | |||
|
135 | WidgetManager.prototype.show_view = function (widget_area, widget_model, widget_view_name) { | |||
|
136 | var widget_view = new this.widget_view_types[widget_view_name]({model: widget_model}); | |||
|
137 | widget_view.render(); | |||
|
138 | widget_model.views.push(widget_view); | |||
|
139 | ||||
|
140 | // Add the view's element to cell's widget div. | |||
|
141 | widget_area | |||
|
142 | .append($("<div />").append(widget_view.$el)); | |||
|
143 | ||||
|
144 | // Update the view based on the model contents. | |||
|
145 | widget_view.refresh(); | |||
|
146 | } | |||
|
147 | ||||
|
148 | // Handle incomming comm msg. | |||
|
149 | WidgetManager.prototype.handle_comm_msg = function (comm, msg) { | |||
|
150 | // Different logic for different methods. | |||
|
151 | var method = msg.content.data.method; | |||
|
152 | switch (method){ | |||
|
153 | case 'show': | |||
|
154 | ||||
|
155 | // TODO: Get cell from registered output handler. | |||
|
156 | var cell = IPython.notebook.get_cell(IPython.notebook.get_selected_index()-1); | |||
|
157 | var widget_subarea = cell.element.find('.widget_area').find('.widget_subarea'); | |||
|
158 | ||||
|
159 | if (msg.content.data.parent != undefined) { | |||
|
160 | var find_results = widget_subarea.find("." + msg.content.data.parent); | |||
|
161 | if (find_results.length > 0) { | |||
|
162 | widget_subarea = find_results; | |||
|
163 | } | |||
|
164 | } | |||
|
165 | ||||
|
166 | this.show_view(widget_subarea, comm.model, msg.content.data.view_name); | |||
|
167 | break; | |||
|
168 | case 'update': | |||
|
169 | this.handle_update(comm, msg.content.data.state); | |||
|
170 | break; | |||
|
171 | } | |||
|
172 | } | |||
|
173 | ||||
|
174 | // Handle when a widget is updated via the python side. | |||
|
175 | WidgetManager.prototype.handle_update = function (comm, state) { | |||
|
176 | for (var key in state) { | |||
|
177 | if (state.hasOwnProperty(key)) { | |||
|
178 | if (key=="_css"){ | |||
|
179 | comm.model.css = state[key]; | |||
|
180 | } else { | |||
|
181 | comm.model.set(key, state[key]); | |||
|
182 | } | |||
|
183 | } | |||
|
184 | } | |||
|
185 | comm.model.save(); | |||
|
186 | } | |||
|
187 | ||||
|
188 | // Handle when a widget is closed. | |||
|
189 | WidgetManager.prototype.handle_comm_closed = function (comm, msg) { | |||
|
190 | for (var view_index in comm.model.views) { | |||
|
191 | var view = comm.model.views[view_index]; | |||
|
192 | view.remove(); | |||
|
193 | } | |||
|
194 | } | |||
|
195 | ||||
|
196 | // Get the cell output area corresponding to the comm. | |||
|
197 | WidgetManager.prototype._get_comm_outputarea = function (comm) { | |||
|
198 | // TODO: get element from comm instead of guessing | |||
|
199 | var cell = IPython.notebook.get_cell(IPython.notebook.get_selected_index()) | |||
|
200 | return cell.output_area; | |||
|
201 | } | |||
|
202 | ||||
|
203 | // Send widget state to python backend. | |||
|
204 | WidgetManager.prototype.send_sync = function (method, model) { | |||
|
205 | ||||
|
206 | // Create a callback for the output if the widget has an output area associate with it. | |||
|
207 | var callbacks = {}; | |||
|
208 | var comm = model.comm; | |||
|
209 | var outputarea = this._get_comm_outputarea(comm); | |||
|
210 | if (outputarea != null) { | |||
|
211 | callbacks = { | |||
|
212 | iopub : { | |||
|
213 | output : $.proxy(outputarea.handle_output, outputarea), | |||
|
214 | clear_output : $.proxy(outputarea.handle_clear_output, outputarea)} | |||
|
215 | }; | |||
|
216 | }; | |||
|
217 | ||||
|
218 | var data = {sync_method: method, sync_data: model.toJSON()}; | |||
|
219 | comm.send(data, callbacks); | |||
|
220 | return data; | |||
|
221 | } | |||
|
222 | ||||
|
223 | IPython.WidgetManager = WidgetManager; | |||
|
224 | IPython.WidgetModel = WidgetModel; | |||
|
225 | IPython.WidgetView = WidgetView; | |||
|
226 | ||||
|
227 | return IPython; | |||
|
228 | ||||
|
229 | }; |
General Comments 0
You need to be logged in to leave comments.
Login now