advanced.js 7.16 KB
Newer Older
1 2
define(["js/views/validation", "jquery", "underscore", "gettext", "codemirror", "js/views/modals/validation_error_modal"],
    function(ValidatingView, $, _, gettext, CodeMirror, ValidationErrorModal) {
cahrens committed
3

4
var AdvancedView = ValidatingView.extend({
5 6
    error_saving : "error_saving",
    successful_changes: "successful_changes",
7
    render_deprecated: false,
cahrens committed
8 9 10

    // Model class is CMS.Models.Settings.Advanced
    events : {
11 12
        'focus :input' : "focusInput",
        'blur :input' : "blurInput"
13
        // TODO enable/disable save based on validation (currently enabled whenever there are changes)
cahrens committed
14 15
    },
    initialize : function() {
16
        this.template = _.template($("#advanced_entry-tpl").text());
17
        this.listenTo(this.model, 'invalid', this.handleValidationError);
18
        this.render();
cahrens committed
19 20 21 22 23 24 25
    },
    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();
26

27 28 29
        // b/c we've deleted all old fields, clear the map and repopulate
        this.fieldToSelectorMap = {};
        this.selectorToField = {};
cahrens committed
30 31 32

        // iterate through model and produce key : value editors for each property in model.get
        var self = this;
33
        _.each(_.sortBy(_.keys(this.model.attributes), function(key) { return self.model.get(key).display_name; }),
cahrens committed
34
            function(key) {
35 36 37
                if (self.render_deprecated || !self.model.get(key).deprecated) {
                    listEle$.append(self.renderTemplate(key, self.model.get(key)));
                }
cahrens committed
38
            });
39

cahrens committed
40 41
        var policyValues = listEle$.find('.json');
        _.each(policyValues, this.attachJSONEditor, this);
42 43
        return this;
    },
cahrens committed
44
    attachJSONEditor : function (textarea) {
45 46
        // 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.
cahrens committed
47
        if ( $(textarea).siblings().hasClass('CodeMirror')) {
48 49 50
            return;
        }

51
        var self = this;
52
        var oldValue = $(textarea).val();
53
        var cm = CodeMirror.fromTextArea(textarea, {
54 55
            mode: "application/json",
            lineNumbers: false,
56 57
            lineWrapping: false});
        cm.on('change', function(instance, changeobj) {
58
                instance.save();
59
                // this event's being called even when there's no change :-(
60 61 62 63
                if (instance.getValue() !== oldValue) {
                    var message = gettext("Your changes will not take effect until you save your progress. Take care with key and value formatting, as validation is not implemented.");
                    self.showNotificationBar(message,
                                             _.bind(self.saveView, self),
64
                                             _.bind(self.revertView, self));
65
                }
66 67
            });
        cm.on('focus', function(mirror) {
68
              $(textarea).parent().children('label').addClass("is-focused");
69 70
            });
        cm.on('blur', function (mirror) {
71
                $(textarea).parent().children('label').removeClass("is-focused");
72
                var key = $(mirror.getWrapperElement()).closest('.field-group').children('.key').attr('id');
73 74 75 76
                var stringValue = $.trim(mirror.getValue());
                // update CodeMirror to show the trimmed value.
                mirror.setValue(stringValue);
                var JSONValue = undefined;
cahrens committed
77
                try {
78 79 80 81 82 83 84 85 86 87 88 89
                    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
90 91
                            // console.log("Error with JSON, even after converting to String.");
                            // console.log(quotedE);
92 93 94
                            JSONValue = undefined;
                        }
                    }
cahrens committed
95
                }
96
                if (JSONValue !== undefined) {
97 98 99
                    var modelVal = self.model.get(key);
                    modelVal.value = JSONValue;
                    self.model.set(key, modelVal);
cahrens committed
100
                }
101
            });
cahrens committed
102
    },
103
    saveView : function() {
cahrens committed
104 105 106
        // TODO one last verification scan:
        //    call validateKey on each to ensure proper format
        //    check for dupes
107
        var self = this;
108
        this.model.save({}, {
cahrens committed
109
            success : function() {
110
                self.render();
111
                var title = gettext("Your policy changes have been saved.");
112
                var message = gettext("Please note that validation of your policy key and value pairs is not currently in place yet. If you are having difficulties, please review your policy pairs.");
113
                self.showSavedBar(title, message);
114 115 116
                analytics.track('Saved Advanced Settings', {
                    'course': course_location_analytics
                });
117
            },
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
            silent: true,
            error: function(model, response, options) {
                var json_response, reset_callback, err_modal;

                /* Check that the server came back with a bad request error*/
                if (response.status === 400) {
                    json_response = $.parseJSON(response.responseText);
                    reset_callback = function() {
                        self.revertView();
                    };

                    /* initialize and show validation error modal */
                    err_modal = new ValidationErrorModal();
                    err_modal.setContent(json_response);
                    err_modal.setResetCallback(reset_callback);
                    err_modal.show();
                }
            }
cahrens committed
136 137
        });
    },
138
    revertView: function() {
139 140
        var self = this;
        this.model.fetch({
141
            success: function() { self.render(); },
142
            reset: true
cahrens committed
143 144
        });
    },
145
    renderTemplate: function (key, model) {
146
        var newKeyId = _.uniqueId('policy_key_'),
147 148
        newEle = this.template({ key: key, display_name : model.display_name, help: model.help,
            value : JSON.stringify(model.value, null, 4), deprecated: model.deprecated,
149
            keyUniqueId: newKeyId, valueUniqueId: _.uniqueId('policy_value_')});
150

151 152
        this.fieldToSelectorMap[key] = newKeyId;
        this.selectorToField[newKeyId] = key;
153 154 155 156 157 158 159
        return newEle;
    },
    focusInput : function(event) {
        $(event.target).prev().addClass("is-focused");
    },
    blurInput : function(event) {
        $(event.target).prev().removeClass("is-focused");
cahrens committed
160
    }
161
});
162 163 164

return AdvancedView;
});