Merge "Further tweaks to pipe trick documentation (follow-up Iaf365e31)"
[mediawiki.git] / resources / mediawiki.api / mediawiki.api.js
bloba184e3ca96d3973f3fad88f2d84a1f086f2df62b
1 /**
2  * mw.Api objects represent the API of a particular MediaWiki server.
3  */
4 ( function ( mw, $ ) {
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 = {
15                         // Query parameters for API requests
16                         parameters: {
17                                 action: 'query',
18                                 format: 'json'
19                         },
21                         // Ajax options for jQuery.ajax()
22                         ajax: {
23                                 url: mw.util.wikiScript( 'api' ),
25                                 timeout: 30 * 1000, // 30 seconds
27                                 dataType: 'json'
28                         }
29                 };
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 ) {
52                 if ( options === undefined ) {
53                         options = {};
54                 }
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                 }
61                 options.parameters = $.extend( {}, defaultOptions.parameters, options.parameters );
62                 options.ajax = $.extend( {}, defaultOptions.ajax, options.ajax );
64                 this.defaults = options;
65         };
67         mw.Api.prototype = {
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                 },
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                 },
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                 },
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();
127                         parameters = $.extend( {}, this.defaults.parameters, parameters );
128                         ajaxOptions = $.extend( {}, this.defaults.ajax, ajaxOptions );
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' );
140                         // If we extracted a token parameter, add it back in.
141                         if ( token ) {
142                                 ajaxOptions.data += '&token=' + encodeURIComponent( token );
143                         }
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                         }
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                                 } );
181                         // Return the Promise
182                         return apiDeferred.promise().fail( function ( code, details ) {
183                                 mw.log( 'mw.Api error: ', code, details );
184                         });
185                 }
187         };
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',
199                 // timeout
200                 'timeout',
202                 // really a warning, but we treat it like an error
203                 'duplicate',
204                 'duplicate-archive',
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         ];
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         ];
249 }( mediaWiki, jQuery ) );