[ Index ]

PHP Cross Reference of WordPress

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


Generated: Wed Sep 18 01:00:03 2019 Cross-referenced by PHPXref 0.7.1