Files
edx-platform/cms/static/js/views/course_outline.js
2014-08-07 12:16:20 -04:00

132 lines
6.0 KiB
JavaScript

/**
* The CourseOutlineView is used to render the contents of the course for the Course Outline page.
* It is a recursive set of views, where each XBlock has its own instance, and each of the children
* are shown as child CourseOutlineViews.
*
* This class extends XBlockOutlineView to add unique capabilities needed by the course outline:
* - sections are initially expanded but subsections and other children are shown as collapsed
* - changes cause a refresh of the entire section rather than just the view for the changed xblock
* - adding units will automatically redirect to the unit page rather than showing them inline
*/
define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_utils",
"js/models/xblock_outline_info"],
function($, _, XBlockOutlineView, ViewUtils, XBlockOutlineInfo) {
var CourseOutlineView = XBlockOutlineView.extend({
// takes XBlockOutlineInfo as a model
templateName: 'course-outline',
shouldExpandChildren: function() {
// Expand the children if this xblock's locator is in the initially expanded state
if (this.initialState && _.contains(this.initialState.expanded_locators, this.model.id)) {
return true;
}
// Only expand the course and its chapters (aka sections) initially
var category = this.model.get('category');
return category === 'course' || category === 'chapter';
},
shouldRenderChildren: function() {
// Render all nodes up to verticals but not below
return this.model.get('category') !== 'vertical';
},
createChildView: function(xblockInfo, parentInfo, parentView) {
return new CourseOutlineView({
model: xblockInfo,
parentInfo: parentInfo,
initialState: this.initialState,
template: this.template,
parentView: parentView || this
});
},
getExpandedLocators: function() {
var expandedLocators = [];
this.$('.outline-item.is-collapsible').each(function(index, rawElement) {
var element = $(rawElement);
if (!element.hasClass('collapsed')) {
expandedLocators.push(element.data('locator'));
}
});
return expandedLocators;
},
/**
* Refresh the containing section (if there is one) or else refresh the entire course.
* Note that the refresh will preserve the expanded state of this view and all of its
* children.
* @param viewState The desired initial state of the view, or null if none.
* @returns {jQuery promise} A promise representing the refresh operation.
*/
refresh: function(viewState) {
var getViewToRefresh, view, expandedLocators;
getViewToRefresh = function(view) {
if (view.model.get('category') === 'chapter' || !view.parentView) {
return view;
}
return getViewToRefresh(view.parentView);
};
view = getViewToRefresh(this);
expandedLocators = view.getExpandedLocators();
viewState = viewState || {};
viewState.expanded_locators = expandedLocators.concat(viewState.expanded_locators || []);
view.initialState = viewState;
return view.model.fetch({});
},
onChildAdded: function(locator, category, event) {
if (category === 'vertical') {
// For units, redirect to the new unit's page in inline edit mode
this.onUnitAdded(locator);
} else if (category === 'chapter' && this.model.hasChildren()) {
this.onSectionAdded(locator);
} else {
// For all other block types, refresh the view and do the following:
// - show the new block expanded
// - ensure it is scrolled into view
// - make its name editable
this.refresh(this.createNewItemViewState(locator, ViewUtils.getScrollOffset($(event.target))));
}
},
onSectionAdded: function(locator) {
var self = this,
initialState = self.createNewItemViewState(locator),
sectionInfo, sectionView;
// For new chapters in a non-empty view, add a new child view and render it
// to avoid the expense of refreshing the entire page.
if (this.model.hasChildren()) {
sectionInfo = new XBlockOutlineInfo({
id: locator,
category: 'chapter'
});
// Fetch the full xblock info for the section and then create a view for it
sectionInfo.fetch().done(function() {
sectionView = self.createChildView(sectionInfo, self.model, self);
sectionView.initialState = initialState;
sectionView.render();
self.addChildView(sectionView);
sectionView.setViewState(initialState);
});
} else {
this.refresh(initialState);
}
},
createNewItemViewState: function(locator, scrollOffset) {
return {
locator_to_show: locator,
edit_display_name: true,
expanded_locators: [ locator ],
scroll_offset: scrollOffset || 0
};
}
});
return CourseOutlineView;
}); // end define();