Merge "Improve sorting on SpecialWanted*-Pages"
[mediawiki.git] / resources / src / mediawiki.widgets / mw.widgets.SearchInputWidget.js
blob0a73befdf78297f0c440c87b162a28098f3cc5ec
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 ) {
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=true] 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' ) : $();
32                 config = $.extend( {
33                         type: 'search',
34                         icon: 'search',
35                         maxLength: undefined,
36                         performSearchOnClick: true,
37                         dataLocation: 'header',
38                         namespace: 0
39                 }, config );
41                 // Parent constructor
42                 mw.widgets.SearchInputWidget.parent.call( this, config );
44                 // Initialization
45                 this.$element.addClass( 'mw-widget-searchInputWidget' );
46                 this.lookupMenu.$element.addClass( 'mw-widget-searchWidget-menu' );
47                 this.lastLookupItems = [];
48                 if ( !config.pushPending ) {
49                         this.pushPending = false;
50                 }
51                 if ( config.dataLocation ) {
52                         this.dataLocation = config.dataLocation;
53                 }
54                 if ( config.performSearchOnClick ) {
55                         this.performSearchOnClick = config.performSearchOnClick;
56                 }
57                 this.setLookupsDisabled( !this.suggestions );
59                 $form.on( 'submit', function () {
60                         mw.track( 'mw.widgets.SearchInputWidget', {
61                                 action: 'submit-form',
62                                 numberOfResults: this.lastLookupItems.length,
63                                 $form: $form,
64                                 inputLocation: this.dataLocation || 'header',
65                                 index: this.lastLookupItems.indexOf(
66                                         this.$input.val()
67                                 )
68                         } );
69                 }.bind( this ) );
70         };
72         /* Setup */
74         OO.inheritClass( mw.widgets.SearchInputWidget, mw.widgets.TitleInputWidget );
76         /* Methods */
78         /**
79          * @inheritdoc mw.widgets.TitleWidget
80          */
81         mw.widgets.SearchInputWidget.prototype.getSuggestionsPromise = function () {
82                 var api = this.getApi(),
83                         promise,
84                         self = this;
86                 // reuse the searchSuggest function from mw.searchSuggest
87                 promise = mw.searchSuggest.request( api, this.getQueryValue(), $.noop, this.limit, this.getNamespace() );
89                 // tracking purposes
90                 promise.done( function ( data, jqXHR ) {
91                         self.requestType = jqXHR.getResponseHeader( 'X-OpenSearch-Type' );
92                 } );
94                 return promise;
95         };
97         /**
98          * @inheritdoc mw.widgets.TitleInputWidget
99          */
100         mw.widgets.SearchInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
101                 var resp;
103                 // mw.widgets.TitleInputWidget uses response.query, which doesn't exist for opensearch,
104                 // so return the whole response (titles only, and links)
105                 resp = {
106                         data: response || {},
107                         metadata: {
108                                 type: this.requestType || 'unknown',
109                                 query: this.getQueryValue()
110                         }
111                 };
112                 this.requestType = undefined;
114                 return resp;
115         };
117         /**
118          * @inheritdoc mw.widgets.TitleWidget
119          */
120         mw.widgets.SearchInputWidget.prototype.getOptionsFromData = function ( data ) {
121                 var items = [],
122                         self = this;
124                 // mw.widgets.TitleWidget does a lot more work here, because the TitleOptionWidgets can
125                 // differ a lot, depending on the returned data from the request. With the request used here
126                 // we get only the search results.
127                 $.each( data.data[ 1 ], function ( i, result ) {
128                         items.push( new mw.widgets.TitleOptionWidget(
129                                 // data[ 3 ][ i ] is the link for this result
130                                 self.getOptionWidgetData( result, null, data.data[ 3 ][ i ] )
131                         ) );
132                 } );
134                 mw.track( 'mw.widgets.SearchInputWidget', {
135                         action: 'impression-results',
136                         numberOfResults: items.length,
137                         resultSetType: data.metadata.type,
138                         query: data.metadata.query,
139                         inputLocation: this.dataLocation || 'header'
140                 } );
142                 return items;
143         };
145         /**
146          * @inheritdoc mw.widgets.TitleWidget
147          *
148          * @param {string} title
149          * @param {Object} data
150          * @param {string} url The Url to the result
151          */
152         mw.widgets.SearchInputWidget.prototype.getOptionWidgetData = function ( title, data, url ) {
153                 // the values used in mw.widgets-TitleWidget doesn't exist here, that's why
154                 // the values are hard-coded here
155                 return {
156                         data: title,
157                         url: url,
158                         imageUrl: null,
159                         description: null,
160                         missing: false,
161                         redirect: false,
162                         disambiguation: false,
163                         query: this.getQueryValue()
164                 };
165         };
167         /**
168          * @inheritdoc
169          */
170         mw.widgets.SearchInputWidget.prototype.onLookupMenuItemChoose = function () {
171                 mw.widgets.SearchInputWidget.parent.prototype.onLookupMenuItemChoose.apply( this, arguments );
173                 if ( this.performSearchOnClick ) {
174                         this.$element.closest( 'form' ).submit();
175                 }
176         };
178         /**
179          * @inheritdoc
180          */
181         mw.widgets.SearchInputWidget.prototype.getLookupMenuOptionsFromData = function () {
182                 var items = mw.widgets.SearchInputWidget.parent.prototype.getLookupMenuOptionsFromData.apply(
183                         this, arguments
184                 );
186                 this.lastLookupItems = items.map( function ( item ) {
187                         return item.data;
188                 } );
190                 return items;
191         };
193 }( jQuery, mediaWiki ) );