mirrored from git://git.moodle.org/moodle.git
/
formchangechecker.js
181 lines (155 loc) · 7.52 KB
/
formchangechecker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
YUI.add('moodle-core-formchangechecker',
function(Y) {
// The CSS selectors we use
var CSS = {
};
var FORMCHANGECHECKERNAME = 'core-formchangechecker';
var FORMCHANGECHECKER = function() {
FORMCHANGECHECKER.superclass.constructor.apply(this, arguments);
}
Y.extend(FORMCHANGECHECKER, Y.Base, {
/**
* Initialize the module
*/
initializer : function(config) {
var formid = 'form#' + this.get('formid');
// Add change events to the form elements
Y.all(formid + ' input').once('change', M.core_formchangechecker.set_form_changed, this);
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);
// YUI doesn't support onbeforeunload properly so we must use the DOM to set the onbeforeunload. As
// 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 (e.target.hasClass('ignoredirty')) {
// Don't warn on elements with the ignoredirty class
return;
}
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,
ATTRS : {
formid : {
'value' : ''
}
}
}
);
M.core_formchangechecker = M.core_formchangechecker || {};
// We might have multiple instances of the form change protector
M.core_formchangechecker.instances = M.core_formchangechecker.instances || [];
M.core_formchangechecker.init = function(config) {
var formchangechecker = new FORMCHANGECHECKER(config);
M.core_formchangechecker.instances.push(formchangechecker);
return formchangechecker;
}
// Store state information
M.core_formchangechecker.stateinformation = [];
/**
* Set the form changed state to true
*/
M.core_formchangechecker.set_form_changed = function(e) {
if (e && e.target && e.target.hasClass('ignoredirty')) {
// Don't warn on elements with the ignoredirty class
return;
}
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;
}
/**
* Set the form submitted state to true
*/
M.core_formchangechecker.set_form_submitted = function() {
M.core_formchangechecker.stateinformation.formsubmitted = 1;
}
/**
* Attempt to determine whether the form has been modified in any way and
* is thus 'dirty'
*
* @return Integer 1 is the form is dirty; 0 if not
*/
M.core_formchangechecker.get_form_dirty_state = function() {
var state = M.core_formchangechecker.stateinformation;
// If the form was submitted, then return a non-dirty state
if (state.formsubmitted) {
return 0;
}
// If any fields have been marked dirty, return a dirty state
if (state.formchanged) {
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
if (typeof tinyMCE != 'undefined') {
for (var editor in tinyMCE.editors) {
if (tinyMCE.editors[editor].isDirty()) {
return 1;
}
}
}
// If we reached here, then the form hasn't met any of the dirty conditions
return 0;
};
/**
* Return a suitable message if changes have been made to a form
*/
M.core_formchangechecker.report_form_dirty_state = function(e) {
if (!M.core_formchangechecker.get_form_dirty_state()) {
// the form is not dirty, so don't display any message
return;
}
// This is the error message that we'll show to browsers which support it
var warningmessage = M.util.get_string('changesmadereallygoaway', 'moodle');
// Most browsers are happy with the returnValue being set on the event
// But some browsers do not consistently pass the event
if (e) {
e.returnValue = warningmessage;
}
// But some require it to be returned instead
return warningmessage;
};
},
'@VERSION@', {
requires : ['base']
}
);