2 * MediaWiki Widgets - SearchInputWidget class.
4 * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
5 * @license The MIT License (MIT); see LICENSE.txt
10 * Creates a mw.widgets.SearchInputWidget object.
13 * @extends OO.ui.SearchInputWidget
14 * @mixins mw.widgets.TitleWidget
15 * @mixins OO.ui.mixin.LookupElement
18 * @param {Object} [config] Configuration options
19 * @cfg {boolean} [pushPending=false] Visually mark the input field as "pending", while
20 * requesting suggestions.
21 * @cfg {boolean} [performSearchOnClick=true] If true, the script will start a search when-
22 * ever a user hits a suggestion. If false, the text of the suggestion is inserted into the
24 * @cfg {string} [dataLocation='header'] Where the search input field will be
25 * used (header or content).
27 mw
.widgets
.SearchInputWidget
= function MwWidgetsSearchInputWidget( config
) {
28 // The parent constructors will detach this from the DOM, and won't
29 // be reattached until after this function is completed. As such
30 // grab a handle here. If no config.$input is passed tracking of
31 // form submissions won't work.
32 var $form
= config
.$input
? config
.$input
.closest( 'form' ) : $();
35 performSearchOnClick
: true,
36 dataLocation
: 'header'
40 mw
.widgets
.SearchInputWidget
.parent
.call( this, $.extend( {}, config
, {
45 mw
.widgets
.TitleWidget
.call( this, config
);
46 OO
.ui
.mixin
.LookupElement
.call( this, config
);
49 this.$element
.addClass( 'mw-widget-searchInputWidget' );
50 this.lookupMenu
.$element
.addClass( 'mw-widget-searchWidget-menu' );
51 this.lastLookupItems
= [];
52 if ( !config
.pushPending
) {
53 // TODO This actually overrides a method, that's pretty crazy. Surely there's a better way?
54 this.pushPending
= false;
56 if ( config
.dataLocation
) {
57 this.dataLocation
= config
.dataLocation
;
59 if ( config
.performSearchOnClick
) {
60 this.performSearchOnClick
= config
.performSearchOnClick
;
62 this.setLookupsDisabled( !this.suggestions
);
64 $form
.on( 'submit', function () {
65 mw
.track( 'mw.widgets.SearchInputWidget', {
66 action
: 'submit-form',
67 numberOfResults
: this.lastLookupItems
.length
,
69 inputLocation
: this.dataLocation
|| 'header',
70 index
: this.lastLookupItems
.indexOf(
79 OO
.inheritClass( mw
.widgets
.SearchInputWidget
, OO
.ui
.SearchInputWidget
);
80 OO
.mixinClass( mw
.widgets
.SearchInputWidget
, mw
.widgets
.TitleWidget
);
81 OO
.mixinClass( mw
.widgets
.SearchInputWidget
, OO
.ui
.mixin
.LookupElement
);
86 * @inheritdoc mw.widgets.TitleWidget
88 mw
.widgets
.SearchInputWidget
.prototype.getQueryValue = function () {
89 return this.getValue();
93 * @inheritdoc OO.ui.mixin.LookupElement
95 mw
.widgets
.SearchInputWidget
.prototype.getLookupRequest = function () {
96 return this.getSuggestionsPromise();
100 * @inheritdoc mw.widgets.TitleWidget
102 mw
.widgets
.SearchInputWidget
.prototype.getSuggestionsPromise = function () {
103 var api
= this.getApi(),
107 // reuse the searchSuggest function from mw.searchSuggest
108 promise
= mw
.searchSuggest
.request( api
, this.getQueryValue(), $.noop
, this.limit
, this.getNamespace() );
111 promise
.done( function ( data
, jqXHR
) {
112 self
.requestType
= jqXHR
.getResponseHeader( 'X-OpenSearch-Type' );
119 * @inheritdoc OO.ui.mixin.LookupElement
121 mw
.widgets
.SearchInputWidget
.prototype.getLookupCacheDataFromResponse = function ( response
) {
125 data
: response
|| {},
127 type
: this.requestType
|| 'unknown',
128 query
: this.getQueryValue()
131 this.requestType
= undefined;
137 * @inheritdoc mw.widgets.TitleWidget
139 mw
.widgets
.SearchInputWidget
.prototype.getOptionsFromData = function ( data
) {
143 // mw.widgets.TitleWidget does a lot more work here, because the TitleOptionWidgets can
144 // differ a lot, depending on the returned data from the request. With the request used here
145 // we get only the search results.
146 $.each( data
.data
[ 1 ], function ( i
, result
) {
147 items
.push( new mw
.widgets
.TitleOptionWidget(
148 // data[ 3 ][ i ] is the link for this result
149 self
.getOptionWidgetData( result
, null, data
.data
[ 3 ][ i
] )
153 mw
.track( 'mw.widgets.SearchInputWidget', {
154 action
: 'impression-results',
155 numberOfResults
: items
.length
,
156 resultSetType
: data
.metadata
.type
,
157 query
: data
.metadata
.query
,
158 inputLocation
: this.dataLocation
|| 'header'
165 * @inheritdoc mw.widgets.TitleWidget
167 * @param {string} title
168 * @param {Object} data
169 * @param {string} url The Url to the result
171 mw
.widgets
.SearchInputWidget
.prototype.getOptionWidgetData = function ( title
, data
, url
) {
172 // the values used in mw.widgets-TitleWidget doesn't exist here, that's why
173 // the values are hard-coded here
181 disambiguation
: false,
182 query
: this.getQueryValue()
187 * @inheritdoc OO.ui.mixin.LookupElement
189 mw
.widgets
.SearchInputWidget
.prototype.onLookupMenuItemChoose = function ( item
) {
190 this.closeLookupMenu();
191 this.setLookupsDisabled( true );
192 this.setValue( item
.getData() );
193 this.setLookupsDisabled( !this.suggestions
);
195 if ( this.performSearchOnClick
) {
196 this.$element
.closest( 'form' ).submit();
201 * @inheritdoc OO.ui.mixin.LookupElement
203 mw
.widgets
.SearchInputWidget
.prototype.getLookupMenuOptionsFromData = function ( response
) {
204 var items
= this.getOptionsFromData( response
);
206 this.lastLookupItems
= items
.map( function ( item
) {
216 mw
.widgets
.SearchInputWidget
.prototype.focus = function () {
219 // Prevent programmatic focus from opening the menu
220 this.setLookupsDisabled( true );
223 retval
= mw
.widgets
.SearchInputWidget
.parent
.prototype.focus
.apply( this, arguments
);
225 this.setLookupsDisabled( !this.suggestions
);
230 }( jQuery
, mediaWiki
) );