Files
edx-platform/cms/static/js/views/course_info_update.js
cahrens 665f983c99 Tweak focus/blur functions for JQuery upgrade.
Fix incorrectly scoped selector.
2016-05-24 16:53:42 -04:00

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 .save-button" : "onSave",
"click .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()