javascript MDL-23850 Moved mforms specific JavaScript to a separate file
[moodle.git] / lib / form / form.js
CommitLineData
c7e3e61c
SH
1/**
2 * This file contains JS functionality required by mforms and is included automatically
3 * when required.
4 */
5
6// Namespace for the form bits and bobs
7M.form = M.form || {};
8
9/**
10 * Initialises the show advanced functionality and events.
11 * This should only ever happen ONCE per page.
12 *
13 * @param {YUI} Y
14 * @param {object} config
15 */
16M.form.initShowAdvanced = function(Y, config) {
17 if (M.form.showAdvanced) {
18 return M.form.showAdvanced;
19 }
20 var showAdvanced = function(config) {
21 showAdvanced.superclass.constructor.apply(this, arguments);
22 }
23 showAdvanced.prototype = {
24 _advButtons : [],
25 _advAreas : [],
26 _stateInput : null,
27 initializer : function() {
28 this._advAreas = Y.all('form .advanced');
29 this._advButtons = Y.all('.showadvancedbtn');
30 if (this._advButtons.size() > 0) {
31 this._advButtons.item(0).get('form').get('elements').each(function(n){
32 if (this._stateInput == null && n.getAttribute('name') == 'mform_showadvanced_last') {
33 this._stateInput = n;
34 }
35 }, this);
36 this._advButtons.on('click', this.switchState, this);
37 }
38 },
39 /**
40 * Toggles between showing advanced items and hiding them.
41 * Should be fired by an event.
42 */
43 switchState : function(e) {
44 e.preventDefault();
45 if (this._stateInput.get('value')=='1') {
46 this._stateInput.set('value', '0');
47 this._advButtons.setAttribute('value', M.str.form.showadvanced);
48 this._advAreas.addClass('hide');
49 } else {
50 this._stateInput.set('value', '1');
51 this._advButtons.setAttribute('value', M.str.form.hideadvanced);
52 this._advAreas.removeClass('hide');
53 }
54 }
55 }
56 // Extend it with the YUI widget fw.
57 Y.extend(showAdvanced, Y.Base, showAdvanced.prototype, {
58 NAME : 'mform-showAdvanced'
59 })
60 M.form.showAdvanced = new showAdvanced(config);
61 return M.form.showAdvanced;
62}
63
64/**
65 * Initialises a manager for a forms dependencies.
66 * This should happen once per form.
67 */
68M.form.initFormDependencies = function(Y, formid, dependencies) {
69
70 // If the dependencies isn't an array or object we don't want to
71 // know about it
72 if (!Y.Lang.isArray(dependencies) && !Y.Lang.isObject(dependencies)) {
73 return false;
74 }
75
76 /**
77 * Fixes an issue with YUI's processing method of form.elements property
78 * in Internet Explorer.
79 * http://yuilibrary.com/projects/yui3/ticket/2528030
80 */
81 Y.Node.ATTRS.elements = {
82 getter: function() {
83 return Y.all(new Y.Array(this._node.elements, 0, true));
84 }
85 };
86
87 // Define the dependency manager if it hasn't already been defined.
88 M.form.dependencyManager = M.form.dependencyManager || (function(){
89 var dependencyManager = function(config) {
90 dependencyManager.superclass.constructor.apply(this, arguments);
91 }
92 dependencyManager.prototype = {
93 _form : null,
94 _depElements : [],
95 _nameCollections : [],
96 initializer : function(config) {
97 var i = 0, nodeName;
98 this._form = Y.one('#'+formid);
99 for (i in dependencies) {
100 this._depElements[i] = this.elementsByName(i);
101 if (this._depElements[i].size() == 0) {
102 continue;
103 }
104 this._depElements[i].each(function(node){
105 nodeName = node.get('nodeName').toUpperCase();
106 if (nodeName == 'INPUT') {
107 if (node.getAttribute('type').match(/^(button|submit|radio|checkbox)$/)) {
108 node.on('click', this.checkDependencies, this);
109 } else {
110 node.on('blur', this.checkDependencies, this);
111 }
112 node.on('change', this.checkDependencies, this);
113 } else if (nodeName == 'SELECT') {
114 node.on('change', this.checkDependencies, this);
115 } else {
116 node.on('click', this.checkDependencies, this);
117 node.on('blur', this.checkDependencies, this);
118 node.on('change', this.checkDependencies, this);
119 }
120 }, this);
121 }
122 this._form.all('reset').each(function(reset){
123 this._form.reset();
124 reset.on('click', this.checkDependencies, this);
125 }, this);
126
127 return this.checkDependencies(null);
128 },
129 /**
130 * Gets all elements in the form by thier name and returns
131 * a YUI NodeList
132 * @return Y.NodeList
133 */
134 elementsByName : function(name) {
135 if (!this._nameCollections[name]) {
136 var elements = [];
137 this._form.get('elements').each(function(){
138 if (this.getAttribute('name') == name) {
139 elements.push(this);
140 }
141 })
142 this._nameCollections[name] = new Y.NodeList(elements);
143 }
144 return this._nameCollections[name];
145 },
146 /**
147 * Checks the dependencies the form has an makes any changes to the
148 * form that are required.
149 *
150 * Changes are made by functions title _dependency_{dependencytype}
151 * and more can easily be introduced by defining further functions.
152 */
153 checkDependencies : function(e) {
154 var tolock = [],
155 tohide = [],
156 dependon, condition, value,
157 lock, hide, checkfunction, result;
158 for (dependon in dependencies) {
159 if (this._depElements[dependon].size() == 0) {
160 continue;
161 }
162 for (condition in dependencies[dependon]) {
163 for (value in dependencies[dependon][condition]) {
164 lock = false;
165 hide = false;
166 checkfunction = '_dependency_'+condition;
167 if (Y.Lang.isFunction(this[checkfunction])) {
168 result = this[checkfunction].apply(this, [this._depElements[dependon], value]);
169 } else {
170 result = this._dependency_default(this._depElements[dependon], value);
171 }
172 lock = result.lock || false;
173 hide = result.hide || false;
174 for (var ei in dependencies[dependon][condition][value]) {
175 var eltolock = dependencies[dependon][condition][value][ei];
176 if (hide) {
177 tohide[eltolock] = true;
178 }
179 if (tolock[eltolock] != null) {
180 tolock[eltolock] = lock || tolock[eltolock];
181 } else {
182 tolock[eltolock] = lock;
183 }
184 }
185 }
186 }
187 }
188 for (var el in tolock) {
189 this._disableElement(el, tolock[el]);
190 if (tohide.propertyIsEnumerable(el)) {
191 this._hideElement(el, tohide[el]);
192 }
193 }
194 return true;
195 },
196 /**
197 * Disabled all form elements with the given name
198 */
199 _disableElement : function(name, disabled) {
200 var els = this.elementsByName(name);
201 els.each(function(){
202 if (disabled) {
203 this.setAttribute('disabled', 'disabled');
204 } else {
205 this.removeAttribute('disabled');
206 }
207 })
208 },
209 /**
210 * Hides all elements with the given name.
211 */
212 _hideElement : function(name, hidden) {
213 var els = this.elementsByName(name);
214 els.each(function(){
215 var e = els.ancestor('.fitem');
216 if (e) {
217 e.setStyles({
218 display : (hidden)?'none':''
219 })
220 }
221 });
222 },
223 _dependency_notchecked : function(elements, value) {
224 var lock = false;
225 elements.each(function(){
226 lock = lock || !this.get('checked');
227 });
228 return {
229 lock : lock,
230 hide : false
231 }
232 },
233 _dependency_checked : function(elements, value) {
234 var lock = false;
235 elements.each(function(){
236 lock = lock || this.get('checked');
237 });
238 return {
239 lock : lock,
240 hide : false
241 }
242 },
243 _dependency_noitemselected : function(elements, value) {
244 var lock = false;
245 elements.each(function(){
246 lock = lock || this.get('selectedIndex') == -1;
247 });
248 return {
249 lock : lock,
250 hide : false
251 }
252 },
253 _dependency_eq : function(elements, value) {
254 var lock = false;
255 elements.each(function(){
256 lock = lock || this.get('value') == value;
257 });
258 return {
259 lock : lock,
260 hide : false
261 }
262 },
263 _dependency_hide : function(elements, value) {
264 return {
265 lock : false,
266 hide : true
267 }
268 },
269 _dependency_default : function(elements, value) {
270 var lock = false;
271 elements.each(function(){
272 lock = lock || this.get('value') != value;
273 });
274 return {
275 lock : lock,
276 hide : false
277 }
278 }
279 }
280 Y.extend(dependencyManager, Y.Base, dependencyManager.prototype, {
281 NAME : 'mform-dependency-manager'
282 });
283
284 return dependencyManager;
285 })();
286
287 return new M.form.dependencyManager();
288}