xblock_string_field_editor.js 4.62 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 12

        var XBlockStringFieldEditor = BaseView.extend({
            events: {
13
                'click .xblock-field-value-edit': 'showInput',
14 15
                'click button[name=submit]': 'onClickSubmit',
                'click button[name=cancel]': 'onClickCancel',
16
                'click .xblock-string-field-editor': 'onClickEditor',
17 18 19 20 21 22 23 24 25 26
                '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');
27
                this.fieldDisplayName = this.$el.data('field-display-name');
28 29 30 31 32 33
                this.template = this.loadTemplate('xblock-string-field-editor');
                this.model.on('change:' + this.fieldName, this.onChangeField, this);
            },

            render: function() {
                this.$el.append(this.template({
34
                    value: this.model.escape(this.fieldName),
35 36
                    fieldName: this.fieldName,
                    fieldDisplayName: this.fieldDisplayName
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
                }));
                return this;
            },

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

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

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

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

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

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

72 73 74 75 76 77 78 79 80 81
            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();
82
                event.stopPropagation();
83
                this.$el.addClass('is-editing');
84
                input.focus().select();
85 86 87
            },

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

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

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

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

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

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