advanced_view.js 7.32 KB
Newer Older
cahrens committed
1 2 3
if (!CMS.Views['Settings']) CMS.Views.Settings = {};

CMS.Views.Settings.Advanced = CMS.Views.ValidatingView.extend({
4 5
    error_saving : "error_saving",
    successful_changes: "successful_changes",
cahrens committed
6 7 8

    // Model class is CMS.Models.Settings.Advanced
    events : {
9 10
        'focus :input' : "focusInput",
        'blur :input' : "blurInput"
11
        // TODO enable/disable save based on validation (currently enabled whenever there are changes)
cahrens committed
12 13 14 15 16 17 18 19 20 21 22
    },
    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();
            }
        );
23
        this.listenTo(this.model, 'invalid', this.handleValidationError);
cahrens committed
24 25 26 27 28 29 30
    },
    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();
31

32 33 34
        // b/c we've deleted all old fields, clear the map and repopulate
        this.fieldToSelectorMap = {};
        this.selectorToField = {};
cahrens committed
35 36 37 38 39

        // 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) {
40
                listEle$.append(self.renderTemplate(key, self.model.get(key)));
cahrens committed
41
            });
42

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

54
        var self = this;
55
        var oldValue = $(textarea).val();
cahrens committed
56
        CodeMirror.fromTextArea(textarea, {
cahrens committed
57
            mode: "application/json", lineNumbers: false, lineWrapping: false,
58 59
            onChange: function(instance, changeobj) {
                // this event's being called even when there's no change :-(
60 61 62
                if (instance.getValue() !== oldValue && !self.notificationBarShowing) {
                    self.showNotificationBar();
                }
63 64 65
            },
            onFocus : function(mirror) {
              $(textarea).parent().children('label').addClass("is-focused");
66
            },
cahrens committed
67
            onBlur: function (mirror) {
68
                $(textarea).parent().children('label').removeClass("is-focused");
69
                var key = $(mirror.getWrapperElement()).closest('.field-group').children('.key').attr('id');
70 71 72 73
                var stringValue = $.trim(mirror.getValue());
                // update CodeMirror to show the trimmed value.
                mirror.setValue(stringValue);
                var JSONValue = undefined;
cahrens committed
74
                try {
75 76 77 78 79 80 81 82 83 84 85 86
                    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
87 88
                            // console.log("Error with JSON, even after converting to String.");
                            // console.log(quotedE);
89 90 91
                            JSONValue = undefined;
                        }
                    }
cahrens committed
92
                }
93
                if (JSONValue !== undefined) {
94
                    self.clearValidationErrors();
95
                    self.model.set(key, JSONValue, {validate: true});
cahrens committed
96
                }
cahrens committed
97 98
            }
        });
cahrens committed
99
    },
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
    showNotificationBar: function() {
        var self = this;
        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.")
        var confirm = new CMS.Views.Notification.Warning({
            title: gettext("You've Made Some Changes"),
            message: message,
            actions: {
                primary: {
                    "text": gettext("Save Changes"),
                    "class": "action-save",
                    "click": function() {
                        self.saveView();
                        confirm.hide();
                        self.notificationBarShowing = false;
                    }
                },
                secondary: [{
                    "text": gettext("Cancel"),
                    "class": "action-cancel",
                    "click": function() {
                        self.revertView();
                        confirm.hide();
                        self.notificationBarShowing = false;
                    }
124
                }]
125 126 127
            }});
        this.notificationBarShowing = true;
        confirm.show();
128 129 130
        if(this.saved) {
            this.saved.hide();
        }
131
    },
132
    saveView : function() {
cahrens committed
133 134 135
        // TODO one last verification scan:
        //    call validateKey on each to ensure proper format
        //    check for dupes
136 137
        var self = this;
        this.model.save({},
cahrens committed
138 139
            {
            success : function() {
140
                self.render();
141
                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.");
142
                self.saved = new CMS.Views.Alert.Confirmation({
143 144 145 146
                    title: gettext("Your policy changes have been saved."),
                    message: message,
                    closeIcon: false
                });
147
                self.saved.show();
148 149 150
                analytics.track('Saved Advanced Settings', {
                    'course': course_location_analytics
                });
151
            }
cahrens committed
152 153
        });
    },
154 155 156 157 158
    revertView : function() {
        var self = this;
        this.model.deleteKeys = [];
        this.model.clear({silent : true});
        this.model.fetch({
cahrens committed
159
            success : function() { self.render(); },
160
            reset: true
cahrens committed
161 162
        });
    },
163 164 165 166
    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_')});
167

168 169
        this.fieldToSelectorMap[key] = newKeyId;
        this.selectorToField[newKeyId] = key;
170 171 172 173 174 175 176
        return newEle;
    },
    focusInput : function(event) {
        $(event.target).prev().addClass("is-focused");
    },
    blurInput : function(event) {
        $(event.target).prev().removeClass("is-focused");
cahrens committed
177
    }
178
});