Import: Handle uploads with sha1 starting with 0 properly
[mediawiki.git] / resources / src / mediawiki / mediawiki.searchSuggest.js
blobada49248155f53e763a97385d06ed795dea30bdb
1 /*!
2  * Add search suggestions to the search form.
3  */
4 ( function ( mw, $ ) {
5         mw.searchSuggest = {
6                 // queries the wiki and calls response with the result
7                 request: function ( api, query, response, maxRows ) {
8                         return api.get( {
9                                 action: 'opensearch',
10                                 search: query,
11                                 namespace: 0,
12                                 limit: maxRows,
13                                 suggest: true
14                         } ).done( function ( data ) {
15                                 response( data[ 1 ] );
16                         } );
17                 },
18                 // The name of the request api for event logging purposes
19                 type: 'prefix'
20         };
22         $( function () {
23                 var api, map, searchboxesSelectors,
24                         // Region where the suggestions box will appear directly below
25                         // (using the same width). Can be a container element or the input
26                         // itself, depending on what suits best in the environment.
27                         // For Vector the suggestion box should align with the simpleSearch
28                         // container's borders, in other skins it should align with the input
29                         // element (not the search form, as that would leave the buttons
30                         // vertically between the input and the suggestions).
31                         $searchRegion = $( '#simpleSearch, #searchInput' ).first(),
32                         $searchInput = $( '#searchInput' ),
33                         previousSearchText = $searchInput.val();
35                 // Compatibility map
36                 map = {
37                         // SimpleSearch is broken in Opera < 9.6
38                         opera: [ [ '>=', 9.6 ] ],
39                         // Older Konquerors are unable to position the suggestions correctly (bug 50805)
40                         konqueror: [ [ '>=', '4.11' ] ],
41                         docomo: false,
42                         blackberry: false,
43                         // Support for iOS 6 or higher. It has not been tested on iOS 5 or lower
44                         ipod: [ [ '>=', 6 ] ],
45                         iphone: [ [ '>=', 6 ] ]
46                 };
48                 if ( !$.client.test( map ) ) {
49                         return;
50                 }
52                 // Compute form data for search suggestions functionality.
53                 function getFormData( context ) {
54                         var $form, baseHref, linkParams;
56                         if ( !context.formData ) {
57                                 // Compute common parameters for links' hrefs
58                                 $form = context.config.$region.closest( 'form' );
60                                 baseHref = $form.attr( 'action' );
61                                 baseHref += baseHref.indexOf( '?' ) > -1 ? '&' : '?';
63                                 linkParams = $form.serializeObject();
65                                 context.formData = {
66                                         textParam: context.data.$textbox.attr( 'name' ),
67                                         linkParams: linkParams,
68                                         baseHref: baseHref
69                                 };
70                         }
72                         return context.formData;
73                 }
75                 /**
76                  * Callback that's run when the user changes the search input text
77                  * 'this' is the search input box (jQuery object)
78                  *
79                  * @ignore
80                  */
81                 function onBeforeUpdate() {
82                         var searchText = this.val();
84                         if ( searchText && searchText !== previousSearchText ) {
85                                 mw.track( 'mediawiki.searchSuggest', {
86                                         action: 'session-start'
87                                 } );
88                         }
89                         previousSearchText = searchText;
90                 }
92                 /**
93                  * Callback that's run when suggestions have been updated either from the cache or the API
94                  * 'this' is the search input box (jQuery object)
95                  *
96                  * @ignore
97                  */
98                 function onAfterUpdate() {
99                         var context = this.data( 'suggestionsContext' );
101                         mw.track( 'mediawiki.searchSuggest', {
102                                 action: 'impression-results',
103                                 numberOfResults: context.config.suggestions.length,
104                                 resultSetType: mw.searchSuggest.type
105                         } );
106                 }
108                 // The function used to render the suggestions.
109                 function renderFunction( text, context ) {
110                         var formData = getFormData( context );
112                         // linkParams object is modified and reused
113                         formData.linkParams[ formData.textParam ] = text;
115                         // this is the container <div>, jQueryfied
116                         this.text( text )
117                                 .wrap(
118                                         $( '<a>' )
119                                                 .attr( 'href', formData.baseHref + $.param( formData.linkParams ) )
120                                                 .attr( 'title', text )
121                                                 .addClass( 'mw-searchSuggest-link' )
122                                 );
123                 }
125                 // The function used when the user makes a selection
126                 function selectFunction( $input ) {
127                         var context = $input.data( 'suggestionsContext' ),
128                                 text = $input.val();
130                         mw.track( 'mediawiki.searchSuggest', {
131                                 action: 'click-result',
132                                 numberOfResults: context.config.suggestions.length,
133                                 clickIndex: context.config.suggestions.indexOf( text ) + 1
134                         } );
136                         // allow the form to be submitted
137                         return true;
138                 }
140                 function specialRenderFunction( query, context ) {
141                         var $el = this,
142                                 formData = getFormData( context );
144                         // linkParams object is modified and reused
145                         formData.linkParams[ formData.textParam ] = query;
147                         if ( $el.children().length === 0 ) {
148                                 $el
149                                         .append(
150                                                 $( '<div>' )
151                                                         .addClass( 'special-label' )
152                                                         .text( mw.msg( 'searchsuggest-containing' ) ),
153                                                 $( '<div>' )
154                                                         .addClass( 'special-query' )
155                                                         .text( query )
156                                         )
157                                         .show();
158                         } else {
159                                 $el.find( '.special-query' )
160                                         .text( query );
161                         }
163                         if ( $el.parent().hasClass( 'mw-searchSuggest-link' ) ) {
164                                 $el.parent().attr( 'href', formData.baseHref + $.param( formData.linkParams ) + '&fulltext=1' );
165                         } else {
166                                 $el.wrap(
167                                         $( '<a>' )
168                                                 .attr( 'href', formData.baseHref + $.param( formData.linkParams ) + '&fulltext=1' )
169                                                 .addClass( 'mw-searchSuggest-link' )
170                                 );
171                         }
172                 }
174                 // Generic suggestions functionality for all search boxes
175                 searchboxesSelectors = [
176                         // Primary searchbox on every page in standard skins
177                         '#searchInput',
178                         // Special:Search
179                         '#powerSearchText',
180                         '#searchText',
181                         // Generic selector for skins with multiple searchboxes (used by CologneBlue)
182                         // and for MediaWiki itself (special pages with page title inputs)
183                         '.mw-searchInput'
184                 ];
185                 $( searchboxesSelectors.join( ', ' ) )
186                         .suggestions( {
187                                 fetch: function ( query, response, maxRows ) {
188                                         var node = this[ 0 ];
190                                         api = api || new mw.Api();
192                                         $.data( node, 'request', mw.searchSuggest.request( api, query, response, maxRows ) );
193                                 },
194                                 cancel: function () {
195                                         var node = this[ 0 ],
196                                                 request = $.data( node, 'request' );
198                                         if ( request ) {
199                                                 request.abort();
200                                                 $.removeData( node, 'request' );
201                                         }
202                                 },
203                                 result: {
204                                         render: renderFunction,
205                                         select: function () {
206                                                 // allow the form to be submitted
207                                                 return true;
208                                         }
209                                 },
210                                 cache: true,
211                                 highlightInput: true
212                         } )
213                         .bind( 'paste cut drop', function () {
214                                 // make sure paste and cut events from the mouse and drag&drop events
215                                 // trigger the keypress handler and cause the suggestions to update
216                                 $( this ).trigger( 'keypress' );
217                         } )
218                         // In most skins (at least Monobook and Vector), the font-size is messed up in <body>.
219                         // (they use 2 elements to get a sane font-height). So, instead of making exceptions for
220                         // each skin or adding more stylesheets, just copy it from the active element so auto-fit.
221                         .each( function () {
222                                 var $this = $( this );
223                                 $this
224                                         .data( 'suggestions-context' )
225                                         .data.$container
226                                                 .css( 'fontSize', $this.css( 'fontSize' ) );
227                         } );
229                 // Ensure that the thing is actually present!
230                 if ( $searchRegion.length === 0 ) {
231                         // Don't try to set anything up if simpleSearch is disabled sitewide.
232                         // The loader code loads us if the option is present, even if we're
233                         // not actually enabled (anymore).
234                         return;
235                 }
237                 // Special suggestions functionality and tracking for skin-provided search box
238                 $searchInput.suggestions( {
239                         update: {
240                                 before: onBeforeUpdate,
241                                 after: onAfterUpdate
242                         },
243                         result: {
244                                 render: renderFunction,
245                                 select: selectFunction
246                         },
247                         special: {
248                                 render: specialRenderFunction,
249                                 select: function ( $input ) {
250                                         $input.closest( 'form' )
251                                                 .append( $( '<input type="hidden" name="fulltext" value="1"/>' ) );
252                                         return true; // allow the form to be submitted
253                                 }
254                         },
255                         $region: $searchRegion
256                 } );
258                 $searchInput.closest( 'form' )
259                         // track the form submit event
260                         .on( 'submit', function () {
261                                 var context = $searchInput.data( 'suggestionsContext' );
262                                 mw.track( 'mediawiki.searchSuggest', {
263                                         action: 'submit-form',
264                                         numberOfResults: context.config.suggestions.length
265                                 } );
266                         } )
267                         // If the form includes any fallback fulltext search buttons, remove them
268                         .find( '.mw-fallbackSearchButton' ).remove();
269         } );
271 }( mediaWiki, jQuery ) );