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