Merge "Split the 'mediawiki.htmlform' module code into multiple files"
[lhc/web/wiklou.git] / resources / src / mediawiki / htmlform / hide-if.js
1 /*
2 * HTMLForm enhancements:
3 * Set up 'hide-if' behaviors for form fields that have them.
4 */
5 ( function ( mw, $ ) {
6
7 /**
8 * Helper function for hide-if to find the nearby form field.
9 *
10 * Find the closest match for the given name, "closest" being the minimum
11 * level of parents to go to find a form field matching the given name or
12 * ending in array keys matching the given name (e.g. "baz" matches
13 * "foo[bar][baz]").
14 *
15 * @ignore
16 * @private
17 * @param {jQuery} $el
18 * @param {string} name
19 * @return {jQuery|null}
20 */
21 function hideIfGetField( $el, name ) {
22 var $found, $p,
23 suffix = name.replace( /^([^\[]+)/, '[$1]' );
24
25 function nameFilter() {
26 return this.name === name ||
27 ( this.name === ( 'wp' + name ) ) ||
28 this.name.slice( -suffix.length ) === suffix;
29 }
30
31 for ( $p = $el.parent(); $p.length > 0; $p = $p.parent() ) {
32 $found = $p.find( '[name]' ).filter( nameFilter );
33 if ( $found.length ) {
34 return $found;
35 }
36 }
37 return null;
38 }
39
40 /**
41 * Helper function for hide-if to return a test function and list of
42 * dependent fields for a hide-if specification.
43 *
44 * @ignore
45 * @private
46 * @param {jQuery} $el
47 * @param {Array} spec
48 * @return {Array}
49 * @return {jQuery} return.0 Dependent fields
50 * @return {Function} return.1 Test function
51 */
52 function hideIfParse( $el, spec ) {
53 var op, i, l, v, $field, $fields, fields, func, funcs, getVal;
54
55 op = spec[ 0 ];
56 l = spec.length;
57 switch ( op ) {
58 case 'AND':
59 case 'OR':
60 case 'NAND':
61 case 'NOR':
62 funcs = [];
63 fields = [];
64 for ( i = 1; i < l; i++ ) {
65 if ( !$.isArray( spec[ i ] ) ) {
66 throw new Error( op + ' parameters must be arrays' );
67 }
68 v = hideIfParse( $el, spec[ i ] );
69 fields = fields.concat( v[ 0 ].toArray() );
70 funcs.push( v[ 1 ] );
71 }
72 $fields = $( fields );
73
74 l = funcs.length;
75 switch ( op ) {
76 case 'AND':
77 func = function () {
78 var i;
79 for ( i = 0; i < l; i++ ) {
80 if ( !funcs[ i ]() ) {
81 return false;
82 }
83 }
84 return true;
85 };
86 break;
87
88 case 'OR':
89 func = function () {
90 var i;
91 for ( i = 0; i < l; i++ ) {
92 if ( funcs[ i ]() ) {
93 return true;
94 }
95 }
96 return false;
97 };
98 break;
99
100 case 'NAND':
101 func = function () {
102 var i;
103 for ( i = 0; i < l; i++ ) {
104 if ( !funcs[ i ]() ) {
105 return true;
106 }
107 }
108 return false;
109 };
110 break;
111
112 case 'NOR':
113 func = function () {
114 var i;
115 for ( i = 0; i < l; i++ ) {
116 if ( funcs[ i ]() ) {
117 return false;
118 }
119 }
120 return true;
121 };
122 break;
123 }
124
125 return [ $fields, func ];
126
127 case 'NOT':
128 if ( l !== 2 ) {
129 throw new Error( 'NOT takes exactly one parameter' );
130 }
131 if ( !$.isArray( spec[ 1 ] ) ) {
132 throw new Error( 'NOT parameters must be arrays' );
133 }
134 v = hideIfParse( $el, spec[ 1 ] );
135 $fields = v[ 0 ];
136 func = v[ 1 ];
137 return [ $fields, function () {
138 return !func();
139 } ];
140
141 case '===':
142 case '!==':
143 if ( l !== 3 ) {
144 throw new Error( op + ' takes exactly two parameters' );
145 }
146 $field = hideIfGetField( $el, spec[ 1 ] );
147 if ( !$field ) {
148 return [ $(), function () {
149 return false;
150 } ];
151 }
152 v = spec[ 2 ];
153
154 if ( $field.first().prop( 'type' ) === 'radio' ||
155 $field.first().prop( 'type' ) === 'checkbox'
156 ) {
157 getVal = function () {
158 var $selected = $field.filter( ':checked' );
159 return $selected.length ? $selected.val() : '';
160 };
161 } else {
162 getVal = function () {
163 return $field.val();
164 };
165 }
166
167 switch ( op ) {
168 case '===':
169 func = function () {
170 return getVal() === v;
171 };
172 break;
173 case '!==':
174 func = function () {
175 return getVal() !== v;
176 };
177 break;
178 }
179
180 return [ $field, func ];
181
182 default:
183 throw new Error( 'Unrecognized operation \'' + op + '\'' );
184 }
185 }
186
187 mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
188 $root.find( '.mw-htmlform-hide-if' ).each( function () {
189 var v, $fields, test, func,
190 $el = $( this ),
191 spec = $el.data( 'hideIf' );
192
193 if ( !spec ) {
194 return;
195 }
196
197 v = hideIfParse( $el, spec );
198 $fields = v[ 0 ];
199 test = v[ 1 ];
200 func = function () {
201 if ( test() ) {
202 $el.hide();
203 } else {
204 $el.show();
205 }
206 };
207 $fields.on( 'change', func );
208 func();
209 } );
210 } );
211
212 }( mediaWiki, jQuery ) );