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


Generated: Mon Nov 11 01:00:04 2019 Cross-referenced by PHPXref 0.7.1