home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / js / plupload / wp-plupload.js < prev    next >
Encoding:
JavaScript  |  2017-09-08  |  12.4 KB  |  426 lines

  1. /* global pluploadL10n, plupload, _wpPluploadSettings */
  2.  
  3. window.wp = window.wp || {};
  4.  
  5. ( function( exports, $ ) {
  6.     var Uploader;
  7.  
  8.     if ( typeof _wpPluploadSettings === 'undefined' ) {
  9.         return;
  10.     }
  11.  
  12.     /**
  13.      * A WordPress uploader.
  14.      *
  15.      * The Plupload library provides cross-browser uploader UI integration.
  16.      * This object bridges the Plupload API to integrate uploads into the
  17.      * WordPress back end and the WordPress media experience.
  18.      *
  19.      * @param {object} options           The options passed to the new plupload instance.
  20.      * @param {object} options.container The id of uploader container.
  21.      * @param {object} options.browser   The id of button to trigger the file select.
  22.      * @param {object} options.dropzone  The id of file drop target.
  23.      * @param {object} options.plupload  An object of parameters to pass to the plupload instance.
  24.      * @param {object} options.params    An object of parameters to pass to $_POST when uploading the file.
  25.      *                                   Extends this.plupload.multipart_params under the hood.
  26.      */
  27.     Uploader = function( options ) {
  28.         var self = this,
  29.             isIE = navigator.userAgent.indexOf('Trident/') != -1 || navigator.userAgent.indexOf('MSIE ') != -1,
  30.             elements = {
  31.                 container: 'container',
  32.                 browser:   'browse_button',
  33.                 dropzone:  'drop_element'
  34.             },
  35.             key, error;
  36.  
  37.         this.supports = {
  38.             upload: Uploader.browser.supported
  39.         };
  40.  
  41.         this.supported = this.supports.upload;
  42.  
  43.         if ( ! this.supported ) {
  44.             return;
  45.         }
  46.  
  47.         // Arguments to send to pluplad.Uploader().
  48.         // Use deep extend to ensure that multipart_params and other objects are cloned.
  49.         this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults );
  50.         this.container = document.body; // Set default container.
  51.  
  52.         // Extend the instance with options.
  53.         //
  54.         // Use deep extend to allow options.plupload to override individual
  55.         // default plupload keys.
  56.         $.extend( true, this, options );
  57.  
  58.         // Proxy all methods so this always refers to the current instance.
  59.         for ( key in this ) {
  60.             if ( $.isFunction( this[ key ] ) ) {
  61.                 this[ key ] = $.proxy( this[ key ], this );
  62.             }
  63.         }
  64.  
  65.         // Ensure all elements are jQuery elements and have id attributes,
  66.         // then set the proper plupload arguments to the ids.
  67.         for ( key in elements ) {
  68.             if ( ! this[ key ] ) {
  69.                 continue;
  70.             }
  71.  
  72.             this[ key ] = $( this[ key ] ).first();
  73.  
  74.             if ( ! this[ key ].length ) {
  75.                 delete this[ key ];
  76.                 continue;
  77.             }
  78.  
  79.             if ( ! this[ key ].prop('id') ) {
  80.                 this[ key ].prop( 'id', '__wp-uploader-id-' + Uploader.uuid++ );
  81.             }
  82.  
  83.             this.plupload[ elements[ key ] ] = this[ key ].prop('id');
  84.         }
  85.  
  86.         // If the uploader has neither a browse button nor a dropzone, bail.
  87.         if ( ! ( this.browser && this.browser.length ) && ! ( this.dropzone && this.dropzone.length ) ) {
  88.             return;
  89.         }
  90.  
  91.         // Make sure flash sends cookies (seems in IE it does without switching to urlstream mode)
  92.         if ( ! isIE && 'flash' === plupload.predictRuntime( this.plupload ) &&
  93.             ( ! this.plupload.required_features || ! this.plupload.required_features.hasOwnProperty( 'send_binary_string' ) ) ) {
  94.  
  95.             this.plupload.required_features = this.plupload.required_features || {};
  96.             this.plupload.required_features.send_binary_string = true;
  97.         }
  98.  
  99.         // Initialize the plupload instance.
  100.         this.uploader = new plupload.Uploader( this.plupload );
  101.         delete this.plupload;
  102.  
  103.         // Set default params and remove this.params alias.
  104.         this.param( this.params || {} );
  105.         delete this.params;
  106.  
  107.         /**
  108.          * Custom error callback.
  109.          *
  110.          * Add a new error to the errors collection, so other modules can track
  111.          * and display errors. @see wp.Uploader.errors.
  112.          *
  113.          * @param  {string}        message
  114.          * @param  {object}        data
  115.          * @param  {plupload.File} file     File that was uploaded.
  116.          */
  117.         error = function( message, data, file ) {
  118.             if ( file.attachment ) {
  119.                 file.attachment.destroy();
  120.             }
  121.  
  122.             Uploader.errors.unshift({
  123.                 message: message || pluploadL10n.default_error,
  124.                 data:    data,
  125.                 file:    file
  126.             });
  127.  
  128.             self.error( message, data, file );
  129.         };
  130.  
  131.         /**
  132.          * After the Uploader has been initialized, initialize some behaviors for the dropzone.
  133.          *
  134.          * @param {plupload.Uploader} uploader Uploader instance.
  135.          */
  136.         this.uploader.bind( 'init', function( uploader ) {
  137.             var timer, active, dragdrop,
  138.                 dropzone = self.dropzone;
  139.  
  140.             dragdrop = self.supports.dragdrop = uploader.features.dragdrop && ! Uploader.browser.mobile;
  141.  
  142.             // Generate drag/drop helper classes.
  143.             if ( ! dropzone ) {
  144.                 return;
  145.             }
  146.  
  147.             dropzone.toggleClass( 'supports-drag-drop', !! dragdrop );
  148.  
  149.             if ( ! dragdrop ) {
  150.                 return dropzone.unbind('.wp-uploader');
  151.             }
  152.  
  153.             // 'dragenter' doesn't fire correctly, simulate it with a limited 'dragover'.
  154.             dropzone.bind( 'dragover.wp-uploader', function() {
  155.                 if ( timer ) {
  156.                     clearTimeout( timer );
  157.                 }
  158.  
  159.                 if ( active ) {
  160.                     return;
  161.                 }
  162.  
  163.                 dropzone.trigger('dropzone:enter').addClass('drag-over');
  164.                 active = true;
  165.             });
  166.  
  167.             dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function() {
  168.                 // Using an instant timer prevents the drag-over class from
  169.                 // being quickly removed and re-added when elements inside the
  170.                 // dropzone are repositioned.
  171.                 //
  172.                 // @see https://core.trac.wordpress.org/ticket/21705
  173.                 timer = setTimeout( function() {
  174.                     active = false;
  175.                     dropzone.trigger('dropzone:leave').removeClass('drag-over');
  176.                 }, 0 );
  177.             });
  178.  
  179.             self.ready = true;
  180.             $(self).trigger( 'uploader:ready' );
  181.         });
  182.  
  183.         this.uploader.bind( 'postinit', function( up ) {
  184.             up.refresh();
  185.             self.init();
  186.         });
  187.  
  188.         this.uploader.init();
  189.  
  190.         if ( this.browser ) {
  191.             this.browser.on( 'mouseenter', this.refresh );
  192.         } else {
  193.             this.uploader.disableBrowse( true );
  194.             // If HTML5 mode, hide the auto-created file container.
  195.             $('#' + this.uploader.id + '_html5_container').hide();
  196.         }
  197.  
  198.         /**
  199.          * After files were filtered and added to the queue, create a model for each.
  200.          *
  201.          * @param {plupload.Uploader} uploader Uploader instance.
  202.          * @param {Array}             files    Array of file objects that were added to queue by the user.
  203.          */
  204.         this.uploader.bind( 'FilesAdded', function( up, files ) {
  205.             _.each( files, function( file ) {
  206.                 var attributes, image;
  207.  
  208.                 // Ignore failed uploads.
  209.                 if ( plupload.FAILED === file.status ) {
  210.                     return;
  211.                 }
  212.  
  213.                 // Generate attributes for a new `Attachment` model.
  214.                 attributes = _.extend({
  215.                     file:      file,
  216.                     uploading: true,
  217.                     date:      new Date(),
  218.                     filename:  file.name,
  219.                     menuOrder: 0,
  220.                     uploadedTo: wp.media.model.settings.post.id
  221.                 }, _.pick( file, 'loaded', 'size', 'percent' ) );
  222.  
  223.                 // Handle early mime type scanning for images.
  224.                 image = /(?:jpe?g|png|gif)$/i.exec( file.name );
  225.  
  226.                 // For images set the model's type and subtype attributes.
  227.                 if ( image ) {
  228.                     attributes.type = 'image';
  229.  
  230.                     // `jpeg`, `png` and `gif` are valid subtypes.
  231.                     // `jpg` is not, so map it to `jpeg`.
  232.                     attributes.subtype = ( 'jpg' === image[0] ) ? 'jpeg' : image[0];
  233.                 }
  234.  
  235.                 // Create a model for the attachment, and add it to the Upload queue collection
  236.                 // so listeners to the upload queue can track and display upload progress.
  237.                 file.attachment = wp.media.model.Attachment.create( attributes );
  238.                 Uploader.queue.add( file.attachment );
  239.  
  240.                 self.added( file.attachment );
  241.             });
  242.  
  243.             up.refresh();
  244.             up.start();
  245.         });
  246.  
  247.         this.uploader.bind( 'UploadProgress', function( up, file ) {
  248.             file.attachment.set( _.pick( file, 'loaded', 'percent' ) );
  249.             self.progress( file.attachment );
  250.         });
  251.  
  252.         /**
  253.          * After a file is successfully uploaded, update its model.
  254.          *
  255.          * @param {plupload.Uploader} uploader Uploader instance.
  256.          * @param {plupload.File}     file     File that was uploaded.
  257.          * @param {Object}            response Object with response properties.
  258.          * @return {mixed}
  259.          */
  260.         this.uploader.bind( 'FileUploaded', function( up, file, response ) {
  261.             var complete;
  262.  
  263.             try {
  264.                 response = JSON.parse( response.response );
  265.             } catch ( e ) {
  266.                 return error( pluploadL10n.default_error, e, file );
  267.             }
  268.  
  269.             if ( ! _.isObject( response ) || _.isUndefined( response.success ) )
  270.                 return error( pluploadL10n.default_error, null, file );
  271.             else if ( ! response.success )
  272.                 return error( response.data && response.data.message, response.data, file );
  273.  
  274.             _.each(['file','loaded','size','percent'], function( key ) {
  275.                 file.attachment.unset( key );
  276.             });
  277.  
  278.             file.attachment.set( _.extend( response.data, { uploading: false }) );
  279.             wp.media.model.Attachment.get( response.data.id, file.attachment );
  280.  
  281.             complete = Uploader.queue.all( function( attachment ) {
  282.                 return ! attachment.get('uploading');
  283.             });
  284.  
  285.             if ( complete )
  286.                 Uploader.queue.reset();
  287.  
  288.             self.success( file.attachment );
  289.         });
  290.  
  291.         /**
  292.          * When plupload surfaces an error, send it to the error handler.
  293.          *
  294.          * @param {plupload.Uploader} uploader Uploader instance.
  295.          * @param {Object}            error    Contains code, message and sometimes file and other details.
  296.          */
  297.         this.uploader.bind( 'Error', function( up, pluploadError ) {
  298.             var message = pluploadL10n.default_error,
  299.                 key;
  300.  
  301.             // Check for plupload errors.
  302.             for ( key in Uploader.errorMap ) {
  303.                 if ( pluploadError.code === plupload[ key ] ) {
  304.                     message = Uploader.errorMap[ key ];
  305.  
  306.                     if ( _.isFunction( message ) ) {
  307.                         message = message( pluploadError.file, pluploadError );
  308.                     }
  309.  
  310.                     break;
  311.                 }
  312.             }
  313.  
  314.             error( message, pluploadError, pluploadError.file );
  315.             up.refresh();
  316.         });
  317.  
  318.     };
  319.  
  320.     // Adds the 'defaults' and 'browser' properties.
  321.     $.extend( Uploader, _wpPluploadSettings );
  322.  
  323.     Uploader.uuid = 0;
  324.  
  325.     // Map Plupload error codes to user friendly error messages.
  326.     Uploader.errorMap = {
  327.         'FAILED':                 pluploadL10n.upload_failed,
  328.         'FILE_EXTENSION_ERROR':   pluploadL10n.invalid_filetype,
  329.         'IMAGE_FORMAT_ERROR':     pluploadL10n.not_an_image,
  330.         'IMAGE_MEMORY_ERROR':     pluploadL10n.image_memory_exceeded,
  331.         'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded,
  332.         'GENERIC_ERROR':          pluploadL10n.upload_failed,
  333.         'IO_ERROR':               pluploadL10n.io_error,
  334.         'HTTP_ERROR':             pluploadL10n.http_error,
  335.         'SECURITY_ERROR':         pluploadL10n.security_error,
  336.  
  337.         'FILE_SIZE_ERROR': function( file ) {
  338.             return pluploadL10n.file_exceeds_size_limit.replace('%s', file.name);
  339.         }
  340.     };
  341.  
  342.     $.extend( Uploader.prototype, /** @lends wp.Uploader.prototype */{
  343.         /**
  344.          * Acts as a shortcut to extending the uploader's multipart_params object.
  345.          *
  346.          * param( key )
  347.          *    Returns the value of the key.
  348.          *
  349.          * param( key, value )
  350.          *    Sets the value of a key.
  351.          *
  352.          * param( map )
  353.          *    Sets values for a map of data.
  354.          */
  355.         param: function( key, value ) {
  356.             if ( arguments.length === 1 && typeof key === 'string' ) {
  357.                 return this.uploader.settings.multipart_params[ key ];
  358.             }
  359.  
  360.             if ( arguments.length > 1 ) {
  361.                 this.uploader.settings.multipart_params[ key ] = value;
  362.             } else {
  363.                 $.extend( this.uploader.settings.multipart_params, key );
  364.             }
  365.         },
  366.  
  367.         /**
  368.          * Make a few internal event callbacks available on the wp.Uploader object
  369.          * to change the Uploader internals if absolutely necessary.
  370.          */
  371.         init:     function() {},
  372.         error:    function() {},
  373.         success:  function() {},
  374.         added:    function() {},
  375.         progress: function() {},
  376.         complete: function() {},
  377.         refresh:  function() {
  378.             var node, attached, container, id;
  379.  
  380.             if ( this.browser ) {
  381.                 node = this.browser[0];
  382.  
  383.                 // Check if the browser node is in the DOM.
  384.                 while ( node ) {
  385.                     if ( node === document.body ) {
  386.                         attached = true;
  387.                         break;
  388.                     }
  389.                     node = node.parentNode;
  390.                 }
  391.  
  392.                 // If the browser node is not attached to the DOM, use a
  393.                 // temporary container to house it, as the browser button
  394.                 // shims require the button to exist in the DOM at all times.
  395.                 if ( ! attached ) {
  396.                     id = 'wp-uploader-browser-' + this.uploader.id;
  397.  
  398.                     container = $( '#' + id );
  399.                     if ( ! container.length ) {
  400.                         container = $('<div class="wp-uploader-browser" />').css({
  401.                             position: 'fixed',
  402.                             top: '-1000px',
  403.                             left: '-1000px',
  404.                             height: 0,
  405.                             width: 0
  406.                         }).attr( 'id', 'wp-uploader-browser-' + this.uploader.id ).appendTo('body');
  407.                     }
  408.  
  409.                     container.append( this.browser );
  410.                 }
  411.             }
  412.  
  413.             this.uploader.refresh();
  414.         }
  415.     });
  416.  
  417.     // Create a collection of attachments in the upload queue,
  418.     // so that other modules can track and display upload progress.
  419.     Uploader.queue = new wp.media.model.Attachments( [], { query: false });
  420.  
  421.     // Create a collection to collect errors incurred while attempting upload.
  422.     Uploader.errors = new Backbone.Collection();
  423.  
  424.     exports.Uploader = Uploader;
  425. })( wp, jQuery );
  426.