[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-admin/js/ -> image-edit.js (source)

   1  /**
   2   * The functions necessary for editing images.
   3   *
   4   * @since 2.9.0
   5   * @output wp-admin/js/image-edit.js
   6   */
   7  
   8   /* global imageEditL10n, ajaxurl, confirm */
   9  
  10  (function($) {
  11  
  12      /**
  13       * Contains all the methods to initialise and control the image editor.
  14       *
  15       * @namespace imageEdit
  16       */
  17      var imageEdit = window.imageEdit = {
  18      iasapi : {},
  19      hold : {},
  20      postid : '',
  21      _view : false,
  22  
  23      /**
  24       * Handle crop tool clicks.
  25       */
  26      handleCropToolClick: function( postid, nonce, cropButton ) {
  27          var img = $( '#image-preview-' + postid ),
  28              selection = this.iasapi.getSelection();
  29  
  30          // Ensure selection is available, otherwise reset to full image.
  31          if ( isNaN( selection.x1 ) ) {
  32              this.setCropSelection( postid, { 'x1': 0, 'y1': 0, 'x2': img.innerWidth(), 'y2': img.innerHeight(), 'width': img.innerWidth(), 'height': img.innerHeight() } );
  33              selection = this.iasapi.getSelection();
  34          }
  35  
  36          // If we don't already have a selection, select the entire image.
  37          if ( 0 === selection.x1 && 0 === selection.y1 && 0 === selection.x2 && 0 === selection.y2 ) {
  38              this.iasapi.setSelection( 0, 0, img.innerWidth(), img.innerHeight(), true );
  39              this.iasapi.setOptions( { show: true } );
  40              this.iasapi.update();
  41          } else {
  42  
  43              // Otherwise, perform the crop.
  44              imageEdit.crop( postid, nonce , cropButton );
  45          }
  46      },
  47  
  48      /**
  49       * Converts a value to an integer.
  50       *
  51       * @since 2.9.0
  52       *
  53       * @memberof imageEdit
  54       *
  55       * @param {number} f The float value that should be converted.
  56       *
  57       * @return {number} The integer representation from the float value.
  58       */
  59      intval : function(f) {
  60          /*
  61           * Bitwise OR operator: one of the obscure ways to truncate floating point figures,
  62           * worth reminding JavaScript doesn't have a distinct "integer" type.
  63           */
  64          return f | 0;
  65      },
  66  
  67      /**
  68       * Adds the disabled attribute and class to a single form element or a field set.
  69       *
  70       * @since 2.9.0
  71       *
  72       * @memberof imageEdit
  73       *
  74       * @param {jQuery}         el The element that should be modified.
  75       * @param {bool|number}    s  The state for the element. If set to true
  76       *                            the element is disabled,
  77       *                            otherwise the element is enabled.
  78       *                            The function is sometimes called with a 0 or 1
  79       *                            instead of true or false.
  80       *
  81       * @return {void}
  82       */
  83      setDisabled : function( el, s ) {
  84          /*
  85           * `el` can be a single form element or a fieldset. Before #28864, the disabled state on
  86           * some text fields  was handled targeting $('input', el). Now we need to handle the
  87           * disabled state on buttons too so we can just target `el` regardless if it's a single
  88           * element or a fieldset because when a fieldset is disabled, its descendants are disabled too.
  89           */
  90          if ( s ) {
  91              el.removeClass( 'disabled' ).prop( 'disabled', false );
  92          } else {
  93              el.addClass( 'disabled' ).prop( 'disabled', true );
  94          }
  95      },
  96  
  97      /**
  98       * Initializes the image editor.
  99       *
 100       * @since 2.9.0
 101       *
 102       * @memberof imageEdit
 103       *
 104       * @param {number} postid The post id.
 105       *
 106       * @return {void}
 107       */
 108      init : function(postid) {
 109          var t = this, old = $('#image-editor-' + t.postid),
 110              x = t.intval( $('#imgedit-x-' + postid).val() ),
 111              y = t.intval( $('#imgedit-y-' + postid).val() );
 112  
 113          if ( t.postid !== postid && old.length ) {
 114              t.close(t.postid);
 115          }
 116  
 117          t.hold.w = t.hold.ow = x;
 118          t.hold.h = t.hold.oh = y;
 119          t.hold.xy_ratio = x / y;
 120          t.hold.sizer = parseFloat( $('#imgedit-sizer-' + postid).val() );
 121          t.postid = postid;
 122          $('#imgedit-response-' + postid).empty();
 123  
 124          $('input[type="text"]', '#imgedit-panel-' + postid).keypress(function(e) {
 125              var k = e.keyCode;
 126  
 127              // Key codes 37 through 40 are the arrow keys.
 128              if ( 36 < k && k < 41 ) {
 129                  $(this).blur();
 130              }
 131  
 132              // The key code 13 is the Enter key.
 133              if ( 13 === k ) {
 134                  e.preventDefault();
 135                  e.stopPropagation();
 136                  return false;
 137              }
 138          });
 139      },
 140  
 141      /**
 142       * Toggles the wait/load icon in the editor.
 143       *
 144       * @since 2.9.0
 145       *
 146       * @memberof imageEdit
 147       *
 148       * @param {number} postid The post id.
 149       * @param {number} toggle Is 0 or 1, fades the icon in then 1 and out when 0.
 150       *
 151       * @return {void}
 152       */
 153      toggleEditor : function(postid, toggle) {
 154          var wait = $('#imgedit-wait-' + postid);
 155  
 156          if ( toggle ) {
 157              wait.fadeIn( 'fast' );
 158          } else {
 159              wait.fadeOut('fast');
 160          }
 161      },
 162  
 163      /**
 164       * Shows or hides the image edit help box.
 165       *
 166       * @since 2.9.0
 167       *
 168       * @memberof imageEdit
 169       *
 170       * @param {HTMLElement} el The element to create the help window in.
 171       *
 172       * @return {boolean} Always returns false.
 173       */
 174      toggleHelp : function(el) {
 175          var $el = $( el );
 176          $el
 177              .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' )
 178              .parents( '.imgedit-group-top' ).toggleClass( 'imgedit-help-toggled' ).find( '.imgedit-help' ).slideToggle( 'fast' );
 179  
 180          return false;
 181      },
 182  
 183      /**
 184       * Gets the value from the image edit target.
 185       *
 186       * The image edit target contains the image sizes where the (possible) changes
 187       * have to be applied to.
 188       *
 189       * @since 2.9.0
 190       *
 191       * @memberof imageEdit
 192       *
 193       * @param {number} postid The post id.
 194       *
 195       * @return {string} The value from the imagedit-save-target input field when available,
 196       *                  or 'full' when not available.
 197       */
 198      getTarget : function(postid) {
 199          return $('input[name="imgedit-target-' + postid + '"]:checked', '#imgedit-save-target-' + postid).val() || 'full';
 200      },
 201  
 202      /**
 203       * Recalculates the height or width and keeps the original aspect ratio.
 204       *
 205       * If the original image size is exceeded a red exclamation mark is shown.
 206       *
 207       * @since 2.9.0
 208       *
 209       * @memberof imageEdit
 210       *
 211       * @param {number}         postid The current post id.
 212       * @param {number}         x      Is 0 when it applies the y-axis
 213       *                                and 1 when applicable for the x-axis.
 214       * @param {jQuery}         el     Element.
 215       *
 216       * @return {void}
 217       */
 218      scaleChanged : function( postid, x, el ) {
 219          var w = $('#imgedit-scale-width-' + postid), h = $('#imgedit-scale-height-' + postid),
 220          warn = $('#imgedit-scale-warn-' + postid), w1 = '', h1 = '';
 221  
 222          if ( false === this.validateNumeric( el ) ) {
 223              return;
 224          }
 225  
 226          if ( x ) {
 227              h1 = ( w.val() !== '' ) ? Math.round( w.val() / this.hold.xy_ratio ) : '';
 228              h.val( h1 );
 229          } else {
 230              w1 = ( h.val() !== '' ) ? Math.round( h.val() * this.hold.xy_ratio ) : '';
 231              w.val( w1 );
 232          }
 233  
 234          if ( ( h1 && h1 > this.hold.oh ) || ( w1 && w1 > this.hold.ow ) ) {
 235              warn.css('visibility', 'visible');
 236          } else {
 237              warn.css('visibility', 'hidden');
 238          }
 239      },
 240  
 241      /**
 242       * Gets the selected aspect ratio.
 243       *
 244       * @since 2.9.0
 245       *
 246       * @memberof imageEdit
 247       *
 248       * @param {number} postid The post id.
 249       *
 250       * @return {string} The aspect ratio.
 251       */
 252      getSelRatio : function(postid) {
 253          var x = this.hold.w, y = this.hold.h,
 254              X = this.intval( $('#imgedit-crop-width-' + postid).val() ),
 255              Y = this.intval( $('#imgedit-crop-height-' + postid).val() );
 256  
 257          if ( X && Y ) {
 258              return X + ':' + Y;
 259          }
 260  
 261          if ( x && y ) {
 262              return x + ':' + y;
 263          }
 264  
 265          return '1:1';
 266      },
 267  
 268      /**
 269       * Removes the last action from the image edit history.
 270       * The history consist of (edit) actions performed on the image.
 271       *
 272       * @since 2.9.0
 273       *
 274       * @memberof imageEdit
 275       *
 276       * @param {number} postid  The post id.
 277       * @param {number} setSize 0 or 1, when 1 the image resets to its original size.
 278       *
 279       * @return {string} JSON string containing the history or an empty string if no history exists.
 280       */
 281      filterHistory : function(postid, setSize) {
 282          // Apply undo state to history.
 283          var history = $('#imgedit-history-' + postid).val(), pop, n, o, i, op = [];
 284  
 285          if ( history !== '' ) {
 286              // Read the JSON string with the image edit history.
 287              history = JSON.parse(history);
 288              pop = this.intval( $('#imgedit-undone-' + postid).val() );
 289              if ( pop > 0 ) {
 290                  while ( pop > 0 ) {
 291                      history.pop();
 292                      pop--;
 293                  }
 294              }
 295  
 296              // Reset size to it's original state.
 297              if ( setSize ) {
 298                  if ( !history.length ) {
 299                      this.hold.w = this.hold.ow;
 300                      this.hold.h = this.hold.oh;
 301                      return '';
 302                  }
 303  
 304                  // Restore original 'o'.
 305                  o = history[history.length - 1];
 306  
 307                  // c = 'crop', r = 'rotate', f = 'flip'.
 308                  o = o.c || o.r || o.f || false;
 309  
 310                  if ( o ) {
 311                      // fw = Full image width.
 312                      this.hold.w = o.fw;
 313                      // fh = Full image height.
 314                      this.hold.h = o.fh;
 315                  }
 316              }
 317  
 318              // Filter the last step/action from the history.
 319              for ( n in history ) {
 320                  i = history[n];
 321                  if ( i.hasOwnProperty('c') ) {
 322                      op[n] = { 'c': { 'x': i.c.x, 'y': i.c.y, 'w': i.c.w, 'h': i.c.h } };
 323                  } else if ( i.hasOwnProperty('r') ) {
 324                      op[n] = { 'r': i.r.r };
 325                  } else if ( i.hasOwnProperty('f') ) {
 326                      op[n] = { 'f': i.f.f };
 327                  }
 328              }
 329              return JSON.stringify(op);
 330          }
 331          return '';
 332      },
 333      /**
 334       * Binds the necessary events to the image.
 335       *
 336       * When the image source is reloaded the image will be reloaded.
 337       *
 338       * @since 2.9.0
 339       *
 340       * @memberof imageEdit
 341       *
 342       * @param {number}   postid   The post id.
 343       * @param {string}   nonce    The nonce to verify the request.
 344       * @param {function} callback Function to execute when the image is loaded.
 345       *
 346       * @return {void}
 347       */
 348      refreshEditor : function(postid, nonce, callback) {
 349          var t = this, data, img;
 350  
 351          t.toggleEditor(postid, 1);
 352          data = {
 353              'action': 'imgedit-preview',
 354              '_ajax_nonce': nonce,
 355              'postid': postid,
 356              'history': t.filterHistory(postid, 1),
 357              'rand': t.intval(Math.random() * 1000000)
 358          };
 359  
 360          img = $( '<img id="image-preview-' + postid + '" alt="" />' )
 361              .on( 'load', { history: data.history }, function( event ) {
 362                  var max1, max2,
 363                      parent = $( '#imgedit-crop-' + postid ),
 364                      t = imageEdit,
 365                      historyObj;
 366  
 367                  // Checks if there already is some image-edit history.
 368                  if ( '' !== event.data.history ) {
 369                      historyObj = JSON.parse( event.data.history );
 370                      // If last executed action in history is a crop action.
 371                      if ( historyObj[historyObj.length - 1].hasOwnProperty( 'c' ) ) {
 372                          /*
 373                           * A crop action has completed and the crop button gets disabled
 374                           * ensure the undo button is enabled.
 375                           */
 376                          t.setDisabled( $( '#image-undo-' + postid) , true );
 377                          // Move focus to the undo button to avoid a focus loss.
 378                          $( '#image-undo-' + postid ).focus();
 379                      }
 380                  }
 381  
 382                  parent.empty().append(img);
 383  
 384                  // w, h are the new full size dimensions.
 385                  max1 = Math.max( t.hold.w, t.hold.h );
 386                  max2 = Math.max( $(img).width(), $(img).height() );
 387                  t.hold.sizer = max1 > max2 ? max2 / max1 : 1;
 388  
 389                  t.initCrop(postid, img, parent);
 390  
 391                  if ( (typeof callback !== 'undefined') && callback !== null ) {
 392                      callback();
 393                  }
 394  
 395                  if ( $('#imgedit-history-' + postid).val() && $('#imgedit-undone-' + postid).val() === '0' ) {
 396                      $('input.imgedit-submit-btn', '#imgedit-panel-' + postid).removeAttr('disabled');
 397                  } else {
 398                      $('input.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', true);
 399                  }
 400  
 401                  t.toggleEditor(postid, 0);
 402              })
 403              .on('error', function() {
 404                  $('#imgedit-crop-' + postid).empty().append('<div class="error"><p>' + imageEditL10n.error + '</p></div>');
 405                  t.toggleEditor(postid, 0);
 406              })
 407              .attr('src', ajaxurl + '?' + $.param(data));
 408      },
 409      /**
 410       * Performs an image edit action.
 411       *
 412       * @since 2.9.0
 413       *
 414       * @memberof imageEdit
 415       *
 416       * @param  {number}  postid The post id.
 417       * @param  {string}  nonce  The nonce to verify the request.
 418       * @param  {string}  action The action to perform on the image.
 419       *                          The possible actions are: "scale" and "restore".
 420       *
 421       * @return {boolean|void} Executes a post request that refreshes the page
 422       *                        when the action is performed.
 423       *                        Returns false if a invalid action is given,
 424       *                        or when the action cannot be performed.
 425       */
 426      action : function(postid, nonce, action) {
 427          var t = this, data, w, h, fw, fh;
 428  
 429          if ( t.notsaved(postid) ) {
 430              return false;
 431          }
 432  
 433          data = {
 434              'action': 'image-editor',
 435              '_ajax_nonce': nonce,
 436              'postid': postid
 437          };
 438  
 439          if ( 'scale' === action ) {
 440              w = $('#imgedit-scale-width-' + postid),
 441              h = $('#imgedit-scale-height-' + postid),
 442              fw = t.intval(w.val()),
 443              fh = t.intval(h.val());
 444  
 445              if ( fw < 1 ) {
 446                  w.focus();
 447                  return false;
 448              } else if ( fh < 1 ) {
 449                  h.focus();
 450                  return false;
 451              }
 452  
 453              if ( fw === t.hold.ow || fh === t.hold.oh ) {
 454                  return false;
 455              }
 456  
 457              data['do'] = 'scale';
 458              data.fwidth = fw;
 459              data.fheight = fh;
 460          } else if ( 'restore' === action ) {
 461              data['do'] = 'restore';
 462          } else {
 463              return false;
 464          }
 465  
 466          t.toggleEditor(postid, 1);
 467          $.post(ajaxurl, data, function(r) {
 468              $('#image-editor-' + postid).empty().append(r);
 469              t.toggleEditor(postid, 0);
 470              // Refresh the attachment model so that changes propagate.
 471              if ( t._view ) {
 472                  t._view.refresh();
 473              }
 474          });
 475      },
 476  
 477      /**
 478       * Stores the changes that are made to the image.
 479       *
 480       * @since 2.9.0
 481       *
 482       * @memberof imageEdit
 483       *
 484       * @param {number}  postid   The post id to get the image from the database.
 485       * @param {string}  nonce    The nonce to verify the request.
 486       *
 487       * @return {boolean|void}  If the actions are successfully saved a response message is shown.
 488       *                         Returns false if there is no image editing history,
 489       *                         thus there are not edit-actions performed on the image.
 490       */
 491      save : function(postid, nonce) {
 492          var data,
 493              target = this.getTarget(postid),
 494              history = this.filterHistory(postid, 0),
 495              self = this;
 496  
 497          if ( '' === history ) {
 498              return false;
 499          }
 500  
 501          this.toggleEditor(postid, 1);
 502          data = {
 503              'action': 'image-editor',
 504              '_ajax_nonce': nonce,
 505              'postid': postid,
 506              'history': history,
 507              'target': target,
 508              'context': $('#image-edit-context').length ? $('#image-edit-context').val() : null,
 509              'do': 'save'
 510          };
 511          // Post the image edit data to the backend.
 512          $.post(ajaxurl, data, function(r) {
 513              // Read the response.
 514              var ret = JSON.parse(r);
 515  
 516              // If a response is returned, close the editor and show an error.
 517              if ( ret.error ) {
 518                  $('#imgedit-response-' + postid).html('<div class="error"><p>' + ret.error + '</p></div>');
 519                  imageEdit.close(postid);
 520                  return;
 521              }
 522  
 523              if ( ret.fw && ret.fh ) {
 524                  $('#media-dims-' + postid).html( ret.fw + ' &times; ' + ret.fh );
 525              }
 526  
 527              if ( ret.thumbnail ) {
 528                  $('.thumbnail', '#thumbnail-head-' + postid).attr('src', ''+ret.thumbnail);
 529              }
 530  
 531              if ( ret.msg ) {
 532                  $('#imgedit-response-' + postid).html('<div class="updated"><p>' + ret.msg + '</p></div>');
 533              }
 534  
 535              if ( self._view ) {
 536                  self._view.save();
 537              } else {
 538                  imageEdit.close(postid);
 539              }
 540          });
 541      },
 542  
 543      /**
 544       * Creates the image edit window.
 545       *
 546       * @since 2.9.0
 547       *
 548       * @memberof imageEdit
 549       *
 550       * @param {number} postid   The post id for the image.
 551       * @param {string} nonce    The nonce to verify the request.
 552       * @param {object} view     The image editor view to be used for the editing.
 553       *
 554       * @return {void|promise} Either returns void if the button was already activated
 555       *                        or returns an instance of the image editor, wrapped in a promise.
 556       */
 557      open : function( postid, nonce, view ) {
 558          this._view = view;
 559  
 560          var dfd, data, elem = $('#image-editor-' + postid), head = $('#media-head-' + postid),
 561              btn = $('#imgedit-open-btn-' + postid), spin = btn.siblings('.spinner');
 562  
 563          /*
 564           * Instead of disabling the button, which causes a focus loss and makes screen
 565           * readers announce "unavailable", return if the button was already clicked.
 566           */
 567          if ( btn.hasClass( 'button-activated' ) ) {
 568              return;
 569          }
 570  
 571          spin.addClass( 'is-active' );
 572  
 573          data = {
 574              'action': 'image-editor',
 575              '_ajax_nonce': nonce,
 576              'postid': postid,
 577              'do': 'open'
 578          };
 579  
 580          dfd = $.ajax({
 581              url:  ajaxurl,
 582              type: 'post',
 583              data: data,
 584              beforeSend: function() {
 585                  btn.addClass( 'button-activated' );
 586              }
 587          }).done(function( html ) {
 588              elem.html( html );
 589              head.fadeOut('fast', function(){
 590                  elem.fadeIn('fast');
 591                  btn.removeClass( 'button-activated' );
 592                  spin.removeClass( 'is-active' );
 593              });
 594              // Initialise the Image Editor now that everything is ready.
 595              imageEdit.init( postid );
 596          });
 597  
 598          return dfd;
 599      },
 600  
 601      /**
 602       * Initializes the cropping tool and sets a default cropping selection.
 603       *
 604       * @since 2.9.0
 605       *
 606       * @memberof imageEdit
 607       *
 608       * @param {number} postid The post id.
 609       *
 610       * @return {void}
 611       */
 612      imgLoaded : function(postid) {
 613          var img = $('#image-preview-' + postid), parent = $('#imgedit-crop-' + postid);
 614  
 615          // Ensure init has run even when directly loaded.
 616          if ( 'undefined' === typeof this.hold.sizer ) {
 617              this.init( postid );
 618          }
 619  
 620          this.initCrop(postid, img, parent);
 621          this.setCropSelection( postid, { 'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0, 'width': img.innerWidth(), 'height': img.innerHeight() } );
 622  
 623          this.toggleEditor(postid, 0);
 624          // Editor is ready, move focus to the first focusable element.
 625          $( '.imgedit-wrap .imgedit-help-toggle' ).eq( 0 ).focus();
 626      },
 627  
 628      /**
 629       * Initializes the cropping tool.
 630       *
 631       * @since 2.9.0
 632       *
 633       * @memberof imageEdit
 634       *
 635       * @param {number}      postid The post id.
 636       * @param {HTMLElement} image  The preview image.
 637       * @param {HTMLElement} parent The preview image container.
 638       *
 639       * @return {void}
 640       */
 641      initCrop : function(postid, image, parent) {
 642          var t = this,
 643              selW = $('#imgedit-sel-width-' + postid),
 644              selH = $('#imgedit-sel-height-' + postid),
 645              $image = $( image ),
 646              $img;
 647  
 648          // Already initialized?
 649          if ( $image.data( 'imgAreaSelect' ) ) {
 650              return;
 651          }
 652  
 653          t.iasapi = $image.imgAreaSelect({
 654              parent: parent,
 655              instance: true,
 656              handles: true,
 657              keys: true,
 658              minWidth: 3,
 659              minHeight: 3,
 660  
 661              /**
 662               * Sets the CSS styles and binds events for locking the aspect ratio.
 663               *
 664               * @ignore
 665               *
 666               * @param {jQuery} img The preview image.
 667               */
 668              onInit: function( img ) {
 669                  // Ensure that the imgAreaSelect wrapper elements are position:absolute
 670                  // (even if we're in a position:fixed modal).
 671                  $img = $( img );
 672                  $img.next().css( 'position', 'absolute' )
 673                      .nextAll( '.imgareaselect-outer' ).css( 'position', 'absolute' );
 674                  /**
 675                   * Binds mouse down event to the cropping container.
 676                   *
 677                   * @return {void}
 678                   */
 679                  parent.children().on( 'mousedown, touchstart', function(e){
 680                      var ratio = false, sel, defRatio;
 681  
 682                      if ( e.shiftKey ) {
 683                          sel = t.iasapi.getSelection();
 684                          defRatio = t.getSelRatio(postid);
 685                          ratio = ( sel && sel.width && sel.height ) ? sel.width + ':' + sel.height : defRatio;
 686                      }
 687  
 688                      t.iasapi.setOptions({
 689                          aspectRatio: ratio
 690                      });
 691                  });
 692              },
 693  
 694              /**
 695               * Event triggered when starting a selection.
 696               *
 697               * @ignore
 698               *
 699               * @return {void}
 700               */
 701              onSelectStart: function() {
 702                  imageEdit.setDisabled($('#imgedit-crop-sel-' + postid), 1);
 703              },
 704              /**
 705               * Event triggered when the selection is ended.
 706               *
 707               * @ignore
 708               *
 709               * @param {object} img jQuery object representing the image.
 710               * @param {object} c   The selection.
 711               *
 712               * @return {object}
 713               */
 714              onSelectEnd: function(img, c) {
 715                  imageEdit.setCropSelection(postid, c);
 716              },
 717  
 718              /**
 719               * Event triggered when the selection changes.
 720               *
 721               * @ignore
 722               *
 723               * @param {object} img jQuery object representing the image.
 724               * @param {object} c   The selection.
 725               *
 726               * @return {void}
 727               */
 728              onSelectChange: function(img, c) {
 729                  var sizer = imageEdit.hold.sizer;
 730                  selW.val( imageEdit.round(c.width / sizer) );
 731                  selH.val( imageEdit.round(c.height / sizer) );
 732              }
 733          });
 734      },
 735  
 736      /**
 737       * Stores the current crop selection.
 738       *
 739       * @since 2.9.0
 740       *
 741       * @memberof imageEdit
 742       *
 743       * @param {number} postid The post id.
 744       * @param {object} c      The selection.
 745       *
 746       * @return {boolean}
 747       */
 748      setCropSelection : function(postid, c) {
 749          var sel;
 750  
 751          c = c || 0;
 752  
 753          if ( !c || ( c.width < 3 && c.height < 3 ) ) {
 754              this.setDisabled( $( '.imgedit-crop', '#imgedit-panel-' + postid ), 1 );
 755              this.setDisabled( $( '#imgedit-crop-sel-' + postid ), 1 );
 756              $('#imgedit-sel-width-' + postid).val('');
 757              $('#imgedit-sel-height-' + postid).val('');
 758              $('#imgedit-selection-' + postid).val('');
 759              return false;
 760          }
 761  
 762          sel = { 'x': c.x1, 'y': c.y1, 'w': c.width, 'h': c.height };
 763          this.setDisabled($('.imgedit-crop', '#imgedit-panel-' + postid), 1);
 764          $('#imgedit-selection-' + postid).val( JSON.stringify(sel) );
 765      },
 766  
 767  
 768      /**
 769       * Closes the image editor.
 770       *
 771       * @since 2.9.0
 772       *
 773       * @memberof imageEdit
 774       *
 775       * @param {number}  postid The post id.
 776       * @param {bool}    warn   Warning message.
 777       *
 778       * @return {void|bool} Returns false if there is a warning.
 779       */
 780      close : function(postid, warn) {
 781          warn = warn || false;
 782  
 783          if ( warn && this.notsaved(postid) ) {
 784              return false;
 785          }
 786  
 787          this.iasapi = {};
 788          this.hold = {};
 789  
 790          // If we've loaded the editor in the context of a Media Modal,
 791          // then switch to the previous view, whatever that might have been.
 792          if ( this._view ){
 793              this._view.back();
 794          }
 795  
 796          // In case we are not accessing the image editor in the context of a View,
 797          // close the editor the old-school way.
 798          else {
 799              $('#image-editor-' + postid).fadeOut('fast', function() {
 800                  $( '#media-head-' + postid ).fadeIn( 'fast', function() {
 801                      // Move focus back to the Edit Image button. Runs also when saving.
 802                      $( '#imgedit-open-btn-' + postid ).focus();
 803                  });
 804                  $(this).empty();
 805              });
 806          }
 807  
 808  
 809      },
 810  
 811      /**
 812       * Checks if the image edit history is saved.
 813       *
 814       * @since 2.9.0
 815       *
 816       * @memberof imageEdit
 817       *
 818       * @param {number} postid The post id.
 819       *
 820       * @return {boolean} Returns true if the history is not saved.
 821       */
 822      notsaved : function(postid) {
 823          var h = $('#imgedit-history-' + postid).val(),
 824              history = ( h !== '' ) ? JSON.parse(h) : [],
 825              pop = this.intval( $('#imgedit-undone-' + postid).val() );
 826  
 827          if ( pop < history.length ) {
 828              if ( confirm( $('#imgedit-leaving-' + postid).html() ) ) {
 829                  return false;
 830              }
 831              return true;
 832          }
 833          return false;
 834      },
 835  
 836      /**
 837       * Adds an image edit action to the history.
 838       *
 839       * @since 2.9.0
 840       *
 841       * @memberof imageEdit
 842       *
 843       * @param {object} op     The original position.
 844       * @param {number} postid The post id.
 845       * @param {string} nonce  The nonce.
 846       *
 847       * @return {void}
 848       */
 849      addStep : function(op, postid, nonce) {
 850          var t = this, elem = $('#imgedit-history-' + postid),
 851              history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [],
 852              undone = $( '#imgedit-undone-' + postid ),
 853              pop = t.intval( undone.val() );
 854  
 855          while ( pop > 0 ) {
 856              history.pop();
 857              pop--;
 858          }
 859          undone.val(0); // Reset.
 860  
 861          history.push(op);
 862          elem.val( JSON.stringify(history) );
 863  
 864          t.refreshEditor(postid, nonce, function() {
 865              t.setDisabled($('#image-undo-' + postid), true);
 866              t.setDisabled($('#image-redo-' + postid), false);
 867          });
 868      },
 869  
 870      /**
 871       * Rotates the image.
 872       *
 873       * @since 2.9.0
 874       *
 875       * @memberof imageEdit
 876       *
 877       * @param {string} angle  The angle the image is rotated with.
 878       * @param {number} postid The post id.
 879       * @param {string} nonce  The nonce.
 880       * @param {object} t      The target element.
 881       *
 882       * @return {boolean}
 883       */
 884      rotate : function(angle, postid, nonce, t) {
 885          if ( $(t).hasClass('disabled') ) {
 886              return false;
 887          }
 888  
 889          this.addStep({ 'r': { 'r': angle, 'fw': this.hold.h, 'fh': this.hold.w }}, postid, nonce);
 890      },
 891  
 892      /**
 893       * Flips the image.
 894       *
 895       * @since 2.9.0
 896       *
 897       * @memberof imageEdit
 898       *
 899       * @param {number} axis   The axle the image is flipped on.
 900       * @param {number} postid The post id.
 901       * @param {string} nonce  The nonce.
 902       * @param {object} t      The target element.
 903       *
 904       * @return {boolean}
 905       */
 906      flip : function (axis, postid, nonce, t) {
 907          if ( $(t).hasClass('disabled') ) {
 908              return false;
 909          }
 910  
 911          this.addStep({ 'f': { 'f': axis, 'fw': this.hold.w, 'fh': this.hold.h }}, postid, nonce);
 912      },
 913  
 914      /**
 915       * Crops the image.
 916       *
 917       * @since 2.9.0
 918       *
 919       * @memberof imageEdit
 920       *
 921       * @param {number} postid The post id.
 922       * @param {string} nonce  The nonce.
 923       * @param {object} t      The target object.
 924       *
 925       * @return {void|boolean} Returns false if the crop button is disabled.
 926       */
 927      crop : function (postid, nonce, t) {
 928          var sel = $('#imgedit-selection-' + postid).val(),
 929              w = this.intval( $('#imgedit-sel-width-' + postid).val() ),
 930              h = this.intval( $('#imgedit-sel-height-' + postid).val() );
 931  
 932          if ( $(t).hasClass('disabled') || sel === '' ) {
 933              return false;
 934          }
 935  
 936          sel = JSON.parse(sel);
 937          if ( sel.w > 0 && sel.h > 0 && w > 0 && h > 0 ) {
 938              sel.fw = w;
 939              sel.fh = h;
 940              this.addStep({ 'c': sel }, postid, nonce);
 941          }
 942      },
 943  
 944      /**
 945       * Undoes an image edit action.
 946       *
 947       * @since 2.9.0
 948       *
 949       * @memberof imageEdit
 950       *
 951       * @param {number} postid   The post id.
 952       * @param {string} nonce    The nonce.
 953       *
 954       * @return {void|false} Returns false if the undo button is disabled.
 955       */
 956      undo : function (postid, nonce) {
 957          var t = this, button = $('#image-undo-' + postid), elem = $('#imgedit-undone-' + postid),
 958              pop = t.intval( elem.val() ) + 1;
 959  
 960          if ( button.hasClass('disabled') ) {
 961              return;
 962          }
 963  
 964          elem.val(pop);
 965          t.refreshEditor(postid, nonce, function() {
 966              var elem = $('#imgedit-history-' + postid),
 967                  history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [];
 968  
 969              t.setDisabled($('#image-redo-' + postid), true);
 970              t.setDisabled(button, pop < history.length);
 971              // When undo gets disabled, move focus to the redo button to avoid a focus loss.
 972              if ( history.length === pop ) {
 973                  $( '#image-redo-' + postid ).focus();
 974              }
 975          });
 976      },
 977  
 978      /**
 979       * Reverts a undo action.
 980       *
 981       * @since 2.9.0
 982       *
 983       * @memberof imageEdit
 984       *
 985       * @param {number} postid The post id.
 986       * @param {string} nonce  The nonce.
 987       *
 988       * @return {void}
 989       */
 990      redo : function(postid, nonce) {
 991          var t = this, button = $('#image-redo-' + postid), elem = $('#imgedit-undone-' + postid),
 992              pop = t.intval( elem.val() ) - 1;
 993  
 994          if ( button.hasClass('disabled') ) {
 995              return;
 996          }
 997  
 998          elem.val(pop);
 999          t.refreshEditor(postid, nonce, function() {
1000              t.setDisabled($('#image-undo-' + postid), true);
1001              t.setDisabled(button, pop > 0);
1002              // When redo gets disabled, move focus to the undo button to avoid a focus loss.
1003              if ( 0 === pop ) {
1004                  $( '#image-undo-' + postid ).focus();
1005              }
1006          });
1007      },
1008  
1009      /**
1010       * Sets the selection for the height and width in pixels.
1011       *
1012       * @since 2.9.0
1013       *
1014       * @memberof imageEdit
1015       *
1016       * @param {number} postid The post id.
1017       * @param {jQuery} el     The element containing the values.
1018       *
1019       * @return {void|boolean} Returns false when the x or y value is lower than 1,
1020       *                        void when the value is not numeric or when the operation
1021       *                        is successful.
1022       */
1023      setNumSelection : function( postid, el ) {
1024          var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid),
1025              x = this.intval( elX.val() ), y = this.intval( elY.val() ),
1026              img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(),
1027              sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi;
1028  
1029          if ( false === this.validateNumeric( el ) ) {
1030              return;
1031          }
1032  
1033          if ( x < 1 ) {
1034              elX.val('');
1035              return false;
1036          }
1037  
1038          if ( y < 1 ) {
1039              elY.val('');
1040              return false;
1041          }
1042  
1043          if ( x && y && ( sel = ias.getSelection() ) ) {
1044              x2 = sel.x1 + Math.round( x * sizer );
1045              y2 = sel.y1 + Math.round( y * sizer );
1046              x1 = sel.x1;
1047              y1 = sel.y1;
1048  
1049              if ( x2 > imgw ) {
1050                  x1 = 0;
1051                  x2 = imgw;
1052                  elX.val( Math.round( x2 / sizer ) );
1053              }
1054  
1055              if ( y2 > imgh ) {
1056                  y1 = 0;
1057                  y2 = imgh;
1058                  elY.val( Math.round( y2 / sizer ) );
1059              }
1060  
1061              ias.setSelection( x1, y1, x2, y2 );
1062              ias.update();
1063              this.setCropSelection(postid, ias.getSelection());
1064          }
1065      },
1066  
1067      /**
1068       * Rounds a number to a whole.
1069       *
1070       * @since 2.9.0
1071       *
1072       * @memberof imageEdit
1073       *
1074       * @param {number} num The number.
1075       *
1076       * @return {number} The number rounded to a whole number.
1077       */
1078      round : function(num) {
1079          var s;
1080          num = Math.round(num);
1081  
1082          if ( this.hold.sizer > 0.6 ) {
1083              return num;
1084          }
1085  
1086          s = num.toString().slice(-1);
1087  
1088          if ( '1' === s ) {
1089              return num - 1;
1090          } else if ( '9' === s ) {
1091              return num + 1;
1092          }
1093  
1094          return num;
1095      },
1096  
1097      /**
1098       * Sets a locked aspect ratio for the selection.
1099       *
1100       * @since 2.9.0
1101       *
1102       * @memberof imageEdit
1103       *
1104       * @param {number} postid     The post id.
1105       * @param {number} n          The ratio to set.
1106       * @param {jQuery} el         The element containing the values.
1107       *
1108       * @return {void}
1109       */
1110      setRatioSelection : function(postid, n, el) {
1111          var sel, r, x = this.intval( $('#imgedit-crop-width-' + postid).val() ),
1112              y = this.intval( $('#imgedit-crop-height-' + postid).val() ),
1113              h = $('#image-preview-' + postid).height();
1114  
1115          if ( false === this.validateNumeric( el ) ) {
1116              this.iasapi.setOptions({
1117                  aspectRatio: null
1118              });
1119  
1120              return;
1121          }
1122  
1123          if ( x && y ) {
1124              this.iasapi.setOptions({
1125                  aspectRatio: x + ':' + y
1126              });
1127  
1128              if ( sel = this.iasapi.getSelection(true) ) {
1129                  r = Math.ceil( sel.y1 + ( ( sel.x2 - sel.x1 ) / ( x / y ) ) );
1130  
1131                  if ( r > h ) {
1132                      r = h;
1133                      if ( n ) {
1134                          $('#imgedit-crop-height-' + postid).val('');
1135                      } else {
1136                          $('#imgedit-crop-width-' + postid).val('');
1137                      }
1138                  }
1139  
1140                  this.iasapi.setSelection( sel.x1, sel.y1, sel.x2, r );
1141                  this.iasapi.update();
1142              }
1143          }
1144      },
1145  
1146      /**
1147       * Validates if a value in a jQuery.HTMLElement is numeric.
1148       *
1149       * @since 4.6.0
1150       *
1151       * @memberof imageEdit
1152       *
1153       * @param {jQuery} el The html element.
1154       *
1155       * @return {void|boolean} Returns false if the value is not numeric,
1156       *                        void when it is.
1157       */
1158      validateNumeric: function( el ) {
1159          if ( ! this.intval( $( el ).val() ) ) {
1160              $( el ).val( '' );
1161              return false;
1162          }
1163      }
1164  };
1165  })(jQuery);


Generated: Sun Mar 29 01:00:04 2020 Cross-referenced by PHPXref 0.7.1