]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - resources/src/mediawiki.widgets/mw.widgets.SearchInputWidget.js
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / resources / src / mediawiki.widgets / mw.widgets.SearchInputWidget.js
1 /*!
2  * MediaWiki Widgets - SearchInputWidget class.
3  *
4  * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
5  * @license The MIT License (MIT); see LICENSE.txt
6  */
7 ( function ( $, mw ) {
8
9         /**
10          * Creates a mw.widgets.SearchInputWidget object.
11          *
12          * @class
13          * @extends mw.widgets.TitleInputWidget
14          *
15          * @constructor
16          * @param {Object} [config] Configuration options
17          * @cfg {boolean} [pushPending=false] Visually mark the input field as "pending", while
18          *  requesting suggestions.
19          * @cfg {boolean} [performSearchOnClick=true] If true, the script will start a search when-
20          *  ever a user hits a suggestion. If false, the text of the suggestion is inserted into the
21          *  text field only.
22          *  @cfg {string} [dataLocation='header'] Where the search input field will be
23          *  used (header or content).
24          */
25         mw.widgets.SearchInputWidget = function MwWidgetsSearchInputWidget( config ) {
26                 // The parent constructors will detach this from the DOM, and won't
27                 // be reattached until after this function is completed. As such
28                 // grab a handle here. If no config.$input is passed tracking of
29                 // form submissions won't work.
30                 var $form = config.$input ? config.$input.closest( 'form' ) : $();
31
32                 config = $.extend( {
33                         icon: 'search',
34                         maxLength: undefined,
35                         performSearchOnClick: true,
36                         dataLocation: 'header',
37                         namespace: 0
38                 }, config );
39
40                 // Parent constructor
41                 mw.widgets.SearchInputWidget.parent.call( this, config );
42
43                 // Initialization
44                 this.$element.addClass( 'mw-widget-searchInputWidget' );
45                 this.lookupMenu.$element.addClass( 'mw-widget-searchWidget-menu' );
46                 this.lastLookupItems = [];
47                 if ( !config.pushPending ) {
48                         this.pushPending = false;
49                 }
50                 if ( config.dataLocation ) {
51                         this.dataLocation = config.dataLocation;
52                 }
53                 if ( config.performSearchOnClick ) {
54                         this.performSearchOnClick = config.performSearchOnClick;
55                 }
56                 this.setLookupsDisabled( !this.suggestions );
57
58                 $form.on( 'submit', function () {
59                         mw.track( 'mw.widgets.SearchInputWidget', {
60                                 action: 'submit-form',
61                                 numberOfResults: this.lastLookupItems.length,
62                                 $form: $form,
63                                 inputLocation: this.dataLocation || 'header',
64                                 index: this.lastLookupItems.indexOf(
65                                         this.$input.val()
66                                 )
67                         } );
68                 }.bind( this ) );
69
70                 this.$element.addClass( 'oo-ui-textInputWidget-type-search' );
71                 this.updateSearchIndicator();
72                 this.connect( this, {
73                         disable: 'onDisable'
74                 } );
75         };
76
77         /* Setup */
78
79         OO.inheritClass( mw.widgets.SearchInputWidget, mw.widgets.TitleInputWidget );
80
81         /* Methods */
82
83         /**
84          * @inheritdoc
85          * @protected
86          */
87         mw.widgets.SearchInputWidget.prototype.getInputElement = function () {
88                 return $( '<input>' ).attr( 'type', 'search' );
89         };
90
91         /**
92          * @inheritdoc
93          */
94         mw.widgets.SearchInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
95                 if ( e.which === OO.ui.MouseButtons.LEFT ) {
96                         // Clear the text field
97                         this.setValue( '' );
98                         this.$input[ 0 ].focus();
99                         return false;
100                 }
101         };
102
103         /**
104          * Update the 'clear' indicator displayed on type: 'search' text
105          * fields, hiding it when the field is already empty or when it's not
106          * editable.
107          */
108         mw.widgets.SearchInputWidget.prototype.updateSearchIndicator = function () {
109                 if ( this.getValue() === '' || this.isDisabled() || this.isReadOnly() ) {
110                         this.setIndicator( null );
111                 } else {
112                         this.setIndicator( 'clear' );
113                 }
114         };
115
116         /**
117          * @see OO.ui.SearchInputWidget#onChange
118          */
119         mw.widgets.SearchInputWidget.prototype.onChange = function () {
120                 mw.widgets.SearchInputWidget.parent.prototype.onChange.call( this );
121                 this.updateSearchIndicator();
122         };
123
124         /**
125          * Handle disable events.
126          *
127          * @param {boolean} disabled Element is disabled
128          * @private
129          */
130         mw.widgets.SearchInputWidget.prototype.onDisable = function () {
131                 this.updateSearchIndicator();
132         };
133
134         /**
135          * @inheritdoc
136          */
137         mw.widgets.SearchInputWidget.prototype.setReadOnly = function ( state ) {
138                 mw.widgets.SearchInputWidget.parent.prototype.setReadOnly.call( this, state );
139                 this.updateSearchIndicator();
140                 return this;
141         };
142
143         /**
144          * @inheritdoc mw.widgets.TitleWidget
145          */
146         mw.widgets.SearchInputWidget.prototype.getSuggestionsPromise = function () {
147                 var api = this.getApi(),
148                         promise,
149                         self = this;
150
151                 // reuse the searchSuggest function from mw.searchSuggest
152                 promise = mw.searchSuggest.request( api, this.getQueryValue(), $.noop, this.limit, this.getNamespace() );
153
154                 // tracking purposes
155                 promise.done( function ( data, jqXHR ) {
156                         self.requestType = jqXHR.getResponseHeader( 'X-OpenSearch-Type' );
157                 } );
158
159                 return promise;
160         };
161
162         /**
163          * @inheritdoc mw.widgets.TitleInputWidget
164          */
165         mw.widgets.SearchInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
166                 var resp;
167
168                 // mw.widgets.TitleInputWidget uses response.query, which doesn't exist for opensearch,
169                 // so return the whole response (titles only, and links)
170                 resp = {
171                         data: response || {},
172                         metadata: {
173                                 type: this.requestType || 'unknown',
174                                 query: this.getQueryValue()
175                         }
176                 };
177                 this.requestType = undefined;
178
179                 return resp;
180         };
181
182         /**
183          * @inheritdoc mw.widgets.TitleWidget
184          */
185         mw.widgets.SearchInputWidget.prototype.getOptionsFromData = function ( data ) {
186                 var items = [],
187                         self = this;
188
189                 // mw.widgets.TitleWidget does a lot more work here, because the TitleOptionWidgets can
190                 // differ a lot, depending on the returned data from the request. With the request used here
191                 // we get only the search results.
192                 $.each( data.data[ 1 ], function ( i, result ) {
193                         items.push( new mw.widgets.TitleOptionWidget(
194                                 // data[ 3 ][ i ] is the link for this result
195                                 self.getOptionWidgetData( result, null, data.data[ 3 ][ i ] )
196                         ) );
197                 } );
198
199                 mw.track( 'mw.widgets.SearchInputWidget', {
200                         action: 'impression-results',
201                         numberOfResults: items.length,
202                         resultSetType: data.metadata.type,
203                         query: data.metadata.query,
204                         inputLocation: this.dataLocation || 'header'
205                 } );
206
207                 return items;
208         };
209
210         /**
211          * @inheritdoc mw.widgets.TitleWidget
212          *
213          * @param {string} title
214          * @param {Object} data
215          * @param {string} url The Url to the result
216          */
217         mw.widgets.SearchInputWidget.prototype.getOptionWidgetData = function ( title, data, url ) {
218                 // the values used in mw.widgets-TitleWidget doesn't exist here, that's why
219                 // the values are hard-coded here
220                 return {
221                         data: title,
222                         url: url,
223                         imageUrl: null,
224                         description: null,
225                         missing: false,
226                         redirect: false,
227                         disambiguation: false,
228                         query: this.getQueryValue()
229                 };
230         };
231
232         /**
233          * @inheritdoc
234          */
235         mw.widgets.SearchInputWidget.prototype.onLookupMenuItemChoose = function () {
236                 mw.widgets.SearchInputWidget.parent.prototype.onLookupMenuItemChoose.apply( this, arguments );
237
238                 if ( this.performSearchOnClick ) {
239                         this.$element.closest( 'form' ).submit();
240                 }
241         };
242
243         /**
244          * @inheritdoc
245          */
246         mw.widgets.SearchInputWidget.prototype.getLookupMenuOptionsFromData = function () {
247                 var items = mw.widgets.SearchInputWidget.parent.prototype.getLookupMenuOptionsFromData.apply(
248                         this, arguments
249                 );
250
251                 this.lastLookupItems = items.map( function ( item ) {
252                         return item.data;
253                 } );
254
255                 return items;
256         };
257
258 }( jQuery, mediaWiki ) );