##// END OF EJS Templates
Fix bug in all children containing views
Jonathan Frederic -
Show More
@@ -1,277 +1,277 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // ContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager) {
18 18
19 19 var ContainerView = IPython.DOMWidgetView.extend({
20 20 render: function(){
21 21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-container');
24 24 this.children={};
25 this.update_children([], this.model.get('children'));
26 this.model.on('change:children', function(model, value, options) {
27 this.update_children(model.previous('children'), value);
25 this.update_children([], this.model.get('_children'));
26 this.model.on('change:_children', function(model, value, options) {
27 this.update_children(model.previous('_children'), value);
28 28 }, this);
29 29 this.update()
30 30 },
31 31
32 32 update_children: function(old_list, new_list) {
33 33 // Called when the children list changes.
34 34 this.do_diff(old_list,
35 35 new_list,
36 36 $.proxy(this.remove_child_model, this),
37 37 $.proxy(this.add_child_model, this));
38 38 },
39 39
40 40 remove_child_model: function(model) {
41 41 // Called when a model is removed from the children list.
42 42 this.child_views[model.id].remove();
43 43 this.delete_child_view(model);
44 44 },
45 45
46 46 add_child_model: function(model) {
47 47 // Called when a model is added to the children list.
48 48 var view = this.create_child_view(model);
49 49 this.$el.append(view.$el);
50 50 },
51 51
52 52 update: function(){
53 53 // Update the contents of this view
54 54 //
55 55 // Called when the model is changed. The model may have been
56 56 // changed by another view or by a state update from the back-end.
57 57 return ContainerView.__super__.update.apply(this);
58 58 },
59 59 });
60 60 widget_manager.register_widget_view('ContainerView', ContainerView);
61 61
62 62
63 63 var ModalView = IPython.DOMWidgetView.extend({
64 64 render: function(){
65 65 // Called when view is rendered.
66 66 var that = this;
67 67 this.children={};
68 this.update_children([], this.model.get('children'));
69 this.model.on('change:children', function(model, value, options) {
70 this.update_children(model.previous('children'), value);
68 this.update_children([], this.model.get('_children'));
69 this.model.on('change:_children', function(model, value, options) {
70 this.update_children(model.previous('_children'), value);
71 71 }, this);
72 72
73 73 this.$el
74 74 .html('')
75 75 .on("remove", function(){
76 76 that.$window.remove();
77 77 });
78 78 this.$window = $('<div />')
79 79 .addClass('modal widget-modal')
80 80 .appendTo($('#notebook-container'))
81 81 .mousedown(function(){
82 82 that.bring_to_front();
83 83 });
84 84 this.$title_bar = $('<div />')
85 85 .addClass('popover-title')
86 86 .appendTo(this.$window)
87 87 .mousedown(function(){
88 88 that.bring_to_front();
89 89 });
90 90 this.$close = $('<button />')
91 91 .addClass('close icon-remove')
92 92 .css('margin-left', '5px')
93 93 .appendTo(this.$title_bar)
94 94 .click(function(){
95 95 that.hide();
96 96 event.stopPropagation();
97 97 });
98 98 this.$minimize = $('<button />')
99 99 .addClass('close icon-arrow-down')
100 100 .appendTo(this.$title_bar)
101 101 .click(function(){
102 102 that.popped_out = !that.popped_out;
103 103 if (!that.popped_out) {
104 104 that.$minimize
105 105 .removeClass('icon-arrow-down')
106 106 .addClass('icon-arrow-up');
107 107
108 108 that.$window
109 109 .draggable('destroy')
110 110 .resizable('destroy')
111 111 .removeClass('widget-modal modal')
112 112 .addClass('docked-widget-modal')
113 113 .detach()
114 114 .insertBefore(that.$show_button);
115 115 that.$show_button.hide();
116 116 that.$close.hide();
117 117 } else {
118 118 that.$minimize
119 119 .addClass('icon-arrow-down')
120 120 .removeClass('icon-arrow-up');
121 121
122 122 that.$window
123 123 .removeClass('docked-widget-modal')
124 124 .addClass('widget-modal modal')
125 125 .detach()
126 126 .appendTo($('#notebook-container'))
127 127 .draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'})
128 128 .resizable()
129 129 .children('.ui-resizable-handle').show();
130 130 that.show();
131 131 that.$show_button.show();
132 132 that.$close.show();
133 133 }
134 134 event.stopPropagation();
135 135 });
136 136 this.$title = $('<div />')
137 137 .addClass('widget-modal-title')
138 138 .html('&nbsp;')
139 139 .appendTo(this.$title_bar);
140 140 this.$body = $('<div />')
141 141 .addClass('modal-body')
142 142 .addClass('widget-modal-body')
143 143 .addClass('widget-container')
144 144 .appendTo(this.$window);
145 145
146 146 this.$show_button = $('<button />')
147 147 .html('&nbsp;')
148 148 .addClass('btn btn-info widget-modal-show')
149 149 .appendTo(this.$el)
150 150 .click(function(){
151 151 that.show();
152 152 });
153 153
154 154 this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
155 155 this.$window.resizable();
156 156 this.$window.on('resize', function(){
157 157 that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
158 158 });
159 159
160 160 this.$el_to_style = this.$body;
161 161 this._shown_once = false;
162 162 this.popped_out = true;
163 163 },
164 164
165 165 hide: function() {
166 166 // Called when the modal hide button is clicked.
167 167 this.$window.hide();
168 168 this.$show_button.removeClass('btn-info');
169 169 },
170 170
171 171 show: function() {
172 172 // Called when the modal show button is clicked.
173 173 this.$show_button.addClass('btn-info');
174 174 this.$window.show();
175 175 if (this.popped_out) {
176 176 this.$window.css("positon", "absolute");
177 177 this.$window.css("top", "0px");
178 178 this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
179 179 $(window).scrollLeft()) + "px");
180 180 this.bring_to_front();
181 181 }
182 182 },
183 183
184 184 bring_to_front: function() {
185 185 // Make the modal top-most, z-ordered about the other modals.
186 186 var $widget_modals = $(".widget-modal");
187 187 var max_zindex = 0;
188 188 $widget_modals.each(function (index, el){
189 189 max_zindex = Math.max(max_zindex, parseInt($(el).css('z-index')));
190 190 });
191 191
192 192 // Start z-index of widget modals at 2000
193 193 max_zindex = Math.max(max_zindex, 2000);
194 194
195 195 $widget_modals.each(function (index, el){
196 196 $el = $(el);
197 197 if (max_zindex == parseInt($el.css('z-index'))) {
198 198 $el.css('z-index', max_zindex - 1);
199 199 }
200 200 });
201 201 this.$window.css('z-index', max_zindex);
202 202 },
203 203
204 204 update_children: function(old_list, new_list) {
205 205 // Called when the children list is modified.
206 206 this.do_diff(old_list,
207 207 new_list,
208 208 $.proxy(this.remove_child_model, this),
209 209 $.proxy(this.add_child_model, this));
210 210 },
211 211
212 212 remove_child_model: function(model) {
213 213 // Called when a child is removed from children list.
214 214 this.child_views[model.id].remove();
215 215 this.delete_child_view(model);
216 216 },
217 217
218 218 add_child_model: function(model) {
219 219 // Called when a child is added to children list.
220 220 var view = this.create_child_view(model);
221 221 this.$body.append(view.$el);
222 222 },
223 223
224 224 update: function(){
225 225 // Update the contents of this view
226 226 //
227 227 // Called when the model is changed. The model may have been
228 228 // changed by another view or by a state update from the back-end.
229 229 var description = this.model.get('description');
230 230 description = description.replace(/ /g, '&nbsp;', 'm');
231 231 description = description.replace(/\n/g, '<br>\n', 'm');
232 232 if (description.length === 0) {
233 233 this.$title.html('&nbsp;'); // Preserve title height
234 234 } else {
235 235 this.$title.html(description);
236 236 }
237 237
238 238 var button_text = this.model.get('button_text');
239 239 button_text = button_text.replace(/ /g, '&nbsp;', 'm');
240 240 button_text = button_text.replace(/\n/g, '<br>\n', 'm');
241 241 if (button_text.length === 0) {
242 242 this.$show_button.html('&nbsp;'); // Preserve button height
243 243 } else {
244 244 this.$show_button.html(button_text);
245 245 }
246 246
247 247 if (!this._shown_once) {
248 248 this._shown_once = true;
249 249 this.show();
250 250 }
251 251
252 252 return ModalView.__super__.update.apply(this);
253 253 },
254 254
255 255 _get_selector_element: function(selector) {
256 256 // Get an element view a 'special' jquery selector. (see widget.js)
257 257 //
258 258 // Since the modal actually isn't within the $el in the DOM, we need to extend
259 259 // the selector logic to allow the user to set css on the modal if need be.
260 260 // The convention used is:
261 261 // "modal" - select the modal div
262 262 // "modal [selector]" - select element(s) within the modal div.
263 263 // "[selector]" - select elements within $el
264 264 // "" - select the $el_to_style
265 265 if (selector.substring(0, 5) == 'modal') {
266 266 if (selector == 'modal') {
267 267 return this.$window;
268 268 } else {
269 269 return this.$window.find(selector.substring(6));
270 270 }
271 271 } else {
272 272 return ModalView.__super__._get_selector_element.apply(this, [selector]);
273 273 }
274 274 },
275 275 });
276 276 widget_manager.register_widget_view('ModalView', ModalView);
277 277 });
@@ -1,244 +1,244 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SelectionContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18 18
19 19 var AccordionView = IPython.DOMWidgetView.extend({
20 20 render: function(){
21 21 // Called when view is rendered.
22 22 var guid = 'accordion' + IPython.utils.uuid();
23 23 this.$el
24 24 .attr('id', guid)
25 25 .addClass('accordion');
26 26 this.containers = [];
27 27 this.model_containers = {};
28 this.update_children([], this.model.get('children'));
29 this.model.on('change:children', function(model, value, options) {
30 this.update_children(model.previous('children'), value);
28 this.update_children([], this.model.get('_children'));
29 this.model.on('change:_children', function(model, value, options) {
30 this.update_children(model.previous('_children'), value);
31 31 }, this);
32 32 },
33 33
34 34 update: function(options) {
35 35 // Update the contents of this view
36 36 //
37 37 // Called when the model is changed. The model may have been
38 38 // changed by another view or by a state update from the back-end.
39 39 if (options === undefined || options.updated_view != this) {
40 40 // Set tab titles
41 41 var titles = this.model.get('_titles');
42 42 for (var page_index in titles) {
43 43
44 44 var accordian = this.containers[page_index];
45 45 if (accordian !== undefined) {
46 46 accordian
47 47 .find('.accordion-heading')
48 48 .find('.accordion-toggle')
49 49 .html(titles[page_index]);
50 50 }
51 51 }
52 52
53 53 // Set selected page
54 54 var selected_index = this.model.get("selected_index");
55 55 if (0 <= selected_index && selected_index < this.containers.length) {
56 56 for (var index in this.containers) {
57 57 if (index==selected_index) {
58 58 this.containers[index].find('.accordion-body').collapse('show');
59 59 } else {
60 60 this.containers[index].find('.accordion-body').collapse('hide');
61 61 }
62 62
63 63 }
64 64 }
65 65 }
66 66 return AccordionView.__super__.update.apply(this);
67 67 },
68 68
69 69 update_children: function(old_list, new_list) {
70 70 // Called when the children list is modified.
71 71 this.do_diff(old_list,
72 72 new_list,
73 73 $.proxy(this.remove_child_model, this),
74 74 $.proxy(this.add_child_model, this));
75 75 },
76 76
77 77 remove_child_model: function(model) {
78 78 // Called when a child is removed from children list.
79 79 var accordion_group = this.model_containers[model.id];
80 80 this.containers.splice(accordion_group.container_index, 1);
81 81 delete this.model_containers[model.id];
82 82 accordion_group.remove();
83 83 this.delete_child_view(model);
84 84 },
85 85
86 86 add_child_model: function(model) {
87 87 // Called when a child is added to children list.
88 88 var view = this.create_child_view(model);
89 89 var index = this.containers.length;
90 90 var uuid = IPython.utils.uuid();
91 91 var accordion_group = $('<div />')
92 92 .addClass('accordion-group')
93 93 .appendTo(this.$el);
94 94 var accordion_heading = $('<div />')
95 95 .addClass('accordion-heading')
96 96 .appendTo(accordion_group);
97 97 var that = this;
98 98 var accordion_toggle = $('<a />')
99 99 .addClass('accordion-toggle')
100 100 .attr('data-toggle', 'collapse')
101 101 .attr('data-parent', '#' + this.$el.attr('id'))
102 102 .attr('href', '#' + uuid)
103 103 .click(function(evt){
104 104
105 105 // Calling model.set will trigger all of the other views of the
106 106 // model to update.
107 107 that.model.set("selected_index", index, {updated_view: this});
108 108 that.touch();
109 109 })
110 110 .html('Page ' + index)
111 111 .appendTo(accordion_heading);
112 112 var accordion_body = $('<div />', {id: uuid})
113 113 .addClass('accordion-body collapse')
114 114 .appendTo(accordion_group);
115 115 var accordion_inner = $('<div />')
116 116 .addClass('accordion-inner')
117 117 .appendTo(accordion_body);
118 118 var container_index = this.containers.push(accordion_group) - 1;
119 119 accordion_group.container_index = container_index;
120 120 this.model_containers[model.id] = accordion_group;
121 121 accordion_inner.append(view.$el);
122 122
123 123 this.update();
124 124
125 125 // Stupid workaround to close the bootstrap accordion tabs which
126 126 // open by default even though they don't have the `in` class
127 127 // attached to them. For some reason a delay is required.
128 128 // TODO: Better fix.
129 129 setTimeout(function(){ that.update(); }, 500);
130 130 },
131 131 });
132 132 widget_manager.register_widget_view('AccordionView', AccordionView);
133 133
134 134
135 135 var TabView = IPython.DOMWidgetView.extend({
136 136 initialize: function() {
137 137 // Public constructor.
138 138 this.containers = [];
139 139 TabView.__super__.initialize.apply(this, arguments);
140 140 },
141 141
142 142 render: function(){
143 143 // Called when view is rendered.
144 144 var uuid = 'tabs'+IPython.utils.uuid();
145 145 var that = this;
146 146 this.$tabs = $('<div />', {id: uuid})
147 147 .addClass('nav')
148 148 .addClass('nav-tabs')
149 149 .appendTo(this.$el);
150 150 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
151 151 .addClass('tab-content')
152 152 .appendTo(this.$el);
153 153 this.containers = [];
154 this.update_children([], this.model.get('children'));
155 this.model.on('change:children', function(model, value, options) {
156 this.update_children(model.previous('children'), value);
154 this.update_children([], this.model.get('_children'));
155 this.model.on('change:_children', function(model, value, options) {
156 this.update_children(model.previous('_children'), value);
157 157 }, this);
158 158 },
159 159
160 160 update_children: function(old_list, new_list) {
161 161 // Called when the children list is modified.
162 162 this.do_diff(old_list,
163 163 new_list,
164 164 $.proxy(this.remove_child_model, this),
165 165 $.proxy(this.add_child_model, this));
166 166 },
167 167
168 168 remove_child_model: function(model) {
169 169 // Called when a child is removed from children list.
170 170 var view = this.child_views[model.id];
171 171 this.containers.splice(view.parent_tab.tab_text_index, 1);
172 172 view.parent_tab.remove();
173 173 view.parent_container.remove();
174 174 view.remove();
175 175 this.delete_child_view(model);
176 176 },
177 177
178 178 add_child_model: function(model) {
179 179 // Called when a child is added to children list.
180 180 var view = this.create_child_view(model);
181 181 var index = this.containers.length;
182 182 var uuid = IPython.utils.uuid();
183 183
184 184 var that = this;
185 185 var tab = $('<li />')
186 186 .css('list-style-type', 'none')
187 187 .appendTo(this.$tabs);
188 188 view.parent_tab = tab;
189 189
190 190 var tab_text = $('<a />')
191 191 .attr('href', '#' + uuid)
192 192 .attr('data-toggle', 'tab')
193 193 .html('Page ' + index)
194 194 .appendTo(tab)
195 195 .click(function (e) {
196 196
197 197 // Calling model.set will trigger all of the other views of the
198 198 // model to update.
199 199 that.model.set("selected_index", index, {updated_view: this});
200 200 that.touch();
201 201 that.select_page(index);
202 202 });
203 203 tab.tab_text_index = this.containers.push(tab_text) - 1;
204 204
205 205 var contents_div = $('<div />', {id: uuid})
206 206 .addClass('tab-pane')
207 207 .addClass('fade')
208 208 .append(view.$el)
209 209 .appendTo(this.$tab_contents);
210 210 view.parent_container = contents_div;
211 211 },
212 212
213 213 update: function(options) {
214 214 // Update the contents of this view
215 215 //
216 216 // Called when the model is changed. The model may have been
217 217 // changed by another view or by a state update from the back-end.
218 218 if (options === undefined || options.updated_view != this) {
219 219 // Set tab titles
220 220 var titles = this.model.get('_titles');
221 221 for (var page_index in titles) {
222 222 var tab_text = this.containers[page_index];
223 223 if (tab_text !== undefined) {
224 224 tab_text.html(titles[page_index]);
225 225 }
226 226 }
227 227
228 228 var selected_index = this.model.get('selected_index');
229 229 if (0 <= selected_index && selected_index < this.containers.length) {
230 230 this.select_page(selected_index);
231 231 }
232 232 }
233 233 return TabView.__super__.update.apply(this);
234 234 },
235 235
236 236 select_page: function(index) {
237 237 // Select a page.
238 238 this.$tabs.find('li')
239 239 .removeClass('active');
240 240 this.containers[index].tab('show');
241 241 },
242 242 });
243 243 widget_manager.register_widget_view('TabView', TabView);
244 244 });
General Comments 0
You need to be logged in to leave comments. Login now