Follow-up r90369: Add third parameter to filetype-unwanted-type (for PLURAL)
[mediawiki.git] / resources / mediawiki / mediawiki.Title.js
bloba3459ac3f77286753b2eb331d617633d96580d66
1 /**
2  * mediaWiki.Title
3  *
4  * @author Neil Kandalgaonkar, 2010
5  * @author Timo Tijhof, 2011
6  * @since 1.19
7  *
8  * Relies on: mw.config (wgFormattedNamespaces, wgNamespaceIds, wgCaseSensitiveNamespaces), mw.util.wikiGetlink
9  */
10 (function( $ ) {
12         /* Local space */
14         /**
15          * Title
16          * @constructor
17          *
18          * @param title {String} Title of the page. If no second argument given,
19          * this will be searched for a namespace.
20          * @param namespace {Number} (optional) Namespace id. If given, title will be taken as-is.
21          * @return {Title} this
22          */
23 var     Title = function( title, namespace ) {
24                         this._ns = 0; // integer namespace id
25                         this._name = null; // name in canonical 'database' form
26                         this._ext = null; // extension
28                         if ( arguments.length === 2 ) {
29                                 this.setNameAndExtension( title ).setNamespaceById( namespace );
30                         } else if ( arguments.length === 1 ) {
31                                 // If title is like "Blabla: Hello" ignore exception by setNamespace(),
32                                 // and instead assume NS_MAIN and keep prefix
33                                 try {
34                                         this.setAll( title );
35                                 } catch(e) {
36                                         this.setNameAndExtension( title );
37                                 }
38                         }
39                         return this;
40         },
42         /**
43          * Strip some illegal chars: control chars, colon, less than, greater than,
44          * brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity
45          * intact, like unicode bidi chars, but it's a good start..
46          * @param s {String}
47          * @return {String}
48          */
49         clean = function( s ) {
50                 if ( s !== undefined ) {
51                         return s.replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' );
52                 }
53         },
55         /**
56          * Convert db-key to readable text.
57          * @param s {String}
58          * @return {String}
59          */
60         text = function ( s ) {
61                 if ( s !== null && s !== undefined ) {
62                         return s.replace( /_/g, ' ' );
63                 } else {
64                         return '';
65                 }
66         };
68         /* Static space */
70         /**
71          * Wether this title exists on the wiki.
72          * @param title {mixed} prefixed db-key name (string) or instance of Title
73          * @return {mixed} Boolean true/false if the information is available. Otherwise null.
74          */
75         Title.exists = function( title ) {
76                 var     type = $.type( title ), obj = Title.exist.pages, match;
77                 if ( type === 'string' ) {
78                         match = obj[title];
79                 } else if ( type === 'object' && title instanceof Title ) {
80                         match = obj[title.toString()];
81                 } else {
82                         throw new Error( 'mw.Title.exists: title must be a string or an instance of Title' );
83                 }
84                 if ( typeof match === 'boolean' ) {
85                         return match;
86                 }
87                 return null;
88         };
90         /**
91          * @var Title.exist {Object}
92          */
93         Title.exist = {
94                 /**
95                  * @var Title.exist.pages {Object} Keyed by PrefixedDb title.
96                  * Boolean true value indicates page does exist.
97                  */
98                 pages: {},
99                 /**
100                  * @example Declare existing titles: Title.exist.set(['User:John_Doe', ...]);
101                  * @example Declare titles inexisting: Title.exist.set(['File:Foo_bar.jpg', ...], false);
102                  * @param titles {String|Array} Title(s) in strict prefixedDb title form.
103                  * @param state {Boolean} (optional) State of the given titles. Defaults to true.
104                  * @return {Boolean}
105                  */
106                 set: function( titles, state ) {
107                         titles = $.isArray( titles ) ? titles : [titles];
108                         state = state === undefined ? true : !!state;
109                         var     pages = this.pages, i, len = titles.length;
110                         for ( i = 0; i < len; i++ ) {
111                                 pages[ titles[i] ] = state;
112                         }
113                         return true;
114                 }
115         };
117         /* Public methods */
119         var fn = {
120                 constructor: Title,
121                 /**
122                  * @param id {Number} Canonical namespace id.
123                  * @return {mw.Title} this
124                  */
125                 setNamespaceById: function( id ) {
126                         // wgFormattedNamespaces is an object of *string* key-vals,
127                         var ns = mw.config.get( 'wgFormattedNamespaces' )[id.toString()];
129                         // Cannot cast to boolean, ns may be '' (main namespace)
130                         if ( ns === undefined ) {
131                                 this._ns = false;
132                         } else {
133                                 this._ns = Number( id );
134                         }
135                         return this;
136                 },
138                 /**
139                  * Set namespace by any known namespace/id pair (localized, canonical or alias)
140                  * On a German wiki this could be 'File', 'Datei', 'Image' or even 'Bild' for NS_FILE.
141                  * @param ns {String} A namespace name (case insensitive, space insensitive)
142                  * @return {mw.Title} this
143                  */
144                 setNamespace: function( ns ) {
145                         ns = clean( $.trim( ns.toLowerCase() ) ); // Normalize
146                         var id = mw.config.get( 'wgNamespaceIds' )[ns];
147                         if ( id === undefined ) {
148                                 throw new Error( 'mw.Title: Unrecognized canonical namespace: ' + ns );
149                         }
150                         return this.setNamespaceById( id );
151                 },
153                 /**
154                  * Get the namespace number.
155                  * @return {Number}
156                  */
157                 getNamespaceId: function(){
158                         return this._ns;
159                 },
161                 /**
162                  * Get the namespace prefix (in the content-language).
163                  * In NS_MAIN this is '', otherwise namespace name plus ':'
164                  * @return {String}
165                  */
166                 getNamespacePrefix: function(){
167                         return mw.config.get( 'wgFormattedNamespaces' )[this._ns].replace( / /g, '_' ) + (this._ns === 0 ? '' : ':');
168                 },
170                 /**
171                  * Set the "name" portion, removing illegal characters.
172                  * @param s {String} Page name (without namespace prefix)
173                  * @return {mw.Title} this
174                  */
175                 setName: function( s ) {
176                         this._name = clean( $.trim( s ) );
177                         return this;
178                 },
180                 /**
181                  * The name, like "Foo_bar"
182                  * @return {String}
183                  */
184                 getName: function() {
185                         if ( $.inArray( this._ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
186                                 return this._name;
187                         } else {
188                                 return $.ucFirst( this._name );
189                         }
190                 },
192                 /**
193                  * The name, like "Foo bar"
194                  * @return {String}
195                  */
196                 getNameText: function() {
197                         return text( this.getName() );
198                 },
200                 /**
201                  * Get full name in prefixed DB form, like File:Foo_bar.jpg,
202                  * most useful for API calls, anything that must identify the "title".
203                  */
204                 getPrefixedDb: function() {
205                         return this.getNamespacePrefix() + this.getMain();
206                 },
208                 /**
209                  * Get full name in text form, like "File:Foo bar.jpg".
210                  * @return {String}
211                  */
212                 getPrefixedText: function() {
213                         return text( this.getPrefixedDb() );
214                 },
216                 /**
217                  * The main title (without namespace), like "Foo_bar.jpg"
218                  * @return {String}
219                  */
220                 getMain: function() {
221                         return this.getName() + this.getDotExtension();
222                 },
224                 /**
225                  * The "text" form, like "Foo bar.jpg"
226                  * @return {String}
227                  */
228                 getMainText: function() {
229                         return text( this.getMain() );
230                 },
232                 /**
233                  * Set the "extension" portion, removing illegal characters.
234                  * @param s {String}
235                  * @return {mw.Title} this
236                  */
237                 setExtension: function( s ) {
238                         this._ext = clean( s.toLowerCase() );
239                         return this;
240                 },
242                 /**
243                  * Get the extension (returns null if there was none)
244                  * @return {String|null} extension
245                  */
246                 getExtension: function() {
247                         return this._ext;
248                 },
250                 /**
251                  * Convenience method: return string like ".jpg", or "" if no extension
252                  * @return {String}
253                  */
254                 getDotExtension: function() {
255                         return this._ext === null ? '' : '.' + this._ext;
256                 },
258                 /**
259                  * @param s {String}
260                  * @return {mw.Title} this
261                  */
262                 setAll: function( s ) {
263                         var matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w{1,5}))?$/ );
264                         if ( matches.length ) {
265                                 if ( matches[1] ) { this.setNamespace( matches[1] ); }
266                                 if ( matches[2] ) { this.setName( matches[2] ); }
267                                 if ( matches[3] ) { this.setExtension( matches[3] ); }
268                         } else {
269                                 throw new Error( 'mw.Title: Could not parse title "' + s + '"' );
270                         }
271                         return this;
272                 },
274                 /**
275                  * @param s {String}
276                  * @return {mw.Title} this
277                  */
278                 setNameAndExtension: function( s ) {
279                         var matches = s.match( /^(?:)?(.*?)(?:\.(\w{1,5}))?$/ );
280                         if ( matches.length ) {
281                                 if ( matches[1] ) { this.setName( matches[1] ); }
282                                 if ( matches[2] ) { this.setExtension( matches[2] ); }
283                         } else {
284                                 throw new Error( 'mw.Title: Could not parse title "' + s + '"' );
285                         }
286                         return this;
287                 },
289                 /**
290                  * Return the URL to this title
291                  * @return {String}
292                  */
293                 getUrl: function() {
294                         return mw.util.wikiGetlink( this.toString() );
295                 },
297                 /**
298                  * Wether this title exists on the wiki.
299                  * @return {mixed} Boolean true/false if the information is available. Otherwise null.
300                  */
301                 exists: function() {
302                         return Title.exists( this );
303                 }
304         };
306         // Alias
307         fn.toString = fn.getPrefixedDb;
308         fn.toText = fn.getPrefixedText;
310         // Assign
311         Title.prototype = fn;
313         // Expose
314         mw.Title = Title;
316 })(jQuery);