##// END OF EJS Templates
Persistence API,...
Persistence API, This is a combination of 10 commits. Enable widget instanciation from front-end. Address @minrk 's review comments. Make API that allows users to persist widget state easily. Added support for view persistence Started adding support for model persistence. Half way there! Finished persistence API. Move persistence code into the widget framework. Fin. Bug fixes

File last commit:

r19176:f48e011c
r19350:a8e5e600
Show More
widget_selectioncontainer.js
298 lines | 10.8 KiB | application/javascript | JavascriptLexer
/ IPython / html / static / widgets / js / widget_selectioncontainer.js
// Copyright (c) IPython Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"widgets/js/widget",
"base/js/utils",
"jquery",
"bootstrap",
], function(widget, utils, $){
var AccordionView = widget.DOMWidgetView.extend({
initialize: function(){
AccordionView.__super__.initialize.apply(this, arguments);
this.containers = [];
this.model_containers = {};
this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
this.listenTo(this.model, 'change:children', function(model, value) {
this.children_views.update(value);
}, this);
},
render: function(){
/**
* Called when view is rendered.
*/
var guid = 'panel-group' + utils.uuid();
this.$el
.attr('id', guid)
.addClass('panel-group');
this.model.on('change:selected_index', function(model, value, options) {
this.update_selected_index(model.previous('selected_index'), value, options);
}, this);
this.model.on('change:_titles', function(model, value, options) {
this.update_titles(value);
}, this);
var that = this;
this.on('displayed', function() {
this.update_titles();
}, this);
this.children_views.update(this.model.get('children'));
},
update_titles: function(titles) {
/**
* Set tab titles
*/
if (!titles) {
titles = this.model.get('_titles');
}
var that = this;
_.each(titles, function(title, page_index) {
var accordian = that.containers[page_index];
if (accordian !== undefined) {
accordian
.find('.panel-heading')
.find('.accordion-toggle')
.text(title);
}
});
},
update_selected_index: function(old_index, new_index, options) {
/**
* Only update the selection if the selection wasn't triggered
* by the front-end. It must be triggered by the back-end.
*/
if (options === undefined || options.updated_view != this) {
this.containers[old_index].find('.panel-collapse').collapse('hide');
if (0 <= new_index && new_index < this.containers.length) {
this.containers[new_index].find('.panel-collapse').collapse('show');
}
}
},
remove_child_view: function(view) {
/**
* Called when a child is removed from children list.
* TODO: does this handle two different views of the same model as children?
*/
var model = view.model;
var accordion_group = this.model_containers[model.id];
this.containers.splice(accordion_group.container_index, 1);
delete this.model_containers[model.id];
accordion_group.remove();
},
add_child_view: function(model) {
/**
* Called when a child is added to children list.
*/
var index = this.containers.length;
var uuid = utils.uuid();
var accordion_group = $('<div />')
.addClass('panel panel-default')
.appendTo(this.$el);
var accordion_heading = $('<div />')
.addClass('panel-heading')
.appendTo(accordion_group);
var that = this;
var accordion_toggle = $('<a />')
.addClass('accordion-toggle')
.attr('data-toggle', 'collapse')
.attr('data-parent', '#' + this.$el.attr('id'))
.attr('href', '#' + uuid)
.click(function(evt){
// Calling model.set will trigger all of the other views of the
// model to update.
that.model.set("selected_index", index, {updated_view: that});
that.touch();
})
.text('Page ' + index)
.appendTo(accordion_heading);
var accordion_body = $('<div />', {id: uuid})
.addClass('panel-collapse collapse')
.appendTo(accordion_group);
var accordion_inner = $('<div />')
.addClass('panel-body')
.appendTo(accordion_body);
var container_index = this.containers.push(accordion_group) - 1;
accordion_group.container_index = container_index;
this.model_containers[model.id] = accordion_group;
var dummy = $('<div/>');
accordion_inner.append(dummy);
return this.create_child_view(model).then(function(view) {
dummy.replaceWith(view.$el);
that.update();
that.update_titles();
// Trigger the displayed event of the child view.
that.after_displayed(function() {
view.trigger('displayed');
});
return view;
}).catch(utils.reject("Couldn't add child view to box", true));
},
remove: function() {
/**
* We remove this widget before removing the children as an optimization
* we want to remove the entire container from the DOM first before
* removing each individual child separately.
*/
AccordionView.__super__.remove.apply(this, arguments);
this.children_views.remove();
},
});
var TabView = widget.DOMWidgetView.extend({
initialize: function() {
/**
* Public constructor.
*/
TabView.__super__.initialize.apply(this, arguments);
this.containers = [];
this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
this.listenTo(this.model, 'change:children', function(model, value) {
this.children_views.update(value);
}, this);
},
render: function(){
/**
* Called when view is rendered.
*/
var uuid = 'tabs'+utils.uuid();
var that = this;
this.$tabs = $('<div />', {id: uuid})
.addClass('nav')
.addClass('nav-tabs')
.appendTo(this.$el);
this.$tab_contents = $('<div />', {id: uuid + 'Content'})
.addClass('tab-content')
.appendTo(this.$el);
this.children_views.update(this.model.get('children'));
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
this.$tabs.css(name, value);
},
remove_child_view: function(view) {
/**
* Called when a child is removed from children list.
*/
this.containers.splice(view.parent_tab.tab_text_index, 1);
view.parent_tab.remove();
view.parent_container.remove();
view.remove();
},
add_child_view: function(model) {
/**
* Called when a child is added to children list.
*/
var index = this.containers.length;
var uuid = utils.uuid();
var that = this;
var tab = $('<li />')
.css('list-style-type', 'none')
.appendTo(this.$tabs);
var tab_text = $('<a />')
.attr('href', '#' + uuid)
.attr('data-toggle', 'tab')
.text('Page ' + index)
.appendTo(tab)
.click(function (e) {
// Calling model.set will trigger all of the other views of the
// model to update.
that.model.set("selected_index", index, {updated_view: that});
that.touch();
that.select_page(index);
});
tab.tab_text_index = that.containers.push(tab_text) - 1;
var dummy = $('<div />');
var contents_div = $('<div />', {id: uuid})
.addClass('tab-pane')
.addClass('fade')
.append(dummy)
.appendTo(that.$tab_contents);
return this.create_child_view(model).then(function(view) {
dummy.replaceWith(view.$el);
view.parent_tab = tab;
view.parent_container = contents_div;
// Trigger the displayed event of the child view.
that.after_displayed(function() {
view.trigger('displayed');
});
return view;
}).catch(utils.reject("Couldn't add child view to box", true));
},
update: function(options) {
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
// Set tab titles
var titles = this.model.get('_titles');
var that = this;
_.each(titles, function(title, page_index) {
var tab_text = that.containers[page_index];
if (tab_text !== undefined) {
tab_text.text(title);
}
});
var selected_index = this.model.get('selected_index');
if (0 <= selected_index && selected_index < this.containers.length) {
this.select_page(selected_index);
}
}
return TabView.__super__.update.apply(this);
},
select_page: function(index) {
/**
* Select a page.
*/
this.$tabs.find('li')
.removeClass('active');
this.containers[index].tab('show');
},
remove: function() {
/**
* We remove this widget before removing the children as an optimization
* we want to remove the entire container from the DOM first before
* removing each individual child separately.
*/
TabView.__super__.remove.apply(this, arguments);
this.children_views.remove();
},
});
return {
'AccordionView': AccordionView,
'TabView': TabView,
};
});