Commit 116c74c0 by Don Mitchell

Highlighting of field labels and smarter decision as to when to show the

save cancel bar.
parent cc63f9c2
...@@ -18,7 +18,7 @@ CMS.Models.Settings.Advanced = Backbone.Model.extend({ ...@@ -18,7 +18,7 @@ CMS.Models.Settings.Advanced = Backbone.Model.extend({
errors[key] = "A key must be entered."; errors[key] = "A key must be entered.";
} }
else if (_.contains(this.blacklistKeys, key)) { else if (_.contains(this.blacklistKeys, key)) {
errors[key] = key + " is a reserved keyword or has another editor"; errors[key] = key + " is a reserved keyword or can be edited on another screen";
} }
} }
if (!_.isEmpty(errors)) return errors; if (!_.isEmpty(errors)) return errors;
......
...@@ -10,7 +10,10 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -10,7 +10,10 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
'click .new-button' : "addEntry", 'click .new-button' : "addEntry",
// update model on changes // update model on changes
'change .policy-key' : "updateKey", 'change .policy-key' : "updateKey",
'keydown .policy-key' : "showSaveCancelButtons" // keyup fired on tab and other non-altering events
'keypress .policy-key' : "showSaveCancelButtons",
'focus :input' : "focusInput",
'blur :input' : "blurInput"
// TODO enable/disable save based on validation (currently enabled whenever there are changes) // TODO enable/disable save based on validation (currently enabled whenever there are changes)
}, },
initialize : function() { initialize : function() {
...@@ -33,17 +36,24 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -33,17 +36,24 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
var listEle$ = this.$el.find('.course-advanced-policy-list'); var listEle$ = this.$el.find('.course-advanced-policy-list');
listEle$.empty(); listEle$.empty();
// In this case, the fieldToSelectorMap (inherited from ValidatingView) use a map
// from the key to itself. Therefore the selectorToField map is the same object. // b/c we've deleted all old fields, clear the map and repopulate
this.fieldToSelectorMap = this.selectorToField = {}; this.fieldToSelectorMap = {};
this.selectorToField = {};
// iterate through model and produce key : value editors for each property in model.get // iterate through model and produce key : value editors for each property in model.get
var self = this; var self = this;
_.each(_.sortBy(_.keys(this.model.attributes), _.identity), _.each(_.sortBy(_.keys(this.model.attributes), _.identity),
function(key) { function(key) {
listEle$.append(self.getTemplate(key, self.model.get(key))); listEle$.append(self.renderTemplate(key, self.model.get(key)));
self.fieldToSelectorMap[key] = key;
}); });
// hilighting labels when fields are focused in
// listEle$.find(":input").focus(function(event) {
// listEle$.find("label[for='" + this.id + "']").addClass("is-focused");
// }).blur(function() {
// listEle$.find("label[for='" + this.id + "']").removeClass("is-focused");
// });
var policyValues = listEle$.find('.json'); var policyValues = listEle$.find('.json');
_.each(policyValues, this.attachJSONEditor, this); _.each(policyValues, this.attachJSONEditor, this);
this.showMessage(); this.showMessage();
...@@ -57,12 +67,18 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -57,12 +67,18 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
} }
var self = this; var self = this;
var oldValue = $(textarea).val();
CodeMirror.fromTextArea(textarea, { CodeMirror.fromTextArea(textarea, {
mode: "application/json", lineNumbers: false, lineWrapping: false, mode: "application/json", lineNumbers: false, lineWrapping: false,
onChange: function() { onChange: function(instance, changeobj) {
self.showSaveCancelButtons(); // 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) { onBlur: function (mirror) {
$(textarea).parent().children('label').removeClass("is-focused");
var key = $(mirror.getWrapperElement()).closest('.field-group').children('.key').attr('id'); var key = $(mirror.getWrapperElement()).closest('.field-group').children('.key').attr('id');
var stringValue = $.trim(mirror.getValue()); var stringValue = $.trim(mirror.getValue());
// update CodeMirror to show the trimmed value. // update CodeMirror to show the trimmed value.
...@@ -93,7 +109,6 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -93,7 +109,6 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
} }
} }
if (JSONValue !== undefined) { if (JSONValue !== undefined) {
// Is it OK to clear all validation errors? If we don't we get problems with errors overlaying.
self.clearValidationErrors(); self.clearValidationErrors();
self.model.set(key, JSONValue, {validate: true}); self.model.set(key, JSONValue, {validate: true});
} }
...@@ -119,8 +134,15 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -119,8 +134,15 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
} }
}, },
showSaveCancelButtons: function() { showSaveCancelButtons: function(event) {
if (!this.buttonsVisible) { if (!this.buttonsVisible) {
if (event && event.type === 'keypress') {
console.log(event.charCode, event.keyCode);
// check whether it's really an altering event
if (!((event.charCode && String.fromCharCode(event.charCode) !== "") ||
// 8 = backspace, 46 = delete
event.keyCode === 8 || event.keyCode === 46)) return;
}
this.$el.find(".message-status").removeClass("is-shown"); this.$el.find(".message-status").removeClass("is-shown");
$('.wrapper-notification').addClass('is-shown'); $('.wrapper-notification').addClass('is-shown');
this.buttonsVisible = true; this.buttonsVisible = true;
...@@ -149,6 +171,7 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -149,6 +171,7 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
// Not data b/c the validation view uses it for a selector // Not data b/c the validation view uses it for a selector
var key = $('.key', li$).attr('id'); var key = $('.key', li$).attr('id');
delete this.selectorToField[this.fieldToSelectorMap[key]];
delete this.fieldToSelectorMap[key]; delete this.fieldToSelectorMap[key];
if (key !== this.model.new_key) { if (key !== this.model.new_key) {
this.model.deleteKeys.push(key); this.model.deleteKeys.push(key);
...@@ -182,15 +205,13 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -182,15 +205,13 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
}, },
addEntry : function() { addEntry : function() {
var listEle$ = this.$el.find('.course-advanced-policy-list'); var listEle$ = this.$el.find('.course-advanced-policy-list');
var newEle = this.getTemplate("", ""); var newEle = this.renderTemplate("", "");
listEle$.append(newEle); listEle$.append(newEle);
this.fieldToSelectorMap[this.model.new_key] = this.model.new_key;
// need to re-find b/c replaceWith seems to copy rather than use the specific ele instance // need to re-find b/c replaceWith seems to copy rather than use the specific ele instance
var policyValueDivs = this.$el.find('#' + this.model.new_key).closest('li').find('.json'); var policyValueDivs = this.$el.find('#' + this.model.new_key).closest('li').find('.json');
// only 1 but hey, let's take advantage of the context mechanism // only 1 but hey, let's take advantage of the context mechanism
_.each(policyValueDivs, this.attachJSONEditor, this); _.each(policyValueDivs, this.attachJSONEditor, this);
this.toggleNewButton(false); this.toggleNewButton(false);
this.showSaveCancelButtons();
}, },
updateKey : function(event) { updateKey : function(event) {
var parentElement = $(event.currentTarget).closest('.key'); var parentElement = $(event.currentTarget).closest('.key');
...@@ -206,14 +227,13 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -206,14 +227,13 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
if (!this.validateKey(oldKey, newKey)) return; if (!this.validateKey(oldKey, newKey)) return;
// TODO: re-enable validation if (this.model.has(newKey)) {
// if (this.model.has(newKey)) { var error = {};
// var error = {}; error[oldKey] = 'You have already defined "' + newKey + '" in the manual policy definitions.';
// error[oldKey] = 'You have already defined "' + newKey + '" in the manual policy definitions.'; error[newKey] = "You tried to enter a duplicate of this key.";
// error[newKey] = "You tried to enter a duplicate of this key."; this.model.trigger("error", this.model, error);
// this.model.trigger("error", this.model, error); return false;
// return false; }
// }
// explicitly call validate to determine whether to proceed (relying on triggered error means putting continuation in the success // explicitly call validate to determine whether to proceed (relying on triggered error means putting continuation in the success
// method which is uglier I think?) // method which is uglier I think?)
...@@ -235,6 +255,10 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -235,6 +255,10 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
// Now safe to actually do the update // Now safe to actually do the update
this.model.set(newEntryModel); this.model.set(newEntryModel);
// update maps
var selector = this.fieldToSelectorMap[oldKey];
this.selectorToField[selector] = newKey;
this.fieldToSelectorMap[newKey] = selector;
delete this.fieldToSelectorMap[oldKey]; delete this.fieldToSelectorMap[oldKey];
if (oldKey !== this.model.new_key) { if (oldKey !== this.model.new_key) {
...@@ -256,25 +280,29 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({ ...@@ -256,25 +280,29 @@ CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
// Update the ID to the new value. // Update the ID to the new value.
parentElement.attr('id', newKey); parentElement.attr('id', newKey);
this.fieldToSelectorMap[newKey] = newKey;
} }
}, },
validateKey : function(oldKey, newKey) { validateKey : function(oldKey, newKey) {
// model validation can't handle malformed keys nor notice if 2 fields have same key; so, need to add that chk here // model validation can't handle malformed keys nor notice if 2 fields have same key; so, need to add that chk here
// TODO ensure there's no spaces or illegal chars (note some checking for spaces currently done in model's // TODO ensure there's no spaces or illegal chars (note some checking for spaces currently done in model's
// validate method. // validate method.
// if (_.isEmpty(newKey)) {
// var error = {};
// error[oldKey] = "Key cannot be an empty string";
// this.model.trigger("error", this.model, error);
// return false;
// }
// else return true;
return true; return true;
}, },
getTemplate: function (key, value) { renderTemplate: function (key, value) {
return this.template({ key : key, value : JSON.stringify(value, null, 4), var newKeyId = _.uniqueId('policy_key_'),
keyUniqueId: _.uniqueId('policy_key_'), valueUniqueId: _.uniqueId('policy_value_')}); newEle = this.template({ key : key, value : JSON.stringify(value, null, 4),
keyUniqueId: newKeyId, valueUniqueId: _.uniqueId('policy_value_')});
this.fieldToSelectorMap[(_.isEmpty(key) ? this.model.new_key : key)] = newKeyId;
this.selectorToField[newKeyId] = (_.isEmpty(key) ? this.model.new_key : key);
return newEle;
},
focusInput : function(event) {
$(event.target).prev().addClass("is-focused");
},
blurInput : function(event) {
$(event.target).prev().removeClass("is-focused");
} }
}); });
\ No newline at end of file
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