[ 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( 'touchstart', cancelEvent ); 97 cancelElement.addEventListener( 'click', cancelEvent ); 98 99 // Submit the comment form when the user types [Ctrl] or [Cmd] + [Enter]. 100 var submitFormHandler = function( e ) { 101 if ( ( e.metaKey || e.ctrlKey ) && e.keyCode === 13 && document.activeElement.tagName.toLowerCase() !== 'a' ) { 102 commentFormElement.removeEventListener( 'keydown', submitFormHandler ); 103 e.preventDefault(); 104 // The submit button ID is 'submit' so we can't call commentFormElement.submit(). Click it instead. 105 commentFormElement.submit.click(); 106 return false; 107 } 108 }; 109 110 if ( commentFormElement ) { 111 commentFormElement.addEventListener( 'keydown', submitFormHandler ); 112 } 113 114 var links = replyLinks( context ); 115 var element; 116 117 for ( var i = 0, l = links.length; i < l; i++ ) { 118 element = links[i]; 119 120 element.addEventListener( 'touchstart', clickEvent ); 121 element.addEventListener( 'click', clickEvent ); 122 } 123 } 124 125 /** 126 * Return all links classed .comment-reply-link. 127 * 128 * @since 5.1.0 129 * 130 * @param {HTMLElement} context The parent DOM element to search for links. 131 * 132 * @return {HTMLCollection|NodeList|Array} 133 */ 134 function replyLinks( context ) { 135 var selectorClass = config.commentReplyClass; 136 var allReplyLinks; 137 138 // childNodes is a handy check to ensure the context is a HTMLElement. 139 if ( ! context || ! context.childNodes ) { 140 context = document; 141 } 142 143 if ( document.getElementsByClassName ) { 144 // Fastest. 145 allReplyLinks = context.getElementsByClassName( selectorClass ); 146 } 147 else { 148 // Fast. 149 allReplyLinks = context.querySelectorAll( '.' + selectorClass ); 150 } 151 152 return allReplyLinks; 153 } 154 155 /** 156 * Cancel event handler. 157 * 158 * @since 5.1.0 159 * 160 * @param {Event} event The calling event. 161 */ 162 function cancelEvent( event ) { 163 var cancelLink = this; 164 var temporaryFormId = config.temporaryFormId; 165 var temporaryElement = getElementById( temporaryFormId ); 166 167 if ( ! temporaryElement || ! respondElement ) { 168 // Conditions for cancel link fail. 169 return; 170 } 171 172 getElementById( config.parentIdFieldId ).value = '0'; 173 174 // Move the respond form back in place of the temporary element. 175 var headingText = temporaryElement.textContent; 176 temporaryElement.parentNode.replaceChild( respondElement, temporaryElement ); 177 cancelLink.style.display = 'none'; 178 179 var replyHeadingElement = getElementById( config.commentReplyTitleId ); 180 var replyHeadingTextNode = replyHeadingElement && replyHeadingElement.firstChild; 181 var replyLinkToParent = replyHeadingTextNode && replyHeadingTextNode.nextSibling; 182 183 if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE && headingText ) { 184 if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) { 185 replyLinkToParent.style.display = ''; 186 } 187 188 replyHeadingTextNode.textContent = headingText; 189 } 190 191 event.preventDefault(); 192 } 193 194 /** 195 * Click event handler. 196 * 197 * @since 5.1.0 198 * 199 * @param {Event} event The calling event. 200 */ 201 function clickEvent( event ) { 202 var replyNode = getElementById( config.commentReplyTitleId ); 203 var defaultReplyHeading = replyNode && replyNode.firstChild.textContent; 204 var replyLink = this, 205 commId = getDataAttribute( replyLink, 'belowelement' ), 206 parentId = getDataAttribute( replyLink, 'commentid' ), 207 respondId = getDataAttribute( replyLink, 'respondelement' ), 208 postId = getDataAttribute( replyLink, 'postid' ), 209 replyTo = getDataAttribute( replyLink, 'replyto' ) || defaultReplyHeading, 210 follow; 211 212 if ( ! commId || ! parentId || ! respondId || ! postId ) { 213 /* 214 * Theme or plugin defines own link via custom `wp_list_comments()` callback 215 * and calls `moveForm()` either directly or via a custom event hook. 216 */ 217 return; 218 } 219 220 /* 221 * Third party comments systems can hook into this function via the global scope, 222 * therefore the click event needs to reference the global scope. 223 */ 224 follow = window.addComment.moveForm( commId, parentId, respondId, postId, replyTo ); 225 if ( false === follow ) { 226 event.preventDefault(); 227 } 228 } 229 230 /** 231 * Creates a mutation observer to check for newly inserted comments. 232 * 233 * @since 5.1.0 234 */ 235 function observeChanges() { 236 if ( ! MutationObserver ) { 237 return; 238 } 239 240 var observerOptions = { 241 childList: true, 242 subtree: true 243 }; 244 245 observer = new MutationObserver( handleChanges ); 246 observer.observe( document.body, observerOptions ); 247 } 248 249 /** 250 * Handles DOM changes, calling init() if any new nodes are added. 251 * 252 * @since 5.1.0 253 * 254 * @param {Array} mutationRecords Array of MutationRecord objects. 255 */ 256 function handleChanges( mutationRecords ) { 257 var i = mutationRecords.length; 258 259 while ( i-- ) { 260 // Call init() once if any record in this set adds nodes. 261 if ( mutationRecords[ i ].addedNodes.length ) { 262 init(); 263 return; 264 } 265 } 266 } 267 268 /** 269 * Backward compatible getter of data-* attribute. 270 * 271 * Uses element.dataset if it exists, otherwise uses getAttribute. 272 * 273 * @since 5.1.0 274 * 275 * @param {HTMLElement} Element DOM element with the attribute. 276 * @param {string} Attribute the attribute to get. 277 * 278 * @return {string} 279 */ 280 function getDataAttribute( element, attribute ) { 281 if ( supportsDataset ) { 282 return element.dataset[attribute]; 283 } 284 else { 285 return element.getAttribute( 'data-' + attribute ); 286 } 287 } 288 289 /** 290 * Get element by ID. 291 * 292 * Local alias for document.getElementById. 293 * 294 * @since 5.1.0 295 * 296 * @param {HTMLElement} The requested element. 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 function moveForm( addBelowId, commentId, respondId, postId, replyTo ) { 316 // Get elements based on their IDs. 317 var addBelowElement = getElementById( addBelowId ); 318 respondElement = getElementById( respondId ); 319 320 // Get the hidden fields. 321 var parentIdField = getElementById( config.parentIdFieldId ); 322 var postIdField = getElementById( config.postIdFieldId ); 323 var element, cssHidden, style; 324 325 var replyHeading = getElementById( config.commentReplyTitleId ); 326 var replyHeadingTextNode = replyHeading && replyHeading.firstChild; 327 var replyLinkToParent = replyHeadingTextNode && replyHeadingTextNode.nextSibling; 328 329 if ( ! addBelowElement || ! respondElement || ! parentIdField ) { 330 // Missing key elements, fail. 331 return; 332 } 333 334 if ( 'undefined' === typeof replyTo ) { 335 replyTo = replyHeadingTextNode && replyHeadingTextNode.textContent; 336 } 337 338 addPlaceHolder( respondElement ); 339 340 // Set the value of the post. 341 if ( postId && postIdField ) { 342 postIdField.value = postId; 343 } 344 345 parentIdField.value = commentId; 346 347 cancelElement.style.display = ''; 348 addBelowElement.parentNode.insertBefore( respondElement, addBelowElement.nextSibling ); 349 350 if ( replyHeadingTextNode && replyHeadingTextNode.nodeType === Node.TEXT_NODE ) { 351 if ( replyLinkToParent && 'A' === replyLinkToParent.nodeName && replyLinkToParent.id !== config.cancelReplyId ) { 352 replyLinkToParent.style.display = 'none'; 353 } 354 355 replyHeadingTextNode.textContent = replyTo; 356 } 357 358 /* 359 * This is for backward compatibility with third party commenting systems 360 * hooking into the event using older techniques. 361 */ 362 cancelElement.onclick = function() { 363 return false; 364 }; 365 366 // Focus on the first field in the comment form. 367 try { 368 for ( var i = 0; i < commentFormElement.elements.length; i++ ) { 369 element = commentFormElement.elements[i]; 370 cssHidden = false; 371 372 // Get elements computed style. 373 if ( 'getComputedStyle' in window ) { 374 // Modern browsers. 375 style = window.getComputedStyle( element ); 376 } else if ( document.documentElement.currentStyle ) { 377 // IE 8. 378 style = element.currentStyle; 379 } 380 381 /* 382 * For display none, do the same thing jQuery does. For visibility, 383 * check the element computed style since browsers are already doing 384 * the job for us. In fact, the visibility computed style is the actual 385 * computed value and already takes into account the element ancestors. 386 */ 387 if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) { 388 cssHidden = true; 389 } 390 391 // Skip form elements that are hidden or disabled. 392 if ( 'hidden' === element.type || element.disabled || cssHidden ) { 393 continue; 394 } 395 396 element.focus(); 397 // Stop after the first focusable element. 398 break; 399 } 400 } 401 catch(e) { 402 403 } 404 405 /* 406 * false is returned for backward compatibility with third party commenting systems 407 * hooking into this function. 408 */ 409 return false; 410 } 411 412 /** 413 * Add placeholder element. 414 * 415 * Places a place holder element above the #respond element for 416 * the form to be returned to if needs be. 417 * 418 * @since 2.7.0 419 * 420 * @param {HTMLelement} respondElement the #respond element holding comment form. 421 */ 422 function addPlaceHolder( respondElement ) { 423 var temporaryFormId = config.temporaryFormId; 424 var temporaryElement = getElementById( temporaryFormId ); 425 var replyElement = getElementById( config.commentReplyTitleId ); 426 var initialHeadingText = replyElement ? replyElement.firstChild.textContent : ''; 427 428 if ( temporaryElement ) { 429 // The element already exists, no need to recreate. 430 return; 431 } 432 433 temporaryElement = document.createElement( 'div' ); 434 temporaryElement.id = temporaryFormId; 435 temporaryElement.style.display = 'none'; 436 temporaryElement.textContent = initialHeadingText; 437 respondElement.parentNode.insertBefore( temporaryElement, respondElement ); 438 } 439 440 return { 441 init: init, 442 moveForm: moveForm 443 }; 444 })( window );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Tue Dec 17 08:20:01 2024 | Cross-referenced by PHPXref |