xblock_string_field_editor.js 4.61 KB
Newer Older
1 2 3 4 5 6 7
/**
 * XBlockStringFieldEditor is a view that allows the user to inline edit an XBlock string field.
 * Clicking on the field value will hide the text and replace it with an input to allow the user
 * to change the value. Once the user leaves the field, a request will be sent to update the
 * XBlock field's value if it has been changed. If the user presses Escape, then any changes will
 * be removed and the input hidden again.
 */
8 9
define(['js/views/baseview', 'js/views/utils/xblock_utils'],
    function(BaseView, XBlockViewUtils) {
10 11
        var XBlockStringFieldEditor = BaseView.extend({
            events: {
12
                'click .xblock-field-value-edit': 'showInput',
13 14
                'click button[name=submit]': 'onClickSubmit',
                'click button[name=cancel]': 'onClickCancel',
15
                'click .xblock-string-field-editor': 'onClickEditor',
16 17 18 19 20 21 22 23 24 25
                'change .xblock-field-input': 'updateField',
                'focusout .xblock-field-input': 'onInputFocusLost',
                'keyup .xblock-field-input': 'handleKeyUp'
            },

            // takes XBlockInfo as a model

            initialize: function() {
                BaseView.prototype.initialize.call(this);
                this.fieldName = this.$el.data('field');
26
                this.fieldDisplayName = this.$el.data('field-display-name');
27 28 29 30 31 32
                this.template = this.loadTemplate('xblock-string-field-editor');
                this.model.on('change:' + this.fieldName, this.onChangeField, this);
            },

            render: function() {
                this.$el.append(this.template({
33
                    value: this.model.escape(this.fieldName),
34 35
                    fieldName: this.fieldName,
                    fieldDisplayName: this.fieldDisplayName
36 37 38 39 40 41 42 43
                }));
                return this;
            },

            getLabel: function() {
                return this.$('.xblock-field-value');
            },

44
            getInput: function() {
45 46 47 48 49 50 51 52 53 54
                return this.$('.xblock-field-input');
            },

            onInputFocusLost: function() {
                var currentValue = this.model.get(this.fieldName);
                if (currentValue === this.getInput().val()) {
                    this.hideInput();
                }
            },

55 56
            onClickSubmit: function(event) {
                event.preventDefault();
57
                event.stopPropagation();
58 59 60
                this.updateField();
            },

61 62
            onClickCancel: function(event) {
                event.preventDefault();
63
                event.stopPropagation();
64 65 66
                this.cancelInput();
            },

67 68 69 70
            onClickEditor: function(event) {
                event.stopPropagation();
            },

71 72 73 74 75 76 77 78 79 80
            onChangeField: function() {
                var value = this.model.get(this.fieldName);
                this.getLabel().text(value);
                this.getInput().val(value);
                this.hideInput();
            },

            showInput: function(event) {
                var input = this.getInput();
                event.preventDefault();
81
                event.stopPropagation();
82
                this.$el.addClass('is-editing');
83
                input.focus().select();
84 85 86
            },

            hideInput: function() {
87
                this.$el.removeClass('is-editing');
88 89
            },

90 91 92 93 94
            cancelInput: function() {
                this.getInput().val(this.model.get(this.fieldName));
                this.hideInput();
            },

95 96 97 98 99 100 101
            /**
             * Refresh the model from the server so that it gets the latest publish and last modified information.
             */
            refresh: function() {
                this.model.fetch();
            },

102
            updateField: function() {
103 104
                var self = this,
                    xblockInfo = this.model,
105
                    newValue = this.getInput().val().trim(),
106 107
                    oldValue = xblockInfo.get(this.fieldName);
                // TODO: generalize this as not all xblock fields want to disallow empty strings...
108 109 110 111
                if (newValue === '' || newValue === oldValue) {
                    this.cancelInput();
                    return;
                }
112 113 114
                return XBlockViewUtils.updateXBlockField(xblockInfo, this.fieldName, newValue).done(function() {
                    self.refresh();
                });
115 116 117 118
            },

            handleKeyUp: function(event) {
                if (event.keyCode === 27) {   // Revert the changes if the user hits escape
119
                    this.cancelInput();
120 121 122 123 124 125
                }
            }
        });

        return XBlockStringFieldEditor;
    }); // end define();