MDL-31655 Warn users of unblurred but modified fields
authorAndrew Robert Nicols <andrew.nicols@luns.net.uk>
Tue, 28 Feb 2012 15:22:52 +0000 (15:22 +0000)
committerAndrew Robert Nicols <andrew.nicols@luns.net.uk>
Wed, 29 Feb 2012 15:04:31 +0000 (15:04 +0000)
lib/yui/formchangechecker/formchangechecker.js

index 67b9a5e..dc819aa 100644 (file)
@@ -22,6 +22,11 @@ YUI.add('moodle-core-formchangechecker',
                     Y.all(formid + ' textarea').once('change', M.core_formchangechecker.set_form_changed, this);
                     Y.all(formid + ' select').once('change', M.core_formchangechecker.set_form_changed, this);
 
+                    // Add a focus event to check for changes which are made without triggering a change event
+                    Y.all(formid + ' input').on('focus', this.store_initial_value, this);
+                    Y.all(formid + ' textarea').on('focus', this.store_initial_value, this);
+                    Y.all(formid + ' select').on('focus', this.store_initial_value, this);
+
                     // We need any submit buttons on the form to set the submitted flag
                     Y.one(formid).on('submit', M.core_formchangechecker.set_form_submitted, this);
 
@@ -29,6 +34,33 @@ YUI.add('moodle-core-formchangechecker',
                     // a result, the has_changed must stay in the DOM too
                     window.onbeforeunload = M.core_formchangechecker.report_form_dirty_state;
                 },
+
+                /**
+                 * Store the initial value of the currently focussed element
+                 *
+                 * If an element has been focussed and changed but not yet blurred, the on change
+                 * event won't be fired. We need to store it's initial value to compare it in the
+                 * get_form_dirty_state function later.
+                 */
+                store_initial_value : function(e) {
+                    if (M.core_formchangechecker.get_form_dirty_state()) {
+                        // Clear the store_initial_value listeners as the form is already dirty so
+                        // we no longer need to call this function
+                        var formid = 'form#' + this.get('formid');
+
+                        Y.all(formid + ' input').detach('focus', this.store_initial_value, this);
+                        Y.all(formid + ' textarea').detach('focus', this.store_initial_value, this);
+                        Y.all(formid + ' select').detach('focus', this.store_initial_value, this);
+                        return;
+                    }
+
+                    // Make a note of the current element so that it can be interrogated and
+                    // compared in the get_form_dirty_state function
+                    M.core_formchangechecker.stateinformation.focused_element = {
+                        element : e.target,
+                        initial_value : e.target.get('value')
+                    }
+                }
             },
             {
                 NAME : FORMCHANGECHECKERNAME,
@@ -58,6 +90,10 @@ YUI.add('moodle-core-formchangechecker',
          */
         M.core_formchangechecker.set_form_changed = function() {
             M.core_formchangechecker.stateinformation.formchanged = 1;
+
+            // Once the form has been marked as dirty, we no longer need to keep track of form elements
+            // which haven't yet blurred
+            delete M.core_formchangechecker.stateinformation.focused_element;
         }
 
         /**
@@ -86,6 +122,14 @@ YUI.add('moodle-core-formchangechecker',
                 return 1;
             }
 
+            // If a field has been focused and changed, but still has focus then the browser won't fire the
+            // onChange event. We check for this eventuality here
+            if (state.focused_element) {
+                if (state.focused_element.element.get('value') != state.focused_element.initial_value) {
+                    return 1;
+                }
+            }
+
             // Handle TinyMCE editor instances
             // We can't add a listener in the initializer as the editors may not have been created by that point
             // so we do so here instead