[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/resources/src/mediawiki.special/ -> mediawiki.special.upload.js (source)

   1  /**
   2   * JavaScript for Special:Upload
   3   *
   4   * @private
   5   * @class mw.special.upload
   6   * @singleton
   7   */
   8  ( function ( mw, $ ) {
   9      var ajaxUploadDestCheck = mw.config.get( 'wgAjaxUploadDestCheck' ),
  10          $license = $( '#wpLicense' ), uploadWarning, uploadLicense;
  11  
  12      window.wgUploadWarningObj = uploadWarning = {
  13          responseCache: { '': ' ' },
  14          nameToCheck: '',
  15          typing: false,
  16          delay: 500, // ms
  17          timeoutID: false,
  18  
  19          keypress: function () {
  20              if ( !ajaxUploadDestCheck ) {
  21                  return;
  22              }
  23  
  24              // Find file to upload
  25              if ( !$( '#wpDestFile' ).length || !$( '#wpDestFile-warning' ).length ) {
  26                  return;
  27              }
  28  
  29              this.nameToCheck = $( '#wpDestFile' ).val();
  30  
  31              // Clear timer
  32              if ( this.timeoutID ) {
  33                  clearTimeout( this.timeoutID );
  34              }
  35              // Check response cache
  36              if ( this.responseCache.hasOwnProperty( this.nameToCheck ) ) {
  37                  this.setWarning( this.responseCache[this.nameToCheck] );
  38                  return;
  39              }
  40  
  41              this.timeoutID = setTimeout( function () {
  42                  uploadWarning.timeout();
  43              }, this.delay );
  44          },
  45  
  46          checkNow: function ( fname ) {
  47              if ( !ajaxUploadDestCheck ) {
  48                  return;
  49              }
  50              if ( this.timeoutID ) {
  51                  clearTimeout( this.timeoutID );
  52              }
  53              this.nameToCheck = fname;
  54              this.timeout();
  55          },
  56  
  57          timeout: function () {
  58              var $spinnerDestCheck;
  59              if ( !ajaxUploadDestCheck || this.nameToCheck === '' ) {
  60                  return;
  61              }
  62              $spinnerDestCheck = $.createSpinner().insertAfter( '#wpDestFile' );
  63  
  64              ( new mw.Api() ).get( {
  65                  action: 'query',
  66                  titles: ( new mw.Title( this.nameToCheck, mw.config.get( 'wgNamespaceIds' ).file ) ).getPrefixedText(),
  67                  prop: 'imageinfo',
  68                  iiprop: 'uploadwarning',
  69                  indexpageids: ''
  70              } ).done( function ( result ) {
  71                  var resultOut = '';
  72                  if ( result.query ) {
  73                      resultOut = result.query.pages[result.query.pageids[0]].imageinfo[0];
  74                  }
  75                  $spinnerDestCheck.remove();
  76                  uploadWarning.processResult( resultOut, uploadWarning.nameToCheck );
  77              } );
  78          },
  79  
  80          processResult: function ( result, fileName ) {
  81              this.setWarning( result.html );
  82              this.responseCache[fileName] = result.html;
  83          },
  84  
  85          setWarning: function ( warning ) {
  86              $( '#wpDestFile-warning' ).html( warning );
  87  
  88              // Set a value in the form indicating that the warning is acknowledged and
  89              // doesn't need to be redisplayed post-upload
  90              if ( !warning ) {
  91                  $( '#wpDestFileWarningAck' ).val( '' );
  92              } else {
  93                  $( '#wpDestFileWarningAck' ).val( '1' );
  94              }
  95  
  96          }
  97      };
  98  
  99      uploadLicense = {
 100  
 101          responseCache: { '': '' },
 102  
 103          fetchPreview: function ( license ) {
 104              var $spinnerLicense;
 105              if ( !mw.config.get( 'wgAjaxLicensePreview' ) ) {
 106                  return;
 107              }
 108              if ( this.responseCache.hasOwnProperty( license ) ) {
 109                  this.showPreview( this.responseCache[license] );
 110                  return;
 111              }
 112  
 113              $spinnerLicense = $.createSpinner().insertAfter( '#wpLicense' );
 114  
 115              ( new mw.Api() ).get( {
 116                  action: 'parse',
 117                  text: '{{' + license + '}}',
 118                  title: $( '#wpDestFile' ).val() || 'File:Sample.jpg',
 119                  prop: 'text',
 120                  pst: ''
 121              } ).done( function ( result ) {
 122                  $spinnerLicense.remove();
 123                  uploadLicense.processResult( result, license );
 124              } );
 125          },
 126  
 127          processResult: function ( result, license ) {
 128              this.responseCache[license] = result.parse.text['*'];
 129              this.showPreview( this.responseCache[license] );
 130          },
 131  
 132          showPreview: function ( preview ) {
 133              $( '#mw-license-preview' ).html( preview );
 134          }
 135  
 136      };
 137  
 138      $( function () {
 139          // Disable URL box if the URL copy upload source type is not selected
 140          if ( !$( '#wpSourceTypeurl' ).prop( 'checked' ) ) {
 141              $( '#wpUploadFileURL' ).prop( 'disabled', true );
 142          }
 143  
 144          // AJAX wpDestFile warnings
 145          if ( ajaxUploadDestCheck ) {
 146              // Insert an event handler that fetches upload warnings when wpDestFile
 147              // has been changed
 148              $( '#wpDestFile' ).change( function () {
 149                  uploadWarning.checkNow( $( this ).val() );
 150              } );
 151              // Insert a row where the warnings will be displayed just below the
 152              // wpDestFile row
 153              $( '#mw-htmlform-description tbody' ).append(
 154                  $( '<tr>' ).append(
 155                      $( '<td>' )
 156                          .attr( 'id', 'wpDestFile-warning' )
 157                          .attr( 'colspan', 2 )
 158                  )
 159              );
 160          }
 161  
 162          if ( mw.config.get( 'wgAjaxLicensePreview' ) && $license.length ) {
 163              // License selector check
 164              $license.change( function () {
 165                  // We might show a preview
 166                  uploadLicense.fetchPreview( $license.val() );
 167              } );
 168  
 169              // License selector table row
 170              $license.closest( 'tr' ).after(
 171                  $( '<tr>' ).append(
 172                      $( '<td>' ),
 173                      $( '<td>' ).attr( 'id', 'mw-license-preview' )
 174                  )
 175              );
 176          }
 177  
 178          // fillDestFile setup
 179          $.each( mw.config.get( 'wgUploadSourceIds' ), function ( index, sourceId ) {
 180              $( '#' + sourceId ).change( function () {
 181                  var path, slash, backslash, fname;
 182                  if ( !mw.config.get( 'wgUploadAutoFill' ) ) {
 183                      return;
 184                  }
 185                  // Remove any previously flagged errors
 186                  $( '#mw-upload-permitted' ).attr( 'class', '' );
 187                  $( '#mw-upload-prohibited' ).attr( 'class', '' );
 188  
 189                  path = $( this ).val();
 190                  // Find trailing part
 191                  slash = path.lastIndexOf( '/' );
 192                  backslash = path.lastIndexOf( '\\' );
 193                  if ( slash === -1 && backslash === -1 ) {
 194                      fname = path;
 195                  } else if ( slash > backslash ) {
 196                      fname = path.slice( slash + 1 );
 197                  } else {
 198                      fname = path.slice( backslash + 1 );
 199                  }
 200  
 201                  // Clear the filename if it does not have a valid extension.
 202                  // URLs are less likely to have a useful extension, so don't include them in the
 203                  // extension check.
 204                  if (
 205                      mw.config.get( 'wgStrictFileExtensions' ) &&
 206                      mw.config.get( 'wgFileExtensions' ) &&
 207                      $( this ).attr( 'id' ) !== 'wpUploadFileURL'
 208                  ) {
 209                      if (
 210                          fname.lastIndexOf( '.' ) === -1 ||
 211                          $.inArray(
 212                              fname.slice( fname.lastIndexOf( '.' ) + 1 ).toLowerCase(),
 213                              $.map( mw.config.get( 'wgFileExtensions' ), function ( element ) {
 214                                  return element.toLowerCase();
 215                              } )
 216                          ) === -1
 217                      ) {
 218                          // Not a valid extension
 219                          // Clear the upload and set mw-upload-permitted to error
 220                          $( this ).val( '' );
 221                          $( '#mw-upload-permitted' ).attr( 'class', 'error' );
 222                          $( '#mw-upload-prohibited' ).attr( 'class', 'error' );
 223                          // Clear wpDestFile as well
 224                          $( '#wpDestFile' ).val( '' );
 225  
 226                          return false;
 227                      }
 228                  }
 229  
 230                  // Replace spaces by underscores
 231                  fname = fname.replace( / /g, '_' );
 232                  // Capitalise first letter if needed
 233                  if ( mw.config.get( 'wgCapitalizeUploads' ) ) {
 234                      fname = fname.charAt( 0 ).toUpperCase().concat( fname.slice( 1 ) );
 235                  }
 236  
 237                  // Output result
 238                  if ( $( '#wpDestFile' ).length ) {
 239                      // Call decodeURIComponent function to remove possible URL-encoded characters
 240                      // from the file name (bug 30390). Especially likely with upload-form-url.
 241                      // decodeURIComponent can throw an exception if input is invalid utf-8
 242                      try {
 243                          $( '#wpDestFile' ).val( decodeURIComponent( fname ) );
 244                      } catch ( err ) {
 245                          $( '#wpDestFile' ).val( fname );
 246                      }
 247                      uploadWarning.checkNow( fname );
 248                  }
 249              } );
 250          } );
 251      } );
 252  
 253      // Add a preview to the upload form
 254      $( function () {
 255          /**
 256           * Is the FileAPI available with sufficient functionality?
 257           */
 258  		function hasFileAPI() {
 259              return window.FileReader !== undefined;
 260          }
 261  
 262          /**
 263           * Check if this is a recognizable image type...
 264           * Also excludes files over 10M to avoid going insane on memory usage.
 265           *
 266           * TODO: Is there a way we can ask the browser what's supported in `<img>`s?
 267           *
 268           * TODO: Put SVG back after working around Firefox 7 bug <https://bugzilla.wikimedia.org/show_bug.cgi?id=31643>
 269           *
 270           * @param {File} file
 271           * @return boolean
 272           */
 273  		function fileIsPreviewable( file ) {
 274              var known = ['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml'],
 275                  tooHuge = 10 * 1024 * 1024;
 276              return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge;
 277          }
 278  
 279          /**
 280           * Show a thumbnail preview of PNG, JPEG, GIF, and SVG files prior to upload
 281           * in browsers supporting HTML5 FileAPI.
 282           *
 283           * As of this writing, known good:
 284           *
 285           * - Firefox 3.6+
 286           * - Chrome 7.something
 287           *
 288           * TODO: Check file size limits and warn of likely failures
 289           *
 290           * @param {File} file
 291           */
 292  		function showPreview( file ) {
 293              var $canvas,
 294                  ctx,
 295                  meta,
 296                  previewSize = 180,
 297                  thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' +
 298                              '<div class="thumbinner">' +
 299                                  '<div class="mw-small-spinner" style="width: 180px; height: 180px"></div>' +
 300                                  '<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' +
 301                              '</div>' +
 302                          '</div>' );
 303  
 304              thumb.find( '.filename' ).text( file.name ).end()
 305                  .find( '.fileinfo' ).text( prettySize( file.size ) ).end();
 306  
 307              $canvas = $( '<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>' );
 308              ctx = $canvas[0].getContext( '2d' );
 309              $( '#mw-htmlform-source' ).parent().prepend( thumb );
 310  
 311              fetchPreview( file, function ( dataURL ) {
 312                  var img = new Image(),
 313                      rotation = 0;
 314  
 315                  if ( meta && meta.tiff && meta.tiff.Orientation ) {
 316                      rotation = ( 360 - ( function () {
 317                          // See includes/media/Bitmap.php
 318                          switch ( meta.tiff.Orientation.value ) {
 319                              case 8:
 320                                  return 90;
 321                              case 3:
 322                                  return 180;
 323                              case 6:
 324                                  return 270;
 325                              default:
 326                                  return 0;
 327                          }
 328                      }() ) ) % 360;
 329                  }
 330  
 331                  img.onload = function () {
 332                      var info, width, height, x, y, dx, dy, logicalWidth, logicalHeight;
 333  
 334                      // Fit the image within the previewSizexpreviewSize box
 335                      if ( img.width > img.height ) {
 336                          width = previewSize;
 337                          height = img.height / img.width * previewSize;
 338                      } else {
 339                          height = previewSize;
 340                          width = img.width / img.height * previewSize;
 341                      }
 342                      // Determine the offset required to center the image
 343                      dx = ( 180 - width ) / 2;
 344                      dy = ( 180 - height ) / 2;
 345                      switch ( rotation ) {
 346                          // If a rotation is applied, the direction of the axis
 347                          // changes as well. You can derive the values below by
 348                          // drawing on paper an axis system, rotate it and see
 349                          // where the positive axis direction is
 350                          case 0:
 351                              x = dx;
 352                              y = dy;
 353                              logicalWidth = img.width;
 354                              logicalHeight = img.height;
 355                              break;
 356                          case 90:
 357  
 358                              x = dx;
 359                              y = dy - previewSize;
 360                              logicalWidth = img.height;
 361                              logicalHeight = img.width;
 362                              break;
 363                          case 180:
 364                              x = dx - previewSize;
 365                              y = dy - previewSize;
 366                              logicalWidth = img.width;
 367                              logicalHeight = img.height;
 368                              break;
 369                          case 270:
 370                              x = dx - previewSize;
 371                              y = dy;
 372                              logicalWidth = img.height;
 373                              logicalHeight = img.width;
 374                              break;
 375                      }
 376  
 377                      ctx.clearRect( 0, 0, 180, 180 );
 378                      ctx.rotate( rotation / 180 * Math.PI );
 379                      ctx.drawImage( img, x, y, width, height );
 380                      thumb.find( '.mw-small-spinner' ).replaceWith( $canvas );
 381  
 382                      // Image size
 383                      info = mw.msg( 'widthheight', logicalWidth, logicalHeight ) +
 384                          ', ' + prettySize( file.size );
 385  
 386                      $( '#mw-upload-thumbnail .fileinfo' ).text( info );
 387                  };
 388                  img.src = dataURL;
 389              }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) {
 390                  /*jshint camelcase:false, nomen:false */
 391                  try {
 392                      meta = mw.libs.jpegmeta( data, file.fileName );
 393                      meta._binary_data = null;
 394                  } catch ( e ) {
 395                      meta = null;
 396                  }
 397              } : null );
 398          }
 399  
 400          /**
 401           * Start loading a file into memory; when complete, pass it as a
 402           * data URL to the callback function. If the callbackBinary is set it will
 403           * first be read as binary and afterwards as data URL. Useful if you want
 404           * to do preprocessing on the binary data first.
 405           *
 406           * @param {File} file
 407           * @param {Function} callback
 408           * @param {Function} callbackBinary
 409           */
 410  		function fetchPreview( file, callback, callbackBinary ) {
 411              var reader = new FileReader();
 412              if ( callbackBinary && 'readAsBinaryString' in reader ) {
 413                  // To fetch JPEG metadata we need a binary string; start there.
 414                  // todo:
 415                  reader.onload = function () {
 416                      callbackBinary( reader.result );
 417  
 418                      // Now run back through the regular code path.
 419                      fetchPreview( file, callback );
 420                  };
 421                  reader.readAsBinaryString( file );
 422              } else if ( callbackBinary && 'readAsArrayBuffer' in reader ) {
 423                  // readAsArrayBuffer replaces readAsBinaryString
 424                  // However, our JPEG metadata library wants a string.
 425                  // So, this is going to be an ugly conversion.
 426                  reader.onload = function () {
 427                      var i,
 428                          buffer = new Uint8Array( reader.result ),
 429                          string = '';
 430                      for ( i = 0; i < buffer.byteLength; i++ ) {
 431                          string += String.fromCharCode( buffer[i] );
 432                      }
 433                      callbackBinary( string );
 434  
 435                      // Now run back through the regular code path.
 436                      fetchPreview( file, callback );
 437                  };
 438                  reader.readAsArrayBuffer( file );
 439              } else if ( 'URL' in window && 'createObjectURL' in window.URL ) {
 440                  // Supported in Firefox 4.0 and above <https://developer.mozilla.org/en/DOM/window.URL.createObjectURL>
 441                  // WebKit has it in a namespace for now but that's ok. ;)
 442                  //
 443                  // Lifetime of this URL is until document close, which is fine
 444                  // for Special:Upload -- if this code gets used on longer-running
 445                  // pages, add a revokeObjectURL() when it's no longer needed.
 446                  //
 447                  // Prefer this over readAsDataURL for Firefox 7 due to bug reading
 448                  // some SVG files from data URIs <https://bugzilla.mozilla.org/show_bug.cgi?id=694165>
 449                  callback( window.URL.createObjectURL( file ) );
 450              } else {
 451                  // This ends up decoding the file to base-64 and back again, which
 452                  // feels horribly inefficient.
 453                  reader.onload = function () {
 454                      callback( reader.result );
 455                  };
 456                  reader.readAsDataURL( file );
 457              }
 458          }
 459  
 460          /**
 461           * Format a file size attractively.
 462           *
 463           * TODO: Match numeric formatting
 464           *
 465           * @param {number} s
 466           * @return {string}
 467           */
 468  		function prettySize( s ) {
 469              var sizeMsgs = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes'];
 470              while ( s >= 1024 && sizeMsgs.length > 1 ) {
 471                  s /= 1024;
 472                  sizeMsgs = sizeMsgs.slice( 1 );
 473              }
 474              return mw.msg( sizeMsgs[0], Math.round( s ) );
 475          }
 476  
 477          /**
 478           * Clear the file upload preview area.
 479           */
 480  		function clearPreview() {
 481              $( '#mw-upload-thumbnail' ).remove();
 482          }
 483  
 484          /**
 485           * Check if the file does not exceed the maximum size
 486           */
 487  		function checkMaxUploadSize( file ) {
 488              var maxSize, $error;
 489  
 490  			function getMaxUploadSize( type ) {
 491                  var sizes = mw.config.get( 'wgMaxUploadSize' );
 492  
 493                  if ( sizes[type] !== undefined ) {
 494                      return sizes[type];
 495                  }
 496                  return sizes['*'];
 497              }
 498  
 499              $( '.mw-upload-source-error' ).remove();
 500  
 501              maxSize = getMaxUploadSize( 'file' );
 502              if ( file.size > maxSize ) {
 503                  $error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
 504                      mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' );
 505  
 506                  $( '#wpUploadFile' ).after( $error );
 507  
 508                  return false;
 509              }
 510  
 511              return true;
 512          }
 513  
 514          /* Initialization */
 515          if ( hasFileAPI() ) {
 516              // Update thumbnail when the file selection control is updated.
 517              $( '#wpUploadFile' ).change( function () {
 518                  clearPreview();
 519                  if ( this.files && this.files.length ) {
 520                      // Note: would need to be updated to handle multiple files.
 521                      var file = this.files[0];
 522  
 523                      if ( !checkMaxUploadSize( file ) ) {
 524                          return;
 525                      }
 526  
 527                      if ( fileIsPreviewable( file ) ) {
 528                          showPreview( file );
 529                      }
 530                  }
 531              } );
 532          }
 533      } );
 534  
 535      // Disable all upload source fields except the selected one
 536      $( function () {
 537          var i, $row,
 538              $rows = $( '.mw-htmlform-field-UploadSourceField' );
 539  
 540          /**
 541           * @param {jQuery} $currentRow
 542           * @return {Function} Handler
 543           * @return {jQuery.Event} return.e
 544           */
 545  		function createHandler( $currentRow ) {
 546              return function () {
 547                  $( '.mw-upload-source-error' ).remove();
 548                  if ( this.checked ) {
 549                      // Disable all inputs
 550                      $rows.find( 'input[name!="wpSourceType"]' ).prop( 'disabled', true );
 551                      // Re-enable the current one
 552                      $currentRow.find( 'input' ).prop( 'disabled', false );
 553                  }
 554              };
 555          }
 556  
 557          for ( i = $rows.length; i; i-- ) {
 558              $row = $rows.eq( i - 1 );
 559              $row
 560                  .find( 'input[name="wpSourceType"]' )
 561                  .change( createHandler( $row ) );
 562          }
 563      } );
 564  
 565  }( mediaWiki, jQuery ) );


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1