wp/wp-includes/js/plupload/wp-plupload.js
changeset 0 d970ebf37754
child 5 5e2f62d02dcd
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     1 window.wp = window.wp || {};
       
     2 
       
     3 (function( exports, $ ) {
       
     4 	var Uploader;
       
     5 
       
     6 	if ( typeof _wpPluploadSettings === 'undefined' )
       
     7 		return;
       
     8 
       
     9 	/*
       
    10 	 * An object that helps create a WordPress uploader using plupload.
       
    11 	 *
       
    12 	 * @param options - object - The options passed to the new plupload instance.
       
    13 	 *    Accepts the following parameters:
       
    14 	 *    - container - The id of uploader container.
       
    15 	 *    - browser   - The id of button to trigger the file select.
       
    16 	 *    - dropzone  - The id of file drop target.
       
    17 	 *    - plupload  - An object of parameters to pass to the plupload instance.
       
    18 	 *    - params    - An object of parameters to pass to $_POST when uploading the file.
       
    19 	 *                  Extends this.plupload.multipart_params under the hood.
       
    20 	 *
       
    21 	 * @param attributes - object - Attributes and methods for this specific instance.
       
    22 	 */
       
    23 	Uploader = function( options ) {
       
    24 		var self = this,
       
    25 			elements = {
       
    26 				container: 'container',
       
    27 				browser:   'browse_button',
       
    28 				dropzone:  'drop_element'
       
    29 			},
       
    30 			key, error;
       
    31 
       
    32 		this.supports = {
       
    33 			upload: Uploader.browser.supported
       
    34 		};
       
    35 
       
    36 		this.supported = this.supports.upload;
       
    37 
       
    38 		if ( ! this.supported )
       
    39 			return;
       
    40 
       
    41 		// Use deep extend to ensure that multipart_params and other objects are cloned.
       
    42 		this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults );
       
    43 		this.container = document.body; // Set default container.
       
    44 
       
    45 		// Extend the instance with options
       
    46 		//
       
    47 		// Use deep extend to allow options.plupload to override individual
       
    48 		// default plupload keys.
       
    49 		$.extend( true, this, options );
       
    50 
       
    51 		// Proxy all methods so this always refers to the current instance.
       
    52 		for ( key in this ) {
       
    53 			if ( $.isFunction( this[ key ] ) )
       
    54 				this[ key ] = $.proxy( this[ key ], this );
       
    55 		}
       
    56 
       
    57 		// Ensure all elements are jQuery elements and have id attributes
       
    58 		// Then set the proper plupload arguments to the ids.
       
    59 		for ( key in elements ) {
       
    60 			if ( ! this[ key ] )
       
    61 				continue;
       
    62 
       
    63 			this[ key ] = $( this[ key ] ).first();
       
    64 
       
    65 			if ( ! this[ key ].length ) {
       
    66 				delete this[ key ];
       
    67 				continue;
       
    68 			}
       
    69 
       
    70 			if ( ! this[ key ].prop('id') )
       
    71 				this[ key ].prop( 'id', '__wp-uploader-id-' + Uploader.uuid++ );
       
    72 			this.plupload[ elements[ key ] ] = this[ key ].prop('id');
       
    73 		}
       
    74 
       
    75 		// If the uploader has neither a browse button nor a dropzone, bail.
       
    76 		if ( ! ( this.browser && this.browser.length ) && ! ( this.dropzone && this.dropzone.length ) )
       
    77 			return;
       
    78 
       
    79 		this.uploader = new plupload.Uploader( this.plupload );
       
    80 		delete this.plupload;
       
    81 
       
    82 		// Set default params and remove this.params alias.
       
    83 		this.param( this.params || {} );
       
    84 		delete this.params;
       
    85 
       
    86 		error = function( message, data, file ) {
       
    87 			if ( file.attachment )
       
    88 				file.attachment.destroy();
       
    89 
       
    90 			Uploader.errors.unshift({
       
    91 				message: message || pluploadL10n.default_error,
       
    92 				data:    data,
       
    93 				file:    file
       
    94 			});
       
    95 
       
    96 			self.error( message, data, file );
       
    97 		};
       
    98 
       
    99 		this.uploader.init();
       
   100 
       
   101 		this.supports.dragdrop = this.uploader.features.dragdrop && ! Uploader.browser.mobile;
       
   102 
       
   103 		// Generate drag/drop helper classes.
       
   104 		(function( dropzone, supported ) {
       
   105 			var timer, active;
       
   106 
       
   107 			if ( ! dropzone )
       
   108 				return;
       
   109 
       
   110 			dropzone.toggleClass( 'supports-drag-drop', !! supported );
       
   111 
       
   112 			if ( ! supported )
       
   113 				return dropzone.unbind('.wp-uploader');
       
   114 
       
   115 			// 'dragenter' doesn't fire correctly,
       
   116 			// simulate it with a limited 'dragover'
       
   117 			dropzone.bind( 'dragover.wp-uploader', function(){
       
   118 				if ( timer )
       
   119 					clearTimeout( timer );
       
   120 
       
   121 				if ( active )
       
   122 					return;
       
   123 
       
   124 				dropzone.trigger('dropzone:enter').addClass('drag-over');
       
   125 				active = true;
       
   126 			});
       
   127 
       
   128 			dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function(){
       
   129 				// Using an instant timer prevents the drag-over class from
       
   130 				// being quickly removed and re-added when elements inside the
       
   131 				// dropzone are repositioned.
       
   132 				//
       
   133 				// See http://core.trac.wordpress.org/ticket/21705
       
   134 				timer = setTimeout( function() {
       
   135 					active = false;
       
   136 					dropzone.trigger('dropzone:leave').removeClass('drag-over');
       
   137 				}, 0 );
       
   138 			});
       
   139 		}( this.dropzone, this.supports.dragdrop ));
       
   140 
       
   141 		if ( this.browser ) {
       
   142 			this.browser.on( 'mouseenter', this.refresh );
       
   143 		} else {
       
   144 			this.uploader.disableBrowse( true );
       
   145 			// If HTML5 mode, hide the auto-created file container.
       
   146 			$('#' + this.uploader.id + '_html5_container').hide();
       
   147 		}
       
   148 
       
   149 		this.uploader.bind( 'FilesAdded', function( up, files ) {
       
   150 			_.each( files, function( file ) {
       
   151 				var attributes, image;
       
   152 
       
   153 				// Ignore failed uploads.
       
   154 				if ( plupload.FAILED === file.status )
       
   155 					return;
       
   156 
       
   157 				// Generate attributes for a new `Attachment` model.
       
   158 				attributes = _.extend({
       
   159 					file:      file,
       
   160 					uploading: true,
       
   161 					date:      new Date(),
       
   162 					filename:  file.name,
       
   163 					menuOrder: 0,
       
   164 					uploadedTo: wp.media.model.settings.post.id
       
   165 				}, _.pick( file, 'loaded', 'size', 'percent' ) );
       
   166 
       
   167 				// Handle early mime type scanning for images.
       
   168 				image = /(?:jpe?g|png|gif)$/i.exec( file.name );
       
   169 
       
   170 				// Did we find an image?
       
   171 				if ( image ) {
       
   172 					attributes.type = 'image';
       
   173 
       
   174 					// `jpeg`, `png` and `gif` are valid subtypes.
       
   175 					// `jpg` is not, so map it to `jpeg`.
       
   176 					attributes.subtype = ( 'jpg' === image[0] ) ? 'jpeg' : image[0];
       
   177 				}
       
   178 
       
   179 				// Create the `Attachment`.
       
   180 				file.attachment = wp.media.model.Attachment.create( attributes );
       
   181 
       
   182 				Uploader.queue.add( file.attachment );
       
   183 
       
   184 				self.added( file.attachment );
       
   185 			});
       
   186 
       
   187 			up.refresh();
       
   188 			up.start();
       
   189 		});
       
   190 
       
   191 		this.uploader.bind( 'UploadProgress', function( up, file ) {
       
   192 			file.attachment.set( _.pick( file, 'loaded', 'percent' ) );
       
   193 			self.progress( file.attachment );
       
   194 		});
       
   195 
       
   196 		this.uploader.bind( 'FileUploaded', function( up, file, response ) {
       
   197 			var complete;
       
   198 
       
   199 			try {
       
   200 				response = JSON.parse( response.response );
       
   201 			} catch ( e ) {
       
   202 				return error( pluploadL10n.default_error, e, file );
       
   203 			}
       
   204 
       
   205 			if ( ! _.isObject( response ) || _.isUndefined( response.success ) )
       
   206 				return error( pluploadL10n.default_error, null, file );
       
   207 			else if ( ! response.success )
       
   208 				return error( response.data && response.data.message, response.data, file );
       
   209 
       
   210 			_.each(['file','loaded','size','percent'], function( key ) {
       
   211 				file.attachment.unset( key );
       
   212 			});
       
   213 
       
   214 			file.attachment.set( _.extend( response.data, { uploading: false }) );
       
   215 			wp.media.model.Attachment.get( response.data.id, file.attachment );
       
   216 
       
   217 			complete = Uploader.queue.all( function( attachment ) {
       
   218 				return ! attachment.get('uploading');
       
   219 			});
       
   220 
       
   221 			if ( complete )
       
   222 				Uploader.queue.reset();
       
   223 
       
   224 			self.success( file.attachment );
       
   225 		});
       
   226 
       
   227 		this.uploader.bind( 'Error', function( up, pluploadError ) {
       
   228 			var message = pluploadL10n.default_error,
       
   229 				key;
       
   230 
       
   231 			// Check for plupload errors.
       
   232 			for ( key in Uploader.errorMap ) {
       
   233 				if ( pluploadError.code === plupload[ key ] ) {
       
   234 					message = Uploader.errorMap[ key ];
       
   235 					if ( _.isFunction( message ) )
       
   236 						message = message( pluploadError.file, pluploadError );
       
   237 					break;
       
   238 				}
       
   239 			}
       
   240 
       
   241 			error( message, pluploadError, pluploadError.file );
       
   242 			up.refresh();
       
   243 		});
       
   244 
       
   245 		this.init();
       
   246 	};
       
   247 
       
   248 	// Adds the 'defaults' and 'browser' properties.
       
   249 	$.extend( Uploader, _wpPluploadSettings );
       
   250 
       
   251 	Uploader.uuid = 0;
       
   252 
       
   253 	Uploader.errorMap = {
       
   254 		'FAILED':                 pluploadL10n.upload_failed,
       
   255 		'FILE_EXTENSION_ERROR':   pluploadL10n.invalid_filetype,
       
   256 		'IMAGE_FORMAT_ERROR':     pluploadL10n.not_an_image,
       
   257 		'IMAGE_MEMORY_ERROR':     pluploadL10n.image_memory_exceeded,
       
   258 		'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded,
       
   259 		'GENERIC_ERROR':          pluploadL10n.upload_failed,
       
   260 		'IO_ERROR':               pluploadL10n.io_error,
       
   261 		'HTTP_ERROR':             pluploadL10n.http_error,
       
   262 		'SECURITY_ERROR':         pluploadL10n.security_error,
       
   263 
       
   264 		'FILE_SIZE_ERROR': function( file ) {
       
   265 			return pluploadL10n.file_exceeds_size_limit.replace('%s', file.name);
       
   266 		}
       
   267 	};
       
   268 
       
   269 	$.extend( Uploader.prototype, {
       
   270 		/**
       
   271 		 * Acts as a shortcut to extending the uploader's multipart_params object.
       
   272 		 *
       
   273 		 * param( key )
       
   274 		 *    Returns the value of the key.
       
   275 		 *
       
   276 		 * param( key, value )
       
   277 		 *    Sets the value of a key.
       
   278 		 *
       
   279 		 * param( map )
       
   280 		 *    Sets values for a map of data.
       
   281 		 */
       
   282 		param: function( key, value ) {
       
   283 			if ( arguments.length === 1 && typeof key === 'string' )
       
   284 				return this.uploader.settings.multipart_params[ key ];
       
   285 
       
   286 			if ( arguments.length > 1 ) {
       
   287 				this.uploader.settings.multipart_params[ key ] = value;
       
   288 			} else {
       
   289 				$.extend( this.uploader.settings.multipart_params, key );
       
   290 			}
       
   291 		},
       
   292 
       
   293 		init:     function() {},
       
   294 		error:    function() {},
       
   295 		success:  function() {},
       
   296 		added:    function() {},
       
   297 		progress: function() {},
       
   298 		complete: function() {},
       
   299 		refresh:  function() {
       
   300 			var node, attached, container, id;
       
   301 
       
   302 			if ( this.browser ) {
       
   303 				node = this.browser[0];
       
   304 
       
   305 				// Check if the browser node is in the DOM.
       
   306 				while ( node ) {
       
   307 					if ( node === document.body ) {
       
   308 						attached = true;
       
   309 						break;
       
   310 					}
       
   311 					node = node.parentNode;
       
   312 				}
       
   313 
       
   314 				// If the browser node is not attached to the DOM, use a
       
   315 				// temporary container to house it, as the browser button
       
   316 				// shims require the button to exist in the DOM at all times.
       
   317 				if ( ! attached ) {
       
   318 					id = 'wp-uploader-browser-' + this.uploader.id;
       
   319 
       
   320 					container = $( '#' + id );
       
   321 					if ( ! container.length ) {
       
   322 						container = $('<div class="wp-uploader-browser" />').css({
       
   323 							position: 'fixed',
       
   324 							top: '-1000px',
       
   325 							left: '-1000px',
       
   326 							height: 0,
       
   327 							width: 0
       
   328 						}).attr( 'id', 'wp-uploader-browser-' + this.uploader.id ).appendTo('body');
       
   329 					}
       
   330 
       
   331 					container.append( this.browser );
       
   332 				}
       
   333 			}
       
   334 
       
   335 			this.uploader.refresh();
       
   336 		}
       
   337 	});
       
   338 
       
   339 	Uploader.queue = new wp.media.model.Attachments( [], { query: false });
       
   340 	Uploader.errors = new Backbone.Collection();
       
   341 
       
   342 	exports.Uploader = Uploader;
       
   343 })( wp, jQuery );