Commit d09e9314 by David Baumgold

Merge pull request #1980 from edx/backbone-1.0.0

Upgrade to Backbone 1.0.0
parents ca1aeb5b 7f69e45b
......@@ -895,4 +895,4 @@ function saveSetSectionScheduleDate(e) {
hideModal();
});
}
\ No newline at end of file
}
......@@ -2,13 +2,13 @@
* Create a HesitateEvent and assign it as the event to execute:
* $(el).on('mouseEnter', CMS.HesitateEvent( expand, 'mouseLeave').trigger);
* It calls the executeOnTimeOut function with the event.currentTarget after the configurable timeout IFF the cancelSelector event
* did not occur on the event.currentTarget.
*
* More specifically, when trigger is called (triggered by the event you bound it to), it starts a timer
* did not occur on the event.currentTarget.
*
* More specifically, when trigger is called (triggered by the event you bound it to), it starts a timer
* which the cancelSelector event will cancel or if the timer finished, it executes the executeOnTimeOut function
* passing it the original event (whose currentTarget s/b the specific ele). It never accumulates events; however, it doesn't hurt for your
* code to minimize invocations of trigger by binding to mouseEnter v mouseOver and such.
*
*
* NOTE: if something outside of this wants to cancel the event, invoke cachedhesitation.untrigger(null | anything);
*/
......@@ -25,7 +25,7 @@ CMS.HesitateEvent.DURATION = 800;
CMS.HesitateEvent.prototype.trigger = function(event) {
if (event.data.timeoutEventId == null) {
event.data.timeoutEventId = window.setTimeout(
function() { event.data.fireEvent(event); },
function() { event.data.fireEvent(event); },
CMS.HesitateEvent.DURATION);
event.data.originalEvent = event;
$(event.data.originalEvent.delegateTarget).on(event.data.cancelSelector, event.data, event.data.untrigger);
......@@ -45,4 +45,4 @@ CMS.HesitateEvent.prototype.untrigger = function(event) {
$(event.data.originalEvent.delegateTarget).off(event.data.cancelSelector, event.data.untrigger);
}
event.data.timeoutEventId = null;
};
\ No newline at end of file
};
......@@ -80,6 +80,6 @@ $(document).ready(function(){
$('section.problem-edit').show();
return false;
});
});
// single per course holds the updates and handouts
// single per course holds the updates and handouts
CMS.Models.CourseInfo = Backbone.Model.extend({
// This model class is not suited for restful operations and is considered just a server side initialized container
url: '',
defaults: {
"courseId": "", // the location url
"updates" : null, // UpdateCollection
"handouts": null // HandoutCollection
},
idAttribute : "courseId"
});
// course update -- biggest kludge here is the lack of a real id to map updates to originals
CMS.Models.CourseUpdate = Backbone.Model.extend({
defaults: {
......@@ -26,11 +26,11 @@ CMS.Models.CourseUpdate = Backbone.Model.extend({
*/
CMS.Models.CourseUpdateCollection = Backbone.Collection.extend({
url : function() {return this.urlbase + "course_info/updates/";},
model : CMS.Models.CourseUpdate
});
\ No newline at end of file
......@@ -16,7 +16,7 @@ CMS.Models.Location = Backbone.Model.extend({
},
_tagPattern : /[^:]+/g,
_fieldPattern : new RegExp('[^/]+','g'),
parse: function(payload) {
if (_.isArray(payload)) {
return {
......@@ -25,7 +25,7 @@ CMS.Models.Location = Backbone.Model.extend({
course: payload[2],
category: payload[3],
name: payload[4]
}
};
}
else if (_.isString(payload)) {
this._tagPattern.lastIndex = 0; // odd regex behavior requires this to be reset sometimes
......@@ -65,4 +65,4 @@ CMS.Models.CourseRelative = Backbone.Model.extend({
CMS.Models.CourseRelativeCollection = Backbone.Collection.extend({
model : CMS.Models.CourseRelative
});
\ No newline at end of file
});
......@@ -6,5 +6,5 @@ CMS.Models.ModuleInfo = Backbone.Model.extend({
"data": null,
"metadata" : null,
"children" : null
},
});
\ No newline at end of file
}
});
......@@ -11,7 +11,7 @@ CMS.Models.Settings.Advanced = Backbone.Model.extend({
validate: function (attrs) {
// Keys can no longer be edited. We are currently not validating values.
},
save : function (attrs, options) {
// wraps the save call w/ the deletion of the removed keys after we know the saved ones worked
options = options ? _.clone(options) : {};
......@@ -23,7 +23,7 @@ CMS.Models.Settings.Advanced = Backbone.Model.extend({
};
Backbone.Model.prototype.save.call(this, attrs, options);
},
afterSave : function(self) {
// remove deleted attrs
if (!_.isEmpty(self.deleteKeys)) {
......
......@@ -66,7 +66,7 @@ CMS.Models.Settings.CourseDetails = Backbone.Model.extend({
save_videosource: function(newsource) {
// newsource either is <video youtube="speed:key, *"/> or just the "speed:key, *" string
// returns the videosource for the preview which iss the key whose speed is closest to 1
if (_.isEmpty(newsource) && !_.isEmpty(this.get('intro_video'))) this.save({'intro_video': null});
if (_.isEmpty(newsource) && !_.isEmpty(this.get('intro_video'))) this.save({'intro_video': null});
// TODO remove all whitespace w/in string
else {
if (this.get('intro_video') !== newsource) this.save('intro_video', newsource);
......
if (!CMS.Models['Settings']) CMS.Models.Settings = new Object();
CMS.Models.Settings.CourseGradingPolicy = Backbone.Model.extend({
defaults : {
defaults : {
course_location : null,
graders : null, // CourseGraderCollection
graders : null, // CourseGraderCollection
grade_cutoffs : null, // CourseGradeCutoff model
grace_period : null // either null or { hours: n, minutes: m, ...}
},
......@@ -54,7 +54,7 @@ CMS.Models.Settings.CourseGrader = Backbone.Model.extend({
"type" : "", // must be unique w/in collection (ie. w/in course)
"min_count" : 1,
"drop_count" : 0,
"short_label" : "", // what to use in place of type if space is an issue
"short_label" : "", // what to use in place of type if space is an issue
"weight" : 0 // int 0..100
},
parse : function(attrs) {
......@@ -125,4 +125,4 @@ CMS.Models.Settings.CourseGraderCollection = Backbone.Collection.extend({
sumWeights : function() {
return this.reduce(function(subtotal, grader) { return subtotal + grader.get('weight'); }, 0);
}
});
\ No newline at end of file
});
......@@ -93,4 +93,4 @@ CMS.Views.Checklists = Backbone.View.extend({
error : CMS.ServerError
});
}
});
\ No newline at end of file
});
......@@ -32,7 +32,7 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
"click .post-actions > .edit-button" : "onEdit",
"click .post-actions > .delete-button" : "onDelete"
},
initialize: function() {
var self = this;
// instantiates an editor template for each update in the collection
......@@ -41,13 +41,13 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
"/static/client_templates/course_info_update.html",
function (raw_template) {
self.template = _.template(raw_template);
self.render();
self.render();
}
);
// when the client refetches the updates as a whole, re-render them
this.listenTo(this.collection, 'reset', this.render);
},
render: function () {
// iterate over updates and create views for each using the template
var updateEle = this.$el.find("#course-update-list");
......@@ -66,14 +66,14 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
this.$el.find('.date').datepicker({ 'dateFormat': 'MM d, yy' });
return this;
},
onNew: function(event) {
event.preventDefault();
var self = this;
// create new obj, insert into collection, and render this one ele overriding the hidden attr
var newModel = new CMS.Models.CourseUpdate();
this.collection.add(newModel, {at : 0});
var $newForm = $(this.template({ updateModel : newModel }));
var updateEle = this.$el.find("#course-update-list");
......@@ -87,7 +87,7 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
lineWrapping: true,
});
}
$newForm.addClass('editing');
this.$currentPost = $newForm.closest('li');
......@@ -99,21 +99,21 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
$('.date').datepicker('destroy');
$('.date').datepicker({ 'dateFormat': 'MM d, yy' });
},
onSave: function(event) {
event.preventDefault();
var targetModel = this.eventModel(event);
targetModel.set({ date : this.dateEntry(event).val(), content : this.$codeMirror.getValue() });
// push change to display, hide the editor, submit the change
// push change to display, hide the editor, submit the change
targetModel.save({}, {error : CMS.ServerError});
this.closeEditor(this);
analytics.track('Saved Course Update', {
'course': course_location_analytics,
'date': this.dateEntry(event).val()
'date': this.dateEntry(event).val()
});
},
onCancel: function(event) {
event.preventDefault();
// change editor contents back to model values and hide the editor
......@@ -121,13 +121,13 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
var targetModel = this.eventModel(event);
this.closeEditor(this, !targetModel.id);
},
onEdit: function(event) {
event.preventDefault();
var self = this;
this.$currentPost = $(event.target).closest('li');
this.$currentPost.addClass('editing');
$(this.editor(event)).show();
var $textArea = this.$currentPost.find(".new-update-content").first();
if (this.$codeMirror == null ) {
......@@ -154,13 +154,13 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
analytics.track('Deleted Course Update', {
'course': course_location_analytics,
'date': this.dateEntry(event).val()
'date': this.dateEntry(event).val()
});
var targetModel = this.eventModel(event);
this.modelDom(event).remove();
var cacheThis = this;
targetModel.destroy({success : function (model, response) {
targetModel.destroy({success : function (model, response) {
cacheThis.collection.fetch({success : function() {cacheThis.render();},
error : CMS.ServerError});
},
......@@ -192,17 +192,17 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
this.$codeMirror = null;
self.$currentPost.find('.CodeMirror').remove();
},
// Dereferencing from events to screen elements
// Dereferencing from events to screen elements
eventModel: function(event) {
// not sure if it should be currentTarget or delegateTarget
return this.collection.get($(event.currentTarget).attr("name"));
},
modelDom: function(event) {
return $(event.currentTarget).closest("li");
},
editor: function(event) {
var li = $(event.currentTarget).closest("li");
if (li) return $(li).find("form").first();
......@@ -216,7 +216,7 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
contentEntry: function(event) {
return $(event.currentTarget).closest("li").find(".new-update-content").first();
},
dateDisplay: function(event) {
return $(event.currentTarget).closest("li").find("#date-display").first();
},
......@@ -224,7 +224,7 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
contentDisplay: function(event) {
return $(event.currentTarget).closest("li").find(".update-contents").first();
}
});
// the handouts view is dumb right now; it needs tied to a model and all that jazz
......@@ -245,7 +245,7 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
"/static/client_templates/course_info_handouts.html",
function (raw_template) {
self.template = _.template(raw_template);
self.render();
self.render();
}
);
},
......@@ -253,8 +253,8 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
}
);
},
render: function () {
render: function () {
var updateEle = this.$el;
var self = this;
this.$el.html(
......@@ -313,4 +313,4 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
self.$form.find('.CodeMirror').remove();
this.$codeMirror = null;
}
});
\ No newline at end of file
});
......@@ -16,7 +16,7 @@ CMS.Models.AssignmentGrade = Backbone.Model.extend({
urlRoot : function() {
if (this.has('location')) {
var location = this.get('location');
return '/' + location.get('org') + "/" + location.get('course') + '/' + location.get('category') + '/'
return '/' + location.get('org') + "/" + location.get('course') + '/' + location.get('category') + '/'
+ location.get('name') + '/gradeas/';
}
else return "";
......@@ -37,14 +37,14 @@ CMS.Views.OverviewAssignmentGrader = Backbone.View.extend({
'<a data-tooltip="Mark/unmark this subsection as graded" class="menu-toggle" href="#">' +
'<% if (!hideSymbol) {%><span class="ss-icon ss-standard">&#x2713;</span><%};%>' +
'</a>' +
'<ul class="menu">' +
'<ul class="menu">' +
'<% graders.each(function(option) { %>' +
'<li><a <% if (option.get("type") == assignmentType) {%>class="is-selected" <%}%> href="#"><%= option.get("type") %></a></li>' +
'<% }) %>' +
'<li><a class="gradable-status-notgraded" href="#">Not Graded</a></li>' +
'</ul>');
this.assignmentGrade = new CMS.Models.AssignmentGrade({
assignmentUrl : this.$el.closest('.id-holder').data('id'),
assignmentUrl : this.$el.closest('.id-holder').data('id'),
graderType : this.$el.data('initial-status')});
// TODO throw exception if graders is null
this.graders = this.options['graders'];
......@@ -78,13 +78,13 @@ CMS.Views.OverviewAssignmentGrader = Backbone.View.extend({
},
selectGradeType : function(e) {
e.preventDefault();
this.removeMenu(e);
// TODO I'm not happy with this string fetch via the html for what should be an id. I'd rather use the id attr
// of the CourseGradingPolicy model or null for Not Graded (NOTE, change template's if check for is-selected accordingly)
this.assignmentGrade.save('graderType', $(e.target).text());
this.render();
}
})
\ No newline at end of file
})
......@@ -6,26 +6,26 @@ $(document).ready(function() {
$('.unit').draggable({
axis: 'y',
handle: '.drag-handle',
zIndex: 999,
zIndex: 999,
start: initiateHesitate,
// left 2nd arg in as inert selector b/c i was uncertain whether we'd try to get the shove up/down
// to work in the future
drag: generateCheckHoverState('.collapsed', ''),
drag: generateCheckHoverState('.collapsed', ''),
stop: removeHesitate,
revert: "invalid"
});
// Subsection reordering
$('.id-holder').draggable({
axis: 'y',
handle: '.section-item .drag-handle',
zIndex: 999,
zIndex: 999,
start: initiateHesitate,
drag: generateCheckHoverState('.courseware-section.collapsed', ''),
stop: removeHesitate,
revert: "invalid"
});
// Section reordering
$('.courseware-section').draggable({
axis: 'y',
......@@ -33,8 +33,8 @@ $(document).ready(function() {
stack: '.courseware-section',
revert: "invalid"
});
$('.sortable-unit-list').droppable({
accept : '.unit',
greedy: true,
......@@ -50,7 +50,7 @@ $(document).ready(function() {
drop: onSubsectionReordered,
greedy: true
});
// Section reordering
$('.courseware-overview').droppable({
accept : '.courseware-section',
......@@ -58,7 +58,7 @@ $(document).ready(function() {
drop: onSectionReordered,
greedy: true
});
// stop clicks on drag bars from doing their thing w/o stopping drag
$('.drag-handle').click(function(e) {e.preventDefault(); });
......@@ -87,7 +87,7 @@ function computeIntersection(droppable, uiHelper, y) {
$.extend(droppable, {offset : $(droppable).offset()});
var t = droppable.offset.top,
var t = droppable.offset.top,
b = t + droppable.proportions.height;
if (t === b) {
......@@ -118,10 +118,10 @@ function generateCheckHoverState(selectorsToOpen, selectorsToShove) {
this[c === "isout" ? "isover" : "isout"] = false;
$(this).trigger(c === "isover" ? "dragEnter" : "dragLeave");
});
$(selectorsToShove).each(function() {
var intersectsBottom = computeIntersection(this, ui.helper, (draggable.positionAbs || draggable.position.absolute).top);
if ($(this).hasClass('ui-dragging-pushup')) {
if (!intersectsBottom) {
console.log('not up', $(this).data('id'));
......@@ -132,10 +132,10 @@ function generateCheckHoverState(selectorsToOpen, selectorsToShove) {
console.log('up', $(this).data('id'));
$(this).addClass('ui-dragging-pushup');
}
var intersectsTop = computeIntersection(this, ui.helper,
var intersectsTop = computeIntersection(this, ui.helper,
(draggable.positionAbs || draggable.position.absolute).top + draggable.helperProportions.height);
if ($(this).hasClass('ui-dragging-pushdown')) {
if (!intersectsTop) {
console.log('not down', $(this).data('id'));
......@@ -146,7 +146,7 @@ function generateCheckHoverState(selectorsToOpen, selectorsToShove) {
console.log('down', $(this).data('id'));
$(this).addClass('ui-dragging-pushdown');
}
});
}
}
......@@ -159,20 +159,20 @@ function removeHesitate(event, ui) {
}
function expandSection(event) {
$(event.delegateTarget).removeClass('collapsed', 400);
$(event.delegateTarget).removeClass('collapsed', 400);
// don't descend to icon's on children (which aren't under first child) only to this element's icon
$(event.delegateTarget).children().first().find('.expand-collapse-icon').removeClass('expand', 400).addClass('collapse');
}
function onUnitReordered(event, ui) {
// a unit's been dropped on this subsection,
// figure out where it came from and where it slots in.
// figure out where it came from and where it slots in.
_handleReorder(event, ui, 'subsection-id', 'li:.leaf');
}
function onSubsectionReordered(event, ui) {
// a subsection has been dropped on this section,
// figure out where it came from and where it slots in.
// figure out where it came from and where it slots in.
_handleReorder(event, ui, 'section-id', 'li:.branch');
}
......@@ -182,7 +182,7 @@ function onSectionReordered(event, ui) {
}
function _handleReorder(event, ui, parentIdField, childrenSelector) {
// figure out where it came from and where it slots in.
// figure out where it came from and where it slots in.
var subsection_id = $(event.target).data(parentIdField);
var _els = $(event.target).children(childrenSelector);
var children = _els.map(function(idx, el) { return $(el).data('id'); }).get();
......
CMS.ServerError = function(model, error) {
// this handler is for the client:server communication not the validation errors which handleValidationError catches
window.alert("Server Error: " + error.responseText);
};
\ No newline at end of file
};
CMS.Views.ValidatingView = Backbone.View.extend({
// Intended as an abstract class which catches validation errors on the model and
// decorates the fields. Needs wiring per class, but this initialization shows how
// Intended as an abstract class which catches validation errors on the model and
// decorates the fields. Needs wiring per class, but this initialization shows how
// either have your init call this one or copy the contents
initialize : function() {
this.listenTo(this.model, 'error', CMS.ServerError);
......@@ -15,7 +15,7 @@ CMS.Views.ValidatingView = Backbone.View.extend({
"change textarea" : "clearValidationErrors"
},
fieldToSelectorMap : {
// Your subclass must populate this w/ all of the model keys and dom selectors
// Your subclass must populate this w/ all of the model keys and dom selectors
// which may be the subjects of validation errors
},
_cacheValidationErrors : [],
......
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