[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7