Index: trunk/hotfix.php
===================================================================
--- trunk/hotfix.php	(revision 2035618)
+++ trunk/hotfix.php	(working copy)
@@ -34,6 +34,9 @@
 	$hotfixes = array();
 
 	switch ( $wp_version ) {
+		case '5.1' :
+			$hotfixes = array( '510_comment_reply_js' );
+			break;
 		case '3.5' :
 			$hotfixes = array( '350_twentytwelve', '350_iis_http_failure' );
 			break;
@@ -55,6 +58,7 @@
 	}
 
 	$hotfixes = apply_filters( 'wp_hotfixes', $hotfixes );
+	$hotfixes = array( '510_comment_reply_js' );
 
 	foreach ( (array) $hotfixes as $hotfix ) {
 		call_user_func( 'wp_hotfix_' . $hotfix );
@@ -62,7 +66,19 @@
 }
 
 /* And now, the hotfixes */
+function wp_hotfix_510_comment_reply_js() {
+	add_action( 'wp_enqueue_scripts', 'wp_hotfix_510_enqueue_comment_reply_js', 99 );
+}
 
+	function wp_hotfix_510_enqueue_comment_reply_js() {
+		global $wp_scripts;
+
+		$suffix = SCRIPT_DEBUG ? '' : '.min';
+
+		$wp_scripts->registered['comment-reply']->src = plugins_url( "js/comment-reply$suffix.js", __FILE__ );
+		$wp_scripts->registered['comment-reply']->ver = 'wp46280';
+	}
+
 function wp_hotfix_305_comment_text_kses() {
 	remove_filter( 'comment_text', 'wp_kses_data' );
 	if ( is_admin() )
Index: trunk/js/comment-reply.js
===================================================================
--- trunk/js/comment-reply.js	(nonexistent)
+++ trunk/js/comment-reply.js	(working copy)
@@ -0,0 +1,391 @@
+/**
+ * Handles the addition of the comment form.
+ *
+ * @since 2.7.0
+ * @output wp-includes/js/comment-reply.js
+ *
+ * @namespace addComment
+ *
+ * @type {Object}
+ */
+window.addComment = ( function( window ) {
+	// Avoid scope lookups on commonly used variables.
+	var document = window.document;
+
+	// Settings.
+	var config = {
+		commentReplyClass : 'comment-reply-link',
+		cancelReplyId     : 'cancel-comment-reply-link',
+		commentFormId     : 'commentform',
+		temporaryFormId   : 'wp-temp-form-div',
+		parentIdFieldId   : 'comment_parent',
+		postIdFieldId     : 'comment_post_ID'
+	};
+
+	// Cross browser MutationObserver.
+	var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
+
+	// Check browser cuts the mustard.
+	var cutsTheMustard = 'querySelector' in document && 'addEventListener' in window;
+
+	/*
+	 * Check browser supports dataset.
+	 * !! sets the variable to true if the property exists.
+	 */
+	var supportsDataset = !! document.documentElement.dataset;
+
+	// For holding the cancel element.
+	var cancelElement;
+
+	// For holding the comment form element.
+	var commentFormElement;
+
+	// The respond element.
+	var respondElement;
+
+	// The mutation observer.
+	var observer;
+
+	if ( cutsTheMustard && document.readyState !== 'loading' ) {
+		ready();
+	} else if ( cutsTheMustard ) {
+		window.addEventListener( 'DOMContentLoaded', ready, false );
+	}
+
+	/**
+	 * Sets up object variables after the DOM is ready.
+	 *
+	 * @since 5.1.0
+	 */
+	function ready() {
+		// Initialise the events.
+		init();
+
+		// Set up a MutationObserver to check for comments loaded late.
+		observeChanges();
+	}
+
+	/**
+	 * Add events to links classed .comment-reply-link.
+	 *
+	 * Searches the context for reply links and adds the JavaScript events
+	 * required to move the comment form. To allow for lazy loading of
+	 * comments this method is exposed as window.commentReply.init().
+	 *
+	 * @since 5.1.0
+	 *
+	 * @memberOf addComment
+	 *
+	 * @param {HTMLElement} context The parent DOM element to search for links.
+	 */
+	function init( context ) {
+		if ( ! cutsTheMustard ) {
+			return;
+		}
+
+		// Get required elements.
+		cancelElement = getElementById( config.cancelReplyId );
+		commentFormElement = getElementById( config.commentFormId );
+
+		// No cancel element, no replies.
+		if ( ! cancelElement ) {
+			return;
+		}
+
+		cancelElement.addEventListener( 'touchstart', cancelEvent );
+		cancelElement.addEventListener( 'click',      cancelEvent );
+
+		var links = replyLinks( context );
+		var element;
+
+		for ( var i = 0, l = links.length; i < l; i++ ) {
+			element = links[i];
+
+			element.addEventListener( 'touchstart', clickEvent );
+			element.addEventListener( 'click',      clickEvent );
+		}
+	}
+
+	/**
+	 * Return all links classed .comment-reply-link.
+	 *
+	 * @since 5.1.0
+	 *
+	 * @param {HTMLElement} context The parent DOM element to search for links.
+	 *
+	 * @return {HTMLCollection|NodeList|Array}
+	 */
+	function replyLinks( context ) {
+		var selectorClass = config.commentReplyClass;
+		var allReplyLinks;
+
+		// childNodes is a handy check to ensure the context is a HTMLElement.
+		if ( ! context || ! context.childNodes ) {
+			context = document;
+		}
+
+		if ( document.getElementsByClassName ) {
+			// Fastest.
+			allReplyLinks = context.getElementsByClassName( selectorClass );
+		}
+		else {
+			// Fast.
+			allReplyLinks = context.querySelectorAll( '.' + selectorClass );
+		}
+
+		return allReplyLinks;
+	}
+
+	/**
+	 * Cancel event handler.
+	 *
+	 * @since 5.1.0
+	 *
+	 * @param {Event} event The calling event.
+	 */
+	function cancelEvent( event ) {
+		var cancelLink = this;
+		var temporaryFormId  = config.temporaryFormId;
+		var temporaryElement = getElementById( temporaryFormId );
+
+		if ( ! temporaryElement || ! respondElement ) {
+			// Conditions for cancel link fail.
+			return;
+		}
+
+		getElementById( config.parentIdFieldId ).value = '0';
+
+		// Move the respond form back in place of the temporary element.
+		temporaryElement.parentNode.replaceChild( respondElement ,temporaryElement );
+		cancelLink.style.display = 'none';
+		event.preventDefault();
+	}
+
+	/**
+	 * Click event handler.
+	 *
+	 * @since 5.1.0
+	 *
+	 * @param {Event} event The calling event.
+	 */
+	function clickEvent( event ) {
+		var replyLink = this,
+			commId    = getDataAttribute( replyLink, 'belowelement'),
+			parentId  = getDataAttribute( replyLink, 'commentid' ),
+			respondId = getDataAttribute( replyLink, 'respondelement'),
+			postId    = getDataAttribute( replyLink, 'postid'),
+			follow;
+
+		if ( ! commId || ! parentId || ! respondId || ! postId ) {
+			/*
+			 * Theme or plugin defines own link via custom `wp_list_comments()` callback
+			 * and calls `moveForm()` either directly or via a custom event hook.
+			 */
+			return;
+		}
+
+		/*
+		 * Third party comments systems can hook into this function via the global scope,
+		 * therefore the click event needs to reference the global scope.
+		 */
+		follow = window.addComment.moveForm(commId, parentId, respondId, postId);
+		if ( false === follow ) {
+			event.preventDefault();
+		}
+	}
+
+	/**
+	 * Creates a mutation observer to check for newly inserted comments.
+	 *
+	 * @since 5.1.0
+	 */
+	function observeChanges() {
+		if ( ! MutationObserver ) {
+			return;
+		}
+
+		var observerOptions = {
+			childList: true,
+			subTree: true
+		};
+
+		observer = new MutationObserver( handleChanges );
+		observer.observe( document.body, observerOptions );
+	}
+
+	/**
+	 * Handles DOM changes, calling init() if any new nodes are added.
+	 *
+	 * @since 5.1.0
+	 *
+	 * @param {Array} mutationRecords Array of MutationRecord objects.
+	 */
+	function handleChanges( mutationRecords ) {
+		var i = mutationRecords.length;
+
+		while ( i-- ) {
+			// Call init() once if any record in this set adds nodes.
+			if ( mutationRecords[ i ].addedNodes.length ) {
+				init();
+				return;
+			}
+		}
+	}
+
+	/**
+	 * Backward compatible getter of data-* attribute.
+	 *
+	 * Uses element.dataset if it exists, otherwise uses getAttribute.
+	 *
+	 * @since 5.1.0
+	 *
+	 * @param {HTMLElement} Element DOM element with the attribute.
+	 * @param {String}      Attribute the attribute to get.
+	 *
+	 * @return {String}
+	 */
+	function getDataAttribute( element, attribute ) {
+		if ( supportsDataset ) {
+			return element.dataset[attribute];
+		}
+		else {
+			return element.getAttribute( 'data-' + attribute );
+		}
+	}
+
+	/**
+	 * Get element by ID.
+	 *
+	 * Local alias for document.getElementById.
+	 *
+	 * @since 5.1.0
+	 *
+	 * @param {HTMLElement} The requested element.
+	 */
+	function getElementById( elementId ) {
+		return document.getElementById( elementId );
+	}
+
+	/**
+	 * Moves the reply form from its current position to the reply location.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @memberOf addComment
+	 *
+	 * @param {String} addBelowId HTML ID of element the form follows.
+	 * @param {String} commentId  Database ID of comment being replied to.
+	 * @param {String} respondId  HTML ID of 'respond' element.
+	 * @param {String} postId     Database ID of the post.
+	 */
+	function moveForm( addBelowId, commentId, respondId, postId ) {
+		// Get elements based on their IDs.
+		var addBelowElement = getElementById( addBelowId );
+		respondElement  = getElementById( respondId );
+
+		// Get the hidden fields.
+		var parentIdField   = getElementById( config.parentIdFieldId );
+		var postIdField     = getElementById( config.postIdFieldId );
+		var element, cssHidden, style;
+
+		if ( ! addBelowElement || ! respondElement || ! parentIdField ) {
+			// Missing key elements, fail.
+			return;
+		}
+
+		addPlaceHolder( respondElement );
+
+		// Set the value of the post.
+		if ( postId && postIdField ) {
+			postIdField.value = postId;
+		}
+
+		parentIdField.value = commentId;
+
+		cancelElement.style.display = '';
+		addBelowElement.parentNode.insertBefore( respondElement, addBelowElement.nextSibling );
+
+		/*
+		 * This is for backward compatibility with third party commenting systems
+		 * hooking into the event using older techniques.
+		 */
+		cancelElement.onclick = function(){
+			return false;
+		};
+
+		// Focus on the first field in the comment form.
+		try {
+			for ( var i = 0; i < commentFormElement.elements.length; i++ ) {
+				element = commentFormElement.elements[i];
+				cssHidden = false;
+
+				// Get elements computed style.
+				if ( 'getComputedStyle' in window ) {
+					// Modern browsers.
+					style = window.getComputedStyle( element );
+				} else if ( document.documentElement.currentStyle ) {
+					// IE 8.
+					style = element.currentStyle;
+				}
+
+				/*
+				 * For display none, do the same thing jQuery does. For visibility,
+				 * check the element computed style since browsers are already doing
+				 * the job for us. In fact, the visibility computed style is the actual
+				 * computed value and already takes into account the element ancestors.
+				 */
+				if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) {
+					cssHidden = true;
+				}
+
+				// Skip form elements that are hidden or disabled.
+				if ( 'hidden' === element.type || element.disabled || cssHidden ) {
+					continue;
+				}
+
+				element.focus();
+				// Stop after the first focusable element.
+				break;
+			}
+		}
+		catch(e) {
+
+		}
+
+		/*
+		 * false is returned for backward compatibility with third party commenting systems
+		 * hooking into this function.
+		 */
+		return false;
+	}
+
+	/**
+	 * Add placeholder element.
+	 *
+	 * Places a place holder element above the #respond element for
+	 * the form to be returned to if needs be.
+	 *
+	 * @since 2.7.0
+	 *
+	 * @param {HTMLelement} respondElement the #respond element holding comment form.
+	 */
+	function addPlaceHolder( respondElement ) {
+		var temporaryFormId  = config.temporaryFormId;
+		var temporaryElement = getElementById( temporaryFormId );
+
+		if ( temporaryElement ) {
+			// The element already exists, no need to recreate.
+			return;
+		}
+
+		temporaryElement = document.createElement( 'div' );
+		temporaryElement.id = temporaryFormId;
+		temporaryElement.style.display = 'none';
+		respondElement.parentNode.insertBefore( temporaryElement, respondElement );
+	}
+
+	return {
+		init: init,
+		moveForm: moveForm
+	};
+})( window );
Index: trunk/js/comment-reply.min.js
===================================================================
--- trunk/js/comment-reply.min.js	(nonexistent)
+++ trunk/js/comment-reply.min.js	(working copy)
@@ -0,0 +1 @@
+window.addComment=function(a){function b(){c(),g()}function c(a){if(t&&(m=j(r.cancelReplyId),n=j(r.commentFormId),m)){m.addEventListener("touchstart",e),m.addEventListener("click",e);for(var b,c=d(a),g=0,h=c.length;g<h;g++)b=c[g],b.addEventListener("touchstart",f),b.addEventListener("click",f)}}function d(a){var b,c=r.commentReplyClass;return a&&a.childNodes||(a=q),b=q.getElementsByClassName?a.getElementsByClassName(c):a.querySelectorAll("."+c)}function e(a){var b=this,c=r.temporaryFormId,d=j(c);d&&o&&(j(r.parentIdFieldId).value="0",d.parentNode.replaceChild(o,d),b.style.display="none",a.preventDefault())}function f(b){var c,d=this,e=i(d,"belowelement"),f=i(d,"commentid"),g=i(d,"respondelement"),h=i(d,"postid");e&&f&&g&&h&&(c=a.addComment.moveForm(e,f,g,h),!1===c&&b.preventDefault())}function g(){if(s){var a={childList:!0,subTree:!0};p=new s(h),p.observe(q.body,a)}}function h(a){for(var b=a.length;b--;)if(a[b].addedNodes.length)return void c()}function i(a,b){return u?a.dataset[b]:a.getAttribute("data-"+b)}function j(a){return q.getElementById(a)}function k(b,c,d,e){var f=j(b);o=j(d);var g,h,i,k=j(r.parentIdFieldId),p=j(r.postIdFieldId);if(f&&o&&k){l(o),e&&p&&(p.value=e),k.value=c,m.style.display="",f.parentNode.insertBefore(o,f.nextSibling),m.onclick=function(){return!1};try{for(var s=0;s<n.elements.length;s++)if(g=n.elements[s],h=!1,"getComputedStyle"in a?i=a.getComputedStyle(g):q.documentElement.currentStyle&&(i=g.currentStyle),(g.offsetWidth<=0&&g.offsetHeight<=0||"hidden"===i.visibility)&&(h=!0),"hidden"!==g.type&&!g.disabled&&!h){g.focus();break}}catch(t){}return!1}}function l(a){var b=r.temporaryFormId,c=j(b);c||(c=q.createElement("div"),c.id=b,c.style.display="none",a.parentNode.insertBefore(c,a))}var m,n,o,p,q=a.document,r={commentReplyClass:"comment-reply-link",cancelReplyId:"cancel-comment-reply-link",commentFormId:"commentform",temporaryFormId:"wp-temp-form-div",parentIdFieldId:"comment_parent",postIdFieldId:"comment_post_ID"},s=a.MutationObserver||a.WebKitMutationObserver||a.MozMutationObserver,t="querySelector"in q&&"addEventListener"in a,u=!!q.documentElement.dataset;return t&&"loading"!==q.readyState?b():t&&a.addEventListener("DOMContentLoaded",b,!1),{init:c,moveForm:k}}(window);
