43ee202f9322df0eef264a38bf19150f900a7ef7
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.base.js
1 /*!
2 * This file is currently loaded as part of the 'mediawiki' module and therefore
3 * concatenated to mediawiki.js and executed at the same time. This file exists
4 * to help prepare for splitting up the 'mediawiki' module.
5 * This effort is tracked at https://phabricator.wikimedia.org/T192623
6 *
7 * In short:
8 *
9 * - mediawiki.js will be reduced to the minimum needed to define mw.loader and
10 * mw.config, and then moved to its own private "mediawiki.loader" module that
11 * can be embedded within the StartupModule response.
12 *
13 * - mediawiki.base.js and other files in this directory will remain part of the
14 * "mediawiki" module, and will remain a default/implicit dependency for all
15 * regular modules, just like jquery and wikibits already are.
16 */
17 /* globals mw */
18 ( function () {
19 'use strict';
20
21 var slice = Array.prototype.slice;
22
23 /**
24 * Object constructor for messages.
25 *
26 * Similar to the Message class in MediaWiki PHP.
27 *
28 * Format defaults to 'text'.
29 *
30 * @example
31 *
32 * var obj, str;
33 * mw.messages.set( {
34 * 'hello': 'Hello world',
35 * 'hello-user': 'Hello, $1!',
36 * 'welcome-user': 'Welcome back to $2, $1! Last visit by $1: $3'
37 * } );
38 *
39 * obj = new mw.Message( mw.messages, 'hello' );
40 * mw.log( obj.text() );
41 * // Hello world
42 *
43 * obj = new mw.Message( mw.messages, 'hello-user', [ 'John Doe' ] );
44 * mw.log( obj.text() );
45 * // Hello, John Doe!
46 *
47 * obj = new mw.Message( mw.messages, 'welcome-user', [ 'John Doe', 'Wikipedia', '2 hours ago' ] );
48 * mw.log( obj.text() );
49 * // Welcome back to Wikipedia, John Doe! Last visit by John Doe: 2 hours ago
50 *
51 * // Using mw.message shortcut
52 * obj = mw.message( 'hello-user', 'John Doe' );
53 * mw.log( obj.text() );
54 * // Hello, John Doe!
55 *
56 * // Using mw.msg shortcut
57 * str = mw.msg( 'hello-user', 'John Doe' );
58 * mw.log( str );
59 * // Hello, John Doe!
60 *
61 * // Different formats
62 * obj = new mw.Message( mw.messages, 'hello-user', [ 'John "Wiki" <3 Doe' ] );
63 *
64 * obj.format = 'text';
65 * str = obj.toString();
66 * // Same as:
67 * str = obj.text();
68 *
69 * mw.log( str );
70 * // Hello, John "Wiki" <3 Doe!
71 *
72 * mw.log( obj.escaped() );
73 * // Hello, John &quot;Wiki&quot; &lt;3 Doe!
74 *
75 * @class mw.Message
76 *
77 * @constructor
78 * @param {mw.Map} map Message store
79 * @param {string} key
80 * @param {Array} [parameters]
81 */
82 function Message( map, key, parameters ) {
83 this.format = 'text';
84 this.map = map;
85 this.key = key;
86 this.parameters = parameters === undefined ? [] : slice.call( parameters );
87 return this;
88 }
89
90 Message.prototype = {
91 /**
92 * Get parsed contents of the message.
93 *
94 * The default parser does simple $N replacements and nothing else.
95 * This may be overridden to provide a more complex message parser.
96 * The primary override is in the mediawiki.jqueryMsg module.
97 *
98 * This function will not be called for nonexistent messages.
99 *
100 * @return {string} Parsed message
101 */
102 parser: function () {
103 return mw.format.apply( null, [ this.map.get( this.key ) ].concat( this.parameters ) );
104 },
105
106 /**
107 * Add (does not replace) parameters for `$N` placeholder values.
108 *
109 * @param {Array} parameters
110 * @return {mw.Message}
111 * @chainable
112 */
113 params: function ( parameters ) {
114 var i;
115 for ( i = 0; i < parameters.length; i++ ) {
116 this.parameters.push( parameters[ i ] );
117 }
118 return this;
119 },
120
121 /**
122 * Convert message object to its string form based on current format.
123 *
124 * @return {string} Message as a string in the current form, or `<key>` if key
125 * does not exist.
126 */
127 toString: function () {
128 var text;
129
130 if ( !this.exists() ) {
131 // Use ⧼key⧽ as text if key does not exist
132 // Err on the side of safety, ensure that the output
133 // is always html safe in the event the message key is
134 // missing, since in that case its highly likely the
135 // message key is user-controlled.
136 // '⧼' is used instead of '<' to side-step any
137 // double-escaping issues.
138 // (Keep synchronised with Message::toString() in PHP.)
139 return '⧼' + mw.html.escape( this.key ) + '⧽';
140 }
141
142 if ( this.format === 'plain' || this.format === 'text' || this.format === 'parse' ) {
143 text = this.parser();
144 }
145
146 if ( this.format === 'escaped' ) {
147 text = this.parser();
148 text = mw.html.escape( text );
149 }
150
151 return text;
152 },
153
154 /**
155 * Change format to 'parse' and convert message to string
156 *
157 * If jqueryMsg is loaded, this parses the message text from wikitext
158 * (where supported) to HTML
159 *
160 * Otherwise, it is equivalent to plain.
161 *
162 * @return {string} String form of parsed message
163 */
164 parse: function () {
165 this.format = 'parse';
166 return this.toString();
167 },
168
169 /**
170 * Change format to 'plain' and convert message to string
171 *
172 * This substitutes parameters, but otherwise does not change the
173 * message text.
174 *
175 * @return {string} String form of plain message
176 */
177 plain: function () {
178 this.format = 'plain';
179 return this.toString();
180 },
181
182 /**
183 * Change format to 'text' and convert message to string
184 *
185 * If jqueryMsg is loaded, {{-transformation is done where supported
186 * (such as {{plural:}}, {{gender:}}, {{int:}}).
187 *
188 * Otherwise, it is equivalent to plain
189 *
190 * @return {string} String form of text message
191 */
192 text: function () {
193 this.format = 'text';
194 return this.toString();
195 },
196
197 /**
198 * Change the format to 'escaped' and convert message to string
199 *
200 * This is equivalent to using the 'text' format (see #text), then
201 * HTML-escaping the output.
202 *
203 * @return {string} String form of html escaped message
204 */
205 escaped: function () {
206 this.format = 'escaped';
207 return this.toString();
208 },
209
210 /**
211 * Check if a message exists
212 *
213 * @see mw.Map#exists
214 * @return {boolean}
215 */
216 exists: function () {
217 return this.map.exists( this.key );
218 }
219 };
220
221 /**
222 * @class mw
223 * @singleton
224 */
225
226 /**
227 * @inheritdoc mw.inspect#runReports
228 * @method
229 */
230 mw.inspect = function () {
231 var args = arguments;
232 mw.loader.using( 'mediawiki.inspect', function () {
233 mw.inspect.runReports.apply( mw.inspect, args );
234 } );
235 };
236
237 /**
238 * Format a string. Replace $1, $2 ... $N with positional arguments.
239 *
240 * Used by Message#parser().
241 *
242 * @since 1.25
243 * @param {string} formatString Format string
244 * @param {...Mixed} parameters Values for $N replacements
245 * @return {string} Formatted string
246 */
247 mw.format = function ( formatString ) {
248 var parameters = slice.call( arguments, 1 );
249 return formatString.replace( /\$(\d+)/g, function ( str, match ) {
250 var index = parseInt( match, 10 ) - 1;
251 return parameters[ index ] !== undefined ? parameters[ index ] : '$' + match;
252 } );
253 };
254
255 // Expose Message constructor
256 mw.Message = Message;
257
258 /**
259 * Get a message object.
260 *
261 * Shortcut for `new mw.Message( mw.messages, key, parameters )`.
262 *
263 * @see mw.Message
264 * @param {string} key Key of message to get
265 * @param {...Mixed} parameters Values for $N replacements
266 * @return {mw.Message}
267 */
268 mw.message = function ( key ) {
269 var parameters = slice.call( arguments, 1 );
270 return new Message( mw.messages, key, parameters );
271 };
272
273 /**
274 * Get a message string using the (default) 'text' format.
275 *
276 * Shortcut for `mw.message( key, parameters... ).text()`.
277 *
278 * @see mw.Message
279 * @param {string} key Key of message to get
280 * @param {...Mixed} parameters Values for $N replacements
281 * @return {string}
282 */
283 mw.msg = function () {
284 return mw.message.apply( mw.message, arguments ).toString();
285 };
286 }() );