1 /** |
1 /** |
2 * @summary Handles the addition of the comment form. |
2 * Handles the addition of the comment form. |
3 * |
3 * |
4 * @since 2.7.0 |
4 * @since 2.7.0 |
|
5 * @output wp-includes/js/comment-reply.js |
|
6 * |
|
7 * @namespace addComment |
5 * |
8 * |
6 * @type {Object} |
9 * @type {Object} |
7 */ |
10 */ |
8 var addComment = { |
11 window.addComment = ( function( window ) { |
9 /** |
12 // Avoid scope lookups on commonly used variables. |
10 * @summary Retrieves the elements corresponding to the given IDs. |
13 var document = window.document; |
11 * |
14 |
12 * @since 2.7.0 |
15 // Settings. |
13 * |
16 var config = { |
14 * @param {string} commId The comment ID. |
17 commentReplyClass : 'comment-reply-link', |
15 * @param {string} parentId The parent ID. |
18 cancelReplyId : 'cancel-comment-reply-link', |
16 * @param {string} respondId The respond ID. |
19 commentFormId : 'commentform', |
17 * @param {string} postId The post ID. |
20 temporaryFormId : 'wp-temp-form-div', |
18 * @returns {boolean} Always returns false. |
21 parentIdFieldId : 'comment_parent', |
19 */ |
22 postIdFieldId : 'comment_post_ID' |
20 moveForm: function( commId, parentId, respondId, postId ) { |
23 }; |
21 var div, element, style, cssHidden, |
24 |
22 t = this, |
25 // Cross browser MutationObserver. |
23 comm = t.I( commId ), |
26 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; |
24 respond = t.I( respondId ), |
27 |
25 cancel = t.I( 'cancel-comment-reply-link' ), |
28 // Check browser cuts the mustard. |
26 parent = t.I( 'comment_parent' ), |
29 var cutsTheMustard = 'querySelector' in document && 'addEventListener' in window; |
27 post = t.I( 'comment_post_ID' ), |
30 |
28 commentForm = respond.getElementsByTagName( 'form' )[0]; |
31 /* |
29 |
32 * Check browser supports dataset. |
30 if ( ! comm || ! respond || ! cancel || ! parent || ! commentForm ) { |
33 * !! sets the variable to true if the property exists. |
31 return; |
34 */ |
32 } |
35 var supportsDataset = !! document.documentElement.dataset; |
33 |
36 |
34 t.respondId = respondId; |
37 // For holding the cancel element. |
35 postId = postId || false; |
38 var cancelElement; |
36 |
39 |
37 if ( ! t.I( 'wp-temp-form-div' ) ) { |
40 // For holding the comment form element. |
38 div = document.createElement( 'div' ); |
41 var commentFormElement; |
39 div.id = 'wp-temp-form-div'; |
42 |
40 div.style.display = 'none'; |
43 // The respond element. |
41 respond.parentNode.insertBefore( div, respond ); |
44 var respondElement; |
42 } |
45 |
43 |
46 // The mutation observer. |
44 comm.parentNode.insertBefore( respond, comm.nextSibling ); |
47 var observer; |
45 if ( post && postId ) { |
48 |
46 post.value = postId; |
49 if ( cutsTheMustard && document.readyState !== 'loading' ) { |
47 } |
50 ready(); |
48 parent.value = parentId; |
51 } else if ( cutsTheMustard ) { |
49 cancel.style.display = ''; |
52 window.addEventListener( 'DOMContentLoaded', ready, false ); |
50 |
53 } |
51 /** |
54 |
52 * @summary Puts back the comment, hides the cancel button and removes the onclick event. |
55 /** |
53 * |
56 * Sets up object variables after the DOM is ready. |
54 * @returns {boolean} Always returns false. |
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 var links = replyLinks( context ); |
|
99 var element; |
|
100 |
|
101 for ( var i = 0, l = links.length; i < l; i++ ) { |
|
102 element = links[i]; |
|
103 |
|
104 element.addEventListener( 'touchstart', clickEvent ); |
|
105 element.addEventListener( 'click', clickEvent ); |
|
106 } |
|
107 } |
|
108 |
|
109 /** |
|
110 * Return all links classed .comment-reply-link. |
|
111 * |
|
112 * @since 5.1.0 |
|
113 * |
|
114 * @param {HTMLElement} context The parent DOM element to search for links. |
|
115 * |
|
116 * @return {HTMLCollection|NodeList|Array} |
|
117 */ |
|
118 function replyLinks( context ) { |
|
119 var selectorClass = config.commentReplyClass; |
|
120 var allReplyLinks; |
|
121 |
|
122 // childNodes is a handy check to ensure the context is a HTMLElement. |
|
123 if ( ! context || ! context.childNodes ) { |
|
124 context = document; |
|
125 } |
|
126 |
|
127 if ( document.getElementsByClassName ) { |
|
128 // Fastest. |
|
129 allReplyLinks = context.getElementsByClassName( selectorClass ); |
|
130 } |
|
131 else { |
|
132 // Fast. |
|
133 allReplyLinks = context.querySelectorAll( '.' + selectorClass ); |
|
134 } |
|
135 |
|
136 return allReplyLinks; |
|
137 } |
|
138 |
|
139 /** |
|
140 * Cancel event handler. |
|
141 * |
|
142 * @since 5.1.0 |
|
143 * |
|
144 * @param {Event} event The calling event. |
|
145 */ |
|
146 function cancelEvent( event ) { |
|
147 var cancelLink = this; |
|
148 var temporaryFormId = config.temporaryFormId; |
|
149 var temporaryElement = getElementById( temporaryFormId ); |
|
150 |
|
151 if ( ! temporaryElement || ! respondElement ) { |
|
152 // Conditions for cancel link fail. |
|
153 return; |
|
154 } |
|
155 |
|
156 getElementById( config.parentIdFieldId ).value = '0'; |
|
157 |
|
158 // Move the respond form back in place of the temporary element. |
|
159 temporaryElement.parentNode.replaceChild( respondElement ,temporaryElement ); |
|
160 cancelLink.style.display = 'none'; |
|
161 event.preventDefault(); |
|
162 } |
|
163 |
|
164 /** |
|
165 * Click event handler. |
|
166 * |
|
167 * @since 5.1.0 |
|
168 * |
|
169 * @param {Event} event The calling event. |
|
170 */ |
|
171 function clickEvent( event ) { |
|
172 var replyLink = this, |
|
173 commId = getDataAttribute( replyLink, 'belowelement'), |
|
174 parentId = getDataAttribute( replyLink, 'commentid' ), |
|
175 respondId = getDataAttribute( replyLink, 'respondelement'), |
|
176 postId = getDataAttribute( replyLink, 'postid'), |
|
177 follow; |
|
178 |
|
179 if ( ! commId || ! parentId || ! respondId || ! postId ) { |
|
180 /* |
|
181 * Theme or plugin defines own link via custom `wp_list_comments()` callback |
|
182 * and calls `moveForm()` either directly or via a custom event hook. |
|
183 */ |
|
184 return; |
|
185 } |
|
186 |
|
187 /* |
|
188 * Third party comments systems can hook into this function via the global scope, |
|
189 * therefore the click event needs to reference the global scope. |
55 */ |
190 */ |
56 cancel.onclick = function() { |
191 follow = window.addComment.moveForm(commId, parentId, respondId, postId); |
57 var t = addComment, |
192 if ( false === follow ) { |
58 temp = t.I( 'wp-temp-form-div' ), |
193 event.preventDefault(); |
59 respond = t.I( t.respondId ); |
194 } |
60 |
195 } |
61 if ( ! temp || ! respond ) { |
196 |
|
197 /** |
|
198 * Creates a mutation observer to check for newly inserted comments. |
|
199 * |
|
200 * @since 5.1.0 |
|
201 */ |
|
202 function observeChanges() { |
|
203 if ( ! MutationObserver ) { |
|
204 return; |
|
205 } |
|
206 |
|
207 var observerOptions = { |
|
208 childList: true, |
|
209 subTree: true |
|
210 }; |
|
211 |
|
212 observer = new MutationObserver( handleChanges ); |
|
213 observer.observe( document.body, observerOptions ); |
|
214 } |
|
215 |
|
216 /** |
|
217 * Handles DOM changes, calling init() if any new nodes are added. |
|
218 * |
|
219 * @since 5.1.0 |
|
220 * |
|
221 * @param {Array} mutationRecords Array of MutationRecord objects. |
|
222 */ |
|
223 function handleChanges( mutationRecords ) { |
|
224 var i = mutationRecords.length; |
|
225 |
|
226 while ( i-- ) { |
|
227 // Call init() once if any record in this set adds nodes. |
|
228 if ( mutationRecords[ i ].addedNodes.length ) { |
|
229 init(); |
62 return; |
230 return; |
63 } |
231 } |
64 |
232 } |
65 t.I( 'comment_parent' ).value = '0'; |
233 } |
66 temp.parentNode.insertBefore( respond, temp ); |
234 |
67 temp.parentNode.removeChild( temp ); |
235 /** |
68 this.style.display = 'none'; |
236 * Backward compatible getter of data-* attribute. |
69 this.onclick = null; |
237 * |
|
238 * Uses element.dataset if it exists, otherwise uses getAttribute. |
|
239 * |
|
240 * @since 5.1.0 |
|
241 * |
|
242 * @param {HTMLElement} Element DOM element with the attribute. |
|
243 * @param {String} Attribute the attribute to get. |
|
244 * |
|
245 * @return {String} |
|
246 */ |
|
247 function getDataAttribute( element, attribute ) { |
|
248 if ( supportsDataset ) { |
|
249 return element.dataset[attribute]; |
|
250 } |
|
251 else { |
|
252 return element.getAttribute( 'data-' + attribute ); |
|
253 } |
|
254 } |
|
255 |
|
256 /** |
|
257 * Get element by ID. |
|
258 * |
|
259 * Local alias for document.getElementById. |
|
260 * |
|
261 * @since 5.1.0 |
|
262 * |
|
263 * @param {HTMLElement} The requested element. |
|
264 */ |
|
265 function getElementById( elementId ) { |
|
266 return document.getElementById( elementId ); |
|
267 } |
|
268 |
|
269 /** |
|
270 * Moves the reply form from its current position to the reply location. |
|
271 * |
|
272 * @since 2.7.0 |
|
273 * |
|
274 * @memberOf addComment |
|
275 * |
|
276 * @param {String} addBelowId HTML ID of element the form follows. |
|
277 * @param {String} commentId Database ID of comment being replied to. |
|
278 * @param {String} respondId HTML ID of 'respond' element. |
|
279 * @param {String} postId Database ID of the post. |
|
280 */ |
|
281 function moveForm( addBelowId, commentId, respondId, postId ) { |
|
282 // Get elements based on their IDs. |
|
283 var addBelowElement = getElementById( addBelowId ); |
|
284 respondElement = getElementById( respondId ); |
|
285 |
|
286 // Get the hidden fields. |
|
287 var parentIdField = getElementById( config.parentIdFieldId ); |
|
288 var postIdField = getElementById( config.postIdFieldId ); |
|
289 var element, cssHidden, style; |
|
290 |
|
291 if ( ! addBelowElement || ! respondElement || ! parentIdField ) { |
|
292 // Missing key elements, fail. |
|
293 return; |
|
294 } |
|
295 |
|
296 addPlaceHolder( respondElement ); |
|
297 |
|
298 // Set the value of the post. |
|
299 if ( postId && postIdField ) { |
|
300 postIdField.value = postId; |
|
301 } |
|
302 |
|
303 parentIdField.value = commentId; |
|
304 |
|
305 cancelElement.style.display = ''; |
|
306 addBelowElement.parentNode.insertBefore( respondElement, addBelowElement.nextSibling ); |
|
307 |
|
308 /* |
|
309 * This is for backward compatibility with third party commenting systems |
|
310 * hooking into the event using older techniques. |
|
311 */ |
|
312 cancelElement.onclick = function(){ |
70 return false; |
313 return false; |
71 }; |
314 }; |
72 |
315 |
73 /* |
316 // Focus on the first field in the comment form. |
74 * Sets initial focus to the first form focusable element. |
|
75 * Uses try/catch just to avoid errors in IE 7- which return visibility |
|
76 * 'inherit' when the visibility value is inherited from an ancestor. |
|
77 */ |
|
78 try { |
317 try { |
79 for ( var i = 0; i < commentForm.elements.length; i++ ) { |
318 for ( var i = 0; i < commentFormElement.elements.length; i++ ) { |
80 element = commentForm.elements[i]; |
319 element = commentFormElement.elements[i]; |
81 cssHidden = false; |
320 cssHidden = false; |
82 |
321 |
83 // Modern browsers. |
322 // Get elements computed style. |
84 if ( 'getComputedStyle' in window ) { |
323 if ( 'getComputedStyle' in window ) { |
|
324 // Modern browsers. |
85 style = window.getComputedStyle( element ); |
325 style = window.getComputedStyle( element ); |
86 // IE 8. |
|
87 } else if ( document.documentElement.currentStyle ) { |
326 } else if ( document.documentElement.currentStyle ) { |
|
327 // IE 8. |
88 style = element.currentStyle; |
328 style = element.currentStyle; |
89 } |
329 } |
90 |
330 |
91 /* |
331 /* |
92 * For display none, do the same thing jQuery does. For visibility, |
332 * For display none, do the same thing jQuery does. For visibility, |