WordPress/wp-includes/js/plupload/wp-plupload.js
Daryl Koopersmith 53ccb09bce Give media a heart transplant.
Revises the concept of the media controller and workspace views (i.e. two central points of control) to be more granular. The main media object is now the `Frame`, which is a hybrid view and state machine.

The state machine is a collection of states, which are just generic instances of `Backbone.Model`. This circumvents the problem of juggling global parameters when changing states. Each state contains its own event loop. All events are also forwarded to the frame itself (as is the case in all model/collection relationships).

The frame view contains several regions, each of which can be overridden without harming or re-rendering the other regions. These work well when used in conjunction with the state machine events.

This removes the upload sidebar (don't worry, visible upload UI will return). Drag and drop uploading still works. The ability to upload has been abstracted into its own view (instead of being attached to the central workspace view).

Editing galleries is temporarily broken — the gallery creation and editing experiences will be unified in a future patch.

Adds events to detect dragging changes in `wp.Uploader` and adds methods to detect and leverage browser support for CSS3 transitions.

see #21390, #21809.


git-svn-id: http://core.svn.wordpress.org/trunk@22320 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2012-10-28 23:29:17 +00:00

289 lines
8.0 KiB
JavaScript

window.wp = window.wp || {};
(function( exports, $ ) {
var Uploader;
if ( typeof _wpPluploadSettings === 'undefined' )
return;
/*
* An object that helps create a WordPress uploader using plupload.
*
* @param options - object - The options passed to the new plupload instance.
* Accepts the following parameters:
* - container - The id of uploader container.
* - browser - The id of button to trigger the file select.
* - dropzone - The id of file drop target.
* - plupload - An object of parameters to pass to the plupload instance.
* - params - An object of parameters to pass to $_POST when uploading the file.
* Extends this.plupload.multipart_params under the hood.
*
* @param attributes - object - Attributes and methods for this specific instance.
*/
Uploader = function( options ) {
var self = this,
elements = {
container: 'container',
browser: 'browse_button',
dropzone: 'drop_element'
},
key, error;
this.supports = {
upload: Uploader.browser.supported
};
this.supported = this.supports.upload;
if ( ! this.supported )
return;
// Use deep extend to ensure that multipart_params and other objects are cloned.
this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults );
this.container = document.body; // Set default container.
// Extend the instance with options
//
// Use deep extend to allow options.plupload to override individual
// default plupload keys.
$.extend( true, this, options );
// Proxy all methods so this always refers to the current instance.
for ( key in this ) {
if ( $.isFunction( this[ key ] ) )
this[ key ] = $.proxy( this[ key ], this );
}
// Ensure all elements are jQuery elements and have id attributes
// Then set the proper plupload arguments to the ids.
for ( key in elements ) {
if ( ! this[ key ] )
continue;
this[ key ] = $( this[ key ] ).first();
if ( ! this[ key ].length ) {
delete this[ key ];
continue;
}
if ( ! this[ key ].prop('id') )
this[ key ].prop( 'id', '__wp-uploader-id-' + Uploader.uuid++ );
this.plupload[ elements[ key ] ] = this[ key ].prop('id');
}
// If the uploader has neither a browse button nor a dropzone, bail.
if ( ! ( this.browser && this.browser.length ) && ! ( this.dropzone && this.dropzone.length ) )
return;
this.uploader = new plupload.Uploader( this.plupload );
delete this.plupload;
// Set default params and remove this.params alias.
this.param( this.params || {} );
delete this.params;
error = function( message, data, file ) {
if ( file.attachment )
file.attachment.destroy();
self.error( message, data, file );
};
this.uploader.init();
this.supports.dragdrop = this.uploader.features.dragdrop && ! Uploader.browser.mobile;
// Generate drag/drop helper classes.
(function( dropzone, supported ) {
var timer, active;
if ( ! dropzone )
return;
dropzone.toggleClass( 'supports-drag-drop', !! supported );
if ( ! supported )
return dropzone.unbind('.wp-uploader');
// 'dragenter' doesn't fire correctly,
// simulate it with a limited 'dragover'
dropzone.bind( 'dragover.wp-uploader', function(){
if ( timer )
clearTimeout( timer );
if ( active )
return;
dropzone.trigger('dropzone:enter').addClass('drag-over');
active = true;
});
dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function(){
// Using an instant timer prevents the drag-over class from
// being quickly removed and re-added when elements inside the
// dropzone are repositioned.
//
// See http://core.trac.wordpress.org/ticket/21705
timer = setTimeout( function() {
active = false;
dropzone.trigger('dropzone:leave').removeClass('drag-over');
}, 0 );
});
}( this.dropzone, this.supports.dragdrop ));
if ( this.browser ) {
this.browser.on( 'mouseenter', this.refresh );
} else {
this.uploader.disableBrowse( true );
// If HTML5 mode, hide the auto-created file container.
$('#' + this.uploader.id + '_html5_container').hide();
}
this.uploader.bind( 'FilesAdded', function( up, files ) {
_.each( files, function( file ) {
var attributes, image;
// Generate attributes for a new `Attachment` model.
attributes = _.extend({
file: file,
uploading: true,
date: new Date()
}, _.pick( file, 'loaded', 'size', 'percent' ) );
// Handle early mime type scanning for images.
image = /(?:jpe?g|png|gif)$/i.exec( file.name );
// Did we find an image?
if ( image ) {
attributes.type = 'image';
// `jpeg`, `png` and `gif` are valid subtypes.
// `jpg` is not, so map it to `jpeg`.
attributes.subtype = ( 'jpg' === image[0] ) ? 'jpeg' : image[0];
}
// Create the `Attachment`.
file.attachment = wp.media.model.Attachment.create( attributes );
Uploader.queue.add( file.attachment );
self.added( file.attachment );
});
up.refresh();
up.start();
});
this.uploader.bind( 'UploadProgress', function( up, file ) {
file.attachment.set( _.pick( file, 'loaded', 'percent' ) );
self.progress( file.attachment );
});
this.uploader.bind( 'FileUploaded', function( up, file, response ) {
var complete;
try {
response = JSON.parse( response.response );
} catch ( e ) {
return error( pluploadL10n.default_error, e, file );
}
if ( ! _.isObject( response ) || _.isUndefined( response.success ) )
return error( pluploadL10n.default_error, null, file );
else if ( ! response.success )
return error( response.data.message, response.data, file );
_.each(['file','loaded','size','uploading','percent'], function( key ) {
file.attachment.unset( key );
});
file.attachment.set( response.data );
wp.media.model.Attachment.get( response.data.id, file.attachment );
complete = Uploader.queue.all( function( attachment ) {
return ! attachment.get('uploading');
});
if ( complete )
Uploader.queue.reset();
self.success( file.attachment );
});
this.uploader.bind( 'Error', function( up, error ) {
var message = pluploadL10n.default_error,
key;
// Check for plupload errors.
for ( key in Uploader.errorMap ) {
if ( error.code === plupload[ key ] ) {
message = Uploader.errorMap[ key ];
break;
}
}
error( message, error, error.file );
up.refresh();
});
this.init();
};
// Adds the 'defaults' and 'browser' properties.
$.extend( Uploader, _wpPluploadSettings );
Uploader.uuid = 0;
Uploader.errorMap = {
'FAILED': pluploadL10n.upload_failed,
'FILE_EXTENSION_ERROR': pluploadL10n.invalid_filetype,
// 'FILE_SIZE_ERROR': '',
'IMAGE_FORMAT_ERROR': pluploadL10n.not_an_image,
'IMAGE_MEMORY_ERROR': pluploadL10n.image_memory_exceeded,
'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded,
'GENERIC_ERROR': pluploadL10n.upload_failed,
'IO_ERROR': pluploadL10n.io_error,
'HTTP_ERROR': pluploadL10n.http_error,
'SECURITY_ERROR': pluploadL10n.security_error
};
$.extend( Uploader.prototype, {
/**
* Acts as a shortcut to extending the uploader's multipart_params object.
*
* param( key )
* Returns the value of the key.
*
* param( key, value )
* Sets the value of a key.
*
* param( map )
* Sets values for a map of data.
*/
param: function( key, value ) {
if ( arguments.length === 1 && typeof key === 'string' )
return this.uploader.settings.multipart_params[ key ];
if ( arguments.length > 1 ) {
this.uploader.settings.multipart_params[ key ] = value;
} else {
$.extend( this.uploader.settings.multipart_params, key );
}
},
init: function() {},
error: function() {},
success: function() {},
added: function() {},
progress: function() {},
complete: function() {},
refresh: function() {
this.uploader.refresh();
}
});
Uploader.queue = new wp.media.model.Attachments( [], { query: false });
exports.Uploader = Uploader;
})( wp, jQuery );