2 * mw.Api objects represent the API of a particular MediaWiki server.
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?
13 var defaultOptions
= {
15 // Query parameters for API requests
21 // Ajax options for jQuery.ajax()
23 url
: mw
.util
.wikiScript( 'api' ),
25 timeout
: 30 * 1000, // 30 seconds
32 * Constructor to create an object to interact with the API of a particular MediaWiki server.
34 * @todo Share API objects with exact same config.
37 * var api = new mw.Api();
42 * ok: function () { console.log( arguments ); }
47 * @param options {Object} See defaultOptions documentation above. Ajax options can also be
48 * overridden for each individual request to jQuery.ajax() later on.
50 mw
.Api = function ( options
) {
52 if ( options
=== undefined ) {
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
);
61 options
.parameters
= $.extend( {}, defaultOptions
.parameters
, options
.parameters
);
62 options
.ajax
= $.extend( {}, defaultOptions
.ajax
, options
.ajax
);
64 this.defaults
= options
;
70 * Normalize the ajax options for compatibility and/or convenience methods.
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.
76 normalizeAjaxOptions: function ( arg
) {
77 // Arg argument is usually empty
78 // (before MW 1.20 it was often used to pass ok/err callbacks)
80 // Options can also be a success callback handler
81 if ( typeof arg
=== 'function' ) {
88 * Perform API get request
90 * @param {Object} request parameters
91 * @param {Object|Function} [optional] ajax options
92 * @return {jQuery.Promise}
94 get: function ( parameters
, ajaxOptions
) {
95 ajaxOptions
= this.normalizeAjaxOptions( ajaxOptions
);
96 ajaxOptions
.type
= 'GET';
97 return this.ajax( parameters
, ajaxOptions
);
101 * Perform API post request
102 * @todo Post actions for nonlocal will need proxy
104 * @param {Object} request parameters
105 * @param {Object|Function} [optional] ajax options
106 * @return {jQuery.Promise}
108 post: function ( parameters
, ajaxOptions
) {
109 ajaxOptions
= this.normalizeAjaxOptions( ajaxOptions
);
110 ajaxOptions
.type
= 'POST';
111 return this.ajax( parameters
, ajaxOptions
);
115 * Perform the API call.
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.
123 ajax: function ( parameters
, ajaxOptions
) {
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
;
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.
142 ajaxOptions
.data
+= '&token=' + encodeURIComponent( token
);
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
;
151 if ( ajaxOptions
.err
) {
152 apiDeferred
.fail( ajaxOptions
.err
);
153 delete ajaxOptions
.err
;
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', {
163 textStatus
: textStatus
,
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?)'
173 } else if ( result
.error
) {
174 var code
= result
.error
.code
=== undefined ? 'unknown' : result
.error
.code
;
175 apiDeferred
.reject( code
, result
);
177 apiDeferred
.resolve( result
);
181 // Return the Promise
182 return apiDeferred
.promise().fail( function ( code
, details
) {
183 mw
.log( 'mw.Api error: ', code
, details
);
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
195 // occurs when POST aborted
196 // jQuery 1.4 can't distinguish abort or lost connection from 200 OK + empty result
202 // really a warning, but we treat it like an error
206 // upload succeeded, but no image info.
207 // this is probably impossible, but might as well check for it
209 // remote errors, defined in API
218 'copyuploaddisabled',
224 'filetype-banned-type',
227 'verification-error',
234 'fileexists-shared-forbidden',
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
249 }( mediaWiki
, jQuery
) );