/** * Provides an interface for uploading files to MediaWiki. * * @class mw.Api.plugin.upload * @singleton */ ( function ( mw, $ ) { var nonce = 0, fieldsAllowed = { stash: true, filekey: true, filename: true, comment: true, text: true, watchlist: true, ignorewarnings: true }; /** * @private * Get nonce for iframe IDs on the page. * * @return {number} */ function getNonce() { return nonce++; } /** * @private * Given a non-empty object, return one of its keys. * * @param {Object} obj * @return {string} */ function getFirstKey( obj ) { for ( var key in obj ) { if ( obj.hasOwnProperty( key ) ) { return key; } } } /** * @private * Get new iframe object for an upload. * * @return {HTMLIframeElement} */ function getNewIframe( id ) { var frame = document.createElement( 'iframe' ); frame.id = id; frame.name = id; return frame; } /** * @private * Shortcut for getting hidden inputs * * @return {jQuery} */ function getHiddenInput( name, val ) { return $( '' ) .attr( 'name', name ) .val( val ); } /** * Process the result of the form submission, returned to an iframe. * This is the iframe's onload event. * * @param {HTMLIframeElement} iframe Iframe to extract result from * @return {Object} Response from the server. The return value may or may * not be an XMLDocument, this code was copied from elsewhere, so if you * see an unexpected return type, please file a bug. */ function processIframeResult( iframe ) { var json, doc = iframe.contentDocument || frames[ iframe.id ].document; if ( doc.XMLDocument ) { // The response is a document property in IE return doc.XMLDocument; } if ( doc.body ) { // Get the json string // We're actually searching through an HTML doc here -- // according to mdale we need to do this // because IE does not load JSON properly in an iframe json = $( doc.body ).find( 'pre' ).text(); return JSON.parse( json ); } // Response is a xml document return doc; } function formDataAvailable() { return window.FormData !== undefined && window.File !== undefined && window.File.prototype.slice !== undefined; } $.extend( mw.Api.prototype, { /** * Upload a file to MediaWiki. * * The file will be uploaded using AJAX and FormData, if the browser supports it, or via an * iframe if it doesn't. * * Caveats of iframe upload: * - The returned jQuery.Promise will not receive `progress` notifications during the upload * - It is incompatible with uploads to a foreign wiki using mw.ForeignApi * - You must pass a HTMLInputElement and not a File for it to be possible * * @param {HTMLInputElement|File} file HTML input type=file element with a file already inside * of it, or a File object. * @param {Object} data Other upload options, see action=upload API docs for more * @return {jQuery.Promise} */ upload: function ( file, data ) { var isFileInput, canUseFormData; isFileInput = file && file.nodeType === Node.ELEMENT_NODE; if ( formDataAvailable() && isFileInput && file.files ) { file = file.files[ 0 ]; } if ( !file ) { throw new Error( 'No file' ); } canUseFormData = formDataAvailable() && file instanceof window.File; if ( !isFileInput && !canUseFormData ) { throw new Error( 'Unsupported argument type passed to mw.Api.upload' ); } if ( canUseFormData ) { return this.uploadWithFormData( file, data ); } return this.uploadWithIframe( file, data ); }, /** * Upload a file to MediaWiki with an iframe and a form. * * This method is necessary for browsers without the File/FormData * APIs, and continues to work in browsers with those APIs. * * The rough sketch of how this method works is as follows: * 1. An iframe is loaded with no content. * 2. A form is submitted with the passed-in file input and some extras. * 3. The MediaWiki API receives that form data, and sends back a response. * 4. The response is sent to the iframe, because we set target=(iframe id) * 5. The response is parsed out of the iframe's document, and passed back * through the promise. * * @private * @param {HTMLInputElement} file The file input with a file in it. * @param {Object} data Other upload options, see action=upload API docs for more * @return {jQuery.Promise} */ uploadWithIframe: function ( file, data ) { var key, tokenPromise = $.Deferred(), api = this, deferred = $.Deferred(), nonce = getNonce(), id = 'uploadframe-' + nonce, $form = $( '