Moodle 2.0.3 release
[moodle.git] / lib / form / form.js
blob140a47502e336d5308f3830e27f834d4b27eaaf0
1 /**
2  * This file contains JS functionality required by mforms and is included automatically
3  * when required.
4  */
6 // Namespace for the form bits and bobs
7 M.form = M.form || {};
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  */
16 M.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._stateInput = new Y.NodeList(document.getElementsByName('mform_showadvanced_last'));
32                 this._advButtons.on('click', this.switchState, this);
33             }
34         },
35         /**
36          * Toggles between showing advanced items and hiding them.
37          * Should be fired by an event.
38          */
39         switchState : function(e) {
40             e.preventDefault();
41             if (this._stateInput.get('value')=='1') {
42                 this._stateInput.set('value', '0');
43                 this._advButtons.setAttribute('value', M.str.form.showadvanced);
44                 this._advAreas.addClass('hide');
45             } else {
46                 this._stateInput.set('value', '1');
47                 this._advButtons.setAttribute('value', M.str.form.hideadvanced);
48                 this._advAreas.removeClass('hide');
49             }
50         }
51     };
52     // Extend it with the YUI widget fw.
53     Y.extend(showAdvanced, Y.Base, showAdvanced.prototype, {
54         NAME : 'mform-showAdvanced'
55     });
56     M.form.showAdvanced = new showAdvanced(config);
57     return M.form.showAdvanced;
60 /**
61  * Initialises a manager for a forms dependencies.
62  * This should happen once per form.
63  */
64 M.form.initFormDependencies = function(Y, formid, dependencies) {
66     // If the dependencies isn't an array or object we don't want to
67     // know about it
68     if (!Y.Lang.isArray(dependencies) && !Y.Lang.isObject(dependencies)) {
69         return false;
70     }
72     /**
73      * Fixes an issue with YUI's processing method of form.elements property
74      * in Internet Explorer.
75      *     http://yuilibrary.com/projects/yui3/ticket/2528030
76      */
77     Y.Node.ATTRS.elements = {
78         getter: function() {
79             return Y.all(new Y.Array(this._node.elements, 0, true));
80         }
81     };
83     // Define the dependency manager if it hasn't already been defined.
84     M.form.dependencyManager = M.form.dependencyManager || (function(){
85         var dependencyManager = function(config) {
86             dependencyManager.superclass.constructor.apply(this, arguments);
87         };
88         dependencyManager.prototype = {
89             _form : null,
90             _depElements : [],
91             _nameCollections : [],
92             initializer : function(config) {
93                 var i = 0, nodeName;
94                 this._form = Y.one('#'+formid);
95                 for (i in dependencies) {
96                     this._depElements[i] = this.elementsByName(i);
97                     if (this._depElements[i].size() == 0) {
98                         continue;
99                     }
100                     this._depElements[i].each(function(node){
101                         nodeName = node.get('nodeName').toUpperCase();
102                         if (nodeName == 'INPUT') {
103                             if (node.getAttribute('type').match(/^(button|submit|radio|checkbox)$/)) {
104                                 node.on('click', this.checkDependencies, this);
105                             } else {
106                                 node.on('blur', this.checkDependencies, this);
107                             }
108                             node.on('change', this.checkDependencies, this);
109                         } else if (nodeName == 'SELECT') {
110                             node.on('change', this.checkDependencies, this);
111                         } else {
112                             node.on('click', this.checkDependencies, this);
113                             node.on('blur', this.checkDependencies, this);
114                             node.on('change', this.checkDependencies, this);
115                         }
116                     }, this);
117                 }
118                 this._form.get('elements').each(function(input){
119                     if (input.getAttribute('type')=='reset') {
120                         input.on('click', function(){
121                             this._form.reset();
122                             this.checkDependencies();
123                         }, this);
124                     }
125                 }, this);
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, e]);
169                             } else {
170                                 result = this._dependency_default(this._depElements[dependon], value, e);
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                 var form = this;
202                 els.each(function(){
203                     if (disabled) {
204                         this.setAttribute('disabled', 'disabled');
205                     } else {
206                         this.removeAttribute('disabled');
207                     }
209                     // Extra code to disable a filepicker
210                     if (this.getAttribute('class') == 'filepickerhidden'){
211                         var pickerbuttons = form.elementsByName(name + 'choose');
212                         pickerbuttons.each(function(){
213                             if (disabled){
214                                 this.setAttribute('disabled','disabled');
215                             } else {
216                                 this.removeAttribute('disabled');
217                             }
218                         });
219                     }
220                 })
221             },
222             /**
223              * Hides all elements with the given name.
224              */
225             _hideElement : function(name, hidden) {
226                 var els = this.elementsByName(name);
227                 els.each(function(){
228                     var e = els.ancestor('.fitem');
229                     if (e) {
230                         e.setStyles({
231                             display : (hidden)?'none':''
232                         })
233                     }
234                 });
235             },
236             _dependency_notchecked : function(elements, value) {
237                 var lock = false;
238                 elements.each(function(){
239                     if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
240                         return;
241                     }
242                     lock = lock || !Y.Node.getDOMNode(this).checked;
243                 });
244                 return {
245                     lock : lock,
246                     hide : false
247                 }
248             },
249             _dependency_checked : function(elements, value) {
250                 var lock = false;
251                 elements.each(function(){
252                     if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
253                         return;
254                     }
255                     lock = lock || Y.Node.getDOMNode(this).checked;
256                 });
257                 return {
258                     lock : lock,
259                     hide : false
260                 }
261             },
262             _dependency_noitemselected : function(elements, value) {
263                 var lock = false;
264                 elements.each(function(){
265                     lock = lock || this.get('selectedIndex') == -1;
266                 });
267                 return {
268                     lock : lock,
269                     hide : false
270                 }
271             },
272             _dependency_eq : function(elements, value) {
273                 var lock = false;
274                 elements.each(function(){
275                     if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
276                         return;
277                     } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
278                         return;
279                     }
280                     lock = lock || this.get('value') == value;
281                 });
282                 return {
283                     lock : lock,
284                     hide : false
285                 }
286             },
287             _dependency_hide : function(elements, value) {
288                 return {
289                     lock : false,
290                     hide : true
291                 }
292             },
293             _dependency_default : function(elements, value, ev) {
294                 var lock = false;
295                 elements.each(function(){
296                     if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
297                         return;
298                     } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
299                         return;
300                     }
301                     lock = lock || this.get('value') != value;
302                 });
303                 return {
304                     lock : lock,
305                     hide : false
306                 }
307             }
308         };
309         Y.extend(dependencyManager, Y.Base, dependencyManager.prototype, {
310             NAME : 'mform-dependency-manager'
311         });
313         return dependencyManager;
314     })();
316     return new M.form.dependencyManager();