67f3e01c1c38ba007e5fc00271349b9bb0f3c2b3
2 * MediaWiki Widgets - TitleWidget class.
4 * @copyright 2011-2015 MediaWiki Widgets Team and others; see AUTHORS.txt
5 * @license The MIT License (MIT); see LICENSE.txt
10 * Mixin for title widgets
16 * @param {Object} [config] Configuration options
17 * @cfg {number} [limit=10] Number of results to show
18 * @cfg {number} [namespace] Namespace to prepend to queries
19 * @cfg {number} [maxLength=255] Maximum query length
20 * @cfg {boolean} [relative=true] If a namespace is set, display titles relative to it
21 * @cfg {boolean} [suggestions=true] Display search suggestions
22 * @cfg {boolean} [showRedirectTargets=true] Show the targets of redirects
23 * @cfg {boolean} [showRedlink] Show red link to exact match if it doesn't exist
24 * @cfg {boolean} [showImages] Show page images
25 * @cfg {boolean} [showDescriptions] Show page descriptions
26 * @cfg {Object} [cache] Result cache which implements a 'set' method, taking keyed values as an argument
28 mw
.widgets
.TitleWidget
= function MwWidgetsTitleWidget( config
) {
31 // Config initialization
38 this.limit
= config
.limit
;
39 this.maxLength
= config
.maxLength
;
40 this.namespace = config
.namespace !== undefined ? config
.namespace : null;
41 this.relative
= config
.relative
!== undefined ? config
.relative
: true;
42 this.suggestions
= config
.suggestions
!== undefined ? config
.suggestions
: true;
43 this.showRedirectTargets
= config
.showRedirectTargets
!== false;
44 this.showRedlink
= !!config
.showRedlink
;
45 this.showImages
= !!config
.showImages
;
46 this.showDescriptions
= !!config
.showDescriptions
;
47 this.cache
= config
.cache
;
50 this.$element
.addClass( 'mw-widget-titleWidget' );
51 this.interwikiPrefixes
= [];
52 this.interwikiPrefixesPromise
= new mw
.Api().get( {
55 siprop
: 'interwikimap'
56 } ).done( function ( data
) {
57 $.each( data
.query
.interwikimap
, function ( index
, interwiki
) {
58 widget
.interwikiPrefixes
.push( interwiki
.prefix
);
65 OO
.initClass( mw
.widgets
.TitleWidget
);
70 * Get the current value of the search query
73 * @return {string} Search query
75 mw
.widgets
.TitleWidget
.prototype.getQueryValue
= null;
78 * Get the namespace to prepend to titles in suggestions, if any.
80 * @return {number|null} Namespace number
82 mw
.widgets
.TitleWidget
.prototype.getNamespace = function () {
83 return this.namespace;
87 * Set the namespace to prepend to titles in suggestions, if any.
89 * @param {number|null} namespace Namespace number
91 mw
.widgets
.TitleWidget
.prototype.setNamespace = function ( namespace ) {
92 this.namespace = namespace;
96 * Get a promise which resolves with an API repsonse for suggested
97 * links for the current query.
99 mw
.widgets
.TitleWidget
.prototype.getSuggestionsPromise = function () {
101 query
= this.getQueryValue(),
103 promiseAbortObject
= { abort: function () {
104 // Do nothing. This is just so OOUI doesn't break due to abort being undefined.
107 if ( mw
.Title
.newFromText( query
) ) {
108 return this.interwikiPrefixesPromise
.then( function () {
110 interwiki
= query
.substring( 0, query
.indexOf( ':' ) );
112 interwiki
&& interwiki
!== '' &&
113 widget
.interwikiPrefixes
.indexOf( interwiki
) !== -1
115 return $.Deferred().resolve( { query
: {
119 } } ).promise( promiseAbortObject
);
123 prop
: [ 'info', 'pageprops' ],
124 generator
: 'prefixsearch',
126 gpsnamespace
: widget
.namespace !== null ? widget
.namespace : undefined,
127 gpslimit
: widget
.limit
,
128 ppprop
: 'disambiguation'
130 if ( widget
.showRedirectTargets
) {
131 params
.redirects
= true;
133 if ( widget
.showImages
) {
134 params
.prop
.push( 'pageimages' );
135 params
.pithumbsize
= 80;
136 params
.pilimit
= widget
.limit
;
138 if ( widget
.showDescriptions
) {
139 params
.prop
.push( 'pageterms' );
140 params
.wbptterms
= 'description';
142 req
= new mw
.Api().get( params
);
143 promiseAbortObject
.abort
= req
.abort
.bind( req
); // TODO ew
146 } ).promise( promiseAbortObject
);
148 // Don't send invalid titles to the API.
149 // Just pretend it returned nothing so we can show the 'invalid title' section
150 return $.Deferred().resolve( {} ).promise( promiseAbortObject
);
155 * Get option widgets from the server response
157 * @param {Object} data Query result
158 * @return {OO.ui.OptionWidget[]} Menu items
160 mw
.widgets
.TitleWidget
.prototype.getOptionsFromData = function ( data
) {
161 var i
, len
, index
, pageExists
, pageExistsExact
, suggestionPage
, page
, redirect
, redirects
,
164 titleObj
= mw
.Title
.newFromText( this.getQueryValue() ),
168 if ( data
.redirects
) {
169 for ( i
= 0, len
= data
.redirects
.length
; i
< len
; i
++ ) {
170 redirect
= data
.redirects
[ i
];
171 redirectsTo
[ redirect
.to
] = redirectsTo
[ redirect
.to
] || [];
172 redirectsTo
[ redirect
.to
].push( redirect
.from );
176 for ( index
in data
.pages
) {
177 suggestionPage
= data
.pages
[ index
];
178 pageData
[ suggestionPage
.title
] = {
179 missing
: suggestionPage
.missing
!== undefined,
180 redirect
: suggestionPage
.redirect
!== undefined,
181 disambiguation
: OO
.getProp( suggestionPage
, 'pageprops', 'disambiguation' ) !== undefined,
182 imageUrl
: OO
.getProp( suggestionPage
, 'thumbnail', 'source' ),
183 description
: OO
.getProp( suggestionPage
, 'terms', 'description' ),
185 index
: suggestionPage
.index
188 // Throw away pages from wrong namespaces. This can happen when 'showRedirectTargets' is true
189 // and we encounter a cross-namespace redirect.
190 if ( this.namespace === null || this.namespace === suggestionPage
.ns
) {
191 titles
.push( suggestionPage
.title
);
194 redirects
= redirectsTo
[ suggestionPage
.title
] || [];
195 for ( i
= 0, len
= redirects
.length
; i
< len
; i
++ ) {
196 pageData
[ redirects
[ i
] ] = {
199 disambiguation
: false,
200 description
: mw
.msg( 'mw-widgets-titleinput-description-redirect', suggestionPage
.title
)
202 titles
.push( redirects
[ i
] );
206 titles
.sort( function ( a
, b
) {
207 return pageData
[ a
].index
- pageData
[ b
].index
;
210 // If not found, run value through mw.Title to avoid treating a match as a
211 // mismatch where normalisation would make them matching (bug 48476)
213 pageExistsExact
= titles
.indexOf( this.getQueryValue() ) !== -1;
214 pageExists
= pageExistsExact
|| (
215 titleObj
&& titles
.indexOf( titleObj
.getPrefixedText() ) !== -1
219 pageData
[ this.getQueryValue() ] = {
220 missing
: true, redirect
: false, disambiguation
: false,
221 description
: mw
.msg( 'mw-widgets-titleinput-description-new-page' )
226 this.cache
.set( pageData
);
229 // Offer the exact text as a suggestion if the page exists
230 if ( pageExists
&& !pageExistsExact
) {
231 titles
.unshift( this.getQueryValue() );
233 // Offer the exact text as a new page if the title is valid
234 if ( this.showRedlink
&& !pageExists
&& titleObj
) {
235 titles
.push( this.getQueryValue() );
237 for ( i
= 0, len
= titles
.length
; i
< len
; i
++ ) {
238 page
= pageData
[ titles
[ i
] ] || {};
239 items
.push( new mw
.widgets
.TitleOptionWidget( this.getOptionWidgetData( titles
[ i
], page
) ) );
246 * Get menu option widget data from the title and page data
248 * @param {string} title Title object
249 * @param {Object} data Page data
250 * @return {Object} Data for option widget
252 mw
.widgets
.TitleWidget
.prototype.getOptionWidgetData = function ( title
, data
) {
253 var mwTitle
= new mw
.Title( title
);
255 data
: this.namespace !== null && this.relative
256 ? mwTitle
.getRelativeText( this.namespace )
258 url
: mwTitle
.getUrl(),
259 imageUrl
: this.showImages
? data
.imageUrl
: null,
260 description
: this.showDescriptions
? data
.description
: null,
261 missing
: data
.missing
,
262 redirect
: data
.redirect
,
263 disambiguation
: data
.disambiguation
,
264 query
: this.getQueryValue()
269 * Get title object corresponding to given value, or #getQueryValue if not given.
271 * @param {string} [value] Value to get a title for
272 * @return {mw.Title|null} Title object, or null if value is invalid
274 mw
.widgets
.TitleWidget
.prototype.getTitle = function ( value
) {
275 var title
= value
!== undefined ? value
: this.getQueryValue(),
276 // mw.Title doesn't handle null well
277 titleObj
= mw
.Title
.newFromText( title
, this.namespace !== null ? this.namespace : undefined );
283 * Check if the query is valid
285 * @return {boolean} The query is valid
287 mw
.widgets
.TitleWidget
.prototype.isQueryValid = function () {
288 return !!this.getTitle();
291 }( jQuery
, mediaWiki
) );