[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  /* global adminCommentsSettings, thousandsSeparator, list_args, QTags, ajaxurl, wpAjax */
   2  /* global commentReply, theExtraList, theList, setCommentsList */
   3  
   4  /**
   5   * @output wp-admin/js/edit-comments.js
   6   */
   7  
   8  /**
   9   * Handles updating and editing comments.
  10   *
  11   * @param {JQueryStatic} $ The jQuery object.
  12   */
  13  (function($) {
  14  var getCount, updateCount, updateCountText, updatePending, updateApproved,
  15      updateHtmlTitle, updateDashboardText, updateInModerationText, adminTitle = document.title,
  16      isDashboard = $('#dashboard_right_now').length,
  17      titleDiv, titleRegEx,
  18      __ = wp.i18n.__, _x = wp.i18n._x;
  19  
  20      /**
  21       * Extracts a number from the content of a jQuery element.
  22       *
  23       * @since 2.9.0
  24       * @access private
  25       *
  26       * @param {jQuery} el jQuery element.
  27       *
  28       * @return {number} The number found in the given element.
  29       */
  30      getCount = function(el) {
  31          var n = parseInt( el.html().replace(/[^0-9]+/g, ''), 10 );
  32          if ( isNaN(n) ) {
  33              return 0;
  34          }
  35          return n;
  36      };
  37  
  38      /**
  39       * Updates an html element with a localized number string.
  40       *
  41       * @since 2.9.0
  42       * @access private
  43       *
  44       * @param {jQuery} el The jQuery element to update.
  45       * @param {number} n  Number to be put in the element.
  46       *
  47       * @return {void}
  48       */
  49      updateCount = function(el, n) {
  50          var n1 = '';
  51          if ( isNaN(n) ) {
  52              return;
  53          }
  54          n = n < 1 ? '0' : n.toString();
  55          if ( n.length > 3 ) {
  56              while ( n.length > 3 ) {
  57                  n1 = thousandsSeparator + n.substr(n.length - 3) + n1;
  58                  n = n.substr(0, n.length - 3);
  59              }
  60              n = n + n1;
  61          }
  62          el.html(n);
  63      };
  64  
  65      /**
  66       * Updates the number of approved comments on a specific post and the filter bar.
  67       *
  68       * @since 4.4.0
  69       * @access private
  70       *
  71       * @param {number} diff          The amount to lower or raise the approved count with.
  72       * @param {number} commentPostId The ID of the post to be updated.
  73       *
  74       * @return {void}
  75       */
  76      updateApproved = function( diff, commentPostId ) {
  77          var postSelector = '.post-com-count-' + commentPostId,
  78              noClass = 'comment-count-no-comments',
  79              approvedClass = 'comment-count-approved',
  80              approved,
  81              noComments;
  82  
  83          updateCountText( 'span.approved-count', diff );
  84  
  85          if ( ! commentPostId ) {
  86              return;
  87          }
  88  
  89          // Cache selectors to not get duplicates.
  90          approved = $( 'span.' + approvedClass, postSelector );
  91          noComments = $( 'span.' + noClass, postSelector );
  92  
  93          approved.each(function() {
  94              var a = $(this), n = getCount(a) + diff;
  95              if ( n < 1 )
  96                  n = 0;
  97  
  98              if ( 0 === n ) {
  99                  a.removeClass( approvedClass ).addClass( noClass );
 100              } else {
 101                  a.addClass( approvedClass ).removeClass( noClass );
 102              }
 103              updateCount( a, n );
 104          });
 105  
 106          noComments.each(function() {
 107              var a = $(this);
 108              if ( diff > 0 ) {
 109                  a.removeClass( noClass ).addClass( approvedClass );
 110              } else {
 111                  a.addClass( noClass ).removeClass( approvedClass );
 112              }
 113              updateCount( a, diff );
 114          });
 115      };
 116  
 117      /**
 118       * Updates a number count in all matched HTML elements
 119       *
 120       * @since 4.4.0
 121       * @access private
 122       *
 123       * @param {string} selector The jQuery selector for elements to update a count
 124       *                          for.
 125       * @param {number} diff     The amount to lower or raise the count with.
 126       *
 127       * @return {void}
 128       */
 129      updateCountText = function( selector, diff ) {
 130          $( selector ).each(function() {
 131              var a = $(this), n = getCount(a) + diff;
 132              if ( n < 1 ) {
 133                  n = 0;
 134              }
 135              updateCount( a, n );
 136          });
 137      };
 138  
 139      /**
 140       * Updates a text about comment count on the dashboard.
 141       *
 142       * @since 4.4.0
 143       * @access private
 144       *
 145       * @param {Object} response Ajax response from the server that includes a
 146       *                          translated "comment count" message.
 147       *
 148       * @return {void}
 149       */
 150      updateDashboardText = function( response ) {
 151          if ( ! isDashboard || ! response || ! response.i18n_comments_text ) {
 152              return;
 153          }
 154  
 155          $( '.comment-count a', '#dashboard_right_now' ).text( response.i18n_comments_text );
 156      };
 157  
 158      /**
 159       * Updates the "comments in moderation" text across the UI.
 160       *
 161       * @since 5.2.0
 162       *
 163       * @param {Object} response Ajax response from the server that includes a
 164       *                          translated "comments in moderation" message.
 165       *
 166       * @return {void}
 167       */
 168      updateInModerationText = function( response ) {
 169          if ( ! response || ! response.i18n_moderation_text ) {
 170              return;
 171          }
 172  
 173          // Update the "comment in moderation" text across the UI.
 174          $( '.comments-in-moderation-text' ).text( response.i18n_moderation_text );
 175          // Hide the "comment in moderation" text in the Dashboard "At a Glance" widget.
 176          if ( isDashboard && response.in_moderation ) {
 177              $( '.comment-mod-count', '#dashboard_right_now' )
 178                  [ response.in_moderation > 0 ? 'removeClass' : 'addClass' ]( 'hidden' );
 179          }
 180      };
 181  
 182      /**
 183       * Updates the title of the document with the number comments to be approved.
 184       *
 185       * @since 4.4.0
 186       * @access private
 187       *
 188       * @param {number} diff The amount to lower or raise the number of to be
 189       *                      approved comments with.
 190       *
 191       * @return {void}
 192       */
 193      updateHtmlTitle = function( diff ) {
 194          var newTitle, regExMatch, titleCount, commentFrag;
 195  
 196          /* translators: %s: Comments count. */
 197          titleRegEx = titleRegEx || new RegExp( __( 'Comments (%s)' ).replace( '%s', '\\([0-9' + thousandsSeparator + ']+\\)' ) + '?' );
 198          // Count funcs operate on a $'d element.
 199          titleDiv = titleDiv || $( '<div />' );
 200          newTitle = adminTitle;
 201  
 202          commentFrag = titleRegEx.exec( document.title );
 203          if ( commentFrag ) {
 204              commentFrag = commentFrag[0];
 205              titleDiv.html( commentFrag );
 206              titleCount = getCount( titleDiv ) + diff;
 207          } else {
 208              titleDiv.html( 0 );
 209              titleCount = diff;
 210          }
 211  
 212          if ( titleCount >= 1 ) {
 213              updateCount( titleDiv, titleCount );
 214              regExMatch = titleRegEx.exec( document.title );
 215              if ( regExMatch ) {
 216                  /* translators: %s: Comments count. */
 217                  newTitle = document.title.replace( regExMatch[0], __( 'Comments (%s)' ).replace( '%s', titleDiv.text() ) + ' ' );
 218              }
 219          } else {
 220              regExMatch = titleRegEx.exec( newTitle );
 221              if ( regExMatch ) {
 222                  newTitle = newTitle.replace( regExMatch[0], __( 'Comments' ) );
 223              }
 224          }
 225          document.title = newTitle;
 226      };
 227  
 228      /**
 229       * Updates the number of pending comments on a specific post and the filter bar.
 230       *
 231       * @since 3.2.0
 232       * @access private
 233       *
 234       * @param {number} diff          The amount to lower or raise the pending count with.
 235       * @param {number} commentPostId The ID of the post to be updated.
 236       *
 237       * @return {void}
 238       */
 239      updatePending = function( diff, commentPostId ) {
 240          var postSelector = '.post-com-count-' + commentPostId,
 241              noClass = 'comment-count-no-pending',
 242              noParentClass = 'post-com-count-no-pending',
 243              pendingClass = 'comment-count-pending',
 244              pending,
 245              noPending;
 246  
 247          if ( ! isDashboard ) {
 248              updateHtmlTitle( diff );
 249          }
 250  
 251          $( 'span.pending-count' ).each(function() {
 252              var a = $(this), n = getCount(a) + diff;
 253              if ( n < 1 )
 254                  n = 0;
 255              a.closest('.awaiting-mod')[ 0 === n ? 'addClass' : 'removeClass' ]('count-0');
 256              updateCount( a, n );
 257          });
 258  
 259          if ( ! commentPostId ) {
 260              return;
 261          }
 262  
 263          // Cache selectors to not get dupes.
 264          pending = $( 'span.' + pendingClass, postSelector );
 265          noPending = $( 'span.' + noClass, postSelector );
 266  
 267          pending.each(function() {
 268              var a = $(this), n = getCount(a) + diff;
 269              if ( n < 1 )
 270                  n = 0;
 271  
 272              if ( 0 === n ) {
 273                  a.parent().addClass( noParentClass );
 274                  a.removeClass( pendingClass ).addClass( noClass );
 275              } else {
 276                  a.parent().removeClass( noParentClass );
 277                  a.addClass( pendingClass ).removeClass( noClass );
 278              }
 279              updateCount( a, n );
 280          });
 281  
 282          noPending.each(function() {
 283              var a = $(this);
 284              if ( diff > 0 ) {
 285                  a.parent().removeClass( noParentClass );
 286                  a.removeClass( noClass ).addClass( pendingClass );
 287              } else {
 288                  a.parent().addClass( noParentClass );
 289                  a.addClass( noClass ).removeClass( pendingClass );
 290              }
 291              updateCount( a, diff );
 292          });
 293      };
 294  
 295  /**
 296   * Initializes the comments list.
 297   *
 298   * @since 4.4.0
 299   *
 300   * @global
 301   *
 302   * @return {void}
 303   */
 304  window.setCommentsList = function() {
 305      var totalInput, perPageInput, pageInput, dimAfter, delBefore, updateTotalCount, delAfter, refillTheExtraList, diff,
 306          lastConfidentTime = 0;
 307  
 308      totalInput = $('input[name="_total"]', '#comments-form');
 309      perPageInput = $('input[name="_per_page"]', '#comments-form');
 310      pageInput = $('input[name="_page"]', '#comments-form');
 311  
 312      /**
 313       * Updates the total with the latest count.
 314       *
 315       * The time parameter makes sure that we only update the total if this value is
 316       * a newer value than we previously received.
 317       *
 318       * The time and setConfidentTime parameters make sure that we only update the
 319       * total when necessary. So a value that has been generated earlier will not
 320       * update the total.
 321       *
 322       * @since 2.8.0
 323       * @access private
 324       *
 325       * @param {number}  total            Total number of comments.
 326       * @param {number}  time             Unix timestamp of response.
 327       * @param {boolean} setConfidentTime Whether to update the last confident time
 328       *                                   with the given time.
 329       *
 330       * @return {void}
 331       */
 332      updateTotalCount = function( total, time, setConfidentTime ) {
 333          if ( time < lastConfidentTime )
 334              return;
 335  
 336          if ( setConfidentTime )
 337              lastConfidentTime = time;
 338  
 339          totalInput.val( total.toString() );
 340      };
 341  
 342      /**
 343       * Changes DOM that need to be changed after a list item has been dimmed.
 344       *
 345       * @since 2.5.0
 346       * @access private
 347       *
 348       * @param {Object} r        Ajax response object.
 349       * @param {Object} settings Settings for the wpList object.
 350       *
 351       * @return {void}
 352       */
 353      dimAfter = function( r, settings ) {
 354          var editRow, replyID, replyButton, response,
 355              c = $( '#' + settings.element );
 356  
 357          if ( true !== settings.parsed ) {
 358              response = settings.parsed.responses[0];
 359          }
 360  
 361          editRow = $('#replyrow');
 362          replyID = $('#comment_ID', editRow).val();
 363          replyButton = $('#replybtn', editRow);
 364  
 365          if ( c.is('.unapproved') ) {
 366              if ( settings.data.id == replyID )
 367                  replyButton.text( __( 'Approve and Reply' ) );
 368  
 369              c.find( '.row-actions span.view' ).addClass( 'hidden' ).end()
 370                  .find( 'div.comment_status' ).html( '0' );
 371  
 372          } else {
 373              if ( settings.data.id == replyID )
 374                  /* translators: Comment reply button text. */
 375                  replyButton.text( _x( 'Reply', 'verb' ) );
 376  
 377              c.find( '.row-actions span.view' ).removeClass( 'hidden' ).end()
 378                  .find( 'div.comment_status' ).html( '1' );
 379          }
 380  
 381          diff = $('#' + settings.element).is('.' + settings.dimClass) ? 1 : -1;
 382          if ( response ) {
 383              updateDashboardText( response.supplemental );
 384              updateInModerationText( response.supplemental );
 385              updatePending( diff, response.supplemental.postId );
 386              updateApproved( -1 * diff, response.supplemental.postId );
 387          } else {
 388              updatePending( diff );
 389              updateApproved( -1 * diff  );
 390          }
 391      };
 392  
 393      /**
 394       * Handles marking a comment as spam or trashing the comment.
 395       *
 396       * Is executed in the list delBefore hook.
 397       *
 398       * @since 2.8.0
 399       * @access private
 400       *
 401       * @param {Object}      settings Settings for the wpList object.
 402       * @param {HTMLElement} list     Comments table element.
 403       *
 404       * @return {Object} The settings object.
 405       */
 406      delBefore = function( settings, list ) {
 407          var note, id, el, n, h, a, author,
 408              action = false,
 409              wpListsData = $( settings.target ).attr( 'data-wp-lists' );
 410  
 411          settings.data._total = totalInput.val() || 0;
 412          settings.data._per_page = perPageInput.val() || 0;
 413          settings.data._page = pageInput.val() || 0;
 414          settings.data._url = document.location.href;
 415          settings.data.comment_status = $('input[name="comment_status"]', '#comments-form').val();
 416  
 417          if ( wpListsData.indexOf(':trash=1') != -1 )
 418              action = 'trash';
 419          else if ( wpListsData.indexOf(':spam=1') != -1 )
 420              action = 'spam';
 421  
 422          if ( action ) {
 423              id = wpListsData.replace(/.*?comment-([0-9]+).*/, '$1');
 424              el = $('#comment-' + id);
 425              note = $('#' + action + '-undo-holder').html();
 426  
 427              el.find('.check-column :checkbox').prop('checked', false); // Uncheck the row so as not to be affected by Bulk Edits.
 428  
 429              if ( el.siblings('#replyrow').length && commentReply.cid == id )
 430                  commentReply.close();
 431  
 432              if ( el.is('tr') ) {
 433                  n = el.children(':visible').length;
 434                  author = $('.author strong', el).text();
 435                  h = $('<tr id="undo-' + id + '" class="undo un' + action + '" style="display:none;"><td colspan="' + n + '">' + note + '</td></tr>');
 436              } else {
 437                  author = $('.comment-author', el).text();
 438                  h = $('<div id="undo-' + id + '" style="display:none;" class="undo un' + action + '">' + note + '</div>');
 439              }
 440  
 441              el.before(h);
 442  
 443              $('strong', '#undo-' + id).text(author);
 444              a = $('.undo a', '#undo-' + id);
 445              a.attr('href', 'comment.php?action=un' + action + 'comment&c=' + id + '&_wpnonce=' + settings.data._ajax_nonce);
 446              a.attr('data-wp-lists', 'delete:the-comment-list:comment-' + id + '::un' + action + '=1');
 447              a.attr('class', 'vim-z vim-destructive aria-button-if-js');
 448              $('.avatar', el).first().clone().prependTo('#undo-' + id + ' .' + action + '-undo-inside');
 449  
 450              a.on( 'click', function( e ){
 451                  e.preventDefault();
 452                  e.stopPropagation(); // Ticket #35904.
 453                  list.wpList.del(this);
 454                  $('#undo-' + id).css( {backgroundColor:'#ceb'} ).fadeOut(350, function(){
 455                      $(this).remove();
 456                      $('#comment-' + id).css('backgroundColor', '').fadeIn(300, function(){ $(this).show(); });
 457                  });
 458              });
 459          }
 460  
 461          return settings;
 462      };
 463  
 464      /**
 465       * Handles actions that need to be done after marking as spam or thrashing a
 466       * comment.
 467       *
 468       * The ajax requests return the unix time stamp a comment was marked as spam or
 469       * trashed. We use this to have a correct total amount of comments.
 470       *
 471       * @since 2.5.0
 472       * @access private
 473       *
 474       * @param {Object} r        Ajax response object.
 475       * @param {Object} settings Settings for the wpList object.
 476       *
 477       * @return {void}
 478       */
 479      delAfter = function( r, settings ) {
 480          var total_items_i18n, total, animated, animatedCallback,
 481              response = true === settings.parsed ? {} : settings.parsed.responses[0],
 482              commentStatus = true === settings.parsed ? '' : response.supplemental.status,
 483              commentPostId = true === settings.parsed ? '' : response.supplemental.postId,
 484              newTotal = true === settings.parsed ? '' : response.supplemental,
 485  
 486              targetParent = $( settings.target ).parent(),
 487              commentRow = $('#' + settings.element),
 488  
 489              spamDiff, trashDiff, pendingDiff, approvedDiff,
 490  
 491              /*
 492               * As `wpList` toggles only the `unapproved` class, the approved comment
 493               * rows can have both the `approved` and `unapproved` classes.
 494               */
 495              approved = commentRow.hasClass( 'approved' ) && ! commentRow.hasClass( 'unapproved' ),
 496              unapproved = commentRow.hasClass( 'unapproved' ),
 497              spammed = commentRow.hasClass( 'spam' ),
 498              trashed = commentRow.hasClass( 'trash' ),
 499              undoing = false; // Ticket #35904.
 500  
 501          updateDashboardText( newTotal );
 502          updateInModerationText( newTotal );
 503  
 504          /*
 505           * The order of these checks is important.
 506           * .unspam can also have .approve or .unapprove.
 507           * .untrash can also have .approve or .unapprove.
 508           */
 509  
 510          if ( targetParent.is( 'span.undo' ) ) {
 511              // The comment was spammed.
 512              if ( targetParent.hasClass( 'unspam' ) ) {
 513                  spamDiff = -1;
 514  
 515                  if ( 'trash' === commentStatus ) {
 516                      trashDiff = 1;
 517                  } else if ( '1' === commentStatus ) {
 518                      approvedDiff = 1;
 519                  } else if ( '0' === commentStatus ) {
 520                      pendingDiff = 1;
 521                  }
 522  
 523              // The comment was trashed.
 524              } else if ( targetParent.hasClass( 'untrash' ) ) {
 525                  trashDiff = -1;
 526  
 527                  if ( 'spam' === commentStatus ) {
 528                      spamDiff = 1;
 529                  } else if ( '1' === commentStatus ) {
 530                      approvedDiff = 1;
 531                  } else if ( '0' === commentStatus ) {
 532                      pendingDiff = 1;
 533                  }
 534              }
 535  
 536              undoing = true;
 537  
 538          // User clicked "Spam".
 539          } else if ( targetParent.is( 'span.spam' ) ) {
 540              // The comment is currently approved.
 541              if ( approved ) {
 542                  approvedDiff = -1;
 543              // The comment is currently pending.
 544              } else if ( unapproved ) {
 545                  pendingDiff = -1;
 546              // The comment was in the Trash.
 547              } else if ( trashed ) {
 548                  trashDiff = -1;
 549              }
 550              // You can't spam an item on the Spam screen.
 551              spamDiff = 1;
 552  
 553          // User clicked "Unspam".
 554          } else if ( targetParent.is( 'span.unspam' ) ) {
 555              if ( approved ) {
 556                  pendingDiff = 1;
 557              } else if ( unapproved ) {
 558                  approvedDiff = 1;
 559              } else if ( trashed ) {
 560                  // The comment was previously approved.
 561                  if ( targetParent.hasClass( 'approve' ) ) {
 562                      approvedDiff = 1;
 563                  // The comment was previously pending.
 564                  } else if ( targetParent.hasClass( 'unapprove' ) ) {
 565                      pendingDiff = 1;
 566                  }
 567              } else if ( spammed ) {
 568                  if ( targetParent.hasClass( 'approve' ) ) {
 569                      approvedDiff = 1;
 570  
 571                  } else if ( targetParent.hasClass( 'unapprove' ) ) {
 572                      pendingDiff = 1;
 573                  }
 574              }
 575              // You can unspam an item on the Spam screen.
 576              spamDiff = -1;
 577  
 578          // User clicked "Trash".
 579          } else if ( targetParent.is( 'span.trash' ) ) {
 580              if ( approved ) {
 581                  approvedDiff = -1;
 582              } else if ( unapproved ) {
 583                  pendingDiff = -1;
 584              // The comment was in the spam queue.
 585              } else if ( spammed ) {
 586                  spamDiff = -1;
 587              }
 588              // You can't trash an item on the Trash screen.
 589              trashDiff = 1;
 590  
 591          // User clicked "Restore".
 592          } else if ( targetParent.is( 'span.untrash' ) ) {
 593              if ( approved ) {
 594                  pendingDiff = 1;
 595              } else if ( unapproved ) {
 596                  approvedDiff = 1;
 597              } else if ( trashed ) {
 598                  if ( targetParent.hasClass( 'approve' ) ) {
 599                      approvedDiff = 1;
 600                  } else if ( targetParent.hasClass( 'unapprove' ) ) {
 601                      pendingDiff = 1;
 602                  }
 603              }
 604              // You can't go from Trash to Spam.
 605              // You can untrash on the Trash screen.
 606              trashDiff = -1;
 607  
 608          // User clicked "Approve".
 609          } else if ( targetParent.is( 'span.approve:not(.unspam):not(.untrash)' ) ) {
 610              approvedDiff = 1;
 611              pendingDiff = -1;
 612  
 613          // User clicked "Unapprove".
 614          } else if ( targetParent.is( 'span.unapprove:not(.unspam):not(.untrash)' ) ) {
 615              approvedDiff = -1;
 616              pendingDiff = 1;
 617  
 618          // User clicked "Delete Permanently".
 619          } else if ( targetParent.is( 'span.delete' ) ) {
 620              if ( spammed ) {
 621                  spamDiff = -1;
 622              } else if ( trashed ) {
 623                  trashDiff = -1;
 624              }
 625          }
 626  
 627          if ( pendingDiff ) {
 628              updatePending( pendingDiff, commentPostId );
 629              updateCountText( 'span.all-count', pendingDiff );
 630          }
 631  
 632          if ( approvedDiff ) {
 633              updateApproved( approvedDiff, commentPostId );
 634              updateCountText( 'span.all-count', approvedDiff );
 635          }
 636  
 637          if ( spamDiff ) {
 638              updateCountText( 'span.spam-count', spamDiff );
 639          }
 640  
 641          if ( trashDiff ) {
 642              updateCountText( 'span.trash-count', trashDiff );
 643          }
 644  
 645          if (
 646              ( ( 'trash' === settings.data.comment_status ) && !getCount( $( 'span.trash-count' ) ) ) ||
 647              ( ( 'spam' === settings.data.comment_status ) && !getCount( $( 'span.spam-count' ) ) )
 648          ) {
 649              $( '#delete_all' ).hide();
 650          }
 651  
 652          if ( ! isDashboard ) {
 653              total = totalInput.val() ? parseInt( totalInput.val(), 10 ) : 0;
 654              if ( $(settings.target).parent().is('span.undo') )
 655                  total++;
 656              else
 657                  total--;
 658  
 659              if ( total < 0 )
 660                  total = 0;
 661  
 662              if ( 'object' === typeof r ) {
 663                  if ( response.supplemental.total_items_i18n && lastConfidentTime < response.supplemental.time ) {
 664                      total_items_i18n = response.supplemental.total_items_i18n || '';
 665                      if ( total_items_i18n ) {
 666                          $('.displaying-num').text( total_items_i18n.replace( '&nbsp;', String.fromCharCode( 160 ) ) );
 667                          $('.total-pages').text( response.supplemental.total_pages_i18n.replace( '&nbsp;', String.fromCharCode( 160 ) ) );
 668                          $('.tablenav-pages').find('.next-page, .last-page').toggleClass('disabled', response.supplemental.total_pages == $('.current-page').val());
 669                      }
 670                      updateTotalCount( total, response.supplemental.time, true );
 671                  } else if ( response.supplemental.time ) {
 672                      updateTotalCount( total, response.supplemental.time, false );
 673                  }
 674              } else {
 675                  updateTotalCount( total, r, false );
 676              }
 677          }
 678  
 679          if ( ! theExtraList || theExtraList.length === 0 || theExtraList.children().length === 0 || undoing ) {
 680              return;
 681          }
 682  
 683          theList.get(0).wpList.add( theExtraList.children( ':eq(0):not(.no-items)' ).remove().clone() );
 684  
 685          refillTheExtraList();
 686  
 687          animated = $( ':animated', '#the-comment-list' );
 688          animatedCallback = function() {
 689              if ( ! $( '#the-comment-list tr:visible' ).length ) {
 690                  theList.get(0).wpList.add( theExtraList.find( '.no-items' ).clone() );
 691              }
 692          };
 693  
 694          if ( animated.length ) {
 695              animated.promise().done( animatedCallback );
 696          } else {
 697              animatedCallback();
 698          }
 699      };
 700  
 701      /**
 702       * Retrieves additional comments to populate the extra list.
 703       *
 704       * @since 3.1.0
 705       * @access private
 706       *
 707       * @param {boolean} [ev] Repopulate the extra comments list if true.
 708       *
 709       * @return {void}
 710       */
 711      refillTheExtraList = function(ev) {
 712          var args = $.query.get(), total_pages = $('.total-pages').text(), per_page = $('input[name="_per_page"]', '#comments-form').val();
 713  
 714          if (! args.paged)
 715              args.paged = 1;
 716  
 717          if (args.paged > total_pages) {
 718              return;
 719          }
 720  
 721          if (ev) {
 722              theExtraList.empty();
 723              args.number = Math.min(8, per_page); // See WP_Comments_List_Table::prepare_items() in class-wp-comments-list-table.php.
 724          } else {
 725              args.number = 1;
 726              args.offset = Math.min(8, per_page) - 1; // Fetch only the next item on the extra list.
 727          }
 728  
 729          args.no_placeholder = true;
 730  
 731          args.paged ++;
 732  
 733          // $.query.get() needs some correction to be sent into an Ajax request.
 734          if ( true === args.comment_type )
 735              args.comment_type = '';
 736  
 737          args = $.extend(args, {
 738              'action': 'fetch-list',
 739              'list_args': list_args,
 740              '_ajax_fetch_list_nonce': $('#_ajax_fetch_list_nonce').val()
 741          });
 742  
 743          $.ajax({
 744              url: ajaxurl,
 745              global: false,
 746              dataType: 'json',
 747              data: args,
 748              success: function(response) {
 749                  theExtraList.get(0).wpList.add( response.rows );
 750              }
 751          });
 752      };
 753  
 754      /**
 755       * Globally available jQuery object referring to the extra comments list.
 756       *
 757       * @global
 758       */
 759      window.theExtraList = $('#the-extra-comment-list').wpList( { alt: '', delColor: 'none', addColor: 'none' } );
 760  
 761      /**
 762       * Globally available jQuery object referring to the comments list.
 763       *
 764       * @global
 765       */
 766      window.theList = $('#the-comment-list').wpList( { alt: '', delBefore: delBefore, dimAfter: dimAfter, delAfter: delAfter, addColor: 'none' } )
 767          .on('wpListDelEnd', function(e, s){
 768              var wpListsData = $(s.target).attr('data-wp-lists'), id = s.element.replace(/[^0-9]+/g, '');
 769  
 770              if ( wpListsData.indexOf(':trash=1') != -1 || wpListsData.indexOf(':spam=1') != -1 )
 771                  $('#undo-' + id).fadeIn(300, function(){ $(this).show(); });
 772          });
 773  };
 774  
 775  /**
 776   * Object containing functionality regarding the comment quick editor and reply
 777   * editor.
 778   *
 779   * @since 2.7.0
 780   *
 781   * @global
 782   */
 783  window.commentReply = {
 784      cid : '',
 785      act : '',
 786      originalContent : '',
 787  
 788      /**
 789       * Initializes the comment reply functionality.
 790       *
 791       * @since 2.7.0
 792       *
 793       * @memberof commentReply
 794       */
 795      init : function() {
 796          var row = $('#replyrow');
 797  
 798          $( '.cancel', row ).on( 'click', function() { return commentReply.revert(); } );
 799          $( '.save', row ).on( 'click', function() { return commentReply.send(); } );
 800          $( 'input#author-name, input#author-email, input#author-url', row ).on( 'keypress', function( e ) {
 801              if ( e.which == 13 ) {
 802                  commentReply.send();
 803                  e.preventDefault();
 804                  return false;
 805              }
 806          });
 807  
 808          // Add events.
 809          $('#the-comment-list .column-comment > p').on( 'dblclick', function(){
 810              commentReply.toggle($(this).parent());
 811          });
 812  
 813          $('#doaction, #post-query-submit').on( 'click', function(){
 814              if ( $('#the-comment-list #replyrow').length > 0 )
 815                  commentReply.close();
 816          });
 817  
 818          this.comments_listing = $('#comments-form > input[name="comment_status"]').val() || '';
 819      },
 820  
 821      /**
 822       * Adds doubleclick event handler to the given comment list row.
 823       *
 824       * The double-click event will toggle the comment edit or reply form.
 825       *
 826       * @since 2.7.0
 827       *
 828       * @memberof commentReply
 829       *
 830       * @param {Object} r The row to add double click handlers to.
 831       *
 832       * @return {void}
 833       */
 834      addEvents : function(r) {
 835          r.each(function() {
 836              $(this).find('.column-comment > p').on( 'dblclick', function(){
 837                  commentReply.toggle($(this).parent());
 838              });
 839          });
 840      },
 841  
 842      /**
 843       * Opens the quick edit for the given element.
 844       *
 845       * @since 2.7.0
 846       *
 847       * @memberof commentReply
 848       *
 849       * @param {HTMLElement} el The element you want to open the quick editor for.
 850       *
 851       * @return {void}
 852       */
 853      toggle : function(el) {
 854          if ( 'none' !== $( el ).css( 'display' ) && ( $( '#replyrow' ).parent().is('#com-reply') || window.confirm( __( 'Are you sure you want to edit this comment?\nThe changes you made will be lost.' ) ) ) ) {
 855              $( el ).find( 'button.vim-q' ).trigger( 'click' );
 856          }
 857      },
 858  
 859      /**
 860       * Closes the comment quick edit or reply form and undoes any changes.
 861       *
 862       * @since 2.7.0
 863       *
 864       * @memberof commentReply
 865       *
 866       * @return {void}
 867       */
 868      revert : function() {
 869  
 870          if ( $('#the-comment-list #replyrow').length < 1 )
 871              return false;
 872  
 873          $('#replyrow').fadeOut('fast', function(){
 874              commentReply.close();
 875          });
 876      },
 877  
 878      /**
 879       * Closes the comment quick edit or reply form and undoes any changes.
 880       *
 881       * @since 2.7.0
 882       *
 883       * @memberof commentReply
 884       *
 885       * @return {void}
 886       */
 887      close : function() {
 888          var commentRow = $(),
 889              replyRow = $( '#replyrow' );
 890  
 891          // Return if the replyrow is not showing.
 892          if ( replyRow.parent().is( '#com-reply' ) ) {
 893              return;
 894          }
 895  
 896          if ( this.cid ) {
 897              commentRow = $( '#comment-' + this.cid );
 898          }
 899  
 900          /*
 901           * When closing the Quick Edit form, show the comment row and move focus
 902           * back to the Quick Edit button.
 903           */
 904          if ( 'edit-comment' === this.act ) {
 905              commentRow.fadeIn( 300, function() {
 906                  commentRow
 907                      .show()
 908                      .find( '.vim-q' )
 909                          .attr( 'aria-expanded', 'false' )
 910                          .trigger( 'focus' );
 911              } ).css( 'backgroundColor', '' );
 912          }
 913  
 914          // When closing the Reply form, move focus back to the Reply button.
 915          if ( 'replyto-comment' === this.act ) {
 916              commentRow.find( '.vim-r' )
 917                  .attr( 'aria-expanded', 'false' )
 918                  .trigger( 'focus' );
 919          }
 920  
 921          // Reset the Quicktags buttons.
 922           if ( typeof QTags != 'undefined' )
 923              QTags.closeAllTags('replycontent');
 924  
 925          $('#add-new-comment').css('display', '');
 926  
 927          replyRow.hide();
 928          $( '#com-reply' ).append( replyRow );
 929          $('#replycontent').css('height', '').val('');
 930          $('#edithead input').val('');
 931          $( '.notice-error', replyRow )
 932              .addClass( 'hidden' )
 933              .find( '.error' ).empty();
 934          $( '.spinner', replyRow ).removeClass( 'is-active' );
 935  
 936          this.cid = '';
 937          this.originalContent = '';
 938      },
 939  
 940      /**
 941       * Opens the comment quick edit or reply form.
 942       *
 943       * @since 2.7.0
 944       *
 945       * @memberof commentReply
 946       *
 947       * @param {number} comment_id The comment ID to open an editor for.
 948       * @param {number} post_id    The post ID to open an editor for.
 949       * @param {string} action     The action to perform. Either 'edit' or 'replyto'.
 950       *
 951       * @return {boolean} Always false.
 952       */
 953      open : function(comment_id, post_id, action) {
 954          var editRow, rowData, act, replyButton, editHeight,
 955              t = this,
 956              c = $('#comment-' + comment_id),
 957              h = c.height(),
 958              colspanVal = 0;
 959  
 960          if ( ! this.discardCommentChanges() ) {
 961              return false;
 962          }
 963  
 964          t.close();
 965          t.cid = comment_id;
 966  
 967          editRow = $('#replyrow');
 968          rowData = $('#inline-'+comment_id);
 969          action = action || 'replyto';
 970          act = 'edit' == action ? 'edit' : 'replyto';
 971          act = t.act = act + '-comment';
 972          t.originalContent = $('textarea.comment', rowData).val();
 973          colspanVal = $( '> th:visible, > td:visible', c ).length;
 974  
 975          // Make sure it's actually a table and there's a `colspan` value to apply.
 976          if ( editRow.hasClass( 'inline-edit-row' ) && 0 !== colspanVal ) {
 977              $( 'td', editRow ).attr( 'colspan', colspanVal );
 978          }
 979  
 980          $('#action', editRow).val(act);
 981          $('#comment_post_ID', editRow).val(post_id);
 982          $('#comment_ID', editRow).val(comment_id);
 983  
 984          if ( action == 'edit' ) {
 985              $( '#author-name', editRow ).val( $( 'div.author', rowData ).text() );
 986              $('#author-email', editRow).val( $('div.author-email', rowData).text() );
 987              $('#author-url', editRow).val( $('div.author-url', rowData).text() );
 988              $('#status', editRow).val( $('div.comment_status', rowData).text() );
 989              $('#replycontent', editRow).val( $('textarea.comment', rowData).val() );
 990              $( '#edithead, #editlegend, #savebtn', editRow ).show();
 991              $('#replyhead, #replybtn, #addhead, #addbtn', editRow).hide();
 992  
 993              if ( h > 120 ) {
 994                  // Limit the maximum height when editing very long comments to make it more manageable.
 995                  // The textarea is resizable in most browsers, so the user can adjust it if needed.
 996                  editHeight = h > 500 ? 500 : h;
 997                  $('#replycontent', editRow).css('height', editHeight + 'px');
 998              }
 999  
1000              c.after( editRow ).fadeOut('fast', function(){
1001                  $('#replyrow').fadeIn(300, function(){ $(this).show(); });
1002              });
1003          } else if ( action == 'add' ) {
1004              $('#addhead, #addbtn', editRow).show();
1005              $( '#replyhead, #replybtn, #edithead, #editlegend, #savebtn', editRow ) .hide();
1006              $('#the-comment-list').prepend(editRow);
1007              $('#replyrow').fadeIn(300);
1008          } else {
1009              replyButton = $('#replybtn', editRow);
1010              $( '#edithead, #editlegend, #savebtn, #addhead, #addbtn', editRow ).hide();
1011              $('#replyhead, #replybtn', editRow).show();
1012              c.after(editRow);
1013  
1014              if ( c.hasClass('unapproved') ) {
1015                  replyButton.text( __( 'Approve and Reply' ) );
1016              } else {
1017                  /* translators: Comment reply button text. */
1018                  replyButton.text( _x( 'Reply', 'verb' ) );
1019              }
1020  
1021              $('#replyrow').fadeIn(300, function(){ $(this).show(); });
1022          }
1023  
1024          setTimeout(function() {
1025              var rtop, rbottom, scrollTop, vp, scrollBottom,
1026                  isComposing = false,
1027                  isContextMenuOpen = false;
1028  
1029              rtop = $('#replyrow').offset().top;
1030              rbottom = rtop + $('#replyrow').height();
1031              scrollTop = window.pageYOffset || document.documentElement.scrollTop;
1032              vp = document.documentElement.clientHeight || window.innerHeight || 0;
1033              scrollBottom = scrollTop + vp;
1034  
1035              if ( scrollBottom - 20 < rbottom )
1036                  window.scroll(0, rbottom - vp + 35);
1037              else if ( rtop - 20 < scrollTop )
1038                  window.scroll(0, rtop - 35);
1039  
1040              $( '#replycontent' )
1041                  .trigger( 'focus' )
1042                  .on( 'contextmenu keydown', function ( e ) {
1043                      // Check if the context menu is open and set state.
1044                      if ( e.type === 'contextmenu' ) {
1045                          isContextMenuOpen = true;
1046                      }
1047  
1048                      // Update the context menu state if the Escape key is pressed.
1049                      if ( e.type === 'keydown' && e.which === 27 && isContextMenuOpen ) {
1050                          isContextMenuOpen = false;
1051                      }
1052                  } )
1053                  .on( 'keyup', function( e ) {
1054                      // Close on Escape unless Input Method Editors (IMEs) are in use or the context menu is open.
1055                      if ( e.which === 27 && ! isComposing && ! isContextMenuOpen ) {
1056                          commentReply.revert();
1057                      }
1058                  } )
1059                  .on( 'compositionstart', function() {
1060                      isComposing = true;
1061                  } );
1062          }, 600);
1063  
1064          return false;
1065      },
1066  
1067      /**
1068       * Submits the comment quick edit or reply form.
1069       *
1070       * @since 2.7.0
1071       *
1072       * @memberof commentReply
1073       *
1074       * @return {void}
1075       */
1076      send : function() {
1077          var post = {},
1078              $errorNotice = $( '#replysubmit .error-notice' );
1079  
1080          $errorNotice.addClass( 'hidden' );
1081          $( '#replysubmit .spinner' ).addClass( 'is-active' );
1082  
1083          $('#replyrow input').not(':button').each(function() {
1084              var t = $(this);
1085              post[ t.attr('name') ] = t.val();
1086          });
1087  
1088          post.content = $('#replycontent').val();
1089          post.id = post.comment_post_ID;
1090          post.comments_listing = this.comments_listing;
1091          post.p = $('[name="p"]').val();
1092  
1093          if ( $('#comment-' + $('#comment_ID').val()).hasClass('unapproved') )
1094              post.approve_parent = 1;
1095  
1096          $.ajax({
1097              type : 'POST',
1098              url : ajaxurl,
1099              data : post,
1100              success : function(x) { commentReply.show(x); },
1101              error : function(r) { commentReply.error(r); }
1102          });
1103      },
1104  
1105      /**
1106       * Shows the new or updated comment or reply.
1107       *
1108       * This function needs to be passed the ajax result as received from the server.
1109       * It will handle the response and show the comment that has just been saved to
1110       * the server.
1111       *
1112       * @since 2.7.0
1113       *
1114       * @memberof commentReply
1115       *
1116       * @param {Object} xml Ajax response object.
1117       *
1118       * @return {void}
1119       */
1120      show : function(xml) {
1121          var t = this, r, c, id, bg, pid;
1122  
1123          if ( typeof(xml) == 'string' ) {
1124              t.error({'responseText': xml});
1125              return false;
1126          }
1127  
1128          r = wpAjax.parseAjaxResponse(xml);
1129          if ( r.errors ) {
1130              t.error({'responseText': wpAjax.broken});
1131              return false;
1132          }
1133  
1134          t.revert();
1135  
1136          r = r.responses[0];
1137          id = '#comment-' + r.id;
1138  
1139          if ( 'edit-comment' == t.act )
1140              $(id).remove();
1141  
1142          if ( r.supplemental.parent_approved ) {
1143              pid = $('#comment-' + r.supplemental.parent_approved);
1144              updatePending( -1, r.supplemental.parent_post_id );
1145  
1146              if ( this.comments_listing == 'moderated' ) {
1147                  pid.animate( { 'backgroundColor':'#CCEEBB' }, 400, function(){
1148                      pid.fadeOut();
1149                  });
1150                  return;
1151              }
1152          }
1153  
1154          if ( r.supplemental.i18n_comments_text ) {
1155              updateDashboardText( r.supplemental );
1156              updateInModerationText( r.supplemental );
1157              updateApproved( 1, r.supplemental.parent_post_id );
1158              updateCountText( 'span.all-count', 1 );
1159          }
1160  
1161          r.data = r.data || '';
1162          c = r.data.toString().trim(); // Trim leading whitespaces.
1163          $(c).hide();
1164          $('#replyrow').after(c);
1165  
1166          id = $(id);
1167          t.addEvents(id);
1168          bg = id.hasClass('unapproved') ? '#FFFFE0' : id.closest('.widefat, .postbox').css('backgroundColor');
1169  
1170          id.animate( { 'backgroundColor':'#CCEEBB' }, 300 )
1171              .animate( { 'backgroundColor': bg }, 300, function() {
1172                  if ( pid && pid.length ) {
1173                      pid.animate( { 'backgroundColor':'#CCEEBB' }, 300 )
1174                          .animate( { 'backgroundColor': bg }, 300 )
1175                          .removeClass('unapproved').addClass('approved')
1176                          .find('div.comment_status').html('1');
1177                  }
1178              });
1179  
1180      },
1181  
1182      /**
1183       * Shows an error for the failed comment update or reply.
1184       *
1185       * @since 2.7.0
1186       *
1187       * @memberof commentReply
1188       *
1189       * @param {string} r The Ajax response.
1190       *
1191       * @return {void}
1192       */
1193      error : function(r) {
1194          var er = r.statusText,
1195              $errorNotice = $( '#replysubmit .notice-error' ),
1196              $error = $errorNotice.find( '.error' );
1197  
1198          $( '#replysubmit .spinner' ).removeClass( 'is-active' );
1199  
1200          if ( r.responseText )
1201              er = r.responseText.replace( /<.[^<>]*?>/g, '' );
1202  
1203          if ( er ) {
1204              $errorNotice.removeClass( 'hidden' );
1205              $error.html( er );
1206              wp.a11y.speak( er );
1207          }
1208      },
1209  
1210      /**
1211       * Opens the add comments form in the comments metabox on the post edit page.
1212       *
1213       * @since 3.4.0
1214       *
1215       * @memberof commentReply
1216       *
1217       * @param {number} post_id The post ID.
1218       *
1219       * @return {void}
1220       */
1221      addcomment: function(post_id) {
1222          var t = this;
1223  
1224          $('#add-new-comment').fadeOut(200, function(){
1225              t.open(0, post_id, 'add');
1226              $('table.comments-box').css('display', '');
1227              $('#no-comments').remove();
1228          });
1229      },
1230  
1231      /**
1232       * Alert the user if they have unsaved changes on a comment that will be lost if
1233       * they proceed with the intended action.
1234       *
1235       * @since 4.6.0
1236       *
1237       * @memberof commentReply
1238       *
1239       * @return {boolean} Whether it is safe the continue with the intended action.
1240       */
1241      discardCommentChanges: function() {
1242          var editRow = $( '#replyrow' );
1243  
1244          if  ( '' === $( '#replycontent', editRow ).val() || this.originalContent === $( '#replycontent', editRow ).val() ) {
1245              return true;
1246          }
1247  
1248          return window.confirm( __( 'Are you sure you want to do this?\nThe comment changes you made will be lost.' ) );
1249      }
1250  };
1251  
1252  $( function(){
1253      var make_hotkeys_redirect, edit_comment, toggle_all, make_bulk;
1254  
1255      setCommentsList();
1256      commentReply.init();
1257  
1258      $(document).on( 'click', 'span.delete a.delete', function( e ) {
1259          e.preventDefault();
1260      });
1261  
1262      if ( typeof $.table_hotkeys != 'undefined' ) {
1263          /**
1264           * Creates a function that navigates to a previous or next page.
1265           *
1266           * @since 2.7.0
1267           * @access private
1268           *
1269           * @param {string} which What page to navigate to: either next or prev.
1270           *
1271           * @return {Function} The function that executes the navigation.
1272           */
1273          make_hotkeys_redirect = function(which) {
1274              return function() {
1275                  var first_last, l;
1276  
1277                  first_last = 'next' == which? 'first' : 'last';
1278                  l = $('.tablenav-pages .'+which+'-page:not(.disabled)');
1279                  if (l.length)
1280                      window.location = l[0].href.replace(/\&hotkeys_highlight_(first|last)=1/g, '')+'&hotkeys_highlight_'+first_last+'=1';
1281              };
1282          };
1283  
1284          /**
1285           * Navigates to the edit page for the selected comment.
1286           *
1287           * @since 2.7.0
1288           * @access private
1289           *
1290           * @param {Object} event       The event that triggered this action.
1291           * @param {Object} current_row A jQuery object of the selected row.
1292           *
1293           * @return {void}
1294           */
1295          edit_comment = function(event, current_row) {
1296              window.location = $('span.edit a', current_row).attr('href');
1297          };
1298  
1299          /**
1300           * Toggles all comments on the screen, for bulk actions.
1301           *
1302           * @since 2.7.0
1303           * @access private
1304           *
1305           * @return {void}
1306           */
1307          toggle_all = function() {
1308              $('#cb-select-all-1').data( 'wp-toggle', 1 ).trigger( 'click' ).removeData( 'wp-toggle' );
1309          };
1310  
1311          /**
1312           * Creates a bulk action function that is executed on all selected comments.
1313           *
1314           * @since 2.7.0
1315           * @access private
1316           *
1317           * @param {string} value The name of the action to execute.
1318           *
1319           * @return {Function} The function that executes the bulk action.
1320           */
1321          make_bulk = function(value) {
1322              return function() {
1323                  var scope = $('select[name="action"]');
1324                  $('option[value="' + value + '"]', scope).prop('selected', true);
1325                  $('#doaction').trigger( 'click' );
1326              };
1327          };
1328  
1329          $.table_hotkeys(
1330              $('table.widefat'),
1331              [
1332                  'a', 'u', 's', 'd', 'r', 'q', 'z',
1333                  ['e', edit_comment],
1334                  ['shift+x', toggle_all],
1335                  ['shift+a', make_bulk('approve')],
1336                  ['shift+s', make_bulk('spam')],
1337                  ['shift+d', make_bulk('delete')],
1338                  ['shift+t', make_bulk('trash')],
1339                  ['shift+z', make_bulk('untrash')],
1340                  ['shift+u', make_bulk('unapprove')]
1341              ],
1342              {
1343                  highlight_first: adminCommentsSettings.hotkeys_highlight_first,
1344                  highlight_last: adminCommentsSettings.hotkeys_highlight_last,
1345                  prev_page_link_cb: make_hotkeys_redirect('prev'),
1346                  next_page_link_cb: make_hotkeys_redirect('next'),
1347                  hotkeys_opts: {
1348                      disableInInput: true,
1349                      type: 'keypress',
1350                      noDisable: '.check-column input[type="checkbox"]'
1351                  },
1352                  cycle_expr: '#the-comment-list tr',
1353                  start_row_index: 0
1354              }
1355          );
1356      }
1357  
1358      // Quick Edit and Reply have an inline comment editor.
1359      $( '#the-comment-list' ).on( 'click', '.comment-inline', function() {
1360          var $el = $( this ),
1361              action = 'replyto';
1362  
1363          if ( 'undefined' !== typeof $el.data( 'action' ) ) {
1364              action = $el.data( 'action' );
1365          }
1366  
1367          $( this ).attr( 'aria-expanded', 'true' );
1368          commentReply.open( $el.data( 'commentId' ), $el.data( 'postId' ), action );
1369      } );
1370  });
1371  
1372  })(jQuery);


Generated : Fri Sep 18 08:20:28 2026 Cross-referenced by PHPXref