4 * @author Neil Kandalgaonkar, 2010
5 * @author Timo Tijhof, 2011
8 * Relies on: mw.config (wgFormattedNamespaces, wgNamespaceIds, wgCaseSensitiveNamespaces), mw.util.wikiGetlink
10 ( function ( mw
, $ ) {
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
23 function Title( 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
);
39 * Strip some illegal chars: control chars, colon, less than, greater than,
40 * brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity
41 * intact, like unicode bidi chars, but it's a good start..
45 clean = function ( s
) {
46 if ( s
!== undefined ) {
47 return s
.replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' );
52 * Convert db-key to readable text.
56 text = function ( s
) {
57 if ( s
!== null && s
!== undefined ) {
58 return s
.replace( /_
/g
, ' ' );
67 fixName = function ( s
) {
68 return clean( $.trim( s
) );
74 fixExt = function ( s
) {
79 * Sanitize namespace id.
80 * @param id {Number} Namespace id.
81 * @return {Number|Boolean} The id as-is or boolean false if invalid.
83 fixNsId = function ( id
) {
84 // wgFormattedNamespaces is an object of *string* key-vals (ie. arr["0"] not arr[0] )
85 var ns
= mw
.config
.get( 'wgFormattedNamespaces' )[id
.toString()];
87 // Check only undefined (may be false-y, such as '' (main namespace) ).
88 if ( ns
=== undefined ) {
96 * Get namespace id from namespace name by any known namespace/id pair (localized, canonical or alias).
98 * @example On a German wiki this would return 6 for any of 'File', 'Datei', 'Image' or even 'Bild'.
99 * @param ns {String} Namespace name (case insensitive, leading/trailing space ignored).
100 * @return {Number|Boolean} Namespace id or boolean false if unrecognized.
102 getNsIdByName = function ( ns
) {
103 // Don't cast non-strings to strings, because null or undefined
104 // should not result in returning the id of a potential namespace
105 // called "Null:" (e.g. on nullwiki.example.org)
106 // Also, toLowerCase throws exception on null/undefined, because
107 // it is a String.prototype method.
108 if ( typeof ns
!== 'string' ) {
111 ns
= clean( $.trim( ns
.toLowerCase() ) ); // Normalize
112 var id
= mw
.config
.get( 'wgNamespaceIds' )[ns
];
113 if ( id
=== undefined ) {
114 mw
.log( 'mw.Title: Unrecognized namespace: ' + ns
);
117 return fixNsId( id
);
121 * Helper to extract namespace, name and extension from a string.
123 * @param title {mw.Title}
124 * @param raw {String}
127 setAll = function ( title
, s
) {
128 // In normal browsers the match-array contains null/undefined if there's no match,
129 // IE returns an empty string.
130 var matches
= s
.match( /^(?:([^:]+):)?(.*?)(?:\.(\w{1,5}))?$/ ),
131 ns_match
= getNsIdByName( matches
[1] );
133 // Namespace must be valid, and title must be a non-empty string.
134 if ( ns_match
&& typeof matches
[2] === 'string' && matches
[2] !== '' ) {
136 title
.name
= fixName( matches
[2] );
137 if ( typeof matches
[3] === 'string' && matches
[3] !== '' ) {
138 title
.ext
= fixExt( matches
[3] );
141 // Consistency with MediaWiki PHP: Unknown namespace -> fallback to main namespace.
143 setNameAndExtension( title
, s
);
149 * Helper to extract name and extension from a string.
151 * @param title {mw.Title}
152 * @param raw {String}
155 setNameAndExtension = function ( title
, raw
) {
156 // In normal browsers the match-array contains null/undefined if there's no match,
157 // IE returns an empty string.
158 var matches
= raw
.match( /^(?:)?(.*?)(?:\.(\w{1,5}))?$/ );
160 // Title must be a non-empty string.
161 if ( typeof matches
[1] === 'string' && matches
[1] !== '' ) {
162 title
.name
= fixName( matches
[1] );
163 if ( typeof matches
[2] === 'string' && matches
[2] !== '' ) {
164 title
.ext
= fixExt( matches
[2] );
167 throw new Error( 'mw.Title: Could not parse title "' + raw
+ '"' );
176 * Whether this title exists on the wiki.
177 * @param title {mixed} prefixed db-key name (string) or instance of Title
178 * @return {mixed} Boolean true/false if the information is available. Otherwise null.
180 Title
.exists = function ( title
) {
181 var type
= $.type( title
), obj
= Title
.exist
.pages
, match
;
182 if ( type
=== 'string' ) {
184 } else if ( type
=== 'object' && title
instanceof Title
) {
185 match
= obj
[title
.toString()];
187 throw new Error( 'mw.Title.exists: title must be a string or an instance of Title' );
189 if ( typeof match
=== 'boolean' ) {
196 * @var Title.exist {Object}
200 * @var Title.exist.pages {Object} Keyed by PrefixedDb title.
201 * Boolean true value indicates page does exist.
205 * @example Declare existing titles: Title.exist.set(['User:John_Doe', ...]);
206 * @example Declare titles nonexistent: Title.exist.set(['File:Foo_bar.jpg', ...], false);
207 * @param titles {String|Array} Title(s) in strict prefixedDb title form.
208 * @param state {Boolean} (optional) State of the given titles. Defaults to true.
211 set: function ( titles
, state
) {
212 titles
= $.isArray( titles
) ? titles
: [titles
];
213 state
= state
=== undefined ? true : !!state
;
214 var pages
= this.pages
, i
, len
= titles
.length
;
215 for ( i
= 0; i
< len
; i
++ ) {
216 pages
[ titles
[i
] ] = state
;
228 * Get the namespace number.
231 getNamespaceId: function (){
236 * Get the namespace prefix (in the content-language).
237 * In NS_MAIN this is '', otherwise namespace name plus ':'
240 getNamespacePrefix: function (){
241 return mw
.config
.get( 'wgFormattedNamespaces' )[this.ns
].replace( / /g
, '_' ) + (this.ns
=== 0 ? '' : ':');
245 * The name, like "Foo_bar"
248 getName: function () {
249 if ( $.inArray( this.ns
, mw
.config
.get( 'wgCaseSensitiveNamespaces' ) ) !== -1 ) {
252 return $.ucFirst( this.name
);
257 * The name, like "Foo bar"
260 getNameText: function () {
261 return text( this.getName() );
265 * Get full name in prefixed DB form, like File:Foo_bar.jpg,
266 * most useful for API calls, anything that must identify the "title".
268 getPrefixedDb: function () {
269 return this.getNamespacePrefix() + this.getMain();
273 * Get full name in text form, like "File:Foo bar.jpg".
276 getPrefixedText: function () {
277 return text( this.getPrefixedDb() );
281 * The main title (without namespace), like "Foo_bar.jpg"
284 getMain: function () {
285 return this.getName() + this.getDotExtension();
289 * The "text" form, like "Foo bar.jpg"
292 getMainText: function () {
293 return text( this.getMain() );
297 * Get the extension (returns null if there was none)
298 * @return {String|null} extension
300 getExtension: function () {
305 * Convenience method: return string like ".jpg", or "" if no extension
308 getDotExtension: function () {
309 return this.ext
=== null ? '' : '.' + this.ext
;
313 * Return the URL to this title
316 getUrl: function () {
317 return mw
.util
.wikiGetlink( this.toString() );
321 * Whether this title exists on the wiki.
322 * @return {mixed} Boolean true/false if the information is available. Otherwise null.
324 exists: function () {
325 return Title
.exists( this );
330 fn
.toString
= fn
.getPrefixedDb
;
331 fn
.toText
= fn
.getPrefixedText
;
334 Title
.prototype = fn
;
339 }( mediaWiki
, jQuery
) );