diff --git a/cms/static/img/spinner-in-field.gif b/cms/static/img/spinner-in-field.gif new file mode 100644 index 0000000000..fe2f556f5e Binary files /dev/null and b/cms/static/img/spinner-in-field.gif differ diff --git a/cms/static/js/base.js b/cms/static/js/base.js index 907e01e3d3..a043bb3bfd 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -4,6 +4,8 @@ var $modalCover; var $newComponentItem; var $newComponentStep1; var $newComponentStep2; +var $changedInput; +var $spinner; $(document).ready(function() { $body = $('body'); @@ -13,6 +15,7 @@ $(document).ready(function() { $newComponentTypePicker = $('.new-component'); $newComponentTemplatePickers = $('.new-component-templates'); $newComponentButton = $('.new-component-button'); + $spinner = $(''); $body.bind('keyup', onKeyUp); $('.expand-collapse-icon').bind('click', toggleSubmodules); @@ -31,6 +34,14 @@ $(document).ready(function() { $('.new-unit-item').bind('click', createNewUnit); $('.save-subsection').bind('click', saveSubsection); + // autosave when a field is updated on the subsection page + $body.on('keyup', '.subsection-display-name-input, .unit-subtitle, .policy-list-name, .policy-list-value', checkForNewValue); + $('.subsection-display-name-input, .unit-subtitle, .policy-list-name, .policy-list-value').each(function(i) { + this.nameString = $(this).val(); + }); + $("#start_date, #start_time, #due_date, #due_time").bind('change', autosaveInput); + $('.sync-date, .remove-date').bind('click', autosaveInput); + // making the unit list sortable $('.sortable-unit-list').sortable({ axis: 'y', @@ -207,8 +218,45 @@ function getEdxTimeFromDateTimeInputs(date_id, time_id, format) { return getEdxTimeFromDateTimeVals(input_date, input_time, format); } +function checkForNewValue(e) { + this.hasChanged = this.nameString != $(this).val() && this.nameString; + this.nameString = $(this).val(); + if(this.hasChanged) { + if(this.saveTimer) { + clearTimeout(this.saveTimer); + } + + this.saveTimer = setTimeout(function() { + $changedInput = $(e.target); + $('.save-subsection').click(); + this.saveTimer = null; + }, 500); + } +} + +function autosaveInput(e) { + if(this.saveTimer) { + clearTimeout(this.saveTimer); + } + + this.saveTimer = setTimeout(function() { + $changedInput = $(e.target); + $('.save-subsection').click(); + this.saveTimer = null; + }, 500); +} + function saveSubsection(e) { e.preventDefault(); + + // add an inline spinner + $spinner.css({ + 'position': 'absolute', + 'top': Math.floor($changedInput.position().top + ($changedInput.outerHeight() / 2) + 3), + 'left': $changedInput.position().left + $changedInput.outerWidth() - 24, + 'margin-top': '-10px' + }); + $changedInput.after($spinner); var id = $(this).data('id'); @@ -252,10 +300,10 @@ function saveSubsection(e) { contentType: "application/json", data:JSON.stringify({ 'id' : id, 'metadata' : metadata, 'data': null, 'children' : children}), success: function() { - showToastMessage('Your changes have been saved.', null, 3); + $spinner.delay(500).fadeOut(150); }, error: function() { - showToastMessage('There has been an error while saving your changes.', null, 3); + showToastMessage('There has been an error while saving your changes.'); } }); } diff --git a/cms/static/sass/_graphics.scss b/cms/static/sass/_graphics.scss index 994372504e..c6d775ed12 100644 --- a/cms/static/sass/_graphics.scss +++ b/cms/static/sass/_graphics.scss @@ -269,3 +269,11 @@ vertical-align: middle; background: url(../img/blue-spinner.gif) no-repeat; } + +.spinner-in-field-icon { + display: inline-block; + width: 14px; + height: 14px; + vertical-align: middle; + background: url(../img/spinner-in-field.gif) no-repeat; +} diff --git a/cms/static/sass/_unit.scss b/cms/static/sass/_unit.scss index 208404eecd..e15288024f 100644 --- a/cms/static/sass/_unit.scss +++ b/cms/static/sass/_unit.scss @@ -323,6 +323,7 @@ .save-button { @include blue-button; + display: none; } .save-button,