| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Sep 15 08:20:32 2026 | Cross-referenced by PHPXref |