Merge "Add $specialPageAliases for Bengali (bn)"
[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|OO.ui.Widget|null}
20 */
21 function hideIfGetField( $el, name ) {
22 var $found, $p, $widget,
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 $widget = $found.closest( '.oo-ui-widget[data-ooui]' );
35 if ( $widget.length ) {
36 return OO.ui.Widget.static.infuse( $widget );
37 }
38 return $found;
39 }
40 }
41 return null;
42 }
43
44 /**
45 * Helper function for hide-if to return a test function and list of
46 * dependent fields for a hide-if specification.
47 *
48 * @ignore
49 * @private
50 * @param {jQuery} $el
51 * @param {Array} spec
52 * @return {Array}
53 * @return {Array} return.0 Dependent fields, array of jQuery objects or OO.ui.Widgets
54 * @return {Function} return.1 Test function
55 */
56 function hideIfParse( $el, spec ) {
57 var op, i, l, v, field, $field, fields, func, funcs, getVal;
58
59 op = spec[ 0 ];
60 l = spec.length;
61 switch ( op ) {
62 case 'AND':
63 case 'OR':
64 case 'NAND':
65 case 'NOR':
66 funcs = [];
67 fields = [];
68 for ( i = 1; i < l; i++ ) {
69 if ( !$.isArray( spec[ i ] ) ) {
70 throw new Error( op + ' parameters must be arrays' );
71 }
72 v = hideIfParse( $el, spec[ i ] );
73 fields = fields.concat( v[ 0 ] );
74 funcs.push( v[ 1 ] );
75 }
76
77 l = funcs.length;
78 switch ( op ) {
79 case 'AND':
80 func = function () {
81 var i;
82 for ( i = 0; i < l; i++ ) {
83 if ( !funcs[ i ]() ) {
84 return false;
85 }
86 }
87 return true;
88 };
89 break;
90
91 case 'OR':
92 func = function () {
93 var i;
94 for ( i = 0; i < l; i++ ) {
95 if ( funcs[ i ]() ) {
96 return true;
97 }
98 }
99 return false;
100 };
101 break;
102
103 case 'NAND':
104 func = function () {
105 var i;
106 for ( i = 0; i < l; i++ ) {
107 if ( !funcs[ i ]() ) {
108 return true;
109 }
110 }
111 return false;
112 };
113 break;
114
115 case 'NOR':
116 func = function () {
117 var i;
118 for ( i = 0; i < l; i++ ) {
119 if ( funcs[ i ]() ) {
120 return false;
121 }
122 }
123 return true;
124 };
125 break;
126 }
127
128 return [ fields, func ];
129
130 case 'NOT':
131 if ( l !== 2 ) {
132 throw new Error( 'NOT takes exactly one parameter' );
133 }
134 if ( !$.isArray( spec[ 1 ] ) ) {
135 throw new Error( 'NOT parameters must be arrays' );
136 }
137 v = hideIfParse( $el, spec[ 1 ] );
138 fields = v[ 0 ];
139 func = v[ 1 ];
140 return [ fields, function () {
141 return !func();
142 } ];
143
144 case '===':
145 case '!==':
146 if ( l !== 3 ) {
147 throw new Error( op + ' takes exactly two parameters' );
148 }
149 field = hideIfGetField( $el, spec[ 1 ] );
150 if ( !field ) {
151 return [ [], function () {
152 return false;
153 } ];
154 }
155 v = spec[ 2 ];
156
157 if ( !( field instanceof jQuery ) ) {
158 // field is a OO.ui.Widget
159 if ( field.supports( 'isSelected' ) ) {
160 getVal = function () {
161 var selected = field.isSelected();
162 return selected ? field.getValue() : '';
163 };
164 } else {
165 getVal = function () {
166 return field.getValue();
167 };
168 }
169 } else {
170 $field = $( field );
171 if ( $field.prop( 'type' ) === 'radio' || $field.prop( 'type' ) === 'checkbox' ) {
172 getVal = function () {
173 var $selected = $field.filter( ':checked' );
174 return $selected.length ? $selected.val() : '';
175 };
176 } else {
177 getVal = function () {
178 return $field.val();
179 };
180 }
181 }
182
183 switch ( op ) {
184 case '===':
185 func = function () {
186 return getVal() === v;
187 };
188 break;
189 case '!==':
190 func = function () {
191 return getVal() !== v;
192 };
193 break;
194 }
195
196 return [ [ field ], func ];
197
198 default:
199 throw new Error( 'Unrecognized operation \'' + op + '\'' );
200 }
201 }
202
203 mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
204 $root.find( '.mw-htmlform-hide-if' ).each( function () {
205 var v, i, fields, test, func, spec, self, modules, data, extraModules,
206 $el = $( this );
207
208 modules = [];
209 if ( $el.is( '[data-ooui]' ) ) {
210 modules.push( 'mediawiki.htmlform.ooui' );
211 data = $el.data( 'mw-modules' );
212 if ( data ) {
213 // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer
214 extraModules = data.split( ',' );
215 modules.push.apply( modules, extraModules );
216 }
217 }
218
219 mw.loader.using( modules ).done( function () {
220 if ( $el.is( '[data-ooui]' ) ) {
221 // self should be a FieldLayout that mixes in mw.htmlform.Element
222 self = OO.ui.FieldLayout.static.infuse( $el );
223 spec = self.hideIf;
224 // The original element has been replaced with infused one
225 $el = self.$element;
226 } else {
227 self = $el;
228 spec = $el.data( 'hideIf' );
229 }
230
231 if ( !spec ) {
232 return;
233 }
234
235 v = hideIfParse( $el, spec );
236 fields = v[ 0 ];
237 test = v[ 1 ];
238 // The .toggle() method works mostly the same for jQuery objects and OO.ui.Widget
239 func = function () {
240 self.toggle( !test() );
241 };
242 for ( i = 0; i < fields.length; i++ ) {
243 // The .on() method works mostly the same for jQuery objects and OO.ui.Widget
244 fields[ i ].on( 'change', func );
245 }
246 func();
247 } );
248 } );
249 } );
250
251 }( mediaWiki, jQuery ) );