Merge "Add ss_active_users in SiteStats::isSane"
[mediawiki.git] / resources / mediawiki / mediawiki.searchSuggest.js
blob00e74c55e5059b65d9b6a0f55c327e56ae9cfd52
1 /**
2  * Add search suggestions to the search form.
3  */
4 ( function ( mw, $ ) {
5         $( document ).ready( function ( $ ) {
6                 var map, resultRenderCache, searchboxesSelectors,
7                         // Region where the suggestions box will appear directly below
8                         // (using the same width). Can be a container element or the input
9                         // itself, depending on what suits best in the environment.
10                         // For Vector the suggestion box should align with the simpleSearch
11                         // container's borders, in other skins it should align with the input
12                         // element (not the search form, as that would leave the buttons
13                         // vertically between the input and the suggestions).
14                         $searchRegion = $( '#simpleSearch, #searchInput' ).first(),
15                         $searchInput = $( '#searchInput' );
17                 // Compatibility map
18                 map = {
19                         browsers: {
20                                 // Left-to-right languages
21                                 ltr: {
22                                         // SimpleSearch is broken in Opera < 9.6
23                                         opera: [['>=', 9.6]],
24                                         docomo: false,
25                                         blackberry: false,
26                                         ipod: false,
27                                         iphone: false
28                                 },
29                                 // Right-to-left languages
30                                 rtl: {
31                                         opera: [['>=', 9.6]],
32                                         docomo: false,
33                                         blackberry: false,
34                                         ipod: false,
35                                         iphone: false
36                                 }
37                         }
38                 };
40                 if ( !$.client.test( map ) ) {
41                         return;
42                 }
44                 // Compute form data for search suggestions functionality.
45                 function computeResultRenderCache( context ) {
46                         var $form, formAction, baseHref, linkParams;
48                         // Compute common parameters for links' hrefs
49                         $form = context.config.$region.closest( 'form' );
51                         formAction = $form.attr( 'action' );
52                         baseHref = formAction + ( formAction.match(/\?/) ? '&' : '?' );
54                         linkParams = {};
55                         $.each( $form.serializeArray(), function ( idx, obj ) {
56                                 linkParams[ obj.name ] = obj.value;
57                         } );
59                         return {
60                                 textParam: context.data.$textbox.attr( 'name' ),
61                                 linkParams: linkParams,
62                                 baseHref: baseHref
63                         };
64                 }
66                 // The function used to render the suggestions.
67                 function renderFunction( text, context ) {
68                         if ( !resultRenderCache ) {
69                                 resultRenderCache = computeResultRenderCache( context );
70                         }
72                         // linkParams object is modified and reused
73                         resultRenderCache.linkParams[ resultRenderCache.textParam ] = text;
75                         // this is the container <div>, jQueryfied
76                         this
77                                 .append(
78                                         // the <span> is needed for $.autoEllipsis to work
79                                         $( '<span>' )
80                                                 .css( 'whiteSpace', 'nowrap' )
81                                                 .text( text )
82                                 )
83                                 .wrap(
84                                         $( '<a>' )
85                                                 .attr( 'href', resultRenderCache.baseHref + $.param( resultRenderCache.linkParams ) )
86                                                 .addClass( 'mw-searchSuggest-link' )
87                                 );
88                 }
90                 function specialRenderFunction( query, context ) {
91                         var $el = this;
93                         if ( !resultRenderCache ) {
94                                 resultRenderCache = computeResultRenderCache( context );
95                         }
97                         // linkParams object is modified and reused
98                         resultRenderCache.linkParams[ resultRenderCache.textParam ] = query;
100                         if ( $el.children().length === 0 ) {
101                                 $el
102                                         .append(
103                                                 $( '<div>' )
104                                                         .addClass( 'special-label' )
105                                                         .text( mw.msg( 'searchsuggest-containing' ) ),
106                                                 $( '<div>' )
107                                                         .addClass( 'special-query' )
108                                                         .text( query )
109                                                         .autoEllipsis()
110                                         )
111                                         .show();
112                         } else {
113                                 $el.find( '.special-query' )
114                                         .text( query )
115                                         .autoEllipsis();
116                         }
118                         if ( $el.parent().hasClass( 'mw-searchSuggest-link' ) ) {
119                                 $el.parent().attr( 'href', resultRenderCache.baseHref + $.param( resultRenderCache.linkParams ) + '&fulltext=1' );
120                         } else {
121                                 $el.wrap(
122                                         $( '<a>' )
123                                                 .attr( 'href', resultRenderCache.baseHref + $.param( resultRenderCache.linkParams ) + '&fulltext=1' )
124                                                 .addClass( 'mw-searchSuggest-link' )
125                                 );
126                         }
127                 }
129                 // General suggestions functionality for all search boxes
130                 searchboxesSelectors = [
131                         // Primary searchbox on every page in standard skins
132                         '#searchInput',
133                         // Special:Search
134                         '#powerSearchText',
135                         '#searchText',
136                         // Generic selector for skins with multiple searchboxes (used by CologneBlue)
137                         '.mw-searchInput'
138                 ];
139                 $( searchboxesSelectors.join(', ') )
140                         .suggestions( {
141                                 fetch: function ( query ) {
142                                         var $el, jqXhr;
144                                         if ( query.length !== 0 ) {
145                                                 $el = $(this);
146                                                 jqXhr = $.ajax( {
147                                                         url: mw.util.wikiScript( 'api' ),
148                                                         data: {
149                                                                 format: 'json',
150                                                                 action: 'opensearch',
151                                                                 search: query,
152                                                                 namespace: 0,
153                                                                 suggest: ''
154                                                         },
155                                                         dataType: 'json',
156                                                         success: function ( data ) {
157                                                                 if ( $.isArray( data ) && data.length ) {
158                                                                         $el.suggestions( 'suggestions', data[1] );
159                                                                 }
160                                                         }
161                                                 });
162                                                 $el.data( 'request', jqXhr );
163                                         }
164                                 },
165                                 cancel: function () {
166                                         var jqXhr = $(this).data( 'request' );
167                                         // If the delay setting has caused the fetch to have not even happened
168                                         // yet, the jqXHR object will have never been set.
169                                         if ( jqXhr && $.isFunction( jqXhr.abort ) ) {
170                                                 jqXhr.abort();
171                                                 $(this).removeData( 'request' );
172                                         }
173                                 },
174                                 result: {
175                                         render: renderFunction,
176                                         select: function ( $input ) {
177                                                 $input.closest( 'form' ).submit();
178                                         }
179                                 },
180                                 delay: 120,
181                                 highlightInput: true
182                         } )
183                         .bind( 'paste cut drop', function () {
184                                 // make sure paste and cut events from the mouse and drag&drop events
185                                 // trigger the keypress handler and cause the suggestions to update
186                                 $( this ).trigger( 'keypress' );
187                         } );
189                 // Ensure that the thing is actually present!
190                 if ( $searchRegion.length === 0 ) {
191                         // Don't try to set anything up if simpleSearch is disabled sitewide.
192                         // The loader code loads us if the option is present, even if we're
193                         // not actually enabled (anymore).
194                         return;
195                 }
197                 // Special suggestions functionality for skin-provided search box
198                 $searchInput.suggestions( {
199                         result: {
200                                 render: renderFunction,
201                                 select: function ( $input ) {
202                                         $input.closest( 'form' ).submit();
203                                 }
204                         },
205                         special: {
206                                 render: specialRenderFunction,
207                                 select: function ( $input ) {
208                                         $input.closest( 'form' ).append(
209                                                 $( '<input type="hidden" name="fulltext" value="1"/>' )
210                                         );
211                                         $input.closest( 'form' ).submit();
212                                 }
213                         },
214                         $region: $searchRegion
215                 } );
217                 // In most skins (at least Monobook and Vector), the font-size is messed up in <body>.
218                 // (they use 2 elements to get a sane font-height). So, instead of making exceptions for
219                 // each skin or adding more stylesheets, just copy it from the active element so auto-fit.
220                 $searchInput
221                         .data( 'suggestions-context' )
222                         .data.$container
223                                 .css( 'fontSize', $searchInput.css( 'fontSize' ) );
225         } );
227 }( mediaWiki, jQuery ) );