| [ 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|Array} 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} 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 {HTMLElement} The requested element. 295 */ 296 function getElementById( elementId ) { 297 return document.getElementById( elementId ); 298 } 299 300 /** 301 * Moves the reply form from its current position to the reply location. 302 * 303 * @since 2.7.0 304 * 305 * @memberOf addComment 306 * 307 * @param {string} addBelowId HTML ID of element the form follows. 308 * @param {string} commentId Database ID of comment being replied to. 309 * @param {string} respondId HTML ID of 'respond' element. 310 * @param {string} postId Database ID of the post. 311 * @param {string} replyTo Form heading content. 312 */ 313 function moveForm( addBelowId, commentId, respondId, postId, replyTo ) { 314 // Get elements based on their IDs. 315 var addBelowElement = getElementById( addBelowId ); 316 respondElement = getElementById( respondId ); 317 318 // Get the hidden fields. 319 var parentIdField = getElementById( config.parentIdFieldId ); 320 var postIdField = getElementById( config.postIdFieldId ); 321 var element, cssHidden, style; 322 323 var replyHeading = getElementById( config.commentReplyTitleId ); 324 var replyHeadingTextNode = replyHeading && replyHeading.firstChild; 325 var replyLinkToParent = replyHeadingTextNode && replyHeadingTextNode.nextSibling; 326 327 if ( ! addBelowElement || ! respondElement || ! parentIdField ) { 328 // Missing key elements, fail. 329 return; 330 } 331 332 if ( 'undefined' === typeof replyTo ) { 333 replyTo = replyHeadingTextNode && replyHeadingTextNode.textContent; 334 } 335 336 addPlaceHolder( respondElement ); 337 338 // Set the value of the post. 339 if ( postId && postIdField ) { 340 postIdField.value = postId; 341 } 342 343 parentIdField.value = commentId; 344 345 cancelElement.style.display = ''; 346 addBelowElement.parentNode.insertBefore( respondElement, addBelowElement.nextSibling ); 347 348 if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE ) { 349 if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) { 350 replyLinkToParent.style.display = 'none'; 351 } 352 353 replyHeadingTextNode.textContent = replyTo; 354 } 355 356 /* 357 * This is for backward compatibility with third party commenting systems 358 * hooking into the event using older techniques. 359 */ 360 cancelElement.onclick = function() { 361 return false; 362 }; 363 364 // Focus on the first field in the comment form. 365 try { 366 for ( var i = 0; i < commentFormElement.elements.length; i++ ) { 367 element = commentFormElement.elements[i]; 368 cssHidden = false; 369 370 // Get elements computed style. 371 if ( 'getComputedStyle' in window ) { 372 // Modern browsers. 373 style = window.getComputedStyle( element ); 374 } else if ( document.documentElement.currentStyle ) { 375 // IE 8. 376 style = element.currentStyle; 377 } 378 379 /* 380 * For display none, do the same thing jQuery does. For visibility, 381 * check the element computed style since browsers are already doing 382 * the job for us. In fact, the visibility computed style is the actual 383 * computed value and already takes into account the element ancestors. 384 */ 385 if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) { 386 cssHidden = true; 387 } 388 389 // Skip form elements that are hidden or disabled. 390 if ( 'hidden' === element.type || element.disabled || cssHidden ) { 391 continue; 392 } 393 394 element.focus(); 395 // Stop after the first focusable element. 396 break; 397 } 398 } 399 catch(e) { 400 401 } 402 403 /* 404 * false is returned for backward compatibility with third party commenting systems 405 * hooking into this function. 406 */ 407 return false; 408 } 409 410 /** 411 * Add placeholder element. 412 * 413 * Places a place holder element above the #respond element for 414 * the form to be returned to if needs be. 415 * 416 * @since 2.7.0 417 * 418 * @param {HTMLelement} respondElement the #respond element holding comment form. 419 */ 420 function addPlaceHolder( respondElement ) { 421 var temporaryFormId = config.temporaryFormId; 422 var temporaryElement = getElementById( temporaryFormId ); 423 var replyElement = getElementById( config.commentReplyTitleId ); 424 var initialHeadingText = replyElement ? replyElement.firstChild.textContent : ''; 425 426 if ( temporaryElement ) { 427 // The element already exists, no need to recreate. 428 return; 429 } 430 431 temporaryElement = document.createElement( 'div' ); 432 temporaryElement.id = temporaryFormId; 433 temporaryElement.style.display = 'none'; 434 temporaryElement.textContent = initialHeadingText; 435 respondElement.parentNode.insertBefore( temporaryElement, respondElement ); 436 } 437 438 return { 439 init: init, 440 moveForm: moveForm 441 }; 442 })( window );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Jun 24 08:20:11 2026 | Cross-referenced by PHPXref |