If the first date the user selects is invalid it is not set on the model. This is because jQuery datepicker's getDate returns the current date when an invalid value is put in the field and the model's initial value is also the current date. So always update the date.
325 lines
13 KiB
JavaScript
325 lines
13 KiB
JavaScript
define(["js/views/validation", "codemirror", "js/models/course_update",
|
|
"common/js/components/views/feedback_prompt", "common/js/components/views/feedback_notification",
|
|
"js/views/course_info_helper", "js/utils/modal", "js/utils/date_utils"],
|
|
function(ValidatingView, CodeMirror, CourseUpdateModel, PromptView, NotificationView,
|
|
CourseInfoHelper, ModalUtils, DateUtils) {
|
|
|
|
'use strict';
|
|
var CourseInfoUpdateView = ValidatingView.extend({
|
|
|
|
// collection is CourseUpdateCollection
|
|
events: {
|
|
"click .new-update-button" : "onNew",
|
|
"click #course-update-view .save-button" : "onSave",
|
|
"click #course-update-view .cancel-button" : "onCancel",
|
|
"click .post-actions > .edit-button" : "onEdit",
|
|
"click .post-actions > .delete-button" : "onDelete"
|
|
},
|
|
|
|
initialize: function() {
|
|
this.template = this.loadTemplate('course_info_update');
|
|
this.render();
|
|
// when the client refetches the updates as a whole, re-render them
|
|
this.listenTo(this.collection, 'reset', this.render);
|
|
this.listenTo(this.collection, 'invalid', this.handleValidationError);
|
|
},
|
|
|
|
render: function () {
|
|
// iterate over updates and create views for each using the template
|
|
var updateEle = this.$el.find("#course-update-list");
|
|
// remove and then add all children
|
|
$(updateEle).empty();
|
|
var self = this;
|
|
this.collection.each(function (update, index) {
|
|
try {
|
|
CourseInfoHelper.changeContentToPreview(
|
|
update, 'content', self.options['base_asset_url']);
|
|
// push notification is always disabled for existing updates
|
|
var newEle = self.template({ updateModel : update, push_notification_enabled : false });
|
|
$(updateEle).append(newEle);
|
|
DateUtils.setupDatePicker("date", self, index);
|
|
update.isValid();
|
|
} catch (e) {
|
|
// ignore
|
|
}
|
|
});
|
|
this.$el.find(".new-update-form").hide();
|
|
return this;
|
|
},
|
|
|
|
collectionSelector: function(uid) {
|
|
return "course-update-list li[name=" + uid + "]";
|
|
},
|
|
|
|
setAndValidate: function(attr, value, event) {
|
|
if (attr === 'date') {
|
|
// If the value to be set was typed, validate that entry rather than the current datepicker value
|
|
if (this.dateEntry(event).length > 0) {
|
|
value = DateUtils.parseDateFromString(this.dateEntry(event).val());
|
|
if (value && isNaN(value.getTime())) {
|
|
value = "";
|
|
}
|
|
}
|
|
value = $.datepicker.formatDate("MM d, yy", value);
|
|
}
|
|
var targetModel = this.collection.get(this.$currentPost.attr('name'));
|
|
var prevValue = targetModel.get(attr);
|
|
if (prevValue !== value) {
|
|
targetModel.set(attr, value);
|
|
this.validateModel(targetModel);
|
|
}
|
|
},
|
|
|
|
handleValidationError : function(model, error) {
|
|
var ele = this.$el.find('#course-update-list li[name=\"'+model.cid+'\"]');
|
|
$(ele).find('.message-error').remove();
|
|
for (var field in error) {
|
|
if (error.hasOwnProperty(field)) {
|
|
$(ele).find('#update-date-'+model.cid).parent().append(
|
|
this.errorTemplate({message : error[field]})
|
|
);
|
|
$(ele).find('.date-display').parent().append(this.errorTemplate({message : error[field]}));
|
|
}
|
|
}
|
|
$(ele).find('.save-button').addClass('is-disabled');
|
|
},
|
|
|
|
validateModel: function(model) {
|
|
if (model.isValid()) {
|
|
var ele = this.$el.find('#course-update-list li[name=\"' + model.cid + '\"]');
|
|
$(ele).find('.message-error').remove();
|
|
$(ele).find('.save-button').removeClass('is-disabled');
|
|
}
|
|
},
|
|
|
|
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 CourseUpdateModel();
|
|
this.collection.add(newModel, {at : 0});
|
|
|
|
var $newForm = $(
|
|
this.template({
|
|
updateModel : newModel,
|
|
push_notification_enabled : this.options.push_notification_enabled
|
|
})
|
|
);
|
|
|
|
var updateEle = this.$el.find("#course-update-list");
|
|
$(updateEle).prepend($newForm);
|
|
|
|
var $textArea = $newForm.find(".new-update-content").first();
|
|
this.$codeMirror = CodeMirror.fromTextArea($textArea.get(0), {
|
|
mode: "text/html",
|
|
lineNumbers: true,
|
|
lineWrapping: true
|
|
});
|
|
|
|
$newForm.addClass('editing');
|
|
this.$currentPost = $newForm.closest('li');
|
|
|
|
// Variable stored for unit test.
|
|
this.$modalCover = ModalUtils.showModalCover(false, function() {
|
|
// Binding empty function to prevent default hideModal.
|
|
});
|
|
|
|
DateUtils.setupDatePicker("date", this, 0);
|
|
},
|
|
|
|
onSave: function(event) {
|
|
event.preventDefault();
|
|
var targetModel = this.eventModel(event);
|
|
targetModel.set({
|
|
// translate short-form date (for input) into long form date (for display)
|
|
date : $.datepicker.formatDate("MM d, yy", new Date(this.dateEntry(event).val())),
|
|
content : this.$codeMirror.getValue(),
|
|
push_notification_selected : this.push_notification_selected(event)
|
|
});
|
|
// push change to display, hide the editor, submit the change
|
|
var saving = new NotificationView.Mini({
|
|
title: gettext('Saving')
|
|
});
|
|
saving.show();
|
|
var ele = this.modelDom(event);
|
|
targetModel.save({}, {
|
|
success: function() {
|
|
saving.hide();
|
|
},
|
|
error: function() {
|
|
ele.remove();
|
|
}
|
|
});
|
|
this.closeEditor(false);
|
|
|
|
analytics.track('Saved Course Update', {
|
|
'course': course_location_analytics,
|
|
'date': this.dateEntry(event).val(),
|
|
'push_notification_selected': this.push_notification_selected(event)
|
|
});
|
|
},
|
|
|
|
onCancel: function(event) {
|
|
event.preventDefault();
|
|
// Since we're cancelling, the model should be using it's previous attributes
|
|
var targetModel = this.eventModel(event);
|
|
targetModel.set(targetModel.previousAttributes());
|
|
this.validateModel(targetModel);
|
|
// Hide the editor
|
|
$(this.editor(event)).hide();
|
|
// targetModel will be lacking an id if it was newly created
|
|
this.closeEditor(!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();
|
|
var targetModel = this.eventModel(event);
|
|
// translate long-form date (for viewing) into short-form date (for input)
|
|
if (targetModel.get('date') && targetModel.isValid()) {
|
|
$(this.dateEntry(event)).val($.datepicker.formatDate("mm/dd/yy", new Date(targetModel.get('date'))));
|
|
}
|
|
else {
|
|
$(this.dateEntry(event)).val("MM/DD/YY");
|
|
}
|
|
this.$codeMirror = CourseInfoHelper.editWithCodeMirror(
|
|
targetModel, 'content', self.options['base_asset_url'], $textArea.get(0));
|
|
|
|
// Variable stored for unit test.
|
|
this.$modalCover = ModalUtils.showModalCover(false,
|
|
function() {
|
|
self.closeEditor(false);
|
|
}
|
|
);
|
|
|
|
// Ensure validity is marked appropriately
|
|
targetModel.isValid();
|
|
},
|
|
|
|
onDelete: function(event) {
|
|
event.preventDefault();
|
|
|
|
var self = this;
|
|
var targetModel = this.eventModel(event);
|
|
var confirm = new PromptView.Warning({
|
|
title: gettext('Are you sure you want to delete this update?'),
|
|
message: gettext('This action cannot be undone.'),
|
|
actions: {
|
|
primary: {
|
|
text: gettext('OK'),
|
|
click: function () {
|
|
analytics.track('Deleted Course Update', {
|
|
'course': course_location_analytics,
|
|
'date': self.dateEntry(event).val()
|
|
});
|
|
self.modelDom(event).remove();
|
|
var deleting = new NotificationView.Mini({
|
|
title: gettext('Deleting')
|
|
});
|
|
deleting.show();
|
|
targetModel.destroy({
|
|
success: function (model, response) {
|
|
self.collection.fetch({
|
|
success: function() {
|
|
self.render();
|
|
deleting.hide();
|
|
},
|
|
reset: true
|
|
});
|
|
}
|
|
});
|
|
confirm.hide();
|
|
}
|
|
},
|
|
secondary: {
|
|
text: gettext('Cancel'),
|
|
click: function() {
|
|
confirm.hide();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
confirm.show();
|
|
},
|
|
|
|
closeEditor: function(removePost) {
|
|
var targetModel = this.collection.get(this.$currentPost.attr('name'));
|
|
|
|
// If the model was never created (user created a new update, then pressed Cancel),
|
|
// we wish to remove it from the DOM.
|
|
if(removePost) {
|
|
this.$currentPost.remove();
|
|
}
|
|
else {
|
|
// close the modal and insert the appropriate data
|
|
this.$currentPost.removeClass('editing');
|
|
this.$currentPost.find('.date-display').html(targetModel.get('date'));
|
|
this.$currentPost.find('.date').val(targetModel.get('date'));
|
|
|
|
var content = CourseInfoHelper.changeContentToPreview(
|
|
targetModel, 'content', this.options['base_asset_url']);
|
|
try {
|
|
// just in case the content causes an error (embedded js errors)
|
|
this.$currentPost.find('.update-contents').html(content);
|
|
this.$currentPost.find('.new-update-content').val(content);
|
|
} catch (e) {
|
|
// ignore but handle rest of page
|
|
}
|
|
this.$currentPost.find('form').hide();
|
|
this.$currentPost.find('.CodeMirror').remove();
|
|
|
|
// hide the push notification checkbox for subsequent edits to the Post
|
|
var push_notification_ele = this.$currentPost.find(".new-update-push-notification");
|
|
if (push_notification_ele) {
|
|
push_notification_ele.hide();
|
|
}
|
|
}
|
|
|
|
ModalUtils.hideModalCover(this.$modalCover);
|
|
this.$codeMirror = null;
|
|
},
|
|
|
|
// 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();
|
|
}
|
|
},
|
|
|
|
dateEntry: function(event) {
|
|
var li = $(event.currentTarget).closest("li");
|
|
if (li) {
|
|
return $(li).find(".date").first();
|
|
}
|
|
},
|
|
|
|
push_notification_selected: function(event) {
|
|
var push_notification_checkbox;
|
|
var li = $(event.currentTarget).closest("li");
|
|
if (li) {
|
|
push_notification_checkbox = li.find(".new-update-push-notification .toggle-checkbox");
|
|
if (push_notification_checkbox) {
|
|
return push_notification_checkbox.is(":checked");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return CourseInfoUpdateView;
|
|
}); // end define()
|