080725af528578fec1c86a8f7a815ef70e3342cd
[lhc/web/wiklou.git] / resources / mediawiki.api / mediawiki.api.js
1 /**
2 * mw.Api objects represent the API of a particular MediaWiki server.
3 */
4 ( function( $, mw, undefined ) {
5
6 /**
7 * @var defaultOptions {Object}
8 * We allow people to omit these default parameters from API requests
9 * there is very customizable error handling here, on a per-call basis
10 * wondering, would it be simpler to make it easy to clone the api object,
11 * change error handling, and use that instead?
12 */
13 var defaultOptions = {
14
15 // Query parameters for API requests
16 parameters: {
17 action: 'query',
18 format: 'json'
19 },
20
21 // Ajax options for jQuery.ajax()
22 ajax: {
23 url: mw.util.wikiScript( 'api' ),
24
25 timeout: 30 * 1000, // 30 seconds
26
27 dataType: 'json'
28 }
29 };
30
31 /**
32 * Constructor to create an object to interact with the API of a particular MediaWiki server.
33 *
34 * @todo Share API objects with exact same config.
35 * @example
36 * <code>
37 * var api = new mw.Api();
38 * api.get( {
39 * action: 'query',
40 * meta: 'userinfo'
41 * }, {
42 * ok: function () { console.log( arguments ); }
43 * } );
44 * </code>
45 *
46 * @constructor
47 * @param options {Object} See defaultOptions documentation above. Ajax options can also be
48 * overridden for each individual request to jQuery.ajax() later on.
49 */
50 mw.Api = function( options ) {
51
52 if ( options === undefined ) {
53 options = {};
54 }
55
56 // Force toString if we got a mw.Uri object
57 if ( options.ajax && options.ajax.url !== undefined ) {
58 options.ajax.url = String( options.ajax.url );
59 }
60
61 options.parameters = $.extend( {}, defaultOptions.parameters, options.parameters );
62 options.ajax = $.extend( {}, defaultOptions.ajax, options.ajax );
63
64 this.defaults = options;
65 };
66
67 mw.Api.prototype = {
68
69 /**
70 * Normalize the ajax options for compatibility and/or convenience methods.
71 *
72 * @param {undefined|Object|Function} An object contaning one or more of options.ajax,
73 * or just a success function (options.ajax.ok).
74 * @return {Object} Normalized ajax options.
75 */
76 normalizeAjaxOptions: function ( arg ) {
77 // Arg argument is usually empty
78 // (before MW 1.20 it was often used to pass ok/err callbacks)
79 var opts = arg || {};
80 // Options can also be a success callback handler
81 if ( typeof arg === 'function' ) {
82 opts = { ok: arg };
83 }
84 return opts;
85 },
86
87 /**
88 * Perform API get request
89 *
90 * @param {Object} request parameters
91 * @param {Object|Function} [optional] ajax options
92 * @return {jQuery.Promise}
93 */
94 get: function( parameters, ajaxOptions ) {
95 ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
96 ajaxOptions.type = 'GET';
97 return this.ajax( parameters, ajaxOptions );
98 },
99
100 /**
101 * Perform API post request
102 * @todo Post actions for nonlocal will need proxy
103 *
104 * @param {Object} request parameters
105 * @param {Object|Function} [optional] ajax options
106 * @return {jQuery.Promise}
107 */
108 post: function( parameters, ajaxOptions ) {
109 ajaxOptions = this.normalizeAjaxOptions( ajaxOptions );
110 ajaxOptions.type = 'POST';
111 return this.ajax( parameters, ajaxOptions );
112 },
113
114 /**
115 * Perform the API call.
116 *
117 * @param {Object} request parameters
118 * @param {Object} ajax options
119 * @return {jQuery.Promise}
120 * - done: API response data as first argument
121 * - fail: errorcode as first arg, details (string or object) as second arg.
122 */
123 ajax: function( parameters, ajaxOptions ) {
124 var token,
125 apiDeferred = $.Deferred();
126
127 parameters = $.extend( {}, this.defaults.parameters, parameters );
128 ajaxOptions = $.extend( {}, this.defaults.ajax, ajaxOptions );
129
130 // Ensure that token parameter is last (per [[mw:API:Edit#Token]]).
131 if ( parameters.token ) {
132 token = parameters.token;
133 delete parameters.token;
134 }
135 // Some deployed MediaWiki >= 1.17 forbid periods in URLs, due to an IE XSS bug
136 // So let's escape them here. See bug #28235
137 // This works because jQuery accepts data as a query string or as an Object
138 ajaxOptions.data = $.param( parameters ).replace( /\./g, '%2E' );
139
140 // If we extracted a token parameter, add it back in.
141 if ( token ) {
142 ajaxOptions.data += '&token=' + encodeURIComponent( token );
143 }
144
145 // Backwards compatibility: Before MediaWiki 1.20,
146 // callbacks were done with the 'ok' and 'err' property in ajaxOptions.
147 if ( ajaxOptions.ok ) {
148 apiDeferred.done( ajaxOptions.ok );
149 delete ajaxOptions.ok;
150 }
151 if ( ajaxOptions.err ) {
152 apiDeferred.fail( ajaxOptions.err );
153 delete ajaxOptions.err;
154 }
155
156 // Make the AJAX request
157 $.ajax( ajaxOptions )
158 // If AJAX fails, reject API call with error code 'http'
159 // and details in second argument.
160 .fail( function ( xhr, textStatus, exception ) {
161 apiDeferred.reject( 'http', {
162 xhr: xhr,
163 textStatus: textStatus,
164 exception: exception
165 } );
166 } )
167 // AJAX success just means "200 OK" response, also check API error codes
168 .done( function ( result ) {
169 if ( result === undefined || result === null || result === '' ) {
170 apiDeferred.reject( 'ok-but-empty',
171 'OK response but empty result (check HTTP headers?)'
172 );
173 } else if ( result.error ) {
174 var code = result.error.code === undefined ? 'unknown' : result.error.code;
175 apiDeferred.reject( code, result );
176 } else {
177 apiDeferred.resolve( result );
178 }
179 } );
180
181 // Return the Promise
182 return apiDeferred.promise().fail( function ( code, details ) {
183 mw.log( 'mw.Api error: ', code, details );
184 });
185 }
186
187 };
188
189 /**
190 * @var {Array} List of errors we might receive from the API.
191 * For now, this just documents our expectation that there should be similar messages
192 * available.
193 */
194 mw.Api.errors = [
195 // occurs when POST aborted
196 // jQuery 1.4 can't distinguish abort or lost connection from 200 OK + empty result
197 'ok-but-empty',
198
199 // timeout
200 'timeout',
201
202 // really a warning, but we treat it like an error
203 'duplicate',
204 'duplicate-archive',
205
206 // upload succeeded, but no image info.
207 // this is probably impossible, but might as well check for it
208 'noimageinfo',
209 // remote errors, defined in API
210 'uploaddisabled',
211 'nomodule',
212 'mustbeposted',
213 'badaccess-groups',
214 'stashfailed',
215 'missingresult',
216 'missingparam',
217 'invalid-file-key',
218 'copyuploaddisabled',
219 'mustbeloggedin',
220 'empty-file',
221 'file-too-large',
222 'filetype-missing',
223 'filetype-banned',
224 'filetype-banned-type',
225 'filename-tooshort',
226 'illegal-filename',
227 'verification-error',
228 'hookaborted',
229 'unknown-error',
230 'internal-error',
231 'overwrite',
232 'badtoken',
233 'fetchfileerror',
234 'fileexists-shared-forbidden',
235 'invalidtitle',
236 'notloggedin'
237 ];
238
239 /**
240 * @var {Array} List of warnings we might receive from the API.
241 * For now, this just documents our expectation that there should be similar messages
242 * available.
243 */
244 mw.Api.warnings = [
245 'duplicate',
246 'exists'
247 ];
248
249 })( jQuery, mediaWiki );