edit-comments.js 37 KB

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