Merge "Use downloaded bower components in polylint_test.sh"
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java
index fbde7be..be4f917 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java
@@ -81,7 +81,7 @@
     if (LuceneIndexModule.isInMemoryTest(cfg)) {
       return new RAMDirectory();
     }
-    Path indexDir = LuceneVersionManager.getDir(sitePaths, ACCOUNTS + "_", schema);
+    Path indexDir = LuceneVersionManager.getDir(sitePaths, ACCOUNTS, schema);
     return FSDirectory.open(indexDir);
   }
 
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 3afcb07..dc9f6c1 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -108,7 +108,7 @@
   static final String UPDATED_SORT_FIELD = sortFieldName(ChangeField.UPDATED);
   static final String ID_SORT_FIELD = sortFieldName(ChangeField.LEGACY_ID);
 
-  private static final String CHANGES_PREFIX = "changes_";
+  private static final String CHANGES = "changes";
   private static final String CHANGES_OPEN = "open";
   private static final String CHANGES_CLOSED = "closed";
   private static final String ADDED_FIELD = ChangeField.ADDED.getName();
@@ -178,7 +178,7 @@
           new ChangeSubIndex(
               schema, sitePaths, new RAMDirectory(), "ramClosed", closedConfig, searcherFactory);
     } else {
-      Path dir = LuceneVersionManager.getDir(sitePaths, CHANGES_PREFIX, schema);
+      Path dir = LuceneVersionManager.getDir(sitePaths, CHANGES, schema);
       openIndex =
           new ChangeSubIndex(
               schema, sitePaths, dir.resolve(CHANGES_OPEN), openConfig, searcherFactory);
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneGroupIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneGroupIndex.java
index c4f10ff..daece8c 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneGroupIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneGroupIndex.java
@@ -80,7 +80,7 @@
     if (LuceneIndexModule.isInMemoryTest(cfg)) {
       return new RAMDirectory();
     }
-    Path indexDir = LuceneVersionManager.getDir(sitePaths, GROUPS + "_", schema);
+    Path indexDir = LuceneVersionManager.getDir(sitePaths, GROUPS, schema);
     return FSDirectory.open(indexDir);
   }
 
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
index f6f0c28..ad13066 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
@@ -48,8 +48,8 @@
     }
   }
 
-  static Path getDir(SitePaths sitePaths, String prefix, Schema<?> schema) {
-    return sitePaths.index_dir.resolve(String.format("%s%04d", prefix, schema.getVersion()));
+  static Path getDir(SitePaths sitePaths, String name, Schema<?> schema) {
+    return sitePaths.index_dir.resolve(String.format("%s_%04d", name, schema.getVersion()));
   }
 
   @Inject
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
index 72dc0b23..975bb5e 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html
@@ -20,24 +20,24 @@
 (function(window) {
   'use strict';
 
+  // Must be declared outside behavior implementation to be accessed inside
+  // behavior functions.
   const getKeyboardEvent = function(e) {
-    return Polymer.dom(e.detail ? e.detail.keyboardEvent : e);
+    e = Polymer.dom(e.detail ? e.detail.keyboardEvent : e);
+    // When e is a keyboardEvent, e.event is not null.
+    if (e.event) { e = e.event; }
+    return e;
   };
 
   /** @polymerBehavior KeyboardShortcutBehaviorImpl */
   const KeyboardShortcutBehaviorImpl = {
     modifierPressed(e) {
       e = getKeyboardEvent(e);
-      // When e is a keyboardEvent, e.event is not null.
-      if (e.event) { e = e.event; }
       return e.altKey || e.ctrlKey || e.metaKey || e.shiftKey;
     },
 
     isModifierPressed(e, modifier) {
-      e = getKeyboardEvent(e);
-      // When e is a keyboardEvent, e.event is not null.
-      if (e.event) { e = e.event; }
-      return e[modifier];
+      return getKeyboardEvent(e)[modifier];
     },
 
     shouldSuppressKeyboardShortcut(e) {
@@ -50,6 +50,11 @@
       }
       return false;
     },
+
+    // Alias for getKeyboardEvent.
+    getKeyboardEvent(e) {
+      return getKeyboardEvent(e);
+    },
   };
 
   window.Gerrit = window.Gerrit || {};
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
index df376ba..da04c37 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
@@ -21,7 +21,7 @@
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="keyboard-shortcut-behavior.html">
 
 <test-fixture id="basic">
@@ -69,7 +69,7 @@
     test('doesn’t block kb shortcuts for non-whitelisted els', done => {
       const divEl = document.createElement('div');
       element.appendChild(divEl);
-      element._handleKey = function(e) {
+      element._handleKey = e => {
         assert.isFalse(element.shouldSuppressKeyboardShortcut(e));
         done();
       };
@@ -79,7 +79,7 @@
     test('blocks kb shortcuts for input els', done => {
       const inputEl = document.createElement('input');
       element.appendChild(inputEl);
-      element._handleKey = function(e) {
+      element._handleKey = e => {
         assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
         done();
       };
@@ -89,7 +89,7 @@
     test('blocks kb shortcuts for textarea els', done => {
       const textareaEl = document.createElement('textarea');
       element.appendChild(textareaEl);
-      element._handleKey = function(e) {
+      element._handleKey = e => {
         assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
         done();
       };
@@ -100,7 +100,7 @@
       const divEl = document.createElement('div');
       const element = overlay.querySelector('test-element');
       element.appendChild(divEl);
-      element._handleKey = function(e) {
+      element._handleKey = e => {
         assert.isTrue(element.shouldSuppressKeyboardShortcut(e));
         done();
       };
@@ -109,7 +109,7 @@
 
     test('modifierPressed returns accurate values', () => {
       const spy = sandbox.spy(element, 'modifierPressed');
-      element._handleKey = function(e) {
+      element._handleKey = e => {
         element.modifierPressed(e);
       };
       MockInteractions.keyDownOn(element, 75, 'shift', 'k');
@@ -130,7 +130,7 @@
 
     test('isModifierPressed returns accurate value', () => {
       const spy = sandbox.spy(element, 'isModifierPressed');
-      element._handleKey = function(e) {
+      element._handleKey = e => {
         element.isModifierPressed(e, 'shiftKey');
       };
       MockInteractions.keyDownOn(element, 75, 'shift', 'k');
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html
index 3ea1ff0..9ceee68 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.html
@@ -46,11 +46,6 @@
         padding: .3em .5em;
         text-align: left;
       }
-      .readOnly,
-      .repositoryBrowser,
-      .description {
-        white-space: nowrap;
-      }
       a {
         color: var(--default-text-color);
         text-decoration: none;
@@ -68,6 +63,9 @@
       nav a:first-of-type {
         margin-right: .5em;
       }
+      .description {
+        width: 70%;
+      }
     </style>
     <table id="projectList">
       <tr class="headerRow">
@@ -76,7 +74,7 @@
         <th class="repositoryBrowser topHeader">Repository Browser</th>
         <th class="readOnly topHeader">Read only</th>
       </tr>
-      <template is="dom-repeat" items="[[_projects]]">
+      <template is="dom-repeat" items="[[_shownProjects]]">
         <tr class="project-table">
           <td class="name">
             <a href$="[[_getUrl(item.id)]]">[[item.name]]</a>
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js
index 3e06605..038b41f 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js
@@ -33,6 +33,15 @@
 
       _projects: Array,
 
+      /**
+       * Because  we request one more than the projectsPerPage, _shownProjects
+       * maybe one less than _projects.
+       * */
+      _shownProjects: {
+        type: Array,
+        computed: '_computeShownProjects(_projects)',
+      },
+
       _projectsPerPage: {
         type: Number,
         value: 25,
@@ -88,16 +97,12 @@
           this.encodeURL(item, false);
     },
 
-    _isProjectWebLink(link) {
-      return link.name === 'gitiles' || link.name === 'gitweb';
-    },
 
     _computeWeblink(project) {
       if (!project.web_links) {
         return '';
       }
-      const webLinks = project.web_links.filter(
-          l => !this._isProjectWebLink(l));
+      const webLinks = project.web_links;
       return webLinks.length ? webLinks : null;
     },
 
@@ -112,6 +117,10 @@
       return href;
     },
 
+    _computeShownProjects(projects) {
+      return projects.slice(0, 25);
+    },
+
     _hidePrevArrow(offset) {
       return offset === 0;
     },
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html
index 7ba231a..f9e08cd 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html
@@ -56,7 +56,7 @@
 
     suite('list with projects', () => {
       setup(done => {
-        projects = _.times(30, projectGenerator);
+        projects = _.times(26, projectGenerator);
 
         stub('gr-rest-api-interface', {
           getProjects(num, offset) {
@@ -99,6 +99,10 @@
           assert.isFalse(element._hidePrevArrow(offset));
         });
       });
+
+      test('_shownProjects', () => {
+        assert.equal(element._shownProjects.length, 25);
+      });
     });
 
     suite('test with less then 25 projects', () => {
@@ -126,6 +130,10 @@
           done();
         });
       });
+
+      test('_shownProjects', () => {
+        assert.equal(element._shownProjects.length, 25);
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 77c2ca0..05c6c68 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -481,7 +481,7 @@
             document.documentElement.scrollTop =
                 document.body.scrollTop = this.viewState.scrollTop;
           } else {
-            this._maybeScrollToMessage();
+            this._maybeScrollToMessage(window.location.hash);
           }
         }, 1);
       });
@@ -513,10 +513,9 @@
       this.viewState.numFilesShown = numFilesShown;
     },
 
-    _maybeScrollToMessage() {
+    _maybeScrollToMessage(hash) {
       const msgPrefix = '#message-';
-      const hash = window.location.hash;
-      if (hash.startsWith(msgPrefix) === 0) {
+      if (hash.startsWith(msgPrefix)) {
         this.$.messageList.scrollToMessage(hash.substr(msgPrefix.length));
       }
     },
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index 7794cd67..5847e73 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -1320,5 +1320,19 @@
             'header wip');
       });
     });
+
+    test('_maybeScrollToMessage', () => {
+      const scrollStub = sandbox.stub(element.$.messageList, 'scrollToMessage');
+
+      element._maybeScrollToMessage('');
+      assert.isFalse(scrollStub.called);
+
+      element._maybeScrollToMessage('message');
+      assert.isFalse(scrollStub.called);
+
+      element._maybeScrollToMessage('#message-TEST');
+      assert.isTrue(scrollStub.called);
+      assert.equal(scrollStub.lastCall.args[0], 'TEST');
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 74181b8..03c7a97 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -516,14 +516,18 @@
     },
 
     _handleLeftBracketKey(e) {
-      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+      // Check for meta key to avoid overriding native chrome shortcut.
+      if (this.shouldSuppressKeyboardShortcut(e) ||
+          this.getKeyboardEvent(e).metaKey) { return; }
 
       e.preventDefault();
       this._openSelectedFile(this._files.length - 1);
     },
 
     _handleRightBracketKey(e) {
-      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+      // Check for meta key to avoid overriding native chrome shortcut.
+      if (this.shouldSuppressKeyboardShortcut(e) ||
+          this.getKeyboardEvent(e).metaKey) { return; }
 
       e.preventDefault();
       this._openSelectedFile(0);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index e8855d7..95e813a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -289,16 +289,18 @@
     },
 
     _handleLeftBracketKey(e) {
+      // Check for meta key to avoid overriding native chrome shortcut.
       if (this.shouldSuppressKeyboardShortcut(e) ||
-          this.modifierPressed(e)) { return; }
+          this.getKeyboardEvent(e).metaKey) { return; }
 
       e.preventDefault();
       this._navToFile(this._path, this._fileList, -1);
     },
 
     _handleRightBracketKey(e) {
+      // Check for meta key to avoid overriding native chrome shortcut.
       if (this.shouldSuppressKeyboardShortcut(e) ||
-          this.modifierPressed(e)) { return; }
+          this.getKeyboardEvent(e).metaKey) { return; }
 
       e.preventDefault();
       this._navToFile(this._path, this._fileList, 1);
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
index e610214..8be89c4 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
@@ -64,6 +64,8 @@
     close() {
       if (this.moveToRoot) {
         Gerrit.getRootElement().removeChild(this);
+      } else {
+        this.hidden = true;
       }
     },
 
@@ -134,7 +136,9 @@
 
     _handleEscape() {
       this._fireClose();
-      this.close();
+      if (!this.hidden) {
+        this.close();
+      }
     },
 
     _handleTapItem(e) {
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
index 41468d1..c071f0c 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
@@ -69,12 +69,14 @@
 
     test('escape key', () => {
       const listener = sandbox.spy();
+      element.hidden = false;
       element.addEventListener('dropdown-closed', listener);
       const closeSpy = sandbox.spy(element, 'close');
       MockInteractions.pressAndReleaseKeyOn(element, 27);
       flushAsynchronousOperations();
       assert.isTrue(listener.called);
       assert.isTrue(closeSpy.called);
+      assert.isTrue(element.hidden);
     });
 
     test('tab key', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 005124d..e371201 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -562,7 +562,7 @@
     getProjects(projectsPerPage, opt_offset) {
       const offset = opt_offset || 0;
       return this._fetchSharedCacheURL(
-          `/projects/?d&n=${projectsPerPage}&S=${offset}`
+          `/projects/?d&n=${projectsPerPage + 1}&S=${offset}`
       );
     },
 
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
index baeb929..8d0b1e3 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.html
@@ -38,8 +38,11 @@
         background-color: var(--background-color, none);
         width: 100%;
       }
+      /*This is needed to not add a scroll bar on the side of gr-textarea
+      since there is 2px of padding in iron-autogrow-textarea for the
+      native textarea*/
       iron-autogrow-textarea {
-        padding: 0;
+        padding: 2px;
       }
       #textarea.noBorder {
         border: none;
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 6b9f655..9a2e404 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -125,6 +125,7 @@
   // Behaviors tests.
   const behaviors = [
     'base-url-behavior/base-url-behavior_test.html',
+    'keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html',
     'rest-client-behavior/rest-client-behavior_test.html',
     'gr-change-table-behavior/gr-change-table-behavior_test.html',
     'gr-patch-set-behavior/gr-patch-set-behavior_test.html',