Commit fbc886aa by cahrens

Store the expanded locators on the page.

STUD-2038
parent 0083cfc4
...@@ -277,7 +277,11 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif ...@@ -277,7 +277,11 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif
refreshParent = function (element) { refreshParent = function (element) {
var refresh = element.data('refresh'); var refresh = element.data('refresh');
if (_.isFunction(refresh)) { refresh(); } // If drop was into a collapsed parent, the parent will have been
// expanded. Views using this class may need to track the
// collapse/expand state, so send it with the refresh callback.
var collapsed = element.hasClass(this.collapsedClass);
if (_.isFunction(refresh)) { refresh(collapsed); }
}; };
// If the parent has changed, update the children of the old parent. // If the parent has changed, update the children of the old parent.
......
...@@ -24,12 +24,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -24,12 +24,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
}, },
shouldExpandChildren: function() { shouldExpandChildren: function() {
// Expand the children if this xblock's locator is in the initially expanded state return this.expandedLocators.contains(this.model.get('id'));
if (this.initialState && _.contains(this.initialState.expanded_locators, this.model.id)) {
return true;
}
// Only expand the course and its chapters (aka sections) initially
return this.model.isCourse() || this.model.isChapter();
}, },
shouldRenderChildren: function() { shouldRenderChildren: function() {
...@@ -42,22 +37,12 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -42,22 +37,12 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
model: xblockInfo, model: xblockInfo,
parentInfo: parentInfo, parentInfo: parentInfo,
initialState: this.initialState, initialState: this.initialState,
expandedLocators: this.expandedLocators,
template: this.template, template: this.template,
parentView: parentView || this parentView: parentView || this
}); });
}, },
getExpandedLocators: function() {
var expandedLocators = [];
this.$('.outline-item.is-collapsible').each(function(index, rawElement) {
var element = $(rawElement);
if (!element.hasClass('is-collapsed')) {
expandedLocators.push(element.data('locator'));
}
});
return expandedLocators;
},
/** /**
* Refresh the containing section (if there is one) or else refresh the entire course. * 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 * Note that the refresh will preserve the expanded state of this view and all of its
...@@ -76,13 +61,26 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -76,13 +61,26 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
}; };
view = getViewToRefresh(this); view = getViewToRefresh(this);
expandedLocators = view.getExpandedLocators();
viewState = viewState || {}; viewState = viewState || {};
viewState.expanded_locators = expandedLocators.concat(viewState.expanded_locators || []);
view.initialState = viewState; view.initialState = viewState;
return view.model.fetch({}); return view.model.fetch({});
}, },
/**
* Updates the collapse/expand state for this outline element, and then calls refresh.
* @param isCollapsed true if the element should be collapsed, else false
*/
refreshWithCollapsedState: function(isCollapsed) {
var locator = this.model.get('id');
if (isCollapsed) {
this.expandedLocators.remove(locator);
}
else {
this.expandedLocators.add(locator);
}
this.refresh();
},
onChildAdded: function(locator, category, event) { onChildAdded: function(locator, category, event) {
if (category === 'vertical') { if (category === 'vertical') {
// For units, redirect to the new unit's page in inline edit mode // For units, redirect to the new unit's page in inline edit mode
...@@ -113,6 +111,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -113,6 +111,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
sectionInfo.fetch().done(function() { sectionInfo.fetch().done(function() {
sectionView = self.createChildView(sectionInfo, self.model, self); sectionView = self.createChildView(sectionInfo, self.model, self);
sectionView.initialState = initialState; sectionView.initialState = initialState;
sectionView.expandedLocators = self.expandedLocators;
sectionView.render(); sectionView.render();
self.addChildView(sectionView); self.addChildView(sectionView);
sectionView.setViewState(initialState); sectionView.setViewState(initialState);
...@@ -136,10 +135,10 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -136,10 +135,10 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
}, },
createNewItemViewState: function(locator, scrollOffset) { createNewItemViewState: function(locator, scrollOffset) {
this.expandedLocators.add(locator);
return { return {
locator_to_show: locator, locator_to_show: locator,
edit_display_name: true, edit_display_name: true,
expanded_locators: [ locator ],
scroll_offset: scrollOffset || 0 scroll_offset: scrollOffset || 0
}; };
}, },
...@@ -168,7 +167,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -168,7 +167,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
handleClass: '.section-drag-handle', handleClass: '.section-drag-handle',
droppableClass: 'ol.list-sections', droppableClass: 'ol.list-sections',
parentLocationSelector: 'article.outline', parentLocationSelector: 'article.outline',
refresh: this.refresh.bind(this) refresh: this.refreshWithCollapsedState.bind(this)
}); });
} }
else if ($(element).hasClass("outline-subsection")) { else if ($(element).hasClass("outline-subsection")) {
...@@ -177,7 +176,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -177,7 +176,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
handleClass: '.subsection-drag-handle', handleClass: '.subsection-drag-handle',
droppableClass: 'ol.list-subsections', droppableClass: 'ol.list-subsections',
parentLocationSelector: 'li.outline-section', parentLocationSelector: 'li.outline-section',
refresh: this.refresh.bind(this) refresh: this.refreshWithCollapsedState.bind(this)
}); });
} }
else if ($(element).hasClass("outline-unit")) { else if ($(element).hasClass("outline-unit")) {
...@@ -186,7 +185,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ ...@@ -186,7 +185,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_
handleClass: '.unit-drag-handle', handleClass: '.unit-drag-handle',
droppableClass: 'ol.list-units', droppableClass: 'ol.list-units',
parentLocationSelector: 'li.outline-subsection', parentLocationSelector: 'li.outline-subsection',
refresh: this.refresh.bind(this) refresh: this.refreshWithCollapsedState.bind(this)
}); });
} }
} }
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views/utils/xblock_utils", define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views/utils/xblock_utils",
"js/views/course_outline"], "js/views/course_outline"],
function ($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView) { function ($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView) {
var CourseOutlinePage = BasePage.extend({ var expandedLocators, CourseOutlinePage;
CourseOutlinePage = BasePage.extend({
// takes XBlockInfo as a model // takes XBlockInfo as a model
events: { events: {
...@@ -36,12 +38,32 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views ...@@ -36,12 +38,32 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
}, },
renderPage: function() { renderPage: function() {
var setInitialExpandState = function (xblockInfo, expandedLocators) {
if (xblockInfo.isCourse() || xblockInfo.isChapter()) {
expandedLocators.add(xblockInfo.get('id'));
}
};
this.setCollapseExpandVisibility(); this.setCollapseExpandVisibility();
this.expandedLocators = expandedLocators;
this.expandedLocators.clear();
if (this.model.get('child_info')) {
_.each(this.model.get('child_info').children, function (childXBlockInfo) {
setInitialExpandState(childXBlockInfo, this.expandedLocators);
}, this);
}
setInitialExpandState(this.model, this.expandedLocators);
if (this.initialState && this.initialState.expanded_locators) {
this.expandedLocators.addAll(this.initialState.expanded_locators);
}
this.outlineView = new CourseOutlineView({ this.outlineView = new CourseOutlineView({
el: this.$('.outline'), el: this.$('.outline'),
model: this.model, model: this.model,
isRoot: true, isRoot: true,
initialState: this.initialState initialState: this.initialState,
expandedLocators: this.expandedLocators
}); });
this.outlineView.render(); this.outlineView.render();
this.outlineView.setViewState(this.initialState || {}); this.outlineView.setViewState(this.initialState || {});
...@@ -65,8 +87,67 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views ...@@ -65,8 +87,67 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
element.removeClass('is-collapsed'); element.removeClass('is-collapsed');
} }
}); });
if (this.model.get('child_info')) {
_.each(this.model.get('child_info').children, function (childXBlockInfo) {
if (collapse) {
this.expandedLocators.remove(childXBlockInfo.get('id'));
}
else {
this.expandedLocators.add(childXBlockInfo.get('id'));
}
}, this);
}
} }
}); });
/**
* Represents the set of locators that should be expanded for the page.
*/
expandedLocators = {
locators: [],
/**
* Add the locator to the set if it is not already present.
*/
add: function (locator) {
if (!this.contains(locator)) {
this.locators.push(locator);
}
},
/**
* Accepts an array of locators and adds them all to the set if not already present.
*/
addAll: function(locators) {
_.each(locators, function(locator) {
this.add(locator);
}, this);
},
/**
* Remove the locator from the set if it is present.
*/
remove: function (locator) {
var index = this.locators.indexOf(locator);
if (index >= 0) {
this.locators.splice(index, 1);
}
},
/**
* Returns true iff the locator is present in the set.
*/
contains: function (locator) {
return this.locators.indexOf(locator) >= 0;
},
/**
* Clears all expanded locators from the set.
*/
clear: function () {
this.locators = [];
}
};
return CourseOutlinePage; return CourseOutlinePage;
}); // end define(); }); // end define();
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
* *
* The view can be constructed with an initialState option which is a JSON structure representing * The view can be constructed with an initialState option which is a JSON structure representing
* the desired initial state. The parameters are as follows: * the desired initial state. The parameters are as follows:
* - expanded_locators - the locators that should be shown as expanded in addition to the defaults
* - locator_to_show - the locator for the xblock which is the one being explicitly shown * - locator_to_show - the locator for the xblock which is the one being explicitly shown
* - scroll_offset - the scroll offset to use for the locator being shown * - scroll_offset - the scroll offset to use for the locator being shown
* - edit_display_name - true if the shown xblock's display name should be in inline edit mode * - edit_display_name - true if the shown xblock's display name should be in inline edit mode
...@@ -30,6 +29,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -30,6 +29,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
initialize: function() { initialize: function() {
BaseView.prototype.initialize.call(this); BaseView.prototype.initialize.call(this);
this.initialState = this.options.initialState; this.initialState = this.options.initialState;
this.expandedLocators = this.options.expandedLocators;
this.template = this.options.template; this.template = this.options.template;
if (!this.template) { if (!this.template) {
this.template = this.loadTemplate(this.templateName); this.template = this.loadTemplate(this.templateName);
...@@ -37,7 +37,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -37,7 +37,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
this.parentInfo = this.options.parentInfo; this.parentInfo = this.options.parentInfo;
this.parentView = this.options.parentView; this.parentView = this.options.parentView;
this.renderedChildren = false; this.renderedChildren = false;
this.model.on('sync', this.onXBlockChange, this); this.model.on('sync', this.onSync, this);
}, },
render: function() { render: function() {
...@@ -47,6 +47,9 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -47,6 +47,9 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
if (this.shouldRenderChildren() && this.shouldExpandChildren()) { if (this.shouldRenderChildren() && this.shouldExpandChildren()) {
this.renderChildren(); this.renderChildren();
} }
else {
this.renderedChildren = false;
}
return this; return this;
}, },
...@@ -132,6 +135,17 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -132,6 +135,17 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
}, },
toggleExpandCollapse: function(event) { toggleExpandCollapse: function(event) {
// The course outline page tracks expanded locators. The unit location sidebar does not.
if (this.expandedLocators) {
var locator = this.model.get('id');
var wasExpanded = this.expandedLocators.contains(locator);
if (wasExpanded) {
this.expandedLocators.remove(locator);
}
else {
this.expandedLocators.add(locator);
}
}
// Ensure that the children have been rendered before expanding // Ensure that the children have been rendered before expanding
if (this.shouldRenderChildren() && !this.renderedChildren) { if (this.shouldRenderChildren() && !this.renderedChildren) {
this.renderChildren(); this.renderChildren();
...@@ -164,6 +178,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -164,6 +178,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
model: xblockInfo, model: xblockInfo,
parentInfo: parentInfo, parentInfo: parentInfo,
initialState: this.initialState, initialState: this.initialState,
expandedLocators: this.expandedLocators,
template: this.template, template: this.template,
parentView: parentView || this parentView: parentView || this
}); });
...@@ -181,6 +196,12 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -181,6 +196,12 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
return xblockType; return xblockType;
}, },
onSync: function(event) {
if (ViewUtils.hasChangedAttributes(this.model, ['visibility_state', 'child_info', 'display_name'])) {
this.onXBlockChange();
}
},
onXBlockChange: function() { onXBlockChange: function() {
var oldElement = this.$el, var oldElement = this.$el,
viewState = this.initialState; viewState = this.initialState;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment