[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/ -> comment-reply.js (source)

   1  /**
   2   * Handles the addition of the comment form.
   3   *
   4   * @since 2.7.0
   5   * @output wp-includes/js/comment-reply.js
   6   *
   7   * @namespace addComment
   8   *
   9   * @type {Object}
  10   */
  11  window.addComment = ( function( window ) {
  12      // Avoid scope lookups on commonly used variables.
  13      var document = window.document;
  14  
  15      // Settings.
  16      var config = {
  17          commentReplyClass   : 'comment-reply-link',
  18          commentReplyTitleId : 'reply-title',
  19          cancelReplyId       : 'cancel-comment-reply-link',
  20          commentFormId       : 'commentform',
  21          temporaryFormId     : 'wp-temp-form-div',
  22          parentIdFieldId     : 'comment_parent',
  23          postIdFieldId       : 'comment_post_ID'
  24      };
  25  
  26      // Cross browser MutationObserver.
  27      var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  28  
  29      // Check browser cuts the mustard.
  30      var cutsTheMustard = 'querySelector' in document && 'addEventListener' in window;
  31  
  32      /*
  33       * Check browser supports dataset.
  34       * !! sets the variable to true if the property exists.
  35       */
  36      var supportsDataset = !! document.documentElement.dataset;
  37  
  38      // For holding the cancel element.
  39      var cancelElement;
  40  
  41      // For holding the comment form element.
  42      var commentFormElement;
  43  
  44      // The respond element.
  45      var respondElement;
  46  
  47      // The mutation observer.
  48      var observer;
  49  
  50      if ( cutsTheMustard && document.readyState !== 'loading' ) {
  51          ready();
  52      } else if ( cutsTheMustard ) {
  53          window.addEventListener( 'DOMContentLoaded', ready, false );
  54      }
  55  
  56      /**
  57       * Sets up object variables after the DOM is ready.
  58       *
  59       * @since 5.1.1
  60       */
  61  	function ready() {
  62          // Initialize the events.
  63          init();
  64  
  65          // Set up a MutationObserver to check for comments loaded late.
  66          observeChanges();
  67      }
  68  
  69      /**
  70       * Add events to links classed .comment-reply-link.
  71       *
  72       * Searches the context for reply links and adds the JavaScript events
  73       * required to move the comment form. To allow for lazy loading of
  74       * comments this method is exposed as window.commentReply.init().
  75       *
  76       * @since 5.1.0
  77       *
  78       * @memberOf addComment
  79       *
  80       * @param {HTMLElement} context The parent DOM element to search for links.
  81       */
  82  	function init( context ) {
  83          if ( ! cutsTheMustard ) {
  84              return;
  85          }
  86  
  87          // Get required elements.
  88          cancelElement = getElementById( config.cancelReplyId );
  89          commentFormElement = getElementById( config.commentFormId );
  90  
  91          // No cancel element, no replies.
  92          if ( ! cancelElement ) {
  93              return;
  94          }
  95  
  96          cancelElement.addEventListener( 'click', cancelEvent );
  97  
  98          // Submit the comment form when the user types [Ctrl] or [Cmd] + [Enter].
  99          var submitFormHandler = function( e ) {
 100              if ( ( e.metaKey || e.ctrlKey ) && e.keyCode === 13 && document.activeElement.tagName.toLowerCase() !== 'a' ) {
 101                  commentFormElement.removeEventListener( 'keydown', submitFormHandler );
 102                  e.preventDefault();
 103                  // The submit button ID is 'submit' so we can't call commentFormElement.submit(). Click it instead.
 104                  commentFormElement.submit.click();
 105                  return false;
 106              }
 107          };
 108  
 109          if ( commentFormElement ) {
 110              commentFormElement.addEventListener( 'keydown', submitFormHandler );
 111          }
 112  
 113          var links = replyLinks( context );
 114          var element;
 115  
 116          for ( var i = 0, l = links.length; i < l; i++ ) {
 117              element = links[i];
 118  
 119              element.addEventListener( 'click', clickEvent );
 120          }
 121      }
 122  
 123      /**
 124       * Return all links classed .comment-reply-link.
 125       *
 126       * @since 5.1.0
 127       *
 128       * @param {HTMLElement} context The parent DOM element to search for links.
 129       *
 130       * @return {HTMLCollection|NodeList} The collection of links.
 131       */
 132  	function replyLinks( context ) {
 133          var selectorClass = config.commentReplyClass;
 134          var allReplyLinks;
 135  
 136          // childNodes is a handy check to ensure the context is a HTMLElement.
 137          if ( ! context || ! context.childNodes ) {
 138              context = document;
 139          }
 140  
 141          if ( document.getElementsByClassName ) {
 142              // Fastest.
 143              allReplyLinks = context.getElementsByClassName( selectorClass );
 144          }
 145          else {
 146              // Fast.
 147              allReplyLinks = context.querySelectorAll( '.' + selectorClass );
 148          }
 149  
 150          return allReplyLinks;
 151      }
 152  
 153      /**
 154       * Cancel event handler.
 155       *
 156       * @since 5.1.0
 157       *
 158       * @param {Event} event The calling event.
 159       */
 160  	function cancelEvent( event ) {
 161          var cancelLink = this;
 162          var temporaryFormId  = config.temporaryFormId;
 163          var temporaryElement = getElementById( temporaryFormId );
 164  
 165          if ( ! temporaryElement || ! respondElement ) {
 166              // Conditions for cancel link fail.
 167              return;
 168          }
 169  
 170          getElementById( config.parentIdFieldId ).value = '0';
 171  
 172          // Move the respond form back in place of the temporary element.
 173          var headingText = temporaryElement.textContent;
 174          temporaryElement.parentNode.replaceChild( respondElement, temporaryElement );
 175          cancelLink.style.display = 'none';
 176  
 177          var replyHeadingElement  = getElementById( config.commentReplyTitleId );
 178          var replyHeadingTextNode = replyHeadingElement && replyHeadingElement.firstChild;
 179          var replyLinkToParent    = replyHeadingTextNode && replyHeadingTextNode.nextSibling;
 180  
 181          if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE && headingText ) {
 182              if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) {
 183                  replyLinkToParent.style.display = '';
 184              }
 185  
 186              replyHeadingTextNode.textContent = headingText;
 187          }
 188  
 189          event.preventDefault();
 190      }
 191  
 192      /**
 193       * Click event handler.
 194       *
 195       * @since 5.1.0
 196       *
 197       * @param {Event} event The calling event.
 198       */
 199  	function clickEvent( event ) {
 200          var replyNode = getElementById( config.commentReplyTitleId );
 201          var defaultReplyHeading = replyNode && replyNode.firstChild.textContent;
 202          var replyLink = this,
 203              commId    = getDataAttribute( replyLink, 'belowelement' ),
 204              parentId  = getDataAttribute( replyLink, 'commentid' ),
 205              respondId = getDataAttribute( replyLink, 'respondelement' ),
 206              postId    = getDataAttribute( replyLink, 'postid' ),
 207              replyTo   = getDataAttribute( replyLink, 'replyto' ) || defaultReplyHeading,
 208              follow;
 209  
 210          if ( ! commId || ! parentId || ! respondId || ! postId ) {
 211              /*
 212               * Theme or plugin defines own link via custom `wp_list_comments()` callback
 213               * and calls `moveForm()` either directly or via a custom event hook.
 214               */
 215              return;
 216          }
 217  
 218          /*
 219           * Third party comments systems can hook into this function via the global scope,
 220           * therefore the click event needs to reference the global scope.
 221           */
 222          follow = window.addComment.moveForm( commId, parentId, respondId, postId, replyTo );
 223          if ( false === follow ) {
 224              event.preventDefault();
 225          }
 226      }
 227  
 228      /**
 229       * Creates a mutation observer to check for newly inserted comments.
 230       *
 231       * @since 5.1.0
 232       */
 233  	function observeChanges() {
 234          if ( ! MutationObserver ) {
 235              return;
 236          }
 237  
 238          var observerOptions = {
 239              childList: true,
 240              subtree: true
 241          };
 242  
 243          observer = new MutationObserver( handleChanges );
 244          observer.observe( document.body, observerOptions );
 245      }
 246  
 247      /**
 248       * Handles DOM changes, calling init() if any new nodes are added.
 249       *
 250       * @since 5.1.0
 251       *
 252       * @param {Array} mutationRecords Array of MutationRecord objects.
 253       */
 254  	function handleChanges( mutationRecords ) {
 255          var i = mutationRecords.length;
 256  
 257          while ( i-- ) {
 258              // Call init() once if any record in this set adds nodes.
 259              if ( mutationRecords[ i ].addedNodes.length ) {
 260                  init();
 261                  return;
 262              }
 263          }
 264      }
 265  
 266      /**
 267       * Backward compatible getter of data-* attribute.
 268       *
 269       * Uses element.dataset if it exists, otherwise uses getAttribute.
 270       *
 271       * @since 5.1.0
 272       *
 273       * @param {HTMLElement} element   DOM element with the attribute.
 274       * @param {string}      attribute The attribute to get.
 275       *
 276       * @return {string} The value of the attribute.
 277       */
 278  	function getDataAttribute( element, attribute ) {
 279          if ( supportsDataset ) {
 280              return element.dataset[attribute];
 281          }
 282          else {
 283              return element.getAttribute( 'data-' + attribute );
 284          }
 285      }
 286  
 287      /**
 288       * Get element by ID.
 289       *
 290       * Local alias for document.getElementById.
 291       *
 292       * @since 5.1.0
 293       *
 294       * @param {string} elementId The requested element.
 295       *
 296       * @return {HTMLElement} The element with the given ID.
 297       */
 298  	function getElementById( elementId ) {
 299          return document.getElementById( elementId );
 300      }
 301  
 302      /**
 303       * Moves the reply form from its current position to the reply location.
 304       *
 305       * @since 2.7.0
 306       *
 307       * @memberOf addComment
 308       *
 309       * @param {string} addBelowId HTML ID of element the form follows.
 310       * @param {string} commentId  Database ID of comment being replied to.
 311       * @param {string} respondId  HTML ID of 'respond' element.
 312       * @param {string} postId     Database ID of the post.
 313       * @param {string} replyTo    Form heading content.
 314       *
 315       * @return {void|boolean} Returns false for backward compatibility with third party commenting systems hooking into this function.
 316       */
 317  	function moveForm( addBelowId, commentId, respondId, postId, replyTo ) {
 318          // Get elements based on their IDs.
 319          var addBelowElement = getElementById( addBelowId );
 320          respondElement  = getElementById( respondId );
 321  
 322          // Get the hidden fields.
 323          var parentIdField   = getElementById( config.parentIdFieldId );
 324          var postIdField     = getElementById( config.postIdFieldId );
 325          var element, cssHidden, style;
 326  
 327          var replyHeading         = getElementById( config.commentReplyTitleId );
 328          var replyHeadingTextNode = replyHeading && replyHeading.firstChild;
 329          var replyLinkToParent    = replyHeadingTextNode && replyHeadingTextNode.nextSibling;
 330  
 331          if ( ! addBelowElement || ! respondElement || ! parentIdField ) {
 332              // Missing key elements, fail.
 333              return;
 334          }
 335  
 336          if ( 'undefined' === typeof replyTo ) {
 337              replyTo = replyHeadingTextNode && replyHeadingTextNode.textContent;
 338          }
 339  
 340          addPlaceHolder( respondElement );
 341  
 342          // Set the value of the post.
 343          if ( postId && postIdField ) {
 344              postIdField.value = postId;
 345          }
 346  
 347          parentIdField.value = commentId;
 348  
 349          cancelElement.style.display = '';
 350          addBelowElement.parentNode.insertBefore( respondElement, addBelowElement.nextSibling );
 351  
 352          if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE ) {
 353              if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) {
 354                  replyLinkToParent.style.display = 'none';
 355              }
 356  
 357              replyHeadingTextNode.textContent = replyTo;
 358          }
 359  
 360          /*
 361           * This is for backward compatibility with third party commenting systems
 362           * hooking into the event using older techniques.
 363           */
 364          cancelElement.onclick = function() {
 365              return false;
 366          };
 367  
 368          // Focus on the first field in the comment form.
 369          try {
 370              for ( var i = 0; i < commentFormElement.elements.length; i++ ) {
 371                  element = commentFormElement.elements[i];
 372                  cssHidden = false;
 373  
 374                  // Get elements computed style.
 375                  if ( 'getComputedStyle' in window ) {
 376                      // Modern browsers.
 377                      style = window.getComputedStyle( element );
 378                  } else if ( document.documentElement.currentStyle ) {
 379                      // IE 8.
 380                      style = element.currentStyle;
 381                  }
 382  
 383                  /*
 384                   * For display none, do the same thing jQuery does. For visibility,
 385                   * check the element computed style since browsers are already doing
 386                   * the job for us. In fact, the visibility computed style is the actual
 387                   * computed value and already takes into account the element ancestors.
 388                   */
 389                  if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) {
 390                      cssHidden = true;
 391                  }
 392  
 393                  // Skip form elements that are hidden or disabled.
 394                  if ( 'hidden' === element.type || element.disabled || cssHidden ) {
 395                      continue;
 396                  }
 397  
 398                  element.focus();
 399                  // Stop after the first focusable element.
 400                  break;
 401              }
 402          }
 403          catch(e) {
 404  
 405          }
 406  
 407          /*
 408           * false is returned for backward compatibility with third party commenting systems
 409           * hooking into this function.
 410           */
 411          return false;
 412      }
 413  
 414      /**
 415       * Add placeholder element.
 416       *
 417       * Places a place holder element above the #respond element for
 418       * the form to be returned to if needs be.
 419       *
 420       * @since 2.7.0
 421       *
 422       * @param {HTMLelement} respondElement the #respond element holding comment form.
 423       */
 424  	function addPlaceHolder( respondElement ) {
 425          var temporaryFormId  = config.temporaryFormId;
 426          var temporaryElement = getElementById( temporaryFormId );
 427          var replyElement = getElementById( config.commentReplyTitleId );
 428          var initialHeadingText = replyElement ? replyElement.firstChild.textContent : '';
 429  
 430          if ( temporaryElement ) {
 431              // The element already exists, no need to recreate.
 432              return;
 433          }
 434  
 435          temporaryElement = document.createElement( 'div' );
 436          temporaryElement.id = temporaryFormId;
 437          temporaryElement.style.display = 'none';
 438          temporaryElement.textContent = initialHeadingText;
 439          respondElement.parentNode.insertBefore( temporaryElement, respondElement );
 440      }
 441  
 442      return {
 443          init: init,
 444          moveForm: moveForm
 445      };
 446  })( window );


Generated : Tue Sep 15 08:20:32 2026 Cross-referenced by PHPXref