MDL-54796 forms: Editor restore event created.
[moodle.git] / lib / yui / build / moodle-core-formchangechecker / moodle-core-formchangechecker-debug.js
CommitLineData
0653469e
ARN
1YUI.add('moodle-core-formchangechecker', function (Y, NAME) {
2
1f777e5c
AN
3/**
4 * A utility to check for form changes before navigating away from a page.
5 *
6 * @module moodle-core-formchangechecker
7 */
8
9/**
10 * A utility to check for form changes before navigating away from a page.
11 *
12 * @class M.core.formchangechecker
13 * @constructor
14 */
15
0653469e
ARN
16var FORMCHANGECHECKERNAME = 'core-formchangechecker',
17
18 FORMCHANGECHECKER = function() {
19 FORMCHANGECHECKER.superclass.constructor.apply(this, arguments);
20 };
21
22Y.extend(FORMCHANGECHECKER, Y.Base, {
23
24 // The delegated listeners we need to detach after the initial value has been stored once
25 initialvaluelisteners : [],
26
27 /**
1f777e5c
AN
28 * Initialize the module
29 *
30 * @method initializer
31 */
0653469e
ARN
32 initializer : function() {
33 var formid = 'form#' + this.get('formid'),
34 currentform = Y.one(formid);
35
36 if (!currentform) {
37 // If the form was not found, then we can't check for changes.
38 return;
39 }
40
b5594973
AG
41 // Add a listener here for an editor restore event.
42 Y.on(M.core.event.EDITOR_CONTENT_RESTORED, M.core_formchangechecker.reset_form_dirty_state, this);
43
0653469e
ARN
44 // Add change events to the form elements
45 currentform.delegate('change', M.core_formchangechecker.set_form_changed, 'input', this);
46 currentform.delegate('change', M.core_formchangechecker.set_form_changed, 'textarea', this);
47 currentform.delegate('change', M.core_formchangechecker.set_form_changed, 'select', this);
48
49 // Add a focus event to check for changes which are made without triggering a change event
50 this.initialvaluelisteners.push(currentform.delegate('focus', this.store_initial_value, 'input', this));
51 this.initialvaluelisteners.push(currentform.delegate('focus', this.store_initial_value, 'textarea', this));
52 this.initialvaluelisteners.push(currentform.delegate('focus', this.store_initial_value, 'select', this));
53
54 // We need any submit buttons on the form to set the submitted flag
55 Y.one(formid).on('submit', M.core_formchangechecker.set_form_submitted, this);
56
57 // YUI doesn't support onbeforeunload properly so we must use the DOM to set the onbeforeunload. As
58 // a result, the has_changed must stay in the DOM too
59 window.onbeforeunload = M.core_formchangechecker.report_form_dirty_state;
60 },
61
62 /**
1f777e5c
AN
63 * Store the initial value of the currently focussed element
64 *
65 * If an element has been focussed and changed but not yet blurred, the on change
66 * event won't be fired. We need to store it's initial value to compare it in the
67 * get_form_dirty_state function later.
68 *
69 * @method store_initial_value
70 * @param {EventFacade} e
71 */
0653469e
ARN
72 store_initial_value : function(e) {
73 var thisevent;
74 if (e.target.hasClass('ignoredirty')) {
75 // Don't warn on elements with the ignoredirty class
76 return;
77 }
78 if (M.core_formchangechecker.get_form_dirty_state()) {
79 // Detach all listen events to prevent duplicate initial value setting
80 while (this.initialvaluelisteners.length) {
81 thisevent = this.initialvaluelisteners.shift();
82 thisevent.detach();
83 }
84
85 return;
86 }
87
88 // Make a note of the current element so that it can be interrogated and
89 // compared in the get_form_dirty_state function
90 M.core_formchangechecker.stateinformation.focused_element = {
91 element : e.target,
92 initial_value : e.target.get('value')
93 };
94 }
95 },
96 {
97 NAME : FORMCHANGECHECKERNAME,
98 ATTRS : {
99 formid : {
100 'value' : ''
101 }
102 }
103 }
104);
105
106M.core_formchangechecker = M.core_formchangechecker || {};
107
108// We might have multiple instances of the form change protector
109M.core_formchangechecker.instances = M.core_formchangechecker.instances || [];
110M.core_formchangechecker.init = function(config) {
111 var formchangechecker = new FORMCHANGECHECKER(config);
112 M.core_formchangechecker.instances.push(formchangechecker);
113 return formchangechecker;
114};
115
116// Store state information
117M.core_formchangechecker.stateinformation = [];
118
1f777e5c
AN
119/*
120 * Set the form changed state to true
121 */
0653469e
ARN
122M.core_formchangechecker.set_form_changed = function(e) {
123 if (e && e.target && e.target.hasClass('ignoredirty')) {
124 // Don't warn on elements with the ignoredirty class
125 return;
126 }
127 M.core_formchangechecker.stateinformation.formchanged = 1;
128
129 // Once the form has been marked as dirty, we no longer need to keep track of form elements
130 // which haven't yet blurred
131 delete M.core_formchangechecker.stateinformation.focused_element;
132};
133
1f777e5c
AN
134/*
135 * Set the form submitted state to true
136 */
0653469e
ARN
137M.core_formchangechecker.set_form_submitted = function() {
138 M.core_formchangechecker.stateinformation.formsubmitted = 1;
139};
140
1f777e5c
AN
141/*
142 * Attempt to determine whether the form has been modified in any way and
143 * is thus 'dirty'
144 *
145 * @return Integer 1 is the form is dirty; 0 if not
146 */
0653469e
ARN
147M.core_formchangechecker.get_form_dirty_state = function() {
148 var state = M.core_formchangechecker.stateinformation,
149 editor;
150
151 // If the form was submitted, then return a non-dirty state
152 if (state.formsubmitted) {
153 return 0;
154 }
155
156 // If any fields have been marked dirty, return a dirty state
157 if (state.formchanged) {
158 return 1;
159 }
160
161 // If a field has been focused and changed, but still has focus then the browser won't fire the
162 // onChange event. We check for this eventuality here
163 if (state.focused_element) {
164 if (state.focused_element.element.get('value') !== state.focused_element.initial_value) {
165 return 1;
166 }
167 }
168
169 // Handle TinyMCE editor instances
170 // We can't add a listener in the initializer as the editors may not have been created by that point
171 // so we do so here instead
557f44d9
AN
172 if (typeof window.tinyMCE !== 'undefined') {
173 for (editor in window.tinyMCE.editors) {
174 if (window.tinyMCE.editors[editor].isDirty()) {
0653469e
ARN
175 return 1;
176 }
177 }
178 }
179
180 // If we reached here, then the form hasn't met any of the dirty conditions
181 return 0;
182};
183
584dd1b3
CB
184/*
185 * Reset the form state
186 */
187M.core_formchangechecker.reset_form_dirty_state = function() {
188 M.core_formchangechecker.stateinformation.formsubmitted = false;
189 M.core_formchangechecker.stateinformation.formchanged = false;
190};
191
1f777e5c
AN
192/*
193 * Return a suitable message if changes have been made to a form
194 */
0653469e
ARN
195M.core_formchangechecker.report_form_dirty_state = function(e) {
196 if (!M.core_formchangechecker.get_form_dirty_state()) {
197 // the form is not dirty, so don't display any message
198 return;
199 }
200
201 // This is the error message that we'll show to browsers which support it
202 var warningmessage = M.util.get_string('changesmadereallygoaway', 'moodle');
203
4415b667
DM
204 if (M.cfg.behatsiterunning) {
205 // If the behat site is running we don't want browser alerts.
206 return;
207 }
208
0653469e
ARN
209 // Most browsers are happy with the returnValue being set on the event
210 // But some browsers do not consistently pass the event
211 if (e) {
212 e.returnValue = warningmessage;
213 }
214
215 // But some require it to be returned instead
216 return warningmessage;
217};
218
219
b5594973 220}, '@VERSION@', {"requires": ["base", "event-focus", "moodle-core-event"]});