Merge "Change CdbException to subclass Exception"
[mediawiki.git] / resources / mediawiki / mediawiki.searchSuggest.js
blob7f078626307bf6f2f0f8d4e5052b774a5306e843
1 /**
2  * Add search suggestions to the search form.
3  */
4 ( function ( mw, $ ) {
5         $( 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;
144                                         if ( query.length !== 0 ) {
145                                                 $el = $( this );
146                                                 $el.data( 'request', ( new mw.Api() ).get( {
147                                                         action: 'opensearch',
148                                                         search: query,
149                                                         namespace: 0,
150                                                         suggest: ''
151                                                 } ).done( function ( data ) {
152                                                         $el.suggestions( 'suggestions', data[1] );
153                                                 } ) );
154                                         }
155                                 },
156                                 cancel: function () {
157                                         var apiPromise = $( this ).data( 'request' );
158                                         // If the delay setting has caused the fetch to have not even happened
159                                         // yet, the apiPromise object will have never been set.
160                                         if ( apiPromise && $.isFunction( apiPromise.abort ) ) {
161                                                 apiPromise.abort();
162                                                 $( this ).removeData( 'request' );
163                                         }
164                                 },
165                                 result: {
166                                         render: renderFunction,
167                                         select: function ( $input ) {
168                                                 $input.closest( 'form' ).submit();
169                                         }
170                                 },
171                                 delay: 120,
172                                 highlightInput: true
173                         } )
174                         .bind( 'paste cut drop', function () {
175                                 // make sure paste and cut events from the mouse and drag&drop events
176                                 // trigger the keypress handler and cause the suggestions to update
177                                 $( this ).trigger( 'keypress' );
178                         } );
180                 // Ensure that the thing is actually present!
181                 if ( $searchRegion.length === 0 ) {
182                         // Don't try to set anything up if simpleSearch is disabled sitewide.
183                         // The loader code loads us if the option is present, even if we're
184                         // not actually enabled (anymore).
185                         return;
186                 }
188                 // Special suggestions functionality for skin-provided search box
189                 $searchInput.suggestions( {
190                         result: {
191                                 render: renderFunction,
192                                 select: function ( $input ) {
193                                         $input.closest( 'form' ).submit();
194                                 }
195                         },
196                         special: {
197                                 render: specialRenderFunction,
198                                 select: function ( $input ) {
199                                         $input.closest( 'form' ).append(
200                                                 $( '<input type="hidden" name="fulltext" value="1"/>' )
201                                         );
202                                         $input.closest( 'form' ).submit();
203                                 }
204                         },
205                         $region: $searchRegion
206                 } );
208                 // In most skins (at least Monobook and Vector), the font-size is messed up in <body>.
209                 // (they use 2 elements to get a sane font-height). So, instead of making exceptions for
210                 // each skin or adding more stylesheets, just copy it from the active element so auto-fit.
211                 $searchInput
212                         .data( 'suggestions-context' )
213                         .data.$container
214                                 .css( 'fontSize', $searchInput.css( 'fontSize' ) );
216         } );
218 }( mediaWiki, jQuery ) );