b03e52c9bd66a7cc946235bc4fd1cc0b5f122dec
2 * Provides an interface for uploading files to MediaWiki.
3 * @class mw.Api.plugin.upload
18 * Get nonce for iframe IDs on the page.
27 * Get new iframe object for an upload.
28 * @return {HTMLIframeElement}
30 function getNewIframe( id
) {
31 var frame
= document
.createElement( 'iframe' );
39 * Shortcut for getting hidden inputs
42 function getHiddenInput( name
, val
) {
43 return $( '<input type="hidden" />')
49 * Parse response from an XHR to the server.
54 function parseXHRResponse( e
) {
58 response
= $.parseJSON( e
.target
.responseText
);
63 info
: e
.target
.responseText
72 * Process the result of the form submission, returned to an iframe.
73 * This is the iframe's onload event.
75 * @param {HTMLIframeElement} iframe Iframe to extract result from
76 * @return {Object} Response from the server. The return value may or may
77 * not be an XMLDocument, this code was copied from elsewhere, so if you
78 * see an unexpected return type, please file a bug.
80 function processIframeResult( iframe
) {
82 doc
= iframe
.contentDocument
|| frames
[iframe
.id
].document
;
84 if ( doc
.XMLDocument
) {
85 // The response is a document property in IE
86 return doc
.XMLDocument
;
90 // Get the json string
91 // We're actually searching through an HTML doc here --
92 // according to mdale we need to do this
93 // because IE does not load JSON properly in an iframe
94 json
= $( doc
.body
).find( 'pre' ).text();
96 return JSON
.parse( json
);
99 // Response is a xml document
103 function formDataAvailable() {
104 return window
.FormData
!== undefined &&
105 window
.File
!== undefined &&
106 window
.File
.prototype.slice
!== undefined;
109 $.extend( mw
.Api
.prototype, {
111 * Upload a file to MediaWiki.
112 * @param {HTMLInputElement|File} file HTML input type=file element with a file already inside of it, or a File object.
113 * @param {Object} data Other upload options, see action=upload API docs for more
114 * @return {jQuery.Promise}
116 upload: function ( file
, data
) {
117 var iframe
, formData
;
120 return $.Deferred().reject( 'No file' );
123 iframe
= file
.nodeType
&& file
.nodeType
=== file
.ELEMENT_NODE
;
124 formData
= formDataAvailable() && file
instanceof window
.File
;
126 if ( !iframe
&& !formData
) {
127 return $.Deferred().reject( 'Unsupported argument type passed to mw.Api.upload' );
131 return this.uploadWithFormData( file
, data
);
134 return this.uploadWithIframe( file
, data
);
138 * Upload a file to MediaWiki with an iframe and a form.
140 * This method is necessary for browsers without the File/FormData
141 * APIs, and continues to work in browsers with those APIs.
143 * The rough sketch of how this method works is as follows:
144 * * An iframe is loaded with no content.
145 * * A form is submitted with the passed-in file input and some extras.
146 * * The MediaWiki API receives that form data, and sends back a response.
147 * * The response is sent to the iframe, because we set target=(iframe id)
148 * * The response is parsed out of the iframe's document, and passed back
149 * through the promise.
150 * @param {HTMLInputElement} file The file input with a file in it.
151 * @param {Object} data Other upload options, see action=upload API docs for more
152 * @return {jQuery.Promise}
154 uploadWithIframe: function ( file
, data
) {
157 filenameFound
= false,
158 deferred
= $.Deferred(),
160 id
= 'uploadframe-' + nonce
,
161 $form
= $( '<form>' ),
162 iframe
= getNewIframe( id
),
163 $iframe
= $( iframe
);
165 $form
.addClass( 'mw-api-upload-form' );
168 getHiddenInput( 'action', 'upload' ),
169 getHiddenInput( 'format', 'json' ),
173 $form
.css( 'display', 'none' )
175 action
: this.defaults
.ajax
.url
,
178 enctype
: 'multipart/form-data'
181 $iframe
.one( 'load', function () {
182 $iframe
.one( 'load', function () {
183 var result
= processIframeResult( iframe
);
186 deferred
.reject( 'No response from API on upload attempt.' );
187 } else if ( result
.error
|| result
.warnings
) {
188 if ( result
.error
&& result
.error
.code
=== 'badtoken' ) {
189 api
.badToken( 'edit' );
192 deferred
.reject( result
.error
|| result
.warnings
);
194 deferred
.notify( 1 );
195 deferred
.resolve( result
);
198 tokenPromise
.done( function () {
203 $iframe
.error( function ( error
) {
204 deferred
.reject( 'iframe failed to load: ' + error
);
207 $iframe
.prop( 'src', 'about:blank' ).hide();
211 $.each( data
, function ( key
, val
) {
212 if ( key
=== 'filename' ) {
213 filenameFound
= true;
216 if ( fieldsAllowed
[key
] === true ) {
217 $form
.append( getHiddenInput( key
, val
) );
221 if ( !filenameFound
) {
222 return $.Deferred().reject( 'Filename not included in file data.' );
225 tokenPromise
= this.getEditToken().then( function ( token
) {
226 $form
.append( getHiddenInput( 'token', token
) );
229 $( 'body' ).append( $form
, $iframe
);
231 return deferred
.promise();
235 * Uploads a file using the FormData API.
237 * @param {Object} data
239 uploadWithFormData: function ( file
, data
) {
240 var xhr
, tokenPromise
,
242 formData
= new FormData(),
243 deferred
= $.Deferred(),
244 filenameFound
= false;
246 formData
.append( 'action', 'upload' );
247 formData
.append( 'format', 'json' );
249 $.each( data
, function ( key
, val
) {
250 if ( key
=== 'filename' ) {
251 filenameFound
= true;
254 if ( fieldsAllowed
[key
] === true ) {
255 formData
.append( key
, val
);
259 if ( !filenameFound
) {
260 return $.Deferred().reject( 'Filename not included in file data.' );
263 formData
.append( 'file', file
);
265 xhr
= new XMLHttpRequest();
267 xhr
.upload
.addEventListener( 'progress', function ( e
) {
268 if ( e
.lengthComputable
) {
269 deferred
.notify( e
.loaded
/ e
.total
);
273 xhr
.addEventListener( 'abort', function ( e
) {
274 deferred
.reject( parseXHRResponse( e
) );
277 xhr
.addEventListener( 'load', function ( e
) {
278 deferred
.resolve( parseXHRResponse( e
) );
281 xhr
.addEventListener( 'error', function ( e
) {
282 deferred
.reject( parseXHRResponse( e
) );
285 xhr
.open( 'POST', this.defaults
.ajax
.url
, true );
287 tokenPromise
= this.getEditToken().then( function ( token
) {
288 formData
.append( 'token', token
);
289 xhr
.send( formData
);
291 // Mark the edit token as bad, it's been used.
292 api
.badToken( 'edit' );
295 return deferred
.promise();
301 * @mixins mw.Api.plugin.upload
303 }( mediaWiki
, jQuery
) );