diff --git a/src/js/_enqueues/lib/comment-reply.js b/src/js/_enqueues/lib/comment-reply.js index 6ebc181551f79..d653f5756039a 100644 --- a/src/js/_enqueues/lib/comment-reply.js +++ b/src/js/_enqueues/lib/comment-reply.js @@ -1,446 +1,212 @@ +/* global addComment, WP */ + /** - * Handles the addition of the comment form. - * - * @since 2.7.0 - * @output wp-includes/js/comment-reply.js + * WordPress Comment Reply 6.6.1 * - * @namespace addComment + * Adds a reply link to comments. + * Used to reply to specific comments and reference them in replies. * - * @type {Object} + * @output wp-includes/js/comment-reply.js */ -window.addComment = ( function( window ) { - // Avoid scope lookups on commonly used variables. - var document = window.document; - - // Settings. - var config = { - commentReplyClass : 'comment-reply-link', - commentReplyTitleId : 'reply-title', - cancelReplyId : 'cancel-comment-reply-link', - commentFormId : 'commentform', - temporaryFormId : 'wp-temp-form-div', - parentIdFieldId : 'comment_parent', - postIdFieldId : 'comment_post_ID' - }; - - // Cross browser MutationObserver. - var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; - - // Check browser cuts the mustard. - var cutsTheMustard = 'querySelector' in document && 'addEventListener' in window; - - /* - * Check browser supports dataset. - * !! sets the variable to true if the property exists. - */ - var supportsDataset = !! document.documentElement.dataset; - - // For holding the cancel element. - var cancelElement; - - // For holding the comment form element. - var commentFormElement; - - // The respond element. - var respondElement; - - // The mutation observer. - var observer; - - if ( cutsTheMustard && document.readyState !== 'loading' ) { - ready(); - } else if ( cutsTheMustard ) { - window.addEventListener( 'DOMContentLoaded', ready, false ); - } - /** - * Sets up object variables after the DOM is ready. - * - * @since 5.1.1 - */ - function ready() { - // Initialize the events. - init(); - - // Set up a MutationObserver to check for comments loaded late. - observeChanges(); - } - - /** - * Add events to links classed .comment-reply-link. - * - * Searches the context for reply links and adds the JavaScript events - * required to move the comment form. To allow for lazy loading of - * comments this method is exposed as window.commentReply.init(). - * - * @since 5.1.0 - * - * @memberOf addComment - * - * @param {HTMLElement} context The parent DOM element to search for links. - */ - function init( context ) { - if ( ! cutsTheMustard ) { - return; - } +window.addComment = { - // Get required elements. - cancelElement = getElementById( config.cancelReplyId ); - commentFormElement = getElementById( config.commentFormId ); + moveForm : function( commId, parentId, respondId, postId ) { + var t = this, + div = document.getElementById( 'wp-temp-form-div' ), + respond = document.getElementById( respondId ), + comment = document.getElementById( commId ), + parentIdField = document.getElementById( 'comment_parent' ), + postIdField = document.getElementById( 'comment_post_ID' ); - // No cancel element, no replies. - if ( ! cancelElement ) { + if ( ! div || ! respond || ! comment || ! parentIdField || ! postIdField ) { return; } - cancelElement.addEventListener( 'click', cancelEvent ); + t.respondId = respondId; + postId = postId || false; - // Submit the comment form when the user types [Ctrl] or [Cmd] + [Enter]. - var submitFormHandler = function( e ) { - if ( ( e.metaKey || e.ctrlKey ) && e.keyCode === 13 && document.activeElement.tagName.toLowerCase() !== 'a' ) { - commentFormElement.removeEventListener( 'keydown', submitFormHandler ); - e.preventDefault(); - // The submit button ID is 'submit' so we can't call commentFormElement.submit(). Click it instead. - commentFormElement.submit.click(); - return false; - } - }; - - if ( commentFormElement ) { - commentFormElement.addEventListener( 'keydown', submitFormHandler ); + if ( ! t.childs ) { + t.childs = []; } - var links = replyLinks( context ); - var element; - - for ( var i = 0, l = links.length; i < l; i++ ) { - element = links[i]; + t.childs.push( comment ); - element.addEventListener( 'click', clickEvent ); + if ( comment.parentNode ) { + if ( respond.firstChild ) { + respond.insertBefore( div, respond.firstChild ); + } else { + respond.appendChild( div ); + } } - } - /** - * Return all links classed .comment-reply-link. - * - * @since 5.1.0 - * - * @param {HTMLElement} context The parent DOM element to search for links. - * - * @return {HTMLCollection|NodeList} The collection of links. - */ - function replyLinks( context ) { - var selectorClass = config.commentReplyClass; - var allReplyLinks; - - // childNodes is a handy check to ensure the context is a HTMLElement. - if ( ! context || ! context.childNodes ) { - context = document; - } + /* + * Set the value of the 'comment_parent' input field to the + * comment_ID of the parent comment. + */ + parentIdField.value = parentId; - if ( document.getElementsByClassName ) { - // Fastest. - allReplyLinks = context.getElementsByClassName( selectorClass ); - } - else { - // Fast. - allReplyLinks = context.querySelectorAll( '.' + selectorClass ); + /* + * Set the value of the 'comment_post_ID' hidden input field to + * the post_ID of the post being commented on. + */ + if ( postId && postIdField ) { + postIdField.value = postId; } - return allReplyLinks; - } + t.ariaFocusForm(); - /** - * Cancel event handler. - * - * @since 5.1.0 - * - * @param {Event} event The calling event. - */ - function cancelEvent( event ) { - var cancelLink = this; - var temporaryFormId = config.temporaryFormId; - var temporaryElement = getElementById( temporaryFormId ); - - if ( ! temporaryElement || ! respondElement ) { - // Conditions for cancel link fail. - return; - } + t.cancelVisible = true; - getElementById( config.parentIdFieldId ).value = '0'; + t.updateCancelReplyLink(); - // Move the respond form back in place of the temporary element. - var headingText = temporaryElement.textContent; - temporaryElement.parentNode.replaceChild( respondElement, temporaryElement ); - cancelLink.style.display = 'none'; + /* + * Lastly, hide any replies to the current comment that + * happen to be showing. + */ + t.toggleCommentDisplay( commId, parentId ); + }, - var replyHeadingElement = getElementById( config.commentReplyTitleId ); - var replyHeadingTextNode = replyHeadingElement && replyHeadingElement.firstChild; - var replyLinkToParent = replyHeadingTextNode && replyHeadingTextNode.nextSibling; + toggleCommentDisplay : function( commId, parentId ) { + var t = this, + comm = t.childs[ 0 ], + display = t.childs[ 0 ] && 'none' !== t.childs[ 0 ].style.display; - if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE && headingText ) { - if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) { - replyLinkToParent.style.display = ''; + for ( var i = 0; i < t.childs.length; i++ ) { + if ( t.childs[ i ] && t.childs[ i ].style ) { + t.childs[ i ].style.display = display ? 'none' : 'block'; } - - replyHeadingTextNode.textContent = headingText; } - event.preventDefault(); - } - - /** - * Click event handler. - * - * @since 5.1.0 - * - * @param {Event} event The calling event. - */ - function clickEvent( event ) { - var replyNode = getElementById( config.commentReplyTitleId ); - var defaultReplyHeading = replyNode && replyNode.firstChild.textContent; - var replyLink = this, - commId = getDataAttribute( replyLink, 'belowelement' ), - parentId = getDataAttribute( replyLink, 'commentid' ), - respondId = getDataAttribute( replyLink, 'respondelement' ), - postId = getDataAttribute( replyLink, 'postid' ), - replyTo = getDataAttribute( replyLink, 'replyto' ) || defaultReplyHeading, - follow; - - if ( ! commId || ! parentId || ! respondId || ! postId ) { - /* - * Theme or plugin defines own link via custom `wp_list_comments()` callback - * and calls `moveForm()` either directly or via a custom event hook. - */ - return; + if ( ! display && ( parentId === commId || parentId === '0' ) ) { + if ( comm && comm.style ) { + comm.style.display = 'block'; + } } + }, - /* - * Third party comments systems can hook into this function via the global scope, - * therefore the click event needs to reference the global scope. - */ - follow = window.addComment.moveForm( commId, parentId, respondId, postId, replyTo ); - if ( false === follow ) { - event.preventDefault(); - } - } + cancelReply : function() { + var self = addComment, + temp = document.getElementById( 'wp-temp-form-div' ), + respond = document.getElementById( self.respondId ), + cancelLink = self.cancelLink; + + self.childs = []; + self.cancelVisible = false; - /** - * Creates a mutation observer to check for newly inserted comments. - * - * @since 5.1.0 - */ - function observeChanges() { - if ( ! MutationObserver ) { + if ( ! temp || ! respond || ! cancelLink ) { return; } - var observerOptions = { - childList: true, - subtree: true - }; + self.toggleCommentDisplay( temp.firstChild.value, '0' ); + respond.parentNode.insertBefore( temp, respond ); - observer = new MutationObserver( handleChanges ); - observer.observe( document.body, observerOptions ); - } + self.ariaUnfocusForm(); - /** - * Handles DOM changes, calling init() if any new nodes are added. - * - * @since 5.1.0 - * - * @param {Array} mutationRecords Array of MutationRecord objects. - */ - function handleChanges( mutationRecords ) { - var i = mutationRecords.length; - - while ( i-- ) { - // Call init() once if any record in this set adds nodes. - if ( mutationRecords[ i ].addedNodes.length ) { - init(); - return; - } - } - } + cancelLink.style.display = 'none'; - /** - * Backward compatible getter of data-* attribute. - * - * Uses element.dataset if it exists, otherwise uses getAttribute. - * - * @since 5.1.0 - * - * @param {HTMLElement} element DOM element with the attribute. - * @param {string} attribute The attribute to get. - * - * @return {string} The value of the attribute. - */ - function getDataAttribute( element, attribute ) { - if ( supportsDataset ) { - return element.dataset[attribute]; - } - else { - return element.getAttribute( 'data-' + attribute ); - } - } + cancelLink.onclick = null; - /** - * Get element by ID. - * - * Local alias for document.getElementById. - * - * @since 5.1.0 - * - * @param {string} elementId The requested element. - * - * @return {HTMLElement} The element with the given ID. - */ - function getElementById( elementId ) { - return document.getElementById( elementId ); - } + return false; + }, + + updateCancelReplyLink : function() { + var t = this, + cancelLink = t.cancelLink; - /** - * Moves the reply form from its current position to the reply location. - * - * @since 2.7.0 - * - * @memberOf addComment - * - * @param {string} addBelowId HTML ID of element the form follows. - * @param {string} commentId Database ID of comment being replied to. - * @param {string} respondId HTML ID of 'respond' element. - * @param {string} postId Database ID of the post. - * @param {string} replyTo Form heading content. - * - * @return {void|boolean} Returns false for backward compatibility with third party commenting systems hooking into this function. - */ - function moveForm( addBelowId, commentId, respondId, postId, replyTo ) { - // Get elements based on their IDs. - var addBelowElement = getElementById( addBelowId ); - respondElement = getElementById( respondId ); - - // Get the hidden fields. - var parentIdField = getElementById( config.parentIdFieldId ); - var postIdField = getElementById( config.postIdFieldId ); - var element, cssHidden, style; - - var replyHeading = getElementById( config.commentReplyTitleId ); - var replyHeadingTextNode = replyHeading && replyHeading.firstChild; - var replyLinkToParent = replyHeadingTextNode && replyHeadingTextNode.nextSibling; - - if ( ! addBelowElement || ! respondElement || ! parentIdField ) { - // Missing key elements, fail. + if ( ! cancelLink ) { return; } - if ( 'undefined' === typeof replyTo ) { - replyTo = replyHeadingTextNode && replyHeadingTextNode.textContent; + if ( t.cancelVisible ) { + cancelLink.style.display = ''; + } else { + cancelLink.style.display = 'none'; } + }, - addPlaceHolder( respondElement ); + ariaFocusForm : function() { + var div = document.getElementById( 'wp-temp-form-div' ); - // Set the value of the post. - if ( postId && postIdField ) { - postIdField.value = postId; + if ( div && div.firstChild ) { + div.firstChild.setAttribute( 'aria-hidden', 'false' ); } + }, - parentIdField.value = commentId; - - cancelElement.style.display = ''; - addBelowElement.parentNode.insertBefore( respondElement, addBelowElement.nextSibling ); - - if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE ) { - if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) { - replyLinkToParent.style.display = 'none'; - } + ariaUnfocusForm : function() { + var div = document.getElementById( 'wp-temp-form-div' ); - replyHeadingTextNode.textContent = replyTo; + if ( div && div.firstChild ) { + div.firstChild.setAttribute( 'aria-hidden', 'true' ); } + }, - /* - * This is for backward compatibility with third party commenting systems - * hooking into the event using older techniques. - */ - cancelElement.onclick = function() { - return false; - }; + init : function() { + var t = this, + onReplyLinkClick, + element, + responseLinks = document.querySelectorAll( '.comment-reply-link' ), + cancelElement, + cancelLink, + i; - // Focus on the first field in the comment form. - try { - for ( var i = 0; i < commentFormElement.elements.length; i++ ) { - element = commentFormElement.elements[i]; - cssHidden = false; - - // Get elements computed style. - if ( 'getComputedStyle' in window ) { - // Modern browsers. - style = window.getComputedStyle( element ); - } else if ( document.documentElement.currentStyle ) { - // IE 8. - style = element.currentStyle; - } + onReplyLinkClick = function( event ) { + event.preventDefault(); // Prevent the default link behavior - /* - * For display none, do the same thing jQuery does. For visibility, - * check the element computed style since browsers are already doing - * the job for us. In fact, the visibility computed style is the actual - * computed value and already takes into account the element ancestors. - */ - if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) { - cssHidden = true; + // Dispatch a custom event with the necessary data + const customEvent = new CustomEvent('commentReplyLinkClicked', { + detail: { + commId: this.dataset.commentid, + parentId: this.dataset.postid, + respondId: this.dataset.belowelement, + postId: this.dataset.respondelement } + }); + this.dispatchEvent(customEvent); + }; - // Skip form elements that are hidden or disabled. - if ( 'hidden' === element.type || element.disabled || cssHidden ) { - continue; + for ( i = 0; i < responseLinks.length; i++ ) { + element = responseLinks[ i ]; + // Now we can safely use passive listeners + element.addEventListener( 'click', onReplyLinkClick, { passive: true }); + } + + // Listen for the custom event and handle the form movement + document.addEventListener('commentReplyLinkClicked', function(event) { + t.moveForm( + event.detail.commId, + event.detail.parentId, + event.detail.respondId, + event.detail.postId + ); + }); + + cancelElement = document.getElementById( 'cancel-comment-reply-link' ); + cancelLink = t.cancelLink = cancelElement || document.getElementById( 'wp-cancel-comment-reply-link' ); + + if ( cancelLink ) { + cancelLink.onclick = function() { + if ( typeof WP !== 'undefined' && typeof WP.mce !== 'undefined' && WP.mce.activeEditor ) { + WP.mce.activeEditor.setContent( '' ); } - element.focus(); - // Stop after the first focusable element. - break; - } - } - catch(e) { - + return t.cancelReply(); + }; } /* - * false is returned for backward compatibility with third party commenting systems - * hooking into this function. + * The following is mainly for accessibility. The cancel link + * should only be visible if the form is being displayed and a + * comment is being replied to. */ - return false; + t.updateCancelReplyLink(); } +}; // end addComment - /** - * Add placeholder element. - * - * Places a place holder element above the #respond element for - * the form to be returned to if needs be. - * - * @since 2.7.0 - * - * @param {HTMLelement} respondElement the #respond element holding comment form. - */ - function addPlaceHolder( respondElement ) { - var temporaryFormId = config.temporaryFormId; - var temporaryElement = getElementById( temporaryFormId ); - var replyElement = getElementById( config.commentReplyTitleId ); - var initialHeadingText = replyElement ? replyElement.firstChild.textContent : ''; - - if ( temporaryElement ) { - // The element already exists, no need to recreate. - return; - } +if ( typeof WP === 'undefined' ) { + window.WP = {}; +} - temporaryElement = document.createElement( 'div' ); - temporaryElement.id = temporaryFormId; - temporaryElement.style.display = 'none'; - temporaryElement.textContent = initialHeadingText; - respondElement.parentNode.insertBefore( temporaryElement, respondElement ); - } +WP.commentReply = addComment; - return { - init: init, - moveForm: moveForm - }; -})( window ); +addComment.init(); \ No newline at end of file