[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/plupload/ -> wp-plupload.js (source)

   1  /* global pluploadL10n, plupload, _wpPluploadSettings */
   2  
   3  /**
   4   * @namespace wp
   5   */
   6  window.wp = window.wp || {};
   7  
   8  ( function( exports, $ ) {
   9      var Uploader;
  10  
  11      if ( typeof _wpPluploadSettings === 'undefined' ) {
  12          return;
  13      }
  14  
  15      /**
  16       * A WordPress uploader.
  17       *
  18       * The Plupload library provides cross-browser uploader UI integration.
  19       * This object bridges the Plupload API to integrate uploads into the
  20       * WordPress back end and the WordPress media experience.
  21       *
  22       * @class
  23       * @memberOf wp
  24       * @alias wp.Uploader
  25       *
  26       * @param {object} options           The options passed to the new plupload instance.
  27       * @param {object} options.container The id of uploader container.
  28       * @param {object} options.browser   The id of button to trigger the file select.
  29       * @param {object} options.dropzone  The id of file drop target.
  30       * @param {object} options.plupload  An object of parameters to pass to the plupload instance.
  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.
  33       */
  34      Uploader = function( options ) {
  35          var self = this,
  36              isIE, // Not used, back-compat.
  37              elements = {
  38                  container: 'container',
  39                  browser:   'browse_button',
  40                  dropzone:  'drop_element'
  41              },
  42              tryAgainCount = {},
  43              tryAgain,
  44              key,
  45              error,
  46              fileUploaded;
  47  
  48          this.supports = {
  49              upload: Uploader.browser.supported
  50          };
  51  
  52          this.supported = this.supports.upload;
  53  
  54          if ( ! this.supported ) {
  55              return;
  56          }
  57  
  58          // Arguments to send to pluplad.Uploader().
  59          // Use deep extend to ensure that multipart_params and other objects are cloned.
  60          this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults );
  61          this.container = document.body; // Set default container.
  62  
  63          /*
  64           * Extend the instance with options.
  65           *
  66           * Use deep extend to allow options.plupload to override individual
  67           * default plupload keys.
  68           */
  69          $.extend( true, this, options );
  70  
  71          // Proxy all methods so this always refers to the current instance.
  72          for ( key in this ) {
  73              if ( $.isFunction( this[ key ] ) ) {
  74                  this[ key ] = $.proxy( this[ key ], this );
  75              }
  76          }
  77  
  78          // Ensure all elements are jQuery elements and have id attributes,
  79          // then set the proper plupload arguments to the ids.
  80          for ( key in elements ) {
  81              if ( ! this[ key ] ) {
  82                  continue;
  83              }
  84  
  85              this[ key ] = $( this[ key ] ).first();
  86  
  87              if ( ! this[ key ].length ) {
  88                  delete this[ key ];
  89                  continue;
  90              }
  91  
  92              if ( ! this[ key ].prop('id') ) {
  93                  this[ key ].prop( 'id', '__wp-uploader-id-' + Uploader.uuid++ );
  94              }
  95  
  96              this.plupload[ elements[ key ] ] = this[ key ].prop('id');
  97          }
  98  
  99          // If the uploader has neither a browse button nor a dropzone, bail.
 100          if ( ! ( this.browser && this.browser.length ) && ! ( this.dropzone && this.dropzone.length ) ) {
 101              return;
 102          }
 103  
 104          // Initialize the plupload instance.
 105          this.uploader = new plupload.Uploader( this.plupload );
 106          delete this.plupload;
 107  
 108          // Set default params and remove this.params alias.
 109          this.param( this.params || {} );
 110          delete this.params;
 111  
 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;
 124              var id;
 125  
 126              if ( ! data || ! data.responseHeaders ) {
 127                  error( pluploadL10n.http_error_image, data, file, 'no-retry' );
 128                  return;
 129              }
 130  
 131              id = data.responseHeaders.match( /x-wp-upload-attachment-id:\s*(\d+)/i );
 132  
 133              if ( id && id[1] ) {
 134                  id = id[1];
 135              } else {
 136                  error( pluploadL10n.http_error_image, data, file, 'no-retry' );
 137                  return;
 138              }
 139  
 140              times = tryAgainCount[ file.id ];
 141  
 142              if ( times && times > 4 ) {
 143                  /*
 144                   * The file may have been uploaded and attachment post created,
 145                   * but post-processing and resizing failed...
 146                   * Do a cleanup then tell the user to scale down the image and upload it again.
 147                   */
 148                  $.ajax({
 149                      type: 'post',
 150                      url: ajaxurl,
 151                      dataType: 'json',
 152                      data: {
 153                          action: 'media-create-image-subsizes',
 154                          _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce,
 155                          attachment_id: id,
 156                          _wp_upload_failed_cleanup: true,
 157                      }
 158                  });
 159  
 160                  error( message, data, file, 'no-retry' );
 161                  return;
 162              }
 163  
 164              if ( ! times ) {
 165                  tryAgainCount[ file.id ] = 1;
 166              } else {
 167                  tryAgainCount[ file.id ] = ++times;
 168              }
 169  
 170              // Another request to try to create the missing image sub-sizes.
 171              $.ajax({
 172                  type: 'post',
 173                  url: ajaxurl,
 174                  dataType: 'json',
 175                  data: {
 176                      action: 'media-create-image-subsizes',
 177                      _wpnonce: _wpPluploadSettings.defaults.multipart_params._wpnonce,
 178                      attachment_id: id,
 179                  }
 180              }).done( function( response ) {
 181                  if ( response.success ) {
 182                      fileUploaded( self.uploader, file, response );
 183                  } else {
 184                      if ( response.data && response.data.message ) {
 185                          message = response.data.message;
 186                      }
 187  
 188                      error( message, data, file, 'no-retry' );
 189                  }
 190              }).fail( function( jqXHR ) {
 191                  // If another HTTP 5xx error, try try again...
 192                  if ( jqXHR.status >= 500 && jqXHR.status < 600 ) {
 193                      tryAgain( message, data, file );
 194                      return;
 195                  }
 196  
 197                  error( message, data, file, 'no-retry' );
 198              });
 199          }
 200  
 201          /**
 202           * Custom error callback.
 203           *
 204           * Add a new error to the errors collection, so other modules can track
 205           * and display errors. @see wp.Uploader.errors.
 206           *
 207           * @param  {string}        message  Error message.
 208           * @param  {object}        data     Error data from Plupload.
 209           * @param  {plupload.File} file     File that was uploaded.
 210           * @param  {string}        retry    Whether to try again to create image sub-sizes. Passing 'no-retry' will prevent it.
 211           */
 212          error = function( message, data, file, retry ) {
 213              var isImage = file.type && file.type.indexOf( 'image/' ) === 0;
 214              var status  = data && data.status;
 215  
 216              // If the file is an image and the error is HTTP 5xx try to create sub-sizes again.
 217              if ( retry !== 'no-retry' && isImage && status >= 500 && status < 600 ) {
 218                  tryAgain( message, data, file );
 219                  return;
 220              }
 221  
 222              if ( file.attachment ) {
 223                  file.attachment.destroy();
 224              }
 225  
 226              Uploader.errors.unshift({
 227                  message: message || pluploadL10n.default_error,
 228                  data:    data,
 229                  file:    file
 230              });
 231  
 232              self.error( message, data, file );
 233          };
 234  
 235          /**
 236           * After a file is successfully uploaded, update its model.
 237           *
 238           * @param {plupload.Uploader} up       Uploader instance.
 239           * @param {plupload.File}     file     File that was uploaded.
 240           * @param {Object}            response Object with response properties.
 241           */
 242          fileUploaded = function( up, file, response ) {
 243              var complete;
 244  
 245              // Remove the "uploading" UI elements.
 246              _.each( ['file','loaded','size','percent'], function( key ) {
 247                  file.attachment.unset( key );
 248              } );
 249  
 250              file.attachment.set( _.extend( response.data, { uploading: false } ) );
 251  
 252              wp.media.model.Attachment.get( response.data.id, file.attachment );
 253  
 254              complete = Uploader.queue.all( function( attachment ) {
 255                  return ! attachment.get( 'uploading' );
 256              });
 257  
 258              if ( complete ) {
 259                  Uploader.queue.reset();
 260              }
 261  
 262              self.success( file.attachment );
 263          }
 264  
 265          /**
 266           * After the Uploader has been initialized, initialize some behaviors for the dropzone.
 267           *
 268           * @param {plupload.Uploader} uploader Uploader instance.
 269           */
 270          this.uploader.bind( 'init', function( uploader ) {
 271              var timer, active, dragdrop,
 272                  dropzone = self.dropzone;
 273  
 274              dragdrop = self.supports.dragdrop = uploader.features.dragdrop && ! Uploader.browser.mobile;
 275  
 276              // Generate drag/drop helper classes.
 277              if ( ! dropzone ) {
 278                  return;
 279              }
 280  
 281              dropzone.toggleClass( 'supports-drag-drop', !! dragdrop );
 282  
 283              if ( ! dragdrop ) {
 284                  return dropzone.unbind('.wp-uploader');
 285              }
 286  
 287              // 'dragenter' doesn't fire correctly, simulate it with a limited 'dragover'.
 288              dropzone.bind( 'dragover.wp-uploader', function() {
 289                  if ( timer ) {
 290                      clearTimeout( timer );
 291                  }
 292  
 293                  if ( active ) {
 294                      return;
 295                  }
 296  
 297                  dropzone.trigger('dropzone:enter').addClass('drag-over');
 298                  active = true;
 299              });
 300  
 301              dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function() {
 302                  /*
 303                   * Using an instant timer prevents the drag-over class
 304                   * from being quickly removed and re-added when elements
 305                   * inside the dropzone are repositioned.
 306                   *
 307                   * @see https://core.trac.wordpress.org/ticket/21705
 308                   */
 309                  timer = setTimeout( function() {
 310                      active = false;
 311                      dropzone.trigger('dropzone:leave').removeClass('drag-over');
 312                  }, 0 );
 313              });
 314  
 315              self.ready = true;
 316              $(self).trigger( 'uploader:ready' );
 317          });
 318  
 319          this.uploader.bind( 'postinit', function( up ) {
 320              up.refresh();
 321              self.init();
 322          });
 323  
 324          this.uploader.init();
 325  
 326          if ( this.browser ) {
 327              this.browser.on( 'mouseenter', this.refresh );
 328          } else {
 329              this.uploader.disableBrowse( true );
 330              // If HTML5 mode, hide the auto-created file container.
 331              $('#' + this.uploader.id + '_html5_container').hide();
 332          }
 333  
 334          /**
 335           * After files were filtered and added to the queue, create a model for each.
 336           *
 337           * @param {plupload.Uploader} up    Uploader instance.
 338           * @param {Array}             files Array of file objects that were added to queue by the user.
 339           */
 340          this.uploader.bind( 'FilesAdded', function( up, files ) {
 341              _.each( files, function( file ) {
 342                  var attributes, image;
 343  
 344                  // Ignore failed uploads.
 345                  if ( plupload.FAILED === file.status ) {
 346                      return;
 347                  }
 348  
 349                  // Generate attributes for a new `Attachment` model.
 350                  attributes = _.extend({
 351                      file:      file,
 352                      uploading: true,
 353                      date:      new Date(),
 354                      filename:  file.name,
 355                      menuOrder: 0,
 356                      uploadedTo: wp.media.model.settings.post.id
 357                  }, _.pick( file, 'loaded', 'size', 'percent' ) );
 358  
 359                  // Handle early mime type scanning for images.
 360                  image = /(?:jpe?g|png|gif)$/i.exec( file.name );
 361  
 362                  // For images set the model's type and subtype attributes.
 363                  if ( image ) {
 364                      attributes.type = 'image';
 365  
 366                      // `jpeg`, `png` and `gif` are valid subtypes.
 367                      // `jpg` is not, so map it to `jpeg`.
 368                      attributes.subtype = ( 'jpg' === image[0] ) ? 'jpeg' : image[0];
 369                  }
 370  
 371                  // Create a model for the attachment, and add it to the Upload queue collection
 372                  // so listeners to the upload queue can track and display upload progress.
 373                  file.attachment = wp.media.model.Attachment.create( attributes );
 374                  Uploader.queue.add( file.attachment );
 375  
 376                  self.added( file.attachment );
 377              });
 378  
 379              up.refresh();
 380              up.start();
 381          });
 382  
 383          this.uploader.bind( 'UploadProgress', function( up, file ) {
 384              file.attachment.set( _.pick( file, 'loaded', 'percent' ) );
 385              self.progress( file.attachment );
 386          });
 387  
 388          /**
 389           * After a file is successfully uploaded, update its model.
 390           *
 391           * @param {plupload.Uploader} up       Uploader instance.
 392           * @param {plupload.File}     file     File that was uploaded.
 393           * @param {Object}            response Object with response properties.
 394           * @return {mixed}
 395           */
 396          this.uploader.bind( 'FileUploaded', function( up, file, response ) {
 397  
 398              try {
 399                  response = JSON.parse( response.response );
 400              } catch ( e ) {
 401                  return error( pluploadL10n.default_error, e, file );
 402              }
 403  
 404              if ( ! _.isObject( response ) || _.isUndefined( response.success ) ) {
 405                  return error( pluploadL10n.default_error, null, file );
 406              } else if ( ! response.success ) {
 407                  return error( response.data && response.data.message, response.data, file );
 408              }
 409  
 410              // Success. Update the UI with the new attachment.
 411              fileUploaded( up, file, response );
 412          });
 413  
 414          /**
 415           * When plupload surfaces an error, send it to the error handler.
 416           *
 417           * @param {plupload.Uploader} up            Uploader instance.
 418           * @param {Object}            pluploadError Contains code, message and sometimes file and other details.
 419           */
 420          this.uploader.bind( 'Error', function( up, pluploadError ) {
 421              var message = pluploadL10n.default_error,
 422                  key;
 423  
 424              // Check for plupload errors.
 425              for ( key in Uploader.errorMap ) {
 426                  if ( pluploadError.code === plupload[ key ] ) {
 427                      message = Uploader.errorMap[ key ];
 428  
 429                      if ( _.isFunction( message ) ) {
 430                          message = message( pluploadError.file, pluploadError );
 431                      }
 432  
 433                      break;
 434                  }
 435              }
 436  
 437              error( message, pluploadError, pluploadError.file );
 438              up.refresh();
 439          });
 440  
 441      };
 442  
 443      // Adds the 'defaults' and 'browser' properties.
 444      $.extend( Uploader, _wpPluploadSettings );
 445  
 446      Uploader.uuid = 0;
 447  
 448      // Map Plupload error codes to user friendly error messages.
 449      Uploader.errorMap = {
 450          'FAILED':                 pluploadL10n.upload_failed,
 451          'FILE_EXTENSION_ERROR':   pluploadL10n.invalid_filetype,
 452          'IMAGE_FORMAT_ERROR':     pluploadL10n.not_an_image,
 453          'IMAGE_MEMORY_ERROR':     pluploadL10n.image_memory_exceeded,
 454          'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded,
 455          'GENERIC_ERROR':          pluploadL10n.upload_failed,
 456          'IO_ERROR':               pluploadL10n.io_error,
 457          'SECURITY_ERROR':         pluploadL10n.security_error,
 458  
 459          'FILE_SIZE_ERROR': function( file ) {
 460              return pluploadL10n.file_exceeds_size_limit.replace( '%s', file.name );
 461          },
 462  
 463          'HTTP_ERROR': function( file ) {
 464              if ( file.type && file.type.indexOf( 'image/' ) === 0 ) {
 465                  return pluploadL10n.http_error_image;
 466              }
 467  
 468              return pluploadL10n.http_error;
 469          },
 470      };
 471  
 472      $.extend( Uploader.prototype, /** @lends wp.Uploader.prototype */{
 473          /**
 474           * Acts as a shortcut to extending the uploader's multipart_params object.
 475           *
 476           * param( key )
 477           *    Returns the value of the key.
 478           *
 479           * param( key, value )
 480           *    Sets the value of a key.
 481           *
 482           * param( map )
 483           *    Sets values for a map of data.
 484           */
 485          param: function( key, value ) {
 486              if ( arguments.length === 1 && typeof key === 'string' ) {
 487                  return this.uploader.settings.multipart_params[ key ];
 488              }
 489  
 490              if ( arguments.length > 1 ) {
 491                  this.uploader.settings.multipart_params[ key ] = value;
 492              } else {
 493                  $.extend( this.uploader.settings.multipart_params, key );
 494              }
 495          },
 496  
 497          /**
 498           * Make a few internal event callbacks available on the wp.Uploader object
 499           * to change the Uploader internals if absolutely necessary.
 500           */
 501          init:     function() {},
 502          error:    function() {},
 503          success:  function() {},
 504          added:    function() {},
 505          progress: function() {},
 506          complete: function() {},
 507          refresh:  function() {
 508              var node, attached, container, id;
 509  
 510              if ( this.browser ) {
 511                  node = this.browser[0];
 512  
 513                  // Check if the browser node is in the DOM.
 514                  while ( node ) {
 515                      if ( node === document.body ) {
 516                          attached = true;
 517                          break;
 518                      }
 519                      node = node.parentNode;
 520                  }
 521  
 522                  /*
 523                   * If the browser node is not attached to the DOM,
 524                   * use a temporary container to house it, as the browser button shims 
 525                   * require the button to exist in the DOM at all times.
 526                   */
 527                  if ( ! attached ) {
 528                      id = 'wp-uploader-browser-' + this.uploader.id;
 529  
 530                      container = $( '#' + id );
 531                      if ( ! container.length ) {
 532                          container = $('<div class="wp-uploader-browser" />').css({
 533                              position: 'fixed',
 534                              top: '-1000px',
 535                              left: '-1000px',
 536                              height: 0,
 537                              width: 0
 538                          }).attr( 'id', 'wp-uploader-browser-' + this.uploader.id ).appendTo('body');
 539                      }
 540  
 541                      container.append( this.browser );
 542                  }
 543              }
 544  
 545              this.uploader.refresh();
 546          }
 547      });
 548  
 549      // Create a collection of attachments in the upload queue,
 550      // so that other modules can track and display upload progress.
 551      Uploader.queue = new wp.media.model.Attachments( [], { query: false });
 552  
 553      // Create a collection to collect errors incurred while attempting upload.
 554      Uploader.errors = new Backbone.Collection();
 555  
 556      exports.Uploader = Uploader;
 557  })( wp, jQuery );


Generated : Thu Apr 2 08:20:01 2020 Cross-referenced by PHPXref