Merge "Assigning the global Gerrit js object just once as window.Gerrit"
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index 1704153..248dc6e 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -115,9 +115,9 @@
     }
   }
 
-  // Generate a candidate list at 2x the size of what the user wants to see to
+  // Generate a candidate list at 3x the size of what the user wants to see to
   // give the ranking algorithm a good set of candidates it can work with
-  private static final int CANDIDATE_LIST_MULTIPLIER = 2;
+  private static final int CANDIDATE_LIST_MULTIPLIER = 3;
 
   private final AccountLoader.Factory accountLoaderFactory;
   private final AccountQueryBuilder accountQueryBuilder;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
index 485b0bb..6dbc399 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
@@ -136,11 +136,11 @@
       }
     },
 
-    moveToNextChunk() {
+    moveToNextChunk(opt_clipToTop) {
       this.$.cursorManager.next(this._isFirstRowOfChunk.bind(this),
           target => {
             return target.parentNode.scrollHeight;
-          });
+          }, opt_clipToTop);
       this._fixSide();
     },
 
@@ -200,7 +200,7 @@
 
     moveToFirstChunk() {
       this.$.cursorManager.moveToStart();
-      this.moveToNextChunk();
+      this.moveToNextChunk(true);
     },
 
     reInitCursor() {
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
index 36e6a7b..2c38c2a 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
@@ -90,8 +90,21 @@
       this.unsetCursor();
     },
 
-    next(opt_condition, opt_getTargetHeight) {
-      this._moveCursor(1, opt_condition, opt_getTargetHeight);
+    /**
+     * Move the cursor forward. Clipped to the ends of the stop list.
+     * @param {!Function=} opt_condition Optional stop condition. If a condition
+     *    is passed the cursor will continue to move in the specified direction
+     *    until the condition is met.
+     * @param {!Function=} opt_getTargetHeight Optional function to calculate the
+     *    height of the target's 'section'. The height of the target itself is
+     *    sometimes different, used by the diff cursor.
+     * @param {boolean=} opt_clipToTop When none of the next indices match, move
+     *     back to first instead of to last.
+     * @private
+     */
+
+    next(opt_condition, opt_getTargetHeight, opt_clipToTop) {
+      this._moveCursor(1, opt_condition, opt_getTargetHeight, opt_clipToTop);
     },
 
     previous(opt_condition) {
@@ -145,8 +158,8 @@
     },
 
     /**
-     * Move the cursor forward or backward by delta. Noop if moving past either
-     * end of the stop list.
+     * Move the cursor forward or backward by delta. Clipped to the beginning or
+     * end of stop list.
      * @param {number} delta either -1 or 1.
      * @param {!Function=} opt_condition Optional stop condition. If a condition
      *    is passed the cursor will continue to move in the specified direction
@@ -154,9 +167,11 @@
      * @param {!Function=} opt_getTargetHeight Optional function to calculate the
      *    height of the target's 'section'. The height of the target itself is
      *    sometimes different, used by the diff cursor.
+     * @param {boolean=} opt_clipToTop When none of the next indices match, move
+     *     back to first instead of to last.
      * @private
      */
-    _moveCursor(delta, opt_condition, opt_getTargetHeight) {
+    _moveCursor(delta, opt_condition, opt_getTargetHeight, opt_clipToTop) {
       if (!this.stops.length) {
         this.unsetCursor();
         return;
@@ -164,7 +179,7 @@
 
       this._unDecorateTarget();
 
-      const newIndex = this._getNextindex(delta, opt_condition);
+      const newIndex = this._getNextindex(delta, opt_condition, opt_clipToTop);
 
       let newTarget = null;
       if (newIndex !== -1) {
@@ -203,10 +218,12 @@
      * Get the next stop index indicated by the delta direction.
      * @param {number} delta either -1 or 1.
      * @param {!Function=} opt_condition Optional stop condition.
+     * @param {boolean=} opt_clipToTop When none of the next indices match, move
+     *     back to first instead of to last.
      * @return {number} the new index.
      * @private
      */
-    _getNextindex(delta, opt_condition) {
+    _getNextindex(delta, opt_condition, opt_clipToTop) {
       if (!this.stops.length || this.index === -1) {
         return -1;
       }
@@ -222,10 +239,10 @@
 
       // If we failed to satisfy the condition:
       if (opt_condition && !opt_condition(this.stops[newIndex])) {
-        if (delta > 0) {
-          return this.stops.length - 1;
-        } else if (delta < 0) {
+        if (delta < 0 || opt_clipToTop) {
           return 0;
+        } else if (delta > 0) {
+          return this.stops.length - 1;
         }
         return this.index;
       }