wp/wp-includes/js/plupload/wp-plupload.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    31 	 * @param {object} options.params    An object of parameters to pass to $_POST when uploading the file.
    31 	 * @param {object} options.params    An object of parameters to pass to $_POST when uploading the file.
    32 	 *                                   Extends this.plupload.multipart_params under the hood.
    32 	 *                                   Extends this.plupload.multipart_params under the hood.
    33 	 */
    33 	 */
    34 	Uploader = function( options ) {
    34 	Uploader = function( options ) {
    35 		var self = this,
    35 		var self = this,
    36 			isIE = navigator.userAgent.indexOf('Trident/') != -1 || navigator.userAgent.indexOf('MSIE ') != -1,
    36 			isIE, // Not used, back-compat.
    37 			elements = {
    37 			elements = {
    38 				container: 'container',
    38 				container: 'container',
    39 				browser:   'browse_button',
    39 				browser:   'browse_button',
    40 				dropzone:  'drop_element'
    40 				dropzone:  'drop_element'
    41 			},
    41 			},
    42 			key, error;
    42 			tryAgainCount = {},
       
    43 			tryAgain,
       
    44 			key,
       
    45 			error,
       
    46 			fileUploaded;
    43 
    47 
    44 		this.supports = {
    48 		this.supports = {
    45 			upload: Uploader.browser.supported
    49 			upload: Uploader.browser.supported
    46 		};
    50 		};
    47 
    51 
    54 		// Arguments to send to pluplad.Uploader().
    58 		// Arguments to send to pluplad.Uploader().
    55 		// Use deep extend to ensure that multipart_params and other objects are cloned.
    59 		// Use deep extend to ensure that multipart_params and other objects are cloned.
    56 		this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults );
    60 		this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults );
    57 		this.container = document.body; // Set default container.
    61 		this.container = document.body; // Set default container.
    58 
    62 
    59 		// Extend the instance with options.
    63 		/*
    60 		//
    64 		 * Extend the instance with options.
    61 		// Use deep extend to allow options.plupload to override individual
    65 		 *
    62 		// default plupload keys.
    66 		 * Use deep extend to allow options.plupload to override individual
       
    67 		 * default plupload keys.
       
    68 		 */
    63 		$.extend( true, this, options );
    69 		$.extend( true, this, options );
    64 
    70 
    65 		// Proxy all methods so this always refers to the current instance.
    71 		// Proxy all methods so this always refers to the current instance.
    66 		for ( key in this ) {
    72 		for ( key in this ) {
    67 			if ( $.isFunction( this[ key ] ) ) {
    73 			if ( $.isFunction( this[ key ] ) ) {
    93 		// If the uploader has neither a browse button nor a dropzone, bail.
    99 		// If the uploader has neither a browse button nor a dropzone, bail.
    94 		if ( ! ( this.browser && this.browser.length ) && ! ( this.dropzone && this.dropzone.length ) ) {
   100 		if ( ! ( this.browser && this.browser.length ) && ! ( this.dropzone && this.dropzone.length ) ) {
    95 			return;
   101 			return;
    96 		}
   102 		}
    97 
   103 
    98 		// Make sure flash sends cookies (seems in IE it does without switching to urlstream mode)
       
    99 		if ( ! isIE && 'flash' === plupload.predictRuntime( this.plupload ) &&
       
   100 			( ! this.plupload.required_features || ! this.plupload.required_features.hasOwnProperty( 'send_binary_string' ) ) ) {
       
   101 
       
   102 			this.plupload.required_features = this.plupload.required_features || {};
       
   103 			this.plupload.required_features.send_binary_string = true;
       
   104 		}
       
   105 
       
   106 		// Initialize the plupload instance.
   104 		// Initialize the plupload instance.
   107 		this.uploader = new plupload.Uploader( this.plupload );
   105 		this.uploader = new plupload.Uploader( this.plupload );
   108 		delete this.plupload;
   106 		delete this.plupload;
   109 
   107 
   110 		// Set default params and remove this.params alias.
   108 		// Set default params and remove this.params alias.
   111 		this.param( this.params || {} );
   109 		this.param( this.params || {} );
   112 		delete this.params;
   110 		delete this.params;
   113 
   111 
   114 		/**
   112 		/**
       
   113 		 * Attempt to create image sub-sizes when an image was uploaded successfully
       
   114 		 * but the server responded with HTTP 5xx error.
       
   115 		 *
       
   116 		 * @since 5.3.0
       
   117 		 *
       
   118 		 * @param {string}        message Error message.
       
   119 		 * @param {object}        data    Error data from Plupload.
       
   120 		 * @param {plupload.File} file    File that was uploaded.
       
   121 		 */
       
   122 		tryAgain = function( message, data, file ) {
       
   123 			var times, id;
       
   124 
       
   125 			if ( ! data || ! data.responseHeaders ) {
       
   126 				error( pluploadL10n.http_error_image, data, file, 'no-retry' );
       
   127 				return;
       
   128 			}
       
   129 
       
   130 			id = data.responseHeaders.match( /x-wp-upload-attachment-id:\s*(\d+)/i );
       
   131 
       
   132 			if ( id && id[1] ) {
       
   133 				id = id[1];
       
   134 			} else {
       
   135 				error( pluploadL10n.http_error_image, data, file, 'no-retry' );
       
   136 				return;
       
   137 			}
       
   138 
       
   139 			times = tryAgainCount[ file.id ];
       
   140 
       
   141 			if ( times && times > 4 ) {
       
   142 				/*
       
   143 				 * The file may have been uploaded and attachment post created,
       
   144 				 * but post-processing and resizing failed...
       
   145 				 * Do a cleanup then tell the user to scale down the image and upload it again.
       
   146 				 */
       
   147 				$.ajax({
       
   148 					type: 'post',
       
   149 					url: ajaxurl,
       
   150 					dataType: 'json',
       
   151 					data: {
       
   152 						action: 'media-create-image-subsizes',
       
   153 						_wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce,
       
   154 						attachment_id: id,
       
   155 						_wp_upload_failed_cleanup: true,
       
   156 					}
       
   157 				});
       
   158 
       
   159 				error( message, data, file, 'no-retry' );
       
   160 				return;
       
   161 			}
       
   162 
       
   163 			if ( ! times ) {
       
   164 				tryAgainCount[ file.id ] = 1;
       
   165 			} else {
       
   166 				tryAgainCount[ file.id ] = ++times;
       
   167 			}
       
   168 
       
   169 			// Another request to try to create the missing image sub-sizes.
       
   170 			$.ajax({
       
   171 				type: 'post',
       
   172 				url: ajaxurl,
       
   173 				dataType: 'json',
       
   174 				data: {
       
   175 					action: 'media-create-image-subsizes',
       
   176 					_wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce,
       
   177 					attachment_id: id,
       
   178 				}
       
   179 			}).done( function( response ) {
       
   180 				if ( response.success ) {
       
   181 					fileUploaded( self.uploader, file, response );
       
   182 				} else {
       
   183 					if ( response.data && response.data.message ) {
       
   184 						message = response.data.message;
       
   185 					}
       
   186 
       
   187 					error( message, data, file, 'no-retry' );
       
   188 				}
       
   189 			}).fail( function( jqXHR ) {
       
   190 				// If another HTTP 5xx error, try try again...
       
   191 				if ( jqXHR.status >= 500 && jqXHR.status < 600 ) {
       
   192 					tryAgain( message, data, file );
       
   193 					return;
       
   194 				}
       
   195 
       
   196 				error( message, data, file, 'no-retry' );
       
   197 			});
       
   198 		}
       
   199 
       
   200 		/**
   115 		 * Custom error callback.
   201 		 * Custom error callback.
   116 		 *
   202 		 *
   117 		 * Add a new error to the errors collection, so other modules can track
   203 		 * Add a new error to the errors collection, so other modules can track
   118 		 * and display errors. @see wp.Uploader.errors.
   204 		 * and display errors. @see wp.Uploader.errors.
   119 		 *
   205 		 *
   120 		 * @param  {string}        message
   206 		 * @param {string}        message Error message.
   121 		 * @param  {object}        data
   207 		 * @param {object}        data    Error data from Plupload.
   122 		 * @param  {plupload.File} file     File that was uploaded.
   208 		 * @param {plupload.File} file    File that was uploaded.
   123 		 */
   209 		 * @param {string}        retry   Whether to try again to create image sub-sizes. Passing 'no-retry' will prevent it.
   124 		error = function( message, data, file ) {
   210 		 */
       
   211 		error = function( message, data, file, retry ) {
       
   212 			var isImage = file.type && file.type.indexOf( 'image/' ) === 0,
       
   213 				status = data && data.status;
       
   214 
       
   215 			// If the file is an image and the error is HTTP 5xx try to create sub-sizes again.
       
   216 			if ( retry !== 'no-retry' && isImage && status >= 500 && status < 600 ) {
       
   217 				tryAgain( message, data, file );
       
   218 				return;
       
   219 			}
       
   220 
   125 			if ( file.attachment ) {
   221 			if ( file.attachment ) {
   126 				file.attachment.destroy();
   222 				file.attachment.destroy();
   127 			}
   223 			}
   128 
   224 
   129 			Uploader.errors.unshift({
   225 			Uploader.errors.unshift({
   134 
   230 
   135 			self.error( message, data, file );
   231 			self.error( message, data, file );
   136 		};
   232 		};
   137 
   233 
   138 		/**
   234 		/**
       
   235 		 * After a file is successfully uploaded, update its model.
       
   236 		 *
       
   237 		 * @param {plupload.Uploader} up       Uploader instance.
       
   238 		 * @param {plupload.File}     file     File that was uploaded.
       
   239 		 * @param {Object}            response Object with response properties.
       
   240 		 */
       
   241 		fileUploaded = function( up, file, response ) {
       
   242 			var complete;
       
   243 
       
   244 			// Remove the "uploading" UI elements.
       
   245 			_.each( ['file','loaded','size','percent'], function( key ) {
       
   246 				file.attachment.unset( key );
       
   247 			} );
       
   248 
       
   249 			file.attachment.set( _.extend( response.data, { uploading: false } ) );
       
   250 
       
   251 			wp.media.model.Attachment.get( response.data.id, file.attachment );
       
   252 
       
   253 			complete = Uploader.queue.all( function( attachment ) {
       
   254 				return ! attachment.get( 'uploading' );
       
   255 			});
       
   256 
       
   257 			if ( complete ) {
       
   258 				Uploader.queue.reset();
       
   259 			}
       
   260 
       
   261 			self.success( file.attachment );
       
   262 		}
       
   263 
       
   264 		/**
   139 		 * After the Uploader has been initialized, initialize some behaviors for the dropzone.
   265 		 * After the Uploader has been initialized, initialize some behaviors for the dropzone.
   140 		 *
   266 		 *
   141 		 * @param {plupload.Uploader} uploader Uploader instance.
   267 		 * @param {plupload.Uploader} uploader Uploader instance.
   142 		 */
   268 		 */
   143 		this.uploader.bind( 'init', function( uploader ) {
   269 		this.uploader.bind( 'init', function( uploader ) {
   170 				dropzone.trigger('dropzone:enter').addClass('drag-over');
   296 				dropzone.trigger('dropzone:enter').addClass('drag-over');
   171 				active = true;
   297 				active = true;
   172 			});
   298 			});
   173 
   299 
   174 			dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function() {
   300 			dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function() {
   175 				// Using an instant timer prevents the drag-over class from
   301 				/*
   176 				// being quickly removed and re-added when elements inside the
   302 				 * Using an instant timer prevents the drag-over class
   177 				// dropzone are repositioned.
   303 				 * from being quickly removed and re-added when elements
   178 				//
   304 				 * inside the dropzone are repositioned.
   179 				// @see https://core.trac.wordpress.org/ticket/21705
   305 				 *
       
   306 				 * @see https://core.trac.wordpress.org/ticket/21705
       
   307 				 */
   180 				timer = setTimeout( function() {
   308 				timer = setTimeout( function() {
   181 					active = false;
   309 					active = false;
   182 					dropzone.trigger('dropzone:leave').removeClass('drag-over');
   310 					dropzone.trigger('dropzone:leave').removeClass('drag-over');
   183 				}, 0 );
   311 				}, 0 );
   184 			});
   312 			});
   196 
   324 
   197 		if ( this.browser ) {
   325 		if ( this.browser ) {
   198 			this.browser.on( 'mouseenter', this.refresh );
   326 			this.browser.on( 'mouseenter', this.refresh );
   199 		} else {
   327 		} else {
   200 			this.uploader.disableBrowse( true );
   328 			this.uploader.disableBrowse( true );
   201 			// If HTML5 mode, hide the auto-created file container.
   329 		}
   202 			$('#' + this.uploader.id + '_html5_container').hide();
   330 
   203 		}
   331 		$( self ).on( 'uploader:ready', function() {
       
   332 			$( '.moxie-shim-html5 input[type="file"]' )
       
   333 				.attr( {
       
   334 					tabIndex:      '-1',
       
   335 					'aria-hidden': 'true'
       
   336 				} );
       
   337 		} );
   204 
   338 
   205 		/**
   339 		/**
   206 		 * After files were filtered and added to the queue, create a model for each.
   340 		 * After files were filtered and added to the queue, create a model for each.
   207 		 *
   341 		 *
   208 		 * @param {plupload.Uploader} uploader Uploader instance.
   342 		 * @param {plupload.Uploader} up    Uploader instance.
   209 		 * @param {Array}             files    Array of file objects that were added to queue by the user.
   343 		 * @param {Array}             files Array of file objects that were added to queue by the user.
   210 		 */
   344 		 */
   211 		this.uploader.bind( 'FilesAdded', function( up, files ) {
   345 		this.uploader.bind( 'FilesAdded', function( up, files ) {
   212 			_.each( files, function( file ) {
   346 			_.each( files, function( file ) {
   213 				var attributes, image;
   347 				var attributes, image;
   214 
   348 
   215 				// Ignore failed uploads.
   349 				// Ignore failed uploads.
   216 				if ( plupload.FAILED === file.status ) {
   350 				if ( plupload.FAILED === file.status ) {
   217 					return;
   351 					return;
       
   352 				}
       
   353 
       
   354 				if ( file.type === 'image/heic' && up.settings.heic_upload_error ) {
       
   355 					// Show error but do not block uploading.
       
   356 					Uploader.errors.unshift({
       
   357 						message: pluploadL10n.unsupported_image,
       
   358 						data:    {},
       
   359 						file:    file
       
   360 					});
   218 				}
   361 				}
   219 
   362 
   220 				// Generate attributes for a new `Attachment` model.
   363 				// Generate attributes for a new `Attachment` model.
   221 				attributes = _.extend({
   364 				attributes = _.extend({
   222 					file:      file,
   365 					file:      file,
   257 		});
   400 		});
   258 
   401 
   259 		/**
   402 		/**
   260 		 * After a file is successfully uploaded, update its model.
   403 		 * After a file is successfully uploaded, update its model.
   261 		 *
   404 		 *
   262 		 * @param {plupload.Uploader} uploader Uploader instance.
   405 		 * @param {plupload.Uploader} up       Uploader instance.
   263 		 * @param {plupload.File}     file     File that was uploaded.
   406 		 * @param {plupload.File}     file     File that was uploaded.
   264 		 * @param {Object}            response Object with response properties.
   407 		 * @param {Object}            response Object with response properties.
   265 		 * @return {mixed}
   408 		 * @return {mixed}
   266 		 */
   409 		 */
   267 		this.uploader.bind( 'FileUploaded', function( up, file, response ) {
   410 		this.uploader.bind( 'FileUploaded', function( up, file, response ) {
   268 			var complete;
       
   269 
   411 
   270 			try {
   412 			try {
   271 				response = JSON.parse( response.response );
   413 				response = JSON.parse( response.response );
   272 			} catch ( e ) {
   414 			} catch ( e ) {
   273 				return error( pluploadL10n.default_error, e, file );
   415 				return error( pluploadL10n.default_error, e, file );
   274 			}
   416 			}
   275 
   417 
   276 			if ( ! _.isObject( response ) || _.isUndefined( response.success ) )
   418 			if ( ! _.isObject( response ) || _.isUndefined( response.success ) ) {
   277 				return error( pluploadL10n.default_error, null, file );
   419 				return error( pluploadL10n.default_error, null, file );
   278 			else if ( ! response.success )
   420 			} else if ( ! response.success ) {
   279 				return error( response.data && response.data.message, response.data, file );
   421 				return error( response.data && response.data.message, response.data, file );
   280 
   422 			}
   281 			_.each(['file','loaded','size','percent'], function( key ) {
   423 
   282 				file.attachment.unset( key );
   424 			// Success. Update the UI with the new attachment.
   283 			});
   425 			fileUploaded( up, file, response );
   284 
       
   285 			file.attachment.set( _.extend( response.data, { uploading: false }) );
       
   286 			wp.media.model.Attachment.get( response.data.id, file.attachment );
       
   287 
       
   288 			complete = Uploader.queue.all( function( attachment ) {
       
   289 				return ! attachment.get('uploading');
       
   290 			});
       
   291 
       
   292 			if ( complete )
       
   293 				Uploader.queue.reset();
       
   294 
       
   295 			self.success( file.attachment );
       
   296 		});
   426 		});
   297 
   427 
   298 		/**
   428 		/**
   299 		 * When plupload surfaces an error, send it to the error handler.
   429 		 * When plupload surfaces an error, send it to the error handler.
   300 		 *
   430 		 *
   301 		 * @param {plupload.Uploader} uploader Uploader instance.
   431 		 * @param {plupload.Uploader} up            Uploader instance.
   302 		 * @param {Object}            error    Contains code, message and sometimes file and other details.
   432 		 * @param {Object}            pluploadError Contains code, message and sometimes file and other details.
   303 		 */
   433 		 */
   304 		this.uploader.bind( 'Error', function( up, pluploadError ) {
   434 		this.uploader.bind( 'Error', function( up, pluploadError ) {
   305 			var message = pluploadL10n.default_error,
   435 			var message = pluploadL10n.default_error,
   306 				key;
   436 				key;
   307 
   437 
   336 		'IMAGE_FORMAT_ERROR':     pluploadL10n.not_an_image,
   466 		'IMAGE_FORMAT_ERROR':     pluploadL10n.not_an_image,
   337 		'IMAGE_MEMORY_ERROR':     pluploadL10n.image_memory_exceeded,
   467 		'IMAGE_MEMORY_ERROR':     pluploadL10n.image_memory_exceeded,
   338 		'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded,
   468 		'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded,
   339 		'GENERIC_ERROR':          pluploadL10n.upload_failed,
   469 		'GENERIC_ERROR':          pluploadL10n.upload_failed,
   340 		'IO_ERROR':               pluploadL10n.io_error,
   470 		'IO_ERROR':               pluploadL10n.io_error,
   341 		'HTTP_ERROR':             pluploadL10n.http_error,
       
   342 		'SECURITY_ERROR':         pluploadL10n.security_error,
   471 		'SECURITY_ERROR':         pluploadL10n.security_error,
   343 
   472 
   344 		'FILE_SIZE_ERROR': function( file ) {
   473 		'FILE_SIZE_ERROR': function( file ) {
   345 			return pluploadL10n.file_exceeds_size_limit.replace('%s', file.name);
   474 			return pluploadL10n.file_exceeds_size_limit.replace( '%s', file.name );
   346 		}
   475 		},
       
   476 
       
   477 		'HTTP_ERROR': function( file ) {
       
   478 			if ( file.type && file.type.indexOf( 'image/' ) === 0 ) {
       
   479 				return pluploadL10n.http_error_image;
       
   480 			}
       
   481 
       
   482 			return pluploadL10n.http_error;
       
   483 		},
   347 	};
   484 	};
   348 
   485 
   349 	$.extend( Uploader.prototype, /** @lends wp.Uploader.prototype */{
   486 	$.extend( Uploader.prototype, /** @lends wp.Uploader.prototype */{
   350 		/**
   487 		/**
   351 		 * Acts as a shortcut to extending the uploader's multipart_params object.
   488 		 * Acts as a shortcut to extending the uploader's multipart_params object.
   394 						break;
   531 						break;
   395 					}
   532 					}
   396 					node = node.parentNode;
   533 					node = node.parentNode;
   397 				}
   534 				}
   398 
   535 
   399 				// If the browser node is not attached to the DOM, use a
   536 				/*
   400 				// temporary container to house it, as the browser button
   537 				 * If the browser node is not attached to the DOM,
   401 				// shims require the button to exist in the DOM at all times.
   538 				 * use a temporary container to house it, as the browser button shims
       
   539 				 * require the button to exist in the DOM at all times.
       
   540 				 */
   402 				if ( ! attached ) {
   541 				if ( ! attached ) {
   403 					id = 'wp-uploader-browser-' + this.uploader.id;
   542 					id = 'wp-uploader-browser-' + this.uploader.id;
   404 
   543 
   405 					container = $( '#' + id );
   544 					container = $( '#' + id );
   406 					if ( ! container.length ) {
   545 					if ( ! container.length ) {