Someone on irc had a very screwed up DB, and he reported that the updater gave a...
[lhc/web/wiklou.git] / resources / mediawiki / mediawiki.Title.js
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( $ ) {
11
12 /* Local space */
13
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
27
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 },
41
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 },
54
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 };
67
68 /* Static space */
69
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 };
89
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 };
116
117 /* Public methods */
118
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()];
128
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 },
137
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 },
152
153 /**
154 * Get the namespace number.
155 * @return {Number}
156 */
157 getNamespaceId: function(){
158 return this._ns;
159 },
160
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 },
169
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 },
179
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 },
191
192 /**
193 * The name, like "Foo bar"
194 * @return {String}
195 */
196 getNameText: function() {
197 return text( this.getName() );
198 },
199
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 },
207
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 },
215
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 },
223
224 /**
225 * The "text" form, like "Foo bar.jpg"
226 * @return {String}
227 */
228 getMainText: function() {
229 return text( this.getMain() );
230 },
231
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 },
241
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 },
249
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 },
257
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 },
273
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 },
288
289 /**
290 * Return the URL to this title
291 * @return {String}
292 */
293 getUrl: function() {
294 return mw.util.wikiGetlink( this.toString() );
295 },
296
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 };
305
306 // Alias
307 fn.toString = fn.getPrefixedDb;
308 fn.toText = fn.getPrefixedText;
309
310 // Assign
311 Title.prototype = fn;
312
313 // Expose
314 mw.Title = Title;
315
316 })(jQuery);