Show More
@@ -62,7 +62,7 b' define([' | |||
|
62 | 62 | dummy.replaceWith(view.$el); |
|
63 | 63 | view.trigger('displayed'); |
|
64 | 64 | return view; |
|
65 |
} |
|
|
65 | }).catch(utils.reject('Could not display view', true)); | |
|
66 | 66 | } |
|
67 | 67 | }; |
|
68 | 68 | |
@@ -103,7 +103,7 b' define([' | |||
|
103 | 103 | view.listenTo(model, 'destroy', view.remove); |
|
104 | 104 | view.render(); |
|
105 | 105 | return view; |
|
106 |
} |
|
|
106 | }).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true)); | |
|
107 | 107 | }); |
|
108 | 108 | return model.state_change; |
|
109 | 109 | }; |
@@ -178,7 +178,7 b' define([' | |||
|
178 | 178 | return this.create_model({ |
|
179 | 179 | model_name: msg.content.data.model_name, |
|
180 | 180 | model_module: msg.content.data.model_module, |
|
181 | comm: comm}).catch(utils.reject("Couldn't create a model.")); | |
|
181 | comm: comm}).catch(utils.reject("Couldn't create a model.", true)); | |
|
182 | 182 | }; |
|
183 | 183 | |
|
184 | 184 | WidgetManager.prototype.create_model = function (options) { |
@@ -95,7 +95,7 b' define(["widgets/js/manager",' | |||
|
95 | 95 | } finally { |
|
96 | 96 | that.state_lock = null; |
|
97 | 97 | } |
|
98 |
} |
|
|
98 | }).catch(utils.reject("Couldn't set model state", true)); | |
|
99 | 99 | }, |
|
100 | 100 | |
|
101 | 101 | _handle_status: function (msg, callbacks) { |
@@ -292,8 +292,6 b' define(["widgets/js/manager",' | |||
|
292 | 292 | // Public constructor. |
|
293 | 293 | this.model.on('change',this.update,this); |
|
294 | 294 | this.options = parameters.options; |
|
295 | this.child_model_views = {}; | |
|
296 | this.child_views = {}; | |
|
297 | 295 | this.id = this.id || utils.uuid(); |
|
298 | 296 | this.model.views[this.id] = this; |
|
299 | 297 | this.on('displayed', function() { |
@@ -311,69 +309,7 b' define(["widgets/js/manager",' | |||
|
311 | 309 | // Create and promise that resolves to a child view of a given model |
|
312 | 310 | var that = this; |
|
313 | 311 | options = $.extend({ parent: this }, options || {}); |
|
314 |
return this.model.widget_manager.create_view(child_model, options).th |
|
|
315 | // Associate the view id with the model id. | |
|
316 | if (that.child_model_views[child_model.id] === undefined) { | |
|
317 | that.child_model_views[child_model.id] = []; | |
|
318 | } | |
|
319 | that.child_model_views[child_model.id].push(child_view.id); | |
|
320 | // Remember the view by id. | |
|
321 | that.child_views[child_view.id] = child_view; | |
|
322 | return child_view; | |
|
323 | }, utils.reject("Couldn't create child view")); | |
|
324 | }, | |
|
325 | ||
|
326 | pop_child_view: function(child_model) { | |
|
327 | // Delete a child view that was previously created using create_child_view. | |
|
328 | var view_ids = this.child_model_views[child_model.id]; | |
|
329 | if (view_ids !== undefined) { | |
|
330 | ||
|
331 | // Only delete the first view in the list. | |
|
332 | var view_id = view_ids[0]; | |
|
333 | var view = this.child_views[view_id]; | |
|
334 | delete this.child_views[view_id]; | |
|
335 | view_ids.splice(0,1); | |
|
336 | delete child_model.views[view_id]; | |
|
337 | ||
|
338 | // Remove the view list specific to this model if it is empty. | |
|
339 | if (view_ids.length === 0) { | |
|
340 | delete this.child_model_views[child_model.id]; | |
|
341 | } | |
|
342 | return view; | |
|
343 | } | |
|
344 | return null; | |
|
345 | }, | |
|
346 | ||
|
347 | do_diff: function(old_list, new_list, removed_callback, added_callback) { | |
|
348 | // Difference a changed list and call remove and add callbacks for | |
|
349 | // each removed and added item in the new list. | |
|
350 | // | |
|
351 | // Parameters | |
|
352 | // ---------- | |
|
353 | // old_list : array | |
|
354 | // new_list : array | |
|
355 | // removed_callback : Callback(item) | |
|
356 | // Callback that is called for each item removed. | |
|
357 | // added_callback : Callback(item) | |
|
358 | // Callback that is called for each item added. | |
|
359 | ||
|
360 | // Walk the lists until an unequal entry is found. | |
|
361 | var i; | |
|
362 | for (i = 0; i < new_list.length; i++) { | |
|
363 | if (i >= old_list.length || new_list[i] !== old_list[i]) { | |
|
364 | break; | |
|
365 | } | |
|
366 | } | |
|
367 | ||
|
368 | // Remove the non-matching items from the old list. | |
|
369 | for (var j = i; j < old_list.length; j++) { | |
|
370 | removed_callback(old_list[j]); | |
|
371 | } | |
|
372 | ||
|
373 | // Add the rest of the new list items. | |
|
374 | for (; i < new_list.length; i++) { | |
|
375 | added_callback(new_list[i]); | |
|
376 | } | |
|
312 | return this.model.widget_manager.create_view(child_model, options).catch(utils.reject("Couldn't create child view"), true); | |
|
377 | 313 | }, |
|
378 | 314 | |
|
379 | 315 | callbacks: function(){ |
@@ -533,11 +469,8 b' define(["widgets/js/manager",' | |||
|
533 | 469 | if ($el===undefined) { |
|
534 | 470 | $el = this.$el; |
|
535 | 471 | } |
|
536 |
|
|
|
537 | $el.removeClass(removed); | |
|
538 | }, function(added) { | |
|
539 | $el.addClass(added); | |
|
540 | }); | |
|
472 | _.difference(old_classes, new_classes).map(function(c) {$el.removeClass(c);}) | |
|
473 | _.difference(new_classes, old_classes).map(function(c) {$el.addClass(c);}) | |
|
541 | 474 | }, |
|
542 | 475 | |
|
543 | 476 | update_mapped_classes: function(class_map, trait_name, previous_trait_value, $el) { |
@@ -588,11 +521,93 b' define(["widgets/js/manager",' | |||
|
588 | 521 | }, |
|
589 | 522 | }); |
|
590 | 523 | |
|
591 | ||
|
524 | ||
|
525 | var ViewList = function(create_view, remove_view, context) { | |
|
526 | // * create_view and remove_view are default functions called when adding or removing views | |
|
527 | // * create_view takes a model and returns a view or a promise for a view for that model | |
|
528 | // * remove_view takes a view and destroys it (including calling `view.remove()`) | |
|
529 | // * each time the update() function is called with a new list, the create and remove | |
|
530 | // callbacks will be called in an order so that if you append the views created in the | |
|
531 | // create callback and remove the views in the remove callback, you will duplicate | |
|
532 | // the order of the list. | |
|
533 | // * the remove callback defaults to just removing the view (e.g., pass in null for the second parameter) | |
|
534 | // * the context defaults to the created ViewList. If you pass another context, the create and remove | |
|
535 | // will be called in that context. | |
|
536 | ||
|
537 | this.initialize.apply(this, arguments); | |
|
538 | }; | |
|
539 | ||
|
540 | _.extend(ViewList.prototype, { | |
|
541 | initialize: function(create_view, remove_view, context) { | |
|
542 | this.state_change = Promise.resolve(); | |
|
543 | this._handler_context = context || this; | |
|
544 | this._models = []; | |
|
545 | this.views = []; | |
|
546 | this._create_view = create_view; | |
|
547 | this._remove_view = remove_view || function(view) {view.remove();}; | |
|
548 | }, | |
|
549 | ||
|
550 | update: function(new_models, create_view, remove_view, context) { | |
|
551 | // the create_view, remove_view, and context arguments override the defaults | |
|
552 | // specified when the list is created. | |
|
553 | // returns a promise that resolves after this update is done | |
|
554 | var remove = remove_view || this._remove_view; | |
|
555 | var create = create_view || this._create_view; | |
|
556 | if (create === undefined || remove === undefined){ | |
|
557 | console.error("Must define a create a remove function"); | |
|
558 | } | |
|
559 | var context = context || this._handler_context; | |
|
560 | var added_views = []; | |
|
561 | var that = this; | |
|
562 | this.state_change = this.state_change.then(function() { | |
|
563 | var i; | |
|
564 | // first, skip past the beginning of the lists if they are identical | |
|
565 | for (i = 0; i < new_models.length; i++) { | |
|
566 | if (i >= that._models.length || new_models[i] !== that._models[i]) { | |
|
567 | break; | |
|
568 | } | |
|
569 | } | |
|
570 | var first_removed = i; | |
|
571 | // Remove the non-matching items from the old list. | |
|
572 | for (var j = first_removed; j < that._models.length; j++) { | |
|
573 | remove.call(context, that.views[j]); | |
|
574 | } | |
|
575 | ||
|
576 | // Add the rest of the new list items. | |
|
577 | for (; i < new_models.length; i++) { | |
|
578 | added_views.push(create.call(context, new_models[i])); | |
|
579 | } | |
|
580 | // make a copy of the input array | |
|
581 | that._models = new_models.slice(); | |
|
582 | return Promise.all(added_views).then(function(added) { | |
|
583 | Array.prototype.splice.apply(that.views, [first_removed, that.views.length].concat(added)); | |
|
584 | return that.views; | |
|
585 | }); | |
|
586 | }); | |
|
587 | return this.state_change; | |
|
588 | }, | |
|
589 | ||
|
590 | remove: function() { | |
|
591 | // removes every view in the list; convenience function for `.update([])` | |
|
592 | // that should be faster | |
|
593 | // returns a promise that resolves after this removal is done | |
|
594 | var that = this; | |
|
595 | this.state_change = this.state_change.then(function() { | |
|
596 | for (var i = 0; i < that.views.length; i++) { | |
|
597 | that._remove_view.call(that._handler_context, that.views[i]); | |
|
598 | } | |
|
599 | that._models = []; | |
|
600 | that.views = []; | |
|
601 | }); | |
|
602 | return this.state_change; | |
|
603 | }, | |
|
604 | }); | |
|
605 | ||
|
592 | 606 | var widget = { |
|
593 | 607 | 'WidgetModel': WidgetModel, |
|
594 | 608 | 'WidgetView': WidgetView, |
|
595 | 609 | 'DOMWidgetView': DOMWidgetView, |
|
610 | 'ViewList': ViewList, | |
|
596 | 611 | }; |
|
597 | 612 | |
|
598 | 613 | // For backwards compatability. |
@@ -12,16 +12,17 b' define([' | |||
|
12 | 12 | initialize: function(){ |
|
13 | 13 | // Public constructor |
|
14 | 14 | BoxView.__super__.initialize.apply(this, arguments); |
|
15 | this.model.on('change:children', function(model, value) { | |
|
16 | this.update_children(model.previous('children'), value); | |
|
15 | this.children_views = new widget.ViewList(this.add_child_model, null, this); | |
|
16 | this.listenTo(this.model, 'change:children', function(model, value) { | |
|
17 | this.children_views.update(value); | |
|
17 | 18 | }, this); |
|
18 |
this.model |
|
|
19 | this.listenTo(this.model, 'change:overflow_x', function(model, value) { | |
|
19 | 20 | this.update_overflow_x(); |
|
20 | 21 | }, this); |
|
21 |
this.model |
|
|
22 | this.listenTo(this.model, 'change:overflow_y', function(model, value) { | |
|
22 | 23 | this.update_overflow_y(); |
|
23 | 24 | }, this); |
|
24 |
this.model |
|
|
25 | this.listenTo(this.model, 'change:box_style', function(model, value) { | |
|
25 | 26 | this.update_box_style(); |
|
26 | 27 | }, this); |
|
27 | 28 | }, |
@@ -35,7 +36,7 b' define([' | |||
|
35 | 36 | // Called when view is rendered. |
|
36 | 37 | this.$box = this.$el; |
|
37 | 38 | this.$box.addClass('widget-box'); |
|
38 |
this. |
|
|
39 | this.children_views.update(this.model.get('children')); | |
|
39 | 40 | this.update_overflow_x(); |
|
40 | 41 | this.update_overflow_y(); |
|
41 | 42 | this.update_box_style(''); |
@@ -61,18 +62,6 b' define([' | |||
|
61 | 62 | this.update_mapped_classes(class_map, 'box_style', previous_trait_value, this.$box); |
|
62 | 63 | }, |
|
63 | 64 | |
|
64 | update_children: function(old_list, new_list) { | |
|
65 | // Called when the children list changes. | |
|
66 | this.do_diff(old_list, new_list, | |
|
67 | $.proxy(this.remove_child_model, this), | |
|
68 | $.proxy(this.add_child_model, this)); | |
|
69 | }, | |
|
70 | ||
|
71 | remove_child_model: function(model) { | |
|
72 | // Called when a model is removed from the children list. | |
|
73 | this.pop_child_view(model).remove(); | |
|
74 | }, | |
|
75 | ||
|
76 | 65 | add_child_model: function(model) { |
|
77 | 66 | // Called when a model is added to the children list. |
|
78 | 67 | var that = this; |
@@ -86,7 +75,15 b' define([' | |||
|
86 | 75 | view.trigger('displayed'); |
|
87 | 76 | }); |
|
88 | 77 | return view; |
|
89 |
} |
|
|
78 | }).catch(utils.reject("Couldn't add child view to box", true)); | |
|
79 | }, | |
|
80 | ||
|
81 | remove: function() { | |
|
82 | // We remove this widget before removing the children as an optimization | |
|
83 | // we want to remove the entire container from the DOM first before | |
|
84 | // removing each individual child separately. | |
|
85 | BoxView.__super__.remove.apply(this, arguments); | |
|
86 | this.children_views.remove(); | |
|
90 | 87 | }, |
|
91 | 88 | }); |
|
92 | 89 | |
@@ -94,10 +91,10 b' define([' | |||
|
94 | 91 | var FlexBoxView = BoxView.extend({ |
|
95 | 92 | render: function(){ |
|
96 | 93 | FlexBoxView.__super__.render.apply(this); |
|
97 |
this.model |
|
|
98 |
this.model |
|
|
99 |
this.model |
|
|
100 |
this.model |
|
|
94 | this.listenTo(this.model, 'change:orientation', this.update_orientation, this); | |
|
95 | this.listenTo(this.model, 'change:flex', this._flex_changed, this); | |
|
96 | this.listenTo(this.model, 'change:pack', this._pack_changed, this); | |
|
97 | this.listenTo(this.model, 'change:align', this._align_changed, this); | |
|
101 | 98 | this._flex_changed(); |
|
102 | 99 | this._pack_changed(); |
|
103 | 100 | this._align_changed(); |
@@ -244,10 +241,7 b' define([' | |||
|
244 | 241 | this._shown_once = false; |
|
245 | 242 | this.popped_out = true; |
|
246 | 243 | |
|
247 |
this. |
|
|
248 | this.model.on('change:children', function(model, value) { | |
|
249 | this.update_children(model.previous('children'), value); | |
|
250 | }, this); | |
|
244 | this.children_views.update(this.model.get('children')) | |
|
251 | 245 | }, |
|
252 | 246 | |
|
253 | 247 | hide: function() { |
@@ -9,18 +9,23 b' define([' | |||
|
9 | 9 | ], function(widget, utils, $){ |
|
10 | 10 | |
|
11 | 11 | var AccordionView = widget.DOMWidgetView.extend({ |
|
12 | initialize: function(){ | |
|
13 | AccordionView.__super__.initialize.apply(this, arguments); | |
|
14 | ||
|
15 | this.containers = []; | |
|
16 | this.model_containers = {}; | |
|
17 | this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this); | |
|
18 | this.listenTo(this.model, 'change:children', function(model, value) { | |
|
19 | this.children_views.update(value); | |
|
20 | }, this); | |
|
21 | }, | |
|
22 | ||
|
12 | 23 | render: function(){ |
|
13 | 24 | // Called when view is rendered. |
|
14 | 25 | var guid = 'panel-group' + utils.uuid(); |
|
15 | 26 | this.$el |
|
16 | 27 | .attr('id', guid) |
|
17 | 28 | .addClass('panel-group'); |
|
18 | this.containers = []; | |
|
19 | this.model_containers = {}; | |
|
20 | this.update_children([], this.model.get('children')); | |
|
21 | this.model.on('change:children', function(model, value, options) { | |
|
22 | this.update_children(model.previous('children'), value); | |
|
23 | }, this); | |
|
24 | 29 | this.model.on('change:selected_index', function(model, value, options) { |
|
25 | 30 | this.update_selected_index(model.previous('selected_index'), value, options); |
|
26 | 31 | }, this); |
@@ -31,6 +36,7 b' define([' | |||
|
31 | 36 | this.on('displayed', function() { |
|
32 | 37 | this.update_titles(); |
|
33 | 38 | }, this); |
|
39 | this.children_views.update(this.model.get('children')); | |
|
34 | 40 | }, |
|
35 | 41 | |
|
36 | 42 | update_titles: function(titles) { |
@@ -61,25 +67,18 b' define([' | |||
|
61 | 67 | } |
|
62 | 68 | } |
|
63 | 69 | }, |
|
64 | ||
|
65 | update_children: function(old_list, new_list) { | |
|
66 | // Called when the children list is modified. | |
|
67 | this.do_diff(old_list, | |
|
68 | new_list, | |
|
69 | $.proxy(this.remove_child_model, this), | |
|
70 | $.proxy(this.add_child_model, this)); | |
|
71 | }, | |
|
72 | 70 | |
|
73 |
remove_child_ |
|
|
71 | remove_child_view: function(view) { | |
|
74 | 72 | // Called when a child is removed from children list. |
|
73 | // TODO: does this handle two different views of the same model as children? | |
|
74 | var model = view.model; | |
|
75 | 75 | var accordion_group = this.model_containers[model.id]; |
|
76 | 76 | this.containers.splice(accordion_group.container_index, 1); |
|
77 | 77 | delete this.model_containers[model.id]; |
|
78 | 78 | accordion_group.remove(); |
|
79 | this.pop_child_view(model); | |
|
80 | 79 | }, |
|
81 | 80 | |
|
82 |
add_child_ |
|
|
81 | add_child_view: function(model) { | |
|
83 | 82 | // Called when a child is added to children list. |
|
84 | 83 | var index = this.containers.length; |
|
85 | 84 | var uuid = utils.uuid(); |
@@ -126,7 +125,15 b' define([' | |||
|
126 | 125 | view.trigger('displayed'); |
|
127 | 126 | }); |
|
128 | 127 | return view; |
|
129 |
} |
|
|
128 | }).catch(utils.reject("Couldn't add child view to box", true)); | |
|
129 | }, | |
|
130 | ||
|
131 | remove: function() { | |
|
132 | // We remove this widget before removing the children as an optimization | |
|
133 | // we want to remove the entire container from the DOM first before | |
|
134 | // removing each individual child separately. | |
|
135 | AccordionView.__super__.remove.apply(this, arguments); | |
|
136 | this.children_views.remove(); | |
|
130 | 137 | }, |
|
131 | 138 | }); |
|
132 | 139 | |
@@ -134,8 +141,13 b' define([' | |||
|
134 | 141 | var TabView = widget.DOMWidgetView.extend({ |
|
135 | 142 | initialize: function() { |
|
136 | 143 | // Public constructor. |
|
137 | this.containers = []; | |
|
138 | 144 | TabView.__super__.initialize.apply(this, arguments); |
|
145 | ||
|
146 | this.containers = []; | |
|
147 | this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this); | |
|
148 | this.listenTo(this.model, 'change:children', function(model, value) { | |
|
149 | this.children_views.update(value); | |
|
150 | }, this); | |
|
139 | 151 | }, |
|
140 | 152 | |
|
141 | 153 | render: function(){ |
@@ -149,11 +161,7 b' define([' | |||
|
149 | 161 | this.$tab_contents = $('<div />', {id: uuid + 'Content'}) |
|
150 | 162 | .addClass('tab-content') |
|
151 | 163 | .appendTo(this.$el); |
|
152 | this.containers = []; | |
|
153 | this.update_children([], this.model.get('children')); | |
|
154 | this.model.on('change:children', function(model, value, options) { | |
|
155 | this.update_children(model.previous('children'), value); | |
|
156 | }, this); | |
|
164 | this.children_views.update(this.model.get('children')); | |
|
157 | 165 | }, |
|
158 | 166 | |
|
159 | 167 | update_attr: function(name, value) { |
@@ -161,24 +169,15 b' define([' | |||
|
161 | 169 | this.$tabs.css(name, value); |
|
162 | 170 | }, |
|
163 | 171 | |
|
164 |
|
|
|
165 | // Called when the children list is modified. | |
|
166 | this.do_diff(old_list, | |
|
167 | new_list, | |
|
168 | $.proxy(this.remove_child_model, this), | |
|
169 | $.proxy(this.add_child_model, this)); | |
|
170 | }, | |
|
171 | ||
|
172 | remove_child_model: function(model) { | |
|
172 | remove_child_view: function(view) { | |
|
173 | 173 | // Called when a child is removed from children list. |
|
174 | var view = this.pop_child_view(model); | |
|
175 | 174 | this.containers.splice(view.parent_tab.tab_text_index, 1); |
|
176 | 175 | view.parent_tab.remove(); |
|
177 | 176 | view.parent_container.remove(); |
|
178 | 177 | view.remove(); |
|
179 | 178 | }, |
|
180 | 179 | |
|
181 |
add_child_ |
|
|
180 | add_child_view: function(model) { | |
|
182 | 181 | // Called when a child is added to children list. |
|
183 | 182 | var index = this.containers.length; |
|
184 | 183 | var uuid = utils.uuid(); |
@@ -221,7 +220,7 b' define([' | |||
|
221 | 220 | view.trigger('displayed'); |
|
222 | 221 | }); |
|
223 | 222 | return view; |
|
224 |
} |
|
|
223 | }).catch(utils.reject("Couldn't add child view to box", true)); | |
|
225 | 224 | }, |
|
226 | 225 | |
|
227 | 226 | update: function(options) { |
@@ -254,6 +253,14 b' define([' | |||
|
254 | 253 | .removeClass('active'); |
|
255 | 254 | this.containers[index].tab('show'); |
|
256 | 255 | }, |
|
256 | ||
|
257 | remove: function() { | |
|
258 | // We remove this widget before removing the children as an optimization | |
|
259 | // we want to remove the entire container from the DOM first before | |
|
260 | // removing each individual child separately. | |
|
261 | TabView.__super__.remove.apply(this, arguments); | |
|
262 | this.children_views.remove(); | |
|
263 | }, | |
|
257 | 264 | }); |
|
258 | 265 | |
|
259 | 266 | return { |
General Comments 0
You need to be logged in to leave comments.
Login now