Merge "selenium: invoke jobs to enforce eventual consistency"
[lhc/web/wiklou.git] / resources / src / mediawiki.widgets / mw.widgets.SelectWithInputWidget.js
1 /*!
2 * MediaWiki Widgets - SelectWithInputWidget class.
3 *
4 * @copyright 2011-2017 MediaWiki Widgets Team and others; see AUTHORS.txt
5 * @license The MIT License (MIT); see LICENSE.txt
6 */
7 ( function () {
8
9 /**
10 * Select with input widget. Displays an OO.ui.TextInputWidget along with
11 * an OO.ui.DropdownInputWidget.
12 * TODO Explain the OTHER option
13 *
14 * mw.loader.using( 'mediawiki.widgets.SelectWithInputWidget', function () {
15 * var swi = new mw.widgets.SelectWithInputWidget( {
16 * or: true,
17 * dropdowninput: {
18 * options: [
19 * { data: 'other', label: 'Other' },
20 * { data: 'a', label: 'First' },
21 * { data: 'b', label: 'Second' },
22 * { data: 'c', label: 'Third' }
23 * ]
24 * },
25 * textinput: {
26 * }
27 * } );
28 *
29 * $( 'body' ).append( swi.$element );
30 * } );
31 *
32 * @class mw.widgets.SelectWithInputWidget
33 * @extends OO.ui.Widget
34 *
35 * @constructor
36 * @param {Object} [config] Configuration options
37 * @cfg {Object} [dropdowninput] Config for the dropdown
38 * @cfg {Object} [textinput] Config for the text input
39 * @cfg {boolean} [or=false] Config for whether the widget is dropdown AND input
40 * or dropdown OR input
41 */
42 mw.widgets.SelectWithInputWidget = function MwWidgetsSelectWithInputWidget( config ) {
43 // Config initialization
44 config = $.extend( { or: false }, config );
45
46 // Properties
47 this.textinput = new OO.ui.TextInputWidget( config.textinput );
48 this.dropdowninput = new OO.ui.DropdownInputWidget( config.dropdowninput );
49 this.or = config.or;
50
51 // Events
52 this.dropdowninput.on( 'change', this.onChange.bind( this ) );
53 this.textinput.on( 'change', function () {
54 this.emit( 'change', this.getValue() );
55 }.bind( this ) );
56
57 // Parent constructor
58 mw.widgets.SelectWithInputWidget.parent.call( this, config );
59
60 // Initialization
61 this.$element
62 .addClass( 'mw-widget-selectWithInputWidget' )
63 .append(
64 this.dropdowninput.$element,
65 this.textinput.$element
66 );
67 this.onChange();
68 };
69
70 /* Setup */
71 OO.inheritClass( mw.widgets.SelectWithInputWidget, OO.ui.Widget );
72
73 /* Static Methods */
74
75 /**
76 * @inheritdoc
77 */
78 mw.widgets.SelectWithInputWidget.static.reusePreInfuseDOM = function ( node, config ) {
79 config = mw.widgets.SelectWithInputWidget.parent.static.reusePreInfuseDOM( node, config );
80 config.dropdowninput = OO.ui.DropdownInputWidget.static.reusePreInfuseDOM(
81 $( node ).find( '.oo-ui-dropdownInputWidget' ),
82 config.dropdowninput
83 );
84 config.textinput = OO.ui.TextInputWidget.static.reusePreInfuseDOM(
85 $( node ).find( '.oo-ui-textInputWidget' ),
86 config.textinput
87 );
88 return config;
89 };
90
91 /**
92 * @inheritdoc
93 */
94 mw.widgets.SelectWithInputWidget.static.gatherPreInfuseState = function ( node, config ) {
95 var state = mw.widgets.SelectWithInputWidget.parent.static.gatherPreInfuseState( node, config );
96 state.dropdowninput = OO.ui.DropdownInputWidget.static.gatherPreInfuseState(
97 $( node ).find( '.oo-ui-dropdownInputWidget' ),
98 config.dropdowninput
99 );
100 state.textinput = OO.ui.TextInputWidget.static.gatherPreInfuseState(
101 $( node ).find( '.oo-ui-textInputWidget' ),
102 config.textinput
103 );
104 return state;
105 };
106
107 /* Methods */
108
109 /**
110 * @inheritdoc
111 */
112 mw.widgets.SelectWithInputWidget.prototype.restorePreInfuseState = function ( state ) {
113 mw.widgets.SelectWithInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
114 this.dropdowninput.restorePreInfuseState( state.dropdowninput );
115 this.textinput.restorePreInfuseState( state.textinput );
116 };
117
118 /**
119 * @inheritdoc
120 */
121 mw.widgets.SelectWithInputWidget.prototype.setDisabled = function ( disabled ) {
122 var textinputIsHidden = this.or && this.dropdowninput.getValue() !== 'other';
123 mw.widgets.SelectWithInputWidget.parent.prototype.setDisabled.call( this, disabled );
124 this.dropdowninput.setDisabled( disabled );
125 // It is impossible to submit a form with hidden fields failing validation, e.g. one that
126 // is required. However, validity is not checked for disabled fields, as these are not
127 // submitted with the form. So we should also disable fields when hiding them.
128 this.textinput.setDisabled( textinputIsHidden || disabled );
129 };
130
131 /**
132 * Set the value from outside.
133 *
134 * @param {string|undefined} value
135 */
136 mw.widgets.SelectWithInputWidget.prototype.setValue = function ( value ) {
137 var selectable = false;
138
139 if ( this.or ) {
140 if ( value !== 'other' ) {
141 selectable = !!this.dropdowninput.dropdownWidget.getMenu().findItemFromData( value );
142 }
143
144 if ( selectable ) {
145 this.dropdowninput.setValue( value );
146 this.textinput.setValue( undefined );
147 } else {
148 this.dropdowninput.setValue( 'other' );
149 this.textinput.setValue( value );
150 }
151
152 this.emit( 'change', value );
153 }
154 };
155
156 /**
157 * Get the value from outside.
158 *
159 * @return {string}
160 */
161 mw.widgets.SelectWithInputWidget.prototype.getValue = function () {
162 if ( this.or ) {
163 if ( this.dropdowninput.getValue() !== 'other' ) {
164 return this.dropdowninput.getValue();
165 }
166
167 return this.textinput.getValue();
168 } else {
169 return '';
170 }
171 };
172
173 /**
174 * Handle change events on the DropdownInput
175 *
176 * @param {string|undefined} value
177 * @private
178 */
179 mw.widgets.SelectWithInputWidget.prototype.onChange = function ( value ) {
180 if ( this.or ) {
181 value = value || this.dropdowninput.getValue();
182 this.textinput.$element.toggle( value === 'other' );
183 // It is impossible to submit a form with hidden fields failing validation, e.g. one that
184 // is required. However, validity is not checked for disabled fields, as these are not
185 // submitted with the form. So we should also disable fields when hiding them.
186 this.textinput.setDisabled( value !== 'other' || this.isDisabled() );
187 }
188
189 this.emit( 'change', this.getValue() );
190 };
191
192 }() );