Files
edx-platform/cms/static/js/views/settings/advanced_view.js
David Baumgold c6c5e50d86 Hook up notification framework to default AJAX error callback
And delete `CMS.ServerError`
2013-05-22 15:55:49 -04:00

175 lines
7.2 KiB
JavaScript

if (!CMS.Views['Settings']) CMS.Views.Settings = {};
CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
error_saving : "error_saving",
successful_changes: "successful_changes",
// Model class is CMS.Models.Settings.Advanced
events : {
'focus :input' : "focusInput",
'blur :input' : "blurInput"
// TODO enable/disable save based on validation (currently enabled whenever there are changes)
},
initialize : function() {
var self = this;
// instantiates an editor template for each update in the collection
window.templateLoader.loadRemoteTemplate("advanced_entry",
"/static/client_templates/advanced_entry.html",
function (raw_template) {
self.template = _.template(raw_template);
self.render();
}
);
// because these are outside of this.$el, they can't be in the event hash
$('.save-button').on('click', this, this.saveView);
$('.cancel-button').on('click', this, this.revertView);
this.listenTo(this.model, 'invalid', this.handleValidationError);
},
render: function() {
// catch potential outside call before template loaded
if (!this.template) return this;
var listEle$ = this.$el.find('.course-advanced-policy-list');
listEle$.empty();
// b/c we've deleted all old fields, clear the map and repopulate
this.fieldToSelectorMap = {};
this.selectorToField = {};
// iterate through model and produce key : value editors for each property in model.get
var self = this;
_.each(_.sortBy(_.keys(this.model.attributes), _.identity),
function(key) {
listEle$.append(self.renderTemplate(key, self.model.get(key)));
});
var policyValues = listEle$.find('.json');
_.each(policyValues, this.attachJSONEditor, this);
this.showMessage();
return this;
},
attachJSONEditor : function (textarea) {
// Since we are allowing duplicate keys at the moment, it is possible that we will try to attach
// JSON Editor to a value that already has one. Therefore only attach if no CodeMirror peer exists.
if ( $(textarea).siblings().hasClass('CodeMirror')) {
return;
}
var self = this;
var oldValue = $(textarea).val();
CodeMirror.fromTextArea(textarea, {
mode: "application/json", lineNumbers: false, lineWrapping: false,
onChange: function(instance, changeobj) {
// this event's being called even when there's no change :-(
if (instance.getValue() !== oldValue) self.showSaveCancelButtons();
},
onFocus : function(mirror) {
$(textarea).parent().children('label').addClass("is-focused");
},
onBlur: function (mirror) {
$(textarea).parent().children('label').removeClass("is-focused");
var key = $(mirror.getWrapperElement()).closest('.field-group').children('.key').attr('id');
var stringValue = $.trim(mirror.getValue());
// update CodeMirror to show the trimmed value.
mirror.setValue(stringValue);
var JSONValue = undefined;
try {
JSONValue = JSON.parse(stringValue);
} catch (e) {
// If it didn't parse, try converting non-arrays/non-objects to a String.
// But don't convert single-quote strings, which are most likely errors.
var firstNonWhite = stringValue.substring(0, 1);
if (firstNonWhite !== "{" && firstNonWhite !== "[" && firstNonWhite !== "'") {
try {
stringValue = '"'+stringValue +'"';
JSONValue = JSON.parse(stringValue);
mirror.setValue(stringValue);
} catch(quotedE) {
// TODO: validation error
// console.log("Error with JSON, even after converting to String.");
// console.log(quotedE);
JSONValue = undefined;
}
}
}
if (JSONValue !== undefined) {
self.clearValidationErrors();
self.model.set(key, JSONValue, {validate: true});
}
}
});
},
showMessage: function (type) {
$(".wrapper-alert").removeClass("is-shown");
if (type) {
if (type === this.error_saving) {
$(".wrapper-alert-error").addClass("is-shown").attr('aria-hidden','false');
}
else if (type === this.successful_changes) {
$(".wrapper-alert-confirmation").addClass("is-shown").attr('aria-hidden','false');
this.hideSaveCancelButtons();
}
}
else {
// This is the case of the page first rendering, or when Cancel is pressed.
this.hideSaveCancelButtons();
}
},
showSaveCancelButtons: function(event) {
if (!this.notificationBarShowing) {
this.$el.find(".message-status").removeClass("is-shown");
$('.wrapper-notification').removeClass('is-hiding').addClass('is-shown').attr('aria-hidden','false');
this.notificationBarShowing = true;
}
},
hideSaveCancelButtons: function() {
if (this.notificationBarShowing) {
$('.wrapper-notification').removeClass('is-shown').addClass('is-hiding').attr('aria-hidden','true');
this.notificationBarShowing = false;
}
},
saveView : function(event) {
window.CmsUtils.smoothScrollTop(event);
// TODO one last verification scan:
// call validateKey on each to ensure proper format
// check for dupes
var self = event.data;
self.model.save({},
{
success : function() {
self.render();
self.showMessage(self.successful_changes);
analytics.track('Saved Advanced Settings', {
'course': course_location_analytics
});
}
});
},
revertView : function(event) {
event.preventDefault();
var self = event.data;
self.model.deleteKeys = [];
self.model.clear({silent : true});
self.model.fetch({
success : function() { self.render(); },
reset: true
});
},
renderTemplate: function (key, value) {
var newKeyId = _.uniqueId('policy_key_'),
newEle = this.template({ key : key, value : JSON.stringify(value, null, 4),
keyUniqueId: newKeyId, valueUniqueId: _.uniqueId('policy_value_')});
this.fieldToSelectorMap[key] = newKeyId;
this.selectorToField[newKeyId] = key;
return newEle;
},
focusInput : function(event) {
$(event.target).prev().addClass("is-focused");
},
blurInput : function(event) {
$(event.target).prev().removeClass("is-focused");
}
});