$out->allowClickjacking();
$out->addModuleStyles( [
'mediawiki.special', 'mediawiki.special.search', 'mediawiki.ui', 'mediawiki.ui.button',
- 'mediawiki.ui.input',
+ 'mediawiki.ui.input', 'mediawiki.widgets.SearchInputWidget.styles',
] );
$this->addHelpLink( 'Help:Searching' );
$num = $titleMatchesNum + $textMatchesNum;
$totalRes = $numTitleMatches + $numTextMatches;
+ $out->enableOOUI();
$out->addHTML(
# This is an awful awful ID name. It's not a table, but we
# named it poorly from when this was a table so now we're
* @return string
*/
protected function shortDialog( $term, $resultsShown, $totalNum ) {
- $out = Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() );
- $out .= Html::hidden( 'profile', $this->profile ) . "\n";
- // Term box
- $out .= Html::input( 'search', $term, 'search', [
- 'id' => $this->isPowerSearch() ? 'powerSearchText' : 'searchText',
- 'size' => '50',
+ $searchWidget = new MediaWiki\Widget\SearchInputWidget( [
+ 'id' => 'searchText',
+ 'name' => 'search',
'autofocus' => trim( $term ) === '',
- 'class' => 'mw-ui-input mw-ui-input-inline',
- // identifies the location of the search bar for tracking purposes
- 'data-search-loc' => 'content',
- ] ) . "\n";
- $out .= Html::hidden( 'fulltext', 'Search' ) . "\n";
- $out .= Html::submitButton(
- $this->msg( 'searchbutton' )->text(),
- [ 'class' => 'mw-ui-button mw-ui-progressive' ],
- [ 'mw-ui-progressive' ]
- ) . "\n";
+ 'value' => $term,
+ ] );
+
+ $out =
+ Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) .
+ Html::hidden( 'profile', $this->profile ) .
+ Html::hidden( 'fulltext', 'Search' ) .
+ $searchWidget .
+ new OOUI\ButtonInputWidget( [
+ 'type' => 'submit',
+ 'label' => $this->msg( 'searchbutton' )->text(),
+ 'flags' => [ 'progressive', 'primary' ],
+ ] );
// Results-info
if ( $totalNum > 0 && $this->offset < $totalNum ) {
class SearchInputWidget extends TitleInputWidget {
protected $pushPending = false;
+ protected $performSearchOnClick = true;
protected $validateTitle = false;
protected $highlightFirst = false;
* @param array $config Configuration options
* @param int|null $config['pushPending'] Whether the input should be visually marked as
* "pending", while requesting suggestions (default: true)
+ * @param boolean|null $config['performSearchOnClick'] If true, the script will start a search
+ * whenever a user hits a suggestion. If false, the text of the suggestion is inserted into the
+ * text field only (default: true)
*/
public function __construct( array $config = [] ) {
+ $config = array_merge( [
+ 'infusable' => true,
+ 'maxLength' => null,
+ 'type' => 'search',
+ 'icon' => 'search',
+ 'dataLocation' => 'content',
+ ], $config );
+
// Parent constructor
- parent::__construct(
- array_merge( [
- 'infusable' => true,
- 'maxLength' => null,
- 'type' => 'search',
- 'icon' => 'search'
- ], $config )
- );
+ parent::__construct( $config );
// Properties, which are ignored in PHP and just shipped back to JS
if ( isset( $config['pushPending'] ) ) {
$this->pushPending = $config['pushPending'];
}
+ if ( isset( $config['performSearchOnClick'] ) ) {
+ $this->performSearchOnClick = $config['performSearchOnClick'];
+ }
+
+ if ( $config['dataLocation'] ) {
+ // identifies the location of the search bar for tracking purposes
+ $this->dataLocation = $config['dataLocation'];
+ }
+
// Initialization
$this->addClasses( [ 'mw-widget-searchInputWidget' ] );
}
public function getConfig( &$config ) {
$config['pushPending'] = $this->pushPending;
+ $config['performSearchOnClick'] = $this->performSearchOnClick;
+ if ( $this->dataLocation ) {
+ $config['dataLocation'] = $this->dataLocation;
+ }
return parent::getConfig( $config );
}
}
'position' => 'top',
'scripts' => 'resources/src/mediawiki.special/mediawiki.special.search.js',
'styles' => 'resources/src/mediawiki.special/mediawiki.special.search.css',
+ 'dependencies' => 'mediawiki.widgets.SearchInputWidget',
'messages' => [
'powersearch-togglelabel',
'powersearch-toggleall',
*/
( function ( mw, $ ) {
$( function () {
- var $checkboxes, $headerLinks;
+ var $checkboxes, $headerLinks, updateHeaderLinks, searchWidget;
// Emulate HTML5 autofocus behavior in non HTML5 compliant browsers
if ( !( 'autofocus' in document.createElement( 'input' ) ) ) {
// Change the header search links to what user entered
$headerLinks = $( '.search-types a' );
- $( '#searchText, #powerSearchText' ).change( function () {
- var searchterm = $( this ).val();
+ searchWidget = OO.ui.infuse( 'searchText' );
+ updateHeaderLinks = function ( value ) {
$headerLinks.each( function () {
var parts = $( this ).attr( 'href' ).split( 'search=' ),
lastpart = '',
} else {
prefix = '&search=';
}
- this.href = parts[ 0 ] + prefix + encodeURIComponent( searchterm ) + lastpart;
+ this.href = parts[ 0 ] + prefix + encodeURIComponent( value ) + lastpart;
} );
- } ).trigger( 'change' );
+ };
+ searchWidget.on( 'change', updateHeaderLinks );
+ updateHeaderLinks( searchWidget.getValue() );
// When saving settings, use the proper request method (POST instead of GET).
$( '#mw-search-powersearch-remember' ).change( function () {
* @constructor
* @cfg {boolean} [pushPending=true] Visually mark the input field as "pending", while
* requesting suggestions.
+ * @cfg {boolean} [performSearchOnClick=true] If true, the script will start a search when-
+ * ever a user hits a suggestion. If false, the text of the suggestion is inserted into the
+ * text field only.
*/
mw.widgets.SearchInputWidget = function MwWidgetsSearchInputWidget( config ) {
config = $.extend( {
type: 'search',
icon: 'search',
- maxLength: undefined
+ maxLength: undefined,
+ performSearchOnClick: true
}, config );
// Parent constructor
if ( !config.pushPending ) {
this.pushPending = false;
}
+ if ( config.dataLocation ) {
+ this.dataLocation = config.dataLocation;
+ }
+ if ( config.performSearchOnClick ) {
+ this.performSearchOnClick = config.performSearchOnClick;
+ }
this.setLookupsDisabled( !this.suggestions );
};
* @inheritdoc mw.widgets.TitleWidget
*/
mw.widgets.SearchInputWidget.prototype.getSuggestionsPromise = function () {
- var api = new mw.Api();
+ var api = new mw.Api(),
+ promise,
+ self = this;
// reuse the searchSuggest function from mw.searchSuggest
- return mw.searchSuggest.request( api, this.getQueryValue(), $.noop, this.limit );
+ promise = mw.searchSuggest.request( api, this.getQueryValue(), $.noop, this.limit );
+
+ // tracking purposes
+ promise.done( function ( data, jqXHR ) {
+ self.requestType = jqXHR.getResponseHeader( 'X-OpenSearch-Type' );
+ } );
+
+ return promise;
};
/**
* @inheritdoc mw.widgets.TitleInputWidget
*/
mw.widgets.SearchInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
+ var resp;
+
// mw.widgets.TitleInputWidget uses response.query, which doesn't exist for opensearch,
// so return the whole response (titles only, and links)
- return response || {};
+ resp = {
+ data: response || {},
+ metadata: {
+ type: this.requestType || 'unknown',
+ query: this.getQueryValue()
+ }
+ };
+ this.requestType = undefined;
+
+ return resp;
};
/**
// mw.widgets.TitleWidget does a lot more work here, because the TitleOptionWidgets can
// differ a lot, depending on the returned data from the request. With the request used here
// we get only the search results.
- $.each( data[ 1 ], function ( i, result ) {
+ $.each( data.data[ 1 ], function ( i, result ) {
items.push( new mw.widgets.TitleOptionWidget(
// data[ 3 ][ i ] is the link for this result
- self.getOptionWidgetData( result, null, data[ 3 ][ i ] )
+ self.getOptionWidgetData( result, null, data.data[ 3 ][ i ] )
) );
} );
mw.track( 'mw.widgets.SearchInputWidget', {
action: 'impression-results',
numberOfResults: items.length,
- resultSetType: mw.searchSuggest.type
+ resultSetType: data.metadata.type,
+ query: data.metadata.query,
+ inputLocation: this.dataLocation || 'header'
} );
return items;
};
};
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.SearchInputWidget.prototype.onLookupMenuItemChoose = function ( item ) {
+ var items;
+
+ // get items which was suggested before the input changes
+ items = this.lookupMenu.items;
+
+ mw.widgets.SearchInputWidget.parent.prototype.onLookupMenuItemChoose.apply( this, arguments );
+
+ mw.track( 'mw.widgets.SearchInputWidget', {
+ action: 'click-result',
+ numberOfResults: items.length,
+ clickIndex: items.indexOf( item ) + 1
+ } );
+
+ if ( this.performSearchOnClick ) {
+ this.$element.closest( 'form' ).submit();
+ }
+ };
+
}( jQuery, mediaWiki ) );
searchboxesSelectors = [
// Primary searchbox on every page in standard skins
'#searchInput',
- // Special:Search
- '#powerSearchText',
- '#searchText',
// Generic selector for skins with multiple searchboxes (used by CologneBlue)
// and for MediaWiki itself (special pages with page title inputs)
'.mw-searchInput'