* fix: eslint operator-linebreak issue * fix: eslint quotes issue * fix: react jsx indent and props issues * fix: eslint trailing spaces issues * fix: eslint line around directives issue * fix: eslint semi rule * fix: eslint newline per chain rule * fix: eslint space infix ops rule * fix: eslint space-in-parens issue * fix: eslint space before function paren issue * fix: eslint space before blocks issue * fix: eslint arrow body style issue * fix: eslint dot-location issue * fix: eslint quotes issue * fix: eslint quote props issue * fix: eslint operator assignment issue * fix: eslint new line after import issue * fix: indent issues * fix: operator assignment issue * fix: all autofixable eslint issues * fix: all react related fixable issues * fix: autofixable eslint issues * chore: remove all template literals * fix: remaining autofixable issues * fix: failing js test
279 lines
12 KiB
JavaScript
279 lines
12 KiB
JavaScript
define(['jquery', 'underscore', 'common/js/components/utils/view_utils', 'js/views/container', 'js/utils/module', 'gettext',
|
|
'common/js/components/views/feedback_notification', 'js/views/paging_header', 'common/js/components/views/paging_footer'],
|
|
function($, _, ViewUtils, ContainerView, ModuleUtils, gettext, NotificationView, PagingHeader, PagingFooter) {
|
|
var PagedContainerView = ContainerView.extend({
|
|
|
|
initialize: function(options) {
|
|
var self = this;
|
|
ContainerView.prototype.initialize.call(this);
|
|
this.page_size = this.options.page_size;
|
|
// Reference to the page model
|
|
this.page = options.page;
|
|
// XBlocks are rendered via Django views and templates rather than underscore templates, and so don't
|
|
// have a Backbone model for us to manipulate in a backbone collection. Here, we emulate the interface
|
|
// of backbone.paginator so that we can use the Paging Header and Footer with this page. As a
|
|
// consequence, however, we have to manipulate its members manually.
|
|
this.collection = {
|
|
currentPage: 0,
|
|
totalPages: 0,
|
|
totalCount: 0,
|
|
sortDirection: 'desc',
|
|
start: 0,
|
|
_size: 0,
|
|
// Paging header and footer expect this to be a Backbone model they can listen to for changes, but
|
|
// they cannot. Provide the bind function for them, but have it do nothing.
|
|
bind: function() {},
|
|
// size() on backbone collections shows how many objects are in the collection, or in the case
|
|
// of paginator, on the current page.
|
|
size: function() { return self.collection._size; },
|
|
// Toggles the functionality for showing and hiding child previews.
|
|
showChildrenPreviews: true,
|
|
|
|
// PagingFooter expects to be able to control paging through the collection instead of the view,
|
|
// so we just make these functions act as pass-throughs
|
|
setPage: function(page) {
|
|
self.setPage(page - 1);
|
|
},
|
|
|
|
nextPage: function() {
|
|
self.nextPage();
|
|
},
|
|
|
|
previousPage: function() {
|
|
self.previousPage();
|
|
},
|
|
|
|
getPage: function() {
|
|
return self.collection.currentPage + 1;
|
|
},
|
|
|
|
hasPreviousPage: function() {
|
|
return self.collection.currentPage > 0;
|
|
},
|
|
|
|
hasNextPage: function() {
|
|
return self.collection.currentPage < self.collection.totalPages - 1;
|
|
},
|
|
|
|
getTotalPages: function() {
|
|
return this.totalPages;
|
|
},
|
|
|
|
getPageNumber: function() {
|
|
return this.getPage();
|
|
},
|
|
|
|
getTotalRecords: function() {
|
|
return this.totalCount;
|
|
},
|
|
|
|
getPageSize: function() {
|
|
return self.page_size;
|
|
}
|
|
};
|
|
},
|
|
|
|
new_child_view: 'container_child_preview',
|
|
|
|
render: function(options) {
|
|
options = options || {};
|
|
options.page_number = typeof options.page_number !== 'undefined'
|
|
? options.page_number
|
|
: this.collection.currentPage;
|
|
return this.renderPage(options);
|
|
},
|
|
|
|
renderPage: function(options) {
|
|
var self = this,
|
|
view = this.view,
|
|
xblockInfo = this.model,
|
|
xblockUrl = xblockInfo.url();
|
|
|
|
return $.ajax({
|
|
url: decodeURIComponent(xblockUrl) + '/' + view,
|
|
type: 'GET',
|
|
cache: false,
|
|
data: this.getRenderParameters(options.page_number, options.force_render),
|
|
headers: {Accept: 'application/json'},
|
|
success: function(fragment) {
|
|
var originalDone = options.done;
|
|
options.done = function() {
|
|
self.processPaging({requested_page: options.page_number});
|
|
self.page.updatePreviewButton(self.collection.showChildrenPreviews);
|
|
self.page.renderAddXBlockComponents();
|
|
if (options.force_render) {
|
|
var $target = $('.studio-xblock-wrapper[data-locator="' + options.force_render + '"]');
|
|
// Scroll us to the element with a little buffer at the top for context.
|
|
ViewUtils.setScrollOffset($target, ($(window).height() * 0.10));
|
|
}
|
|
if (originalDone) {
|
|
originalDone();
|
|
}
|
|
};
|
|
self.handleXBlockFragment(fragment, options);
|
|
}
|
|
});
|
|
},
|
|
|
|
getRenderParameters: function(page_number, force_render) {
|
|
// Options should at least contain page_number.
|
|
return {
|
|
page_size: this.page_size,
|
|
enable_paging: true,
|
|
page_number: page_number,
|
|
force_render: force_render
|
|
};
|
|
},
|
|
|
|
getPageCount: function(total_count) {
|
|
if (total_count === 0) {
|
|
return 1;
|
|
}
|
|
return Math.ceil(total_count / this.page_size);
|
|
},
|
|
|
|
setPage: function(page_number, additional_options) {
|
|
additional_options = additional_options || {};
|
|
var options = _.extend({page_number: page_number}, additional_options);
|
|
this.render(options);
|
|
},
|
|
|
|
nextPage: function() {
|
|
var collection = this.collection,
|
|
currentPage = collection.currentPage,
|
|
lastPage = collection.totalPages - 1;
|
|
if (currentPage < lastPage) {
|
|
this.setPage(currentPage + 1);
|
|
}
|
|
},
|
|
|
|
previousPage: function() {
|
|
var collection = this.collection,
|
|
currentPage = collection.currentPage;
|
|
if (currentPage > 0) {
|
|
this.setPage(currentPage - 1);
|
|
}
|
|
},
|
|
|
|
processPaging: function(options) {
|
|
// We have the Django template sneak us the pagination information,
|
|
// and we load it from a div here.
|
|
var $element = this.$el.find('.xblock-container-paging-parameters'),
|
|
total = $element.data('total'),
|
|
displayed = $element.data('displayed'),
|
|
start = $element.data('start'),
|
|
previews = $element.data('previews');
|
|
|
|
this.collection.currentPage = options.requested_page;
|
|
this.collection.totalCount = total;
|
|
this.collection.totalPages = this.getPageCount(total);
|
|
this.collection.start = start;
|
|
this.collection._size = displayed;
|
|
this.collection.showChildrenPreviews = previews;
|
|
|
|
this.processPagingHeaderAndFooter();
|
|
},
|
|
|
|
processPagingHeaderAndFooter: function() {
|
|
// Rendering the container view detaches the header and footer from the DOM.
|
|
// It's just as easy to recreate them as it is to try to shove them back into the tree.
|
|
if (this.pagingHeader) { this.pagingHeader.undelegateEvents(); }
|
|
if (this.pagingFooter) { this.pagingFooter.undelegateEvents(); }
|
|
|
|
this.pagingHeader = new PagingHeader({
|
|
view: this,
|
|
el: this.$el.find('.container-paging-header')
|
|
});
|
|
this.pagingFooter = new PagingFooter({
|
|
collection: this.collection,
|
|
el: this.$el.find('.container-paging-footer')
|
|
});
|
|
|
|
this.pagingHeader.render();
|
|
this.pagingFooter.render();
|
|
},
|
|
|
|
refresh: function(xblockView, block_added, is_duplicate) {
|
|
if (!block_added) {
|
|
return;
|
|
}
|
|
if (is_duplicate) {
|
|
// Duplicated blocks can be inserted onto the current page.
|
|
var xblock = xblockView.xblock.element.parents('.studio-xblock-wrapper').first();
|
|
var all_xblocks = xblock.parent().children('.studio-xblock-wrapper');
|
|
var index = all_xblocks.index(xblock);
|
|
if ((index + 1 <= this.page_size) && (all_xblocks.length > this.page_size)) {
|
|
// Pop the last XBlock off the bottom.
|
|
all_xblocks[all_xblocks.length - 1].remove();
|
|
return;
|
|
}
|
|
}
|
|
this.collection.totalCount += 1;
|
|
this.collection._size += 1;
|
|
if (this.collection.totalCount == 1) {
|
|
this.render();
|
|
return;
|
|
}
|
|
this.collection.totalPages = this.getPageCount(this.collection.totalCount);
|
|
var target_page = this.collection.totalPages - 1;
|
|
// If we're on a new page due to overflow, or this is the first item, set the page.
|
|
if (((this.collection.currentPage) != target_page) || this.collection.totalCount == 1) {
|
|
var force_render = xblockView.model.id;
|
|
if (is_duplicate) {
|
|
// The duplicate should be on the next page if we've gotten here.
|
|
target_page = this.collection.currentPage + 1;
|
|
}
|
|
this.setPage(
|
|
target_page,
|
|
{force_render: force_render}
|
|
);
|
|
} else {
|
|
this.pagingHeader.render();
|
|
this.pagingFooter.render();
|
|
}
|
|
},
|
|
|
|
acknowledgeXBlockDeletion: function(locator) {
|
|
this.notifyRuntime('deleted-child', locator);
|
|
this.collection._size -= 1;
|
|
this.collection.totalCount -= 1;
|
|
var current_page = this.collection.currentPage;
|
|
var total_pages = this.getPageCount(this.collection.totalCount);
|
|
this.collection.totalPages = total_pages;
|
|
// Starts counting from 0
|
|
if ((current_page + 1) > total_pages) {
|
|
// The number of total pages has changed. Move down.
|
|
// Also, be mindful of the off-by-one.
|
|
this.setPage(total_pages - 1);
|
|
} else if ((current_page + 1) != total_pages) {
|
|
// Refresh page to get any blocks shifted from the next page.
|
|
this.setPage(current_page);
|
|
} else {
|
|
// We're on the last page, just need to update the numbers in the
|
|
// pagination interface.
|
|
this.pagingHeader.render();
|
|
this.pagingFooter.render();
|
|
}
|
|
},
|
|
|
|
sortDisplayName: function() {
|
|
return gettext('Date added'); // TODO add support for sorting
|
|
},
|
|
|
|
togglePreviews: function() {
|
|
var self = this,
|
|
xblockUrl = this.model.url();
|
|
return $.ajax({
|
|
// No runtime, so can't get this via the handler() call.
|
|
url: '/preview' + decodeURIComponent(xblockUrl) + '/handler/trigger_previews',
|
|
type: 'POST',
|
|
data: JSON.stringify({showChildrenPreviews: !this.collection.showChildrenPreviews}),
|
|
dataType: 'json'
|
|
})
|
|
.then(self.render).promise();
|
|
}
|
|
});
|
|
|
|
return PagedContainerView;
|
|
}); // end define();
|