define(["js/views/baseview", "underscore", "codemirror", "js/models/course_update", "js/views/feedback_prompt", "js/views/feedback_notification", "js/views/course_info_helper", "js/utils/modal"], function(BaseView, _, CodeMirror, CourseUpdateModel, PromptView, NotificationView, CourseInfoHelper, ModalUtils) { var CourseInfoUpdateView = BaseView.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 = _.template($("#course_info_update-tpl").text()); this.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"); // remove and then add all children $(updateEle).empty(); var self = this; this.collection.each(function (update) { try { CourseInfoHelper.changeContentToPreview( update, 'content', self.options['base_asset_url']); var newEle = self.template({ updateModel : update }); $(updateEle).append(newEle); } catch (e) { // ignore } }); this.$el.find(".new-update-form").hide(); 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 CourseUpdateModel(); this.collection.add(newModel, {at : 0}); var $newForm = $(this.template({ updateModel : newModel })); 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() { self.closeEditor(true) }); $('.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 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() }); }, onCancel: function(event) { event.preventDefault(); // change editor contents back to model values and hide the editor $(this.editor(event)).hide(); // If the model was never created (user created a new update, then pressed Cancel), // we wish to remove it from the DOM. var targetModel = this.eventModel(event); 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); 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) } ); }, 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(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(); } 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(); }, 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(); }, contentDisplay: function(event) { return $(event.currentTarget).closest("li").find(".update-contents").first(); } }); return CourseInfoUpdateView; }); // end define()