| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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( ' ', String.fromCharCode( 160 ) ) ); 667 $('.total-pages').text( response.supplemental.total_pages_i18n.replace( ' ', 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);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Fri Sep 18 08:20:28 2026 | Cross-referenced by PHPXref |