Apply lowerCamelCase to files for constructors as well.
[mediawiki.git] / resources / mediawiki / mediawiki.title.js
blob34f85abfa479f9047d17a0b3f7e8d4bc659f87d3
1 /**
2  * mediaWiki.Title
3  *
4  * @author Neil Kandalgaonkar, 2010
5  * @author Timo Tijhof, 2011
6  * @since 1.18
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                                 setNameAndExtension( this, title );
30                                 this._ns = fixNsId( namespace );
31                         } else if ( arguments.length === 1 ) {
32                                 setAll( this, title );
33                         }
34                         return this;
35         },
37         /**
38          * Strip some illegal chars: control chars, colon, less than, greater than,
39          * brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity
40          * intact, like unicode bidi chars, but it's a good start..
41          * @param s {String}
42          * @return {String}
43          */
44         clean = function( s ) {
45                 if ( s !== undefined ) {
46                         return s.replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' );
47                 }
48         },
50         /**
51          * Convert db-key to readable text.
52          * @param s {String}
53          * @return {String}
54          */
55         text = function ( s ) {
56                 if ( s !== null && s !== undefined ) {
57                         return s.replace( /_/g, ' ' );
58                 } else {
59                         return '';
60                 }
61         },
63         /**
64          * Sanitize name.
65          */
66         fixName = function( s ) {
67                 return clean( $.trim( s ) );
68         },
70         /**
71          * Sanitize name.
72          */
73         fixExt = function( s ) {
74                 return clean( s.toLowerCase() );
75         },
77         /**
78          * Sanitize namespace id.
79          * @param id {Number} Namespace id.
80          * @return {Number|Boolean} The id as-is or boolean false if invalid.
81          */
82         fixNsId = function( id ) {
83                 // wgFormattedNamespaces is an object of *string* key-vals (ie. arr["0"] not arr[0] )
84                 var ns = mw.config.get( 'wgFormattedNamespaces' )[id.toString()];
86                 // Check only undefined (may be false-y, such as '' (main namespace) ).
87                 if ( ns === undefined ) {
88                         return false;
89                 } else {
90                         return Number( id );
91                 }
92         },
94         /**
95          * Get namespace id from namespace name by any known namespace/id pair (localized, canonical or alias).
96          *
97          * @example On a German wiki this would return 6 for any of 'File', 'Datei', 'Image' or even 'Bild'.
98          * @param ns {String} Namespace name (case insensitive, leading/trailing space ignored).
99          * @return {Number|Boolean} Namespace id or boolean false if unrecognized.
100          */
101         getNsIdByName = function( ns ) {
102                 // toLowerCase throws exception on null/undefined. Return early.
103                 if ( ns == null ) {
104                         return false;
105                 }
106                 ns = clean( $.trim( ns.toLowerCase() ) ); // Normalize
107                 var id = mw.config.get( 'wgNamespaceIds' )[ns];
108                 if ( id === undefined ) {
109                         mw.log( 'mw.Title: Unrecognized namespace: ' + ns );
110                         return false;
111                 }
112                 return fixNsId( id );
113         },
115         /**
116          * Helper to extract namespace, name and extension from a string.
117          *
118          * @param title {mw.Title}
119          * @param raw {String}
120          * @return {mw.Title}
121          */
122         setAll = function( title, s ) {
123                 // In normal browsers the match-array contains null/undefined if there's no match,
124                 // IE returns an empty string.
125                 var     matches = s.match( /^(?:([^:]+):)?(.*?)(?:\.(\w{1,5}))?$/ ),
126                         ns_match = getNsIdByName( matches[1] );
128                 // Namespace must be valid, and title must be a non-empty string.
129                 if ( ns_match && typeof matches[2] === 'string' && matches[2] !== '' ) {
130                         title._ns = ns_match;
131                         title._name = fixName( matches[2] );
132                         if ( typeof matches[3] === 'string' && matches[3] !== '' ) {
133                                 title._ext = fixExt( matches[3] );
134                         }
135                 } else {
136                         // Consistency with MediaWiki PHP: Unknown namespace -> fallback to main namespace.
137                         title._ns = 0;
138                         setNameAndExtension( title, s );
139                 }
140                 return title;
141         },
143         /**
144          * Helper to extract name and extension from a string.
145          *
146          * @param title {mw.Title}
147          * @param raw {String}
148          * @return {mw.Title}
149          */
150         setNameAndExtension = function( title, raw ) {
151                 // In normal browsers the match-array contains null/undefined if there's no match,
152                 // IE returns an empty string.
153                 var matches = raw.match( /^(?:)?(.*?)(?:\.(\w{1,5}))?$/ );
155                 // Title must be a non-empty string.
156                 if ( typeof matches[1] === 'string' && matches[1] !== '' ) {
157                         title._name = fixName( matches[1] );
158                         if ( typeof matches[2] === 'string' && matches[2] !== '' ) {
159                                 title._ext = fixExt( matches[2] );
160                         }
161                 } else {
162                         throw new Error( 'mw.Title: Could not parse title "' + raw + '"' );
163                 }
164                 return title;
165         };
166          
168         /* Static space */
170         /**
171          * Wether this title exists on the wiki.
172          * @param title {mixed} prefixed db-key name (string) or instance of Title
173          * @return {mixed} Boolean true/false if the information is available. Otherwise null.
174          */
175         Title.exists = function( title ) {
176                 var     type = $.type( title ), obj = Title.exist.pages, match;
177                 if ( type === 'string' ) {
178                         match = obj[title];
179                 } else if ( type === 'object' && title instanceof Title ) {
180                         match = obj[title.toString()];
181                 } else {
182                         throw new Error( 'mw.Title.exists: title must be a string or an instance of Title' );
183                 }
184                 if ( typeof match === 'boolean' ) {
185                         return match;
186                 }
187                 return null;
188         };
190         /**
191          * @var Title.exist {Object}
192          */
193         Title.exist = {
194                 /**
195                  * @var Title.exist.pages {Object} Keyed by PrefixedDb title.
196                  * Boolean true value indicates page does exist.
197                  */
198                 pages: {},
199                 /**
200                  * @example Declare existing titles: Title.exist.set(['User:John_Doe', ...]);
201                  * @example Declare titles inexisting: Title.exist.set(['File:Foo_bar.jpg', ...], false);
202                  * @param titles {String|Array} Title(s) in strict prefixedDb title form.
203                  * @param state {Boolean} (optional) State of the given titles. Defaults to true.
204                  * @return {Boolean}
205                  */
206                 set: function( titles, state ) {
207                         titles = $.isArray( titles ) ? titles : [titles];
208                         state = state === undefined ? true : !!state;
209                         var     pages = this.pages, i, len = titles.length;
210                         for ( i = 0; i < len; i++ ) {
211                                 pages[ titles[i] ] = state;
212                         }
213                         return true;
214                 }
215         };
217         /* Public methods */
219         var fn = {
220                 constructor: Title,
222                 /**
223                  * Get the namespace number.
224                  * @return {Number}
225                  */
226                 getNamespaceId: function(){
227                         return this._ns;
228                 },
230                 /**
231                  * Get the namespace prefix (in the content-language).
232                  * In NS_MAIN this is '', otherwise namespace name plus ':'
233                  * @return {String}
234                  */
235                 getNamespacePrefix: function(){
236                         return mw.config.get( 'wgFormattedNamespaces' )[this._ns].replace( / /g, '_' ) + (this._ns === 0 ? '' : ':');
237                 },
239                 /**
240                  * The name, like "Foo_bar"
241                  * @return {String}
242                  */
243                 getName: function() {
244                         if ( $.inArray( this._ns, mw.config.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
245                                 return this._name;
246                         } else {
247                                 return $.ucFirst( this._name );
248                         }
249                 },
251                 /**
252                  * The name, like "Foo bar"
253                  * @return {String}
254                  */
255                 getNameText: function() {
256                         return text( this.getName() );
257                 },
259                 /**
260                  * Get full name in prefixed DB form, like File:Foo_bar.jpg,
261                  * most useful for API calls, anything that must identify the "title".
262                  */
263                 getPrefixedDb: function() {
264                         return this.getNamespacePrefix() + this.getMain();
265                 },
267                 /**
268                  * Get full name in text form, like "File:Foo bar.jpg".
269                  * @return {String}
270                  */
271                 getPrefixedText: function() {
272                         return text( this.getPrefixedDb() );
273                 },
275                 /**
276                  * The main title (without namespace), like "Foo_bar.jpg"
277                  * @return {String}
278                  */
279                 getMain: function() {
280                         return this.getName() + this.getDotExtension();
281                 },
283                 /**
284                  * The "text" form, like "Foo bar.jpg"
285                  * @return {String}
286                  */
287                 getMainText: function() {
288                         return text( this.getMain() );
289                 },
291                 /**
292                  * Get the extension (returns null if there was none)
293                  * @return {String|null} extension
294                  */
295                 getExtension: function() {
296                         return this._ext;
297                 },
299                 /**
300                  * Convenience method: return string like ".jpg", or "" if no extension
301                  * @return {String}
302                  */
303                 getDotExtension: function() {
304                         return this._ext === null ? '' : '.' + this._ext;
305                 },
307                 /**
308                  * Return the URL to this title
309                  * @return {String}
310                  */
311                 getUrl: function() {
312                         return mw.util.wikiGetlink( this.toString() );
313                 },
315                 /**
316                  * Wether this title exists on the wiki.
317                  * @return {mixed} Boolean true/false if the information is available. Otherwise null.
318                  */
319                 exists: function() {
320                         return Title.exists( this );
321                 }
322         };
324         // Alias
325         fn.toString = fn.getPrefixedDb;
326         fn.toText = fn.getPrefixedText;
328         // Assign
329         Title.prototype = fn;
331         // Expose
332         mw.Title = Title;
334 })(jQuery);