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