Convert legacy Polyemer elements to class-based
This commit converts almost all Polymer elements from Polymer-function based components to class-based components. There are few files which should be converted manually after this commit. Change-Id: I9e597e79053e0a6b5d5c0f1b54676d11b9d81db7
This commit is contained in:
		@@ -36,61 +36,69 @@
 | 
			
		||||
  const ON_BEHALF_OF = '(On Behalf Of)';
 | 
			
		||||
  const LABEL = 'Label';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-access-section',
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      capabilities: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      section: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        observer: '_updateSection',
 | 
			
		||||
      },
 | 
			
		||||
      groups: Object,
 | 
			
		||||
      labels: Object,
 | 
			
		||||
      editing: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_handleEditingChanged',
 | 
			
		||||
      },
 | 
			
		||||
      canUpload: Boolean,
 | 
			
		||||
      ownerOf: Array,
 | 
			
		||||
      _originalId: String,
 | 
			
		||||
      _editingRef: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _deleted: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _permissions: Array,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.AccessBehavior,
 | 
			
		||||
      /**
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.AccessMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrAccessSection extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.AccessBehavior,
 | 
			
		||||
    /**
 | 
			
		||||
       * Unused in this element, but called by other elements in tests
 | 
			
		||||
       * e.g gr-repo-access_test.
 | 
			
		||||
       */
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-access-section'; }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'access-saved': '_handleAccessSaved',
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        capabilities: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        section: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          observer: '_updateSection',
 | 
			
		||||
        },
 | 
			
		||||
        groups: Object,
 | 
			
		||||
        labels: Object,
 | 
			
		||||
        editing: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_handleEditingChanged',
 | 
			
		||||
        },
 | 
			
		||||
        canUpload: Boolean,
 | 
			
		||||
        ownerOf: Array,
 | 
			
		||||
        _originalId: String,
 | 
			
		||||
        _editingRef: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _deleted: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _permissions: Array,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('access-saved',
 | 
			
		||||
          () => this._handleAccessSaved());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateSection(section) {
 | 
			
		||||
      this._permissions = this.toSortedArray(section.value.permissions);
 | 
			
		||||
      this._originalId = section.id;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAccessSaved() {
 | 
			
		||||
      // Set a new 'original' value to keep track of after the value has been
 | 
			
		||||
      // saved.
 | 
			
		||||
      this._updateSection(this.section);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleValueChange() {
 | 
			
		||||
      if (!this.section.value.added) {
 | 
			
		||||
@@ -103,7 +111,7 @@
 | 
			
		||||
            'access-modified', {bubbles: true, composed: true}));
 | 
			
		||||
      }
 | 
			
		||||
      this.section.value.updatedId = this.section.id;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEditingChanged(editing, editingOld) {
 | 
			
		||||
      // Ignore when editing gets set initially.
 | 
			
		||||
@@ -123,7 +131,7 @@
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePermissions(name, capabilities, labels) {
 | 
			
		||||
      let allPermissions;
 | 
			
		||||
@@ -140,17 +148,17 @@
 | 
			
		||||
      return allPermissions.filter(permission => {
 | 
			
		||||
        return !this.section.value.permissions[permission.id];
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideEditClass(section) {
 | 
			
		||||
      return section.id === 'GLOBAL_CAPABILITIES' ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAddedPermissionRemoved(e) {
 | 
			
		||||
      const index = e.model.index;
 | 
			
		||||
      this._permissions = this._permissions.slice(0, index).concat(
 | 
			
		||||
          this._permissions.slice(index + 1, this._permissions.length));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelOptions(labels) {
 | 
			
		||||
      const labelOptions = [];
 | 
			
		||||
@@ -172,7 +180,7 @@
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      return labelOptions;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePermissionName(name, permission, permissionValues, capabilities) {
 | 
			
		||||
      if (name === GLOBAL_NAME) {
 | 
			
		||||
@@ -186,7 +194,7 @@
 | 
			
		||||
        }
 | 
			
		||||
        return `${LABEL} ${permission.value.label}${behalfOf}`;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSectionName(name) {
 | 
			
		||||
      // When a new section is created, it doesn't yet have a ref. Set into
 | 
			
		||||
@@ -204,7 +212,7 @@
 | 
			
		||||
        return `Reference: ${name}`;
 | 
			
		||||
      }
 | 
			
		||||
      return name;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRemoveReference() {
 | 
			
		||||
      if (this.section.value.added) {
 | 
			
		||||
@@ -215,27 +223,27 @@
 | 
			
		||||
      this.section.value.deleted = true;
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('access-modified', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUndoRemove() {
 | 
			
		||||
      this._deleted = false;
 | 
			
		||||
      delete this.section.value.deleted;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    editRefInput() {
 | 
			
		||||
      return Polymer.dom(this.root).querySelector(Polymer.Element ?
 | 
			
		||||
        'iron-input.editRefInput' :
 | 
			
		||||
        'input[is=iron-input].editRefInput');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    editReference() {
 | 
			
		||||
      this._editingRef = true;
 | 
			
		||||
      this.editRefInput().focus();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isEditEnabled(canUpload, ownerOf, sectionId) {
 | 
			
		||||
      return canUpload || (ownerOf && ownerOf.indexOf(sectionId) >= 0);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSectionClass(editing, canUpload, ownerOf, editingRef, deleted) {
 | 
			
		||||
      const classList = [];
 | 
			
		||||
@@ -249,11 +257,11 @@
 | 
			
		||||
        classList.push('deleted');
 | 
			
		||||
      }
 | 
			
		||||
      return classList.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeEditBtnClass(name) {
 | 
			
		||||
      return name === GLOBAL_NAME ? 'global' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAddPermission() {
 | 
			
		||||
      const value = this.$.permissionSelect.value;
 | 
			
		||||
@@ -286,6 +294,8 @@
 | 
			
		||||
      this.push('_permissions', permission);
 | 
			
		||||
      this.set(['section.value.permissions', permission.id],
 | 
			
		||||
          permission.value);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrAccessSection.is, GrAccessSection);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,65 +17,72 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-admin-group-list',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.ListViewMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrAdminGroupList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.ListViewBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-admin-group-list'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Offset of currently visible query results.
 | 
			
		||||
       */
 | 
			
		||||
      _offset: Number,
 | 
			
		||||
      _path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
        value: '/admin/groups',
 | 
			
		||||
      },
 | 
			
		||||
      _hasNewGroupName: Boolean,
 | 
			
		||||
      _createNewCapability: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _groups: Array,
 | 
			
		||||
        _offset: Number,
 | 
			
		||||
        _path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
          value: '/admin/groups',
 | 
			
		||||
        },
 | 
			
		||||
        _hasNewGroupName: Boolean,
 | 
			
		||||
        _createNewCapability: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _groups: Array,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Because  we request one more than the groupsPerPage, _shownGroups
 | 
			
		||||
       * may be one less than _groups.
 | 
			
		||||
       * */
 | 
			
		||||
      _shownGroups: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: 'computeShownItems(_groups)',
 | 
			
		||||
      },
 | 
			
		||||
        _shownGroups: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: 'computeShownItems(_groups)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _groupsPerPage: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 25,
 | 
			
		||||
      },
 | 
			
		||||
        _groupsPerPage: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 25,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _filter: String,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.ListViewBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _filter: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._getCreateGroupCapability();
 | 
			
		||||
      this.fire('title-change', {title: 'Groups'});
 | 
			
		||||
      this._maybeOpenCreateOverlay(this.params);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(params) {
 | 
			
		||||
      this._loading = true;
 | 
			
		||||
@@ -84,7 +91,7 @@
 | 
			
		||||
 | 
			
		||||
      return this._getGroups(this._filter, this._groupsPerPage,
 | 
			
		||||
          this._offset);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens the create overlay if the route has a hash 'create'
 | 
			
		||||
@@ -94,11 +101,11 @@
 | 
			
		||||
      if (params && params.openCreateModal) {
 | 
			
		||||
        this.$.createOverlay.open();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupUrl(id) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForGroup(id);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCreateGroupCapability() {
 | 
			
		||||
      return this.$.restAPI.getAccount().then(account => {
 | 
			
		||||
@@ -110,7 +117,7 @@
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getGroups(filter, groupsPerPage, offset) {
 | 
			
		||||
      this._groups = [];
 | 
			
		||||
@@ -127,30 +134,32 @@
 | 
			
		||||
                });
 | 
			
		||||
            this._loading = false;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _refreshGroupsList() {
 | 
			
		||||
      this.$.restAPI.invalidateGroupsCache();
 | 
			
		||||
      return this._getGroups(this._filter, this._groupsPerPage,
 | 
			
		||||
          this._offset);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateGroup() {
 | 
			
		||||
      this.$.createNewModal.handleCreateGroup().then(() => {
 | 
			
		||||
        this._refreshGroupsList();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseCreate() {
 | 
			
		||||
      this.$.createOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateClicked() {
 | 
			
		||||
      this.$.createOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _visibleToAll(item) {
 | 
			
		||||
      return item.options.visible_to_all === true ? 'Y' : 'N';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrAdminGroupList.is, GrAdminGroupList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,63 +19,73 @@
 | 
			
		||||
 | 
			
		||||
  const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-admin-view',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.AdminNavMixin
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrAdminView extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.AdminNavBehavior,
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-admin-view'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      params: Object,
 | 
			
		||||
      path: String,
 | 
			
		||||
      adminView: String,
 | 
			
		||||
        params: Object,
 | 
			
		||||
        path: String,
 | 
			
		||||
        adminView: String,
 | 
			
		||||
 | 
			
		||||
      _breadcrumbParentName: String,
 | 
			
		||||
      _repoName: String,
 | 
			
		||||
      _groupId: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        observer: '_computeGroupName',
 | 
			
		||||
      },
 | 
			
		||||
      _groupIsInternal: Boolean,
 | 
			
		||||
      _groupName: String,
 | 
			
		||||
      _groupOwner: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _subsectionLinks: Array,
 | 
			
		||||
      _filteredLinks: Array,
 | 
			
		||||
      _showDownload: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _isAdmin: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _showGroup: Boolean,
 | 
			
		||||
      _showGroupAuditLog: Boolean,
 | 
			
		||||
      _showGroupList: Boolean,
 | 
			
		||||
      _showGroupMembers: Boolean,
 | 
			
		||||
      _showRepoAccess: Boolean,
 | 
			
		||||
      _showRepoCommands: Boolean,
 | 
			
		||||
      _showRepoDashboards: Boolean,
 | 
			
		||||
      _showRepoDetailList: Boolean,
 | 
			
		||||
      _showRepoMain: Boolean,
 | 
			
		||||
      _showRepoList: Boolean,
 | 
			
		||||
      _showPluginList: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
        _breadcrumbParentName: String,
 | 
			
		||||
        _repoName: String,
 | 
			
		||||
        _groupId: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          observer: '_computeGroupName',
 | 
			
		||||
        },
 | 
			
		||||
        _groupIsInternal: Boolean,
 | 
			
		||||
        _groupName: String,
 | 
			
		||||
        _groupOwner: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _subsectionLinks: Array,
 | 
			
		||||
        _filteredLinks: Array,
 | 
			
		||||
        _showDownload: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _isAdmin: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _showGroup: Boolean,
 | 
			
		||||
        _showGroupAuditLog: Boolean,
 | 
			
		||||
        _showGroupList: Boolean,
 | 
			
		||||
        _showGroupMembers: Boolean,
 | 
			
		||||
        _showRepoAccess: Boolean,
 | 
			
		||||
        _showRepoCommands: Boolean,
 | 
			
		||||
        _showRepoDashboards: Boolean,
 | 
			
		||||
        _showRepoDetailList: Boolean,
 | 
			
		||||
        _showRepoMain: Boolean,
 | 
			
		||||
        _showRepoList: Boolean,
 | 
			
		||||
        _showPluginList: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.AdminNavBehavior,
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_paramsChanged(params)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_paramsChanged(params)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.reload();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reload() {
 | 
			
		||||
      const promises = [
 | 
			
		||||
@@ -123,18 +133,18 @@
 | 
			
		||||
                  });
 | 
			
		||||
            });
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSelectValue(params) {
 | 
			
		||||
      if (!params || !params.view) { return; }
 | 
			
		||||
      return params.view + (params.detail || '');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _selectedIsCurrentPage(selected) {
 | 
			
		||||
      return (selected.parent === (this._repoName || this._groupId) &&
 | 
			
		||||
          selected.view === this.params.view &&
 | 
			
		||||
          selected.detailType === this.params.detail);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSubsectionChange(e) {
 | 
			
		||||
      const selected = this._subsectionLinks
 | 
			
		||||
@@ -145,7 +155,7 @@
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      Gerrit.Nav.navigateToRelativeUrl(selected.url);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(params) {
 | 
			
		||||
      const isGroupView = params.view === Gerrit.Nav.View.GROUP;
 | 
			
		||||
@@ -194,19 +204,19 @@
 | 
			
		||||
      }
 | 
			
		||||
      if (!needsReload) { return; }
 | 
			
		||||
      this.reload();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO (beckysiegel): Update these functions after router abstraction is
 | 
			
		||||
    // updated. They are currently copied from gr-dropdown (and should be
 | 
			
		||||
    // updated there as well once complete).
 | 
			
		||||
    _computeURLHelper(host, path) {
 | 
			
		||||
      return '//' + host + this.getBaseUrl() + path;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRelativeURL(path) {
 | 
			
		||||
      const host = window.location.host;
 | 
			
		||||
      return this._computeURLHelper(host, path);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLinkURL(link) {
 | 
			
		||||
      if (!link || typeof link.url === 'undefined') { return ''; }
 | 
			
		||||
@@ -214,7 +224,7 @@
 | 
			
		||||
        return link.url;
 | 
			
		||||
      }
 | 
			
		||||
      return this._computeRelativeURL(link.url);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} itemView
 | 
			
		||||
@@ -244,7 +254,7 @@
 | 
			
		||||
        return '';
 | 
			
		||||
      }
 | 
			
		||||
      return itemView === params.adminView ? 'selected' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupName(groupId) {
 | 
			
		||||
      if (!groupId) { return ''; }
 | 
			
		||||
@@ -270,11 +280,13 @@
 | 
			
		||||
          this.reload();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateGroupName(e) {
 | 
			
		||||
      this._groupName = e.detail.name;
 | 
			
		||||
      this.reload();
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrAdminView.is, GrAdminView);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,15 @@
 | 
			
		||||
    TAGS: 'tags',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-delete-item-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrConfirmDeleteItemDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-confirm-delete-item-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -38,26 +44,24 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      item: String,
 | 
			
		||||
      itemType: String,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        item: String,
 | 
			
		||||
        itemType: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('confirm', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeItemName(detailType) {
 | 
			
		||||
      if (detailType === DETAIL_TYPES.BRANCHES) {
 | 
			
		||||
@@ -67,6 +71,9 @@
 | 
			
		||||
      } else if (detailType === DETAIL_TYPES.ID) {
 | 
			
		||||
        return 'ID';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmDeleteItemDialog.is,
 | 
			
		||||
      GrConfirmDeleteItemDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -20,44 +20,52 @@
 | 
			
		||||
  const SUGGESTIONS_LIMIT = 15;
 | 
			
		||||
  const REF_PREFIX = 'refs/heads/';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-create-change-dialog',
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      repoName: String,
 | 
			
		||||
      branch: String,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _repoConfig: Object,
 | 
			
		||||
      subject: String,
 | 
			
		||||
      topic: String,
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getRepoBranchesSuggestions.bind(this);
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      baseChange: String,
 | 
			
		||||
      baseCommit: String,
 | 
			
		||||
      privateByDefault: String,
 | 
			
		||||
      canCreate: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _privateChangesEnabled: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      /**
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrCreateChangeDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    /**
 | 
			
		||||
       * Unused in this element, but called by other elements in tests
 | 
			
		||||
       * e.g gr-repo-commands_test.
 | 
			
		||||
       */
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-create-change-dialog'; }
 | 
			
		||||
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        repoName: String,
 | 
			
		||||
        branch: String,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _repoConfig: Object,
 | 
			
		||||
        subject: String,
 | 
			
		||||
        topic: String,
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getRepoBranchesSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        baseChange: String,
 | 
			
		||||
        baseCommit: String,
 | 
			
		||||
        privateByDefault: String,
 | 
			
		||||
        canCreate: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _privateChangesEnabled: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      if (!this.repoName) { return Promise.resolve(); }
 | 
			
		||||
 | 
			
		||||
      const promises = [];
 | 
			
		||||
@@ -75,19 +83,21 @@
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return Promise.all(promises);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_allowCreate(branch, subject)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_allowCreate(branch, subject)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeBranchClass(baseChange) {
 | 
			
		||||
      return baseChange ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _allowCreate(branch, subject) {
 | 
			
		||||
      this.canCreate = !!branch && !!subject;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleCreateChange() {
 | 
			
		||||
      const isPrivate = this.$.privateChangeCheckBox.checked;
 | 
			
		||||
@@ -99,7 +109,7 @@
 | 
			
		||||
            if (!changeCreated) { return; }
 | 
			
		||||
            Gerrit.Nav.navigateToChange(changeCreated);
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRepoBranchesSuggestions(input) {
 | 
			
		||||
      if (input.startsWith(REF_PREFIX)) {
 | 
			
		||||
@@ -122,7 +132,7 @@
 | 
			
		||||
        }
 | 
			
		||||
        return branches;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _formatBooleanString(config) {
 | 
			
		||||
      if (config && config.configured_value === 'TRUE') {
 | 
			
		||||
@@ -138,10 +148,12 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePrivateSectionClass(config) {
 | 
			
		||||
      return config ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCreateChangeDialog.is, GrCreateChangeDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,40 +17,48 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-create-group-dialog',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrCreateGroupDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-create-group-dialog'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      params: Object,
 | 
			
		||||
      hasNewGroupName: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _name: Object,
 | 
			
		||||
      _groupCreated: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        params: Object,
 | 
			
		||||
        hasNewGroupName: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _name: Object,
 | 
			
		||||
        _groupCreated: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_updateGroupName(_name)',
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_updateGroupName(_name)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupUrl(groupId) {
 | 
			
		||||
      return this.getBaseUrl() + '/admin/groups/' +
 | 
			
		||||
          this.encodeURL(groupId, true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateGroupName(name) {
 | 
			
		||||
      this.hasNewGroupName = !!name;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleCreateGroup() {
 | 
			
		||||
      return this.$.restAPI.createGroup({name: this._name})
 | 
			
		||||
@@ -62,6 +70,8 @@
 | 
			
		||||
                  page.show(this._computeGroupUrl(group.group_id));
 | 
			
		||||
                });
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCreateGroupDialog.is, GrCreateGroupDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -22,35 +22,43 @@
 | 
			
		||||
    tags: 'tags',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-create-pointer-dialog',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrCreatePointerDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-create-pointer-dialog'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      detailType: String,
 | 
			
		||||
      repoName: String,
 | 
			
		||||
      hasNewItemName: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      itemDetail: String,
 | 
			
		||||
      _itemName: String,
 | 
			
		||||
      _itemRevision: String,
 | 
			
		||||
      _itemAnnotation: String,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        detailType: String,
 | 
			
		||||
        repoName: String,
 | 
			
		||||
        hasNewItemName: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        itemDetail: String,
 | 
			
		||||
        _itemName: String,
 | 
			
		||||
        _itemRevision: String,
 | 
			
		||||
        _itemAnnotation: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_updateItemName(_itemName)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_updateItemName(_itemName)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateItemName(name) {
 | 
			
		||||
      this.hasNewItemName = !!name;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeItemUrl(project) {
 | 
			
		||||
      if (this.itemDetail === DETAIL_TYPES.branches) {
 | 
			
		||||
@@ -60,7 +68,7 @@
 | 
			
		||||
        return this.getBaseUrl() + '/admin/repos/' +
 | 
			
		||||
            this.encodeURL(this.repoName, true) + ',tags';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleCreateItem() {
 | 
			
		||||
      const USE_HEAD = this._itemRevision ? this._itemRevision : 'HEAD';
 | 
			
		||||
@@ -82,10 +90,12 @@
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideItemClass(type) {
 | 
			
		||||
      return type === DETAIL_TYPES.branches ? 'hideItem' : '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCreatePointerDialog.is, GrCreatePointerDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,69 +17,77 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-create-repo-dialog',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrCreateRepoDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-create-repo-dialog'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      params: Object,
 | 
			
		||||
      hasNewRepoName: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        params: Object,
 | 
			
		||||
        hasNewRepoName: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _repoConfig: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: () => {
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _repoConfig: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: () => {
 | 
			
		||||
          // Set default values for dropdowns.
 | 
			
		||||
          return {
 | 
			
		||||
            create_empty_commit: true,
 | 
			
		||||
            permissions_only: false,
 | 
			
		||||
          };
 | 
			
		||||
            return {
 | 
			
		||||
              create_empty_commit: true,
 | 
			
		||||
              permissions_only: false,
 | 
			
		||||
            };
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _repoCreated: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _repoOwner: String,
 | 
			
		||||
      _repoOwnerId: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_repoOwnerIdUpdate',
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getRepoSuggestions.bind(this);
 | 
			
		||||
        _repoCreated: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _queryGroups: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getGroupSuggestions.bind(this);
 | 
			
		||||
        _repoOwner: String,
 | 
			
		||||
        _repoOwnerId: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_repoOwnerIdUpdate',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_updateRepoName(_repoConfig.name)',
 | 
			
		||||
    ],
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getRepoSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _queryGroups: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getGroupSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_updateRepoName(_repoConfig.name)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRepoUrl(repoName) {
 | 
			
		||||
      return this.getBaseUrl() + '/admin/repos/' +
 | 
			
		||||
          this.encodeURL(repoName, true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateRepoName(name) {
 | 
			
		||||
      this.hasNewRepoName = !!name;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _repoOwnerIdUpdate(id) {
 | 
			
		||||
      if (id) {
 | 
			
		||||
@@ -87,7 +95,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.set('_repoConfig.owners', undefined);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleCreateRepo() {
 | 
			
		||||
      return this.$.restAPI.createRepo(this._repoConfig)
 | 
			
		||||
@@ -97,7 +105,7 @@
 | 
			
		||||
              page.show(this._computeRepoUrl(this._repoConfig.name));
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRepoSuggestions(input) {
 | 
			
		||||
      return this.$.restAPI.getSuggestedProjects(input)
 | 
			
		||||
@@ -112,7 +120,7 @@
 | 
			
		||||
            }
 | 
			
		||||
            return repos;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getGroupSuggestions(input) {
 | 
			
		||||
      return this.$.restAPI.getSuggestedGroups(input)
 | 
			
		||||
@@ -127,6 +135,8 @@
 | 
			
		||||
            }
 | 
			
		||||
            return groups;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCreateRepoDialog.is, GrCreateRepoDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,30 +19,38 @@
 | 
			
		||||
 | 
			
		||||
  const GROUP_EVENTS = ['ADD_GROUP', 'REMOVE_GROUP'];
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-group-audit-log',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.ListViewMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrGroupAuditLog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.ListViewBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-group-audit-log'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      groupId: String,
 | 
			
		||||
      _auditLog: Array,
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.ListViewBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        groupId: String,
 | 
			
		||||
        _auditLog: Array,
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.fire('title-change', {title: 'Audit Log'});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this._getAuditLogs();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getAuditLogs() {
 | 
			
		||||
      if (!this.groupId) { return ''; }
 | 
			
		||||
@@ -60,11 +68,11 @@
 | 
			
		||||
            this._auditLog = auditLog;
 | 
			
		||||
            this._loading = false;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _status(item) {
 | 
			
		||||
      return item.disabled ? 'Disabled' : 'Enabled';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    itemType(type) {
 | 
			
		||||
      let item;
 | 
			
		||||
@@ -81,11 +89,11 @@
 | 
			
		||||
          item = '';
 | 
			
		||||
      }
 | 
			
		||||
      return item;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isGroupEvent(type) {
 | 
			
		||||
      return GROUP_EVENTS.indexOf(type) !== -1;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupUrl(group) {
 | 
			
		||||
      if (group && group.url && group.id) {
 | 
			
		||||
@@ -93,11 +101,11 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getIdForUser(account) {
 | 
			
		||||
      return account._account_id ? ' (' + account._account_id + ')' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getNameForGroup(group) {
 | 
			
		||||
      if (group && group.name) {
 | 
			
		||||
@@ -108,6 +116,8 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrGroupAuditLog.is, GrGroupAuditLog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -23,57 +23,65 @@
 | 
			
		||||
 | 
			
		||||
  const URL_REGEX = '^(?:[a-z]+:)?//';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-group-members',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrGroupMembers extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-group-members'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      groupId: Number,
 | 
			
		||||
      _groupMemberSearchId: String,
 | 
			
		||||
      _groupMemberSearchName: String,
 | 
			
		||||
      _includedGroupSearchId: String,
 | 
			
		||||
      _includedGroupSearchName: String,
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _groupName: String,
 | 
			
		||||
      _groupMembers: Object,
 | 
			
		||||
      _includedGroups: Object,
 | 
			
		||||
      _itemName: String,
 | 
			
		||||
      _itemType: String,
 | 
			
		||||
      _queryMembers: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getAccountSuggestions.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        groupId: Number,
 | 
			
		||||
        _groupMemberSearchId: String,
 | 
			
		||||
        _groupMemberSearchName: String,
 | 
			
		||||
        _includedGroupSearchId: String,
 | 
			
		||||
        _includedGroupSearchName: String,
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _queryIncludedGroup: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getGroupSuggestions.bind(this);
 | 
			
		||||
        _groupName: String,
 | 
			
		||||
        _groupMembers: Object,
 | 
			
		||||
        _includedGroups: Object,
 | 
			
		||||
        _itemName: String,
 | 
			
		||||
        _itemType: String,
 | 
			
		||||
        _queryMembers: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getAccountSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _groupOwner: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _isAdmin: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _queryIncludedGroup: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getGroupSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _groupOwner: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _isAdmin: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._loadGroupDetails();
 | 
			
		||||
 | 
			
		||||
      this.fire('title-change', {title: 'Members'});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadGroupDetails() {
 | 
			
		||||
      if (!this.groupId) { return; }
 | 
			
		||||
@@ -113,15 +121,15 @@
 | 
			
		||||
              this._loading = false;
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoadingClass(loading) {
 | 
			
		||||
      return loading ? 'loading' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isLoading() {
 | 
			
		||||
      return this._loading || this._loading === undefined;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupUrl(url) {
 | 
			
		||||
      if (!url) { return; }
 | 
			
		||||
@@ -136,7 +144,7 @@
 | 
			
		||||
        return this.getBaseUrl() + url.slice(1);
 | 
			
		||||
      }
 | 
			
		||||
      return this.getBaseUrl() + url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSavingGroupMember() {
 | 
			
		||||
      return this.$.restAPI.saveGroupMembers(this._groupName,
 | 
			
		||||
@@ -150,7 +158,7 @@
 | 
			
		||||
        this._groupMemberSearchName = '';
 | 
			
		||||
        this._groupMemberSearchId = '';
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDeleteConfirm() {
 | 
			
		||||
      this.$.overlay.close();
 | 
			
		||||
@@ -177,11 +185,11 @@
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmDialogCancel() {
 | 
			
		||||
      this.$.overlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDeleteMember(e) {
 | 
			
		||||
      const id = e.model.get('item._account_id');
 | 
			
		||||
@@ -196,7 +204,7 @@
 | 
			
		||||
      this._itemId = id;
 | 
			
		||||
      this._itemType = 'member';
 | 
			
		||||
      this.$.overlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSavingIncludedGroups() {
 | 
			
		||||
      return this.$.restAPI.saveIncludedGroup(this._groupName,
 | 
			
		||||
@@ -222,7 +230,7 @@
 | 
			
		||||
            this._includedGroupSearchName = '';
 | 
			
		||||
            this._includedGroupSearchId = '';
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDeleteIncludedGroup(e) {
 | 
			
		||||
      const id = decodeURIComponent(e.model.get('item.id')).replace(/\+/g, ' ');
 | 
			
		||||
@@ -233,7 +241,7 @@
 | 
			
		||||
      this._itemId = id;
 | 
			
		||||
      this._itemType = 'includedGroup';
 | 
			
		||||
      this.$.overlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getAccountSuggestions(input) {
 | 
			
		||||
      if (input.length === 0) { return Promise.resolve([]); }
 | 
			
		||||
@@ -257,7 +265,7 @@
 | 
			
		||||
        }
 | 
			
		||||
        return accountSuggestions;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getGroupSuggestions(input) {
 | 
			
		||||
      return this.$.restAPI.getSuggestedGroups(input)
 | 
			
		||||
@@ -272,10 +280,12 @@
 | 
			
		||||
            }
 | 
			
		||||
            return groups;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideItemClass(owner, admin) {
 | 
			
		||||
      return admin || owner ? '' : 'canModify';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrGroupMembers.is, GrGroupMembers);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -30,78 +30,85 @@
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-group',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrGroup extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-group'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the group name changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @event name-changed
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      groupId: Number,
 | 
			
		||||
      _rename: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _groupIsInternal: Boolean,
 | 
			
		||||
      _description: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _owner: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _options: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _groupConfig: Object,
 | 
			
		||||
      _groupConfigOwner: String,
 | 
			
		||||
      _groupName: Object,
 | 
			
		||||
      _groupOwner: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _submitTypes: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() {
 | 
			
		||||
          return Object.values(OPTIONS);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        groupId: Number,
 | 
			
		||||
        _rename: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getGroupSuggestions.bind(this);
 | 
			
		||||
        _groupIsInternal: Boolean,
 | 
			
		||||
        _description: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _isAdmin: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _owner: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _options: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _groupConfig: Object,
 | 
			
		||||
        _groupConfigOwner: String,
 | 
			
		||||
        _groupName: Object,
 | 
			
		||||
        _groupOwner: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _submitTypes: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() {
 | 
			
		||||
            return Object.values(OPTIONS);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getGroupSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _isAdmin: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_handleConfigName(_groupConfig.name)',
 | 
			
		||||
      '_handleConfigOwner(_groupConfig.owner, _groupConfigOwner)',
 | 
			
		||||
      '_handleConfigDescription(_groupConfig.description)',
 | 
			
		||||
      '_handleConfigOptions(_groupConfig.options.visible_to_all)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_handleConfigName(_groupConfig.name)',
 | 
			
		||||
        '_handleConfigOwner(_groupConfig.owner, _groupConfigOwner)',
 | 
			
		||||
        '_handleConfigDescription(_groupConfig.description)',
 | 
			
		||||
        '_handleConfigOptions(_groupConfig.options.visible_to_all)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._loadGroup();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadGroup() {
 | 
			
		||||
      if (!this.groupId) { return; }
 | 
			
		||||
@@ -143,15 +150,15 @@
 | 
			
		||||
              this._loading = false;
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoadingClass(loading) {
 | 
			
		||||
      return loading ? 'loading' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isLoading() {
 | 
			
		||||
      return this._loading || this._loading === undefined;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveName() {
 | 
			
		||||
      return this.$.restAPI.saveGroupName(this.groupId, this._groupConfig.name)
 | 
			
		||||
@@ -163,7 +170,7 @@
 | 
			
		||||
              this._rename = false;
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveOwner() {
 | 
			
		||||
      let owner = this._groupConfig.owner;
 | 
			
		||||
@@ -174,14 +181,14 @@
 | 
			
		||||
          owner).then(config => {
 | 
			
		||||
        this._owner = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveDescription() {
 | 
			
		||||
      return this.$.restAPI.saveGroupDescription(this.groupId,
 | 
			
		||||
          this._groupConfig.description).then(config => {
 | 
			
		||||
        this._description = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveOptions() {
 | 
			
		||||
      const visible = this._groupConfig.options.visible_to_all;
 | 
			
		||||
@@ -192,31 +199,31 @@
 | 
			
		||||
          options).then(config => {
 | 
			
		||||
        this._options = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfigName() {
 | 
			
		||||
      if (this._isLoading()) { return; }
 | 
			
		||||
      this._rename = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfigOwner() {
 | 
			
		||||
      if (this._isLoading()) { return; }
 | 
			
		||||
      this._owner = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfigDescription() {
 | 
			
		||||
      if (this._isLoading()) { return; }
 | 
			
		||||
      this._description = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfigOptions() {
 | 
			
		||||
      if (this._isLoading()) { return; }
 | 
			
		||||
      this._options = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHeaderClass(configChanged) {
 | 
			
		||||
      return configChanged ? 'edited' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getGroupSuggestions(input) {
 | 
			
		||||
      return this.$.restAPI.getSuggestedGroups(input)
 | 
			
		||||
@@ -231,10 +238,12 @@
 | 
			
		||||
            }
 | 
			
		||||
            return groups;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupDisabled(owner, admin, groupIsInternal) {
 | 
			
		||||
      return groupIsInternal && (admin || owner) ? false : true;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrGroup.is, GrGroup);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,92 +24,101 @@
 | 
			
		||||
    'BATCH CHANGES LIMIT',
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.AccessMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  /**
 | 
			
		||||
   * Fired when the permission has been modified or removed.
 | 
			
		||||
   *
 | 
			
		||||
   * @event access-modified
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Fired when a permission that was previously added was removed.
 | 
			
		||||
   * @event added-permission-removed
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-permission',
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      labels: Object,
 | 
			
		||||
      name: String,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      permission: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_sortPermission',
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      groups: Object,
 | 
			
		||||
      section: String,
 | 
			
		||||
      editing: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_handleEditingChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _label: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computeLabel(permission, labels)',
 | 
			
		||||
      },
 | 
			
		||||
      _groupFilter: String,
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getGroupSuggestions.bind(this);
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _rules: Array,
 | 
			
		||||
      _groupsWithRules: Object,
 | 
			
		||||
      _deleted: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _originalExclusiveValue: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.AccessBehavior,
 | 
			
		||||
      /**
 | 
			
		||||
  class GrPermission extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.AccessBehavior,
 | 
			
		||||
    /**
 | 
			
		||||
       * Unused in this element, but called by other elements in tests
 | 
			
		||||
       * e.g gr-access-section_test.
 | 
			
		||||
       */
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-permission'; }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_handleRulesChanged(_rules.splices)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        labels: Object,
 | 
			
		||||
        name: String,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        permission: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_sortPermission',
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        groups: Object,
 | 
			
		||||
        section: String,
 | 
			
		||||
        editing: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_handleEditingChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _label: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computeLabel(permission, labels)',
 | 
			
		||||
        },
 | 
			
		||||
        _groupFilter: String,
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getGroupSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _rules: Array,
 | 
			
		||||
        _groupsWithRules: Object,
 | 
			
		||||
        _deleted: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _originalExclusiveValue: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'access-saved': '_handleAccessSaved',
 | 
			
		||||
    },
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_handleRulesChanged(_rules.splices)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('access-saved',
 | 
			
		||||
          () => this._handleAccessSaved());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this._setupValues();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setupValues() {
 | 
			
		||||
      if (!this.permission) { return; }
 | 
			
		||||
      this._originalExclusiveValue = !!this.permission.value.exclusive;
 | 
			
		||||
      Polymer.dom.flush();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAccessSaved() {
 | 
			
		||||
      // Set a new 'original' value to keep track of after the value has been
 | 
			
		||||
      // saved.
 | 
			
		||||
      this._setupValues();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _permissionIsOwnerOrGlobal(permissionId, section) {
 | 
			
		||||
      return permissionId === 'owner' || section === 'GLOBAL_CAPABILITIES';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEditingChanged(editing, editingOld) {
 | 
			
		||||
      // Ignore when editing gets set initially.
 | 
			
		||||
@@ -130,20 +139,20 @@
 | 
			
		||||
        this.set(['permission', 'value', 'exclusive'],
 | 
			
		||||
            this._originalExclusiveValue);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAddedRuleRemoved(e) {
 | 
			
		||||
      const index = e.model.index;
 | 
			
		||||
      this._rules = this._rules.slice(0, index)
 | 
			
		||||
          .concat(this._rules.slice(index + 1, this._rules.length));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleValueChange() {
 | 
			
		||||
      this.permission.value.modified = true;
 | 
			
		||||
      // Allows overall access page to know a change has been made.
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('access-modified', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRemovePermission() {
 | 
			
		||||
      if (this.permission.value.added) {
 | 
			
		||||
@@ -154,16 +163,16 @@
 | 
			
		||||
      this.permission.value.deleted = true;
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('access-modified', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRulesChanged(changeRecord) {
 | 
			
		||||
      // Update the groups to exclude in the autocomplete.
 | 
			
		||||
      this._groupsWithRules = this._computeGroupsWithRules(this._rules);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _sortPermission(permission) {
 | 
			
		||||
      this._rules = this.toSortedArray(permission.value.rules);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSectionClass(editing, deleted) {
 | 
			
		||||
      const classList = [];
 | 
			
		||||
@@ -174,12 +183,12 @@
 | 
			
		||||
        classList.push('deleted');
 | 
			
		||||
      }
 | 
			
		||||
      return classList.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUndoRemove() {
 | 
			
		||||
      this._deleted = false;
 | 
			
		||||
      delete this.permission.value.deleted;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabel(permission, labels) {
 | 
			
		||||
      if (!labels || !permission ||
 | 
			
		||||
@@ -195,7 +204,7 @@
 | 
			
		||||
        values: this._computeLabelValues(labels[labelName].values),
 | 
			
		||||
      };
 | 
			
		||||
      return label;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelValues(values) {
 | 
			
		||||
      const valuesArr = [];
 | 
			
		||||
@@ -211,7 +220,7 @@
 | 
			
		||||
        valuesArr.push({value: parseInt(key, 10), text});
 | 
			
		||||
      }
 | 
			
		||||
      return valuesArr;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Array} rules
 | 
			
		||||
@@ -224,12 +233,12 @@
 | 
			
		||||
        groups[rule.id] = true;
 | 
			
		||||
      }
 | 
			
		||||
      return groups;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupName(groups, groupId) {
 | 
			
		||||
      return groups && groups[groupId] && groups[groupId].name ?
 | 
			
		||||
        groups[groupId].name : groupId;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getGroupSuggestions() {
 | 
			
		||||
      return this.$.restAPI.getSuggestedGroups(
 | 
			
		||||
@@ -249,7 +258,7 @@
 | 
			
		||||
              return !this._groupsWithRules[group.value.id];
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handles adding a skeleton item to the dom-repeat.
 | 
			
		||||
@@ -283,12 +292,14 @@
 | 
			
		||||
      this.set(['permission', 'value', 'rules', groupId], value);
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('access-modified', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHasRange(name) {
 | 
			
		||||
      if (!name) { return false; }
 | 
			
		||||
 | 
			
		||||
      return RANGE_NAMES.includes(name.toUpperCase());
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrPermission.is, GrPermission);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,39 +17,42 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-plugin-config-array-editor',
 | 
			
		||||
 | 
			
		||||
  class GrPluginConfigArrayEditor extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-plugin-config-array-editor'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the plugin config option changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @event plugin-config-option-changed
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      pluginOption: Object,
 | 
			
		||||
      /** @type {Boolean} */
 | 
			
		||||
      disabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeDisabled(pluginOption.*)',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _newValue: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        pluginOption: Object,
 | 
			
		||||
        /** @type {Boolean} */
 | 
			
		||||
        disabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeDisabled(pluginOption.*)',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _newValue: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDisabled(record) {
 | 
			
		||||
      return !(record && record.base && record.base.info &&
 | 
			
		||||
          record.base.info.editable);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAddTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._handleAdd();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleInputKeydown(e) {
 | 
			
		||||
      // Enter.
 | 
			
		||||
@@ -57,20 +60,20 @@
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        this._handleAdd();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAdd() {
 | 
			
		||||
      if (!this._newValue.length) { return; }
 | 
			
		||||
      this._dispatchChanged(
 | 
			
		||||
          this.pluginOption.info.values.concat([this._newValue]));
 | 
			
		||||
      this._newValue = '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDelete(e) {
 | 
			
		||||
      const value = Polymer.dom(e).localTarget.dataset.item;
 | 
			
		||||
      this._dispatchChanged(
 | 
			
		||||
          this.pluginOption.info.values.filter(str => str !== value));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _dispatchChanged(values) {
 | 
			
		||||
      const {_key, info} = this.pluginOption;
 | 
			
		||||
@@ -81,10 +84,13 @@
 | 
			
		||||
      };
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('plugin-config-option-changed', {detail}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowInputRow(disabled) {
 | 
			
		||||
      return disabled ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrPluginConfigArrayEditor.is,
 | 
			
		||||
      GrPluginConfigArrayEditor);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,60 +17,67 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-plugin-list',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.ListViewMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrPluginList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.ListViewBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-plugin-list'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * Offset of currently visible query results.
 | 
			
		||||
       */
 | 
			
		||||
      _offset: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 0,
 | 
			
		||||
      },
 | 
			
		||||
      _path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
        value: '/admin/plugins',
 | 
			
		||||
      },
 | 
			
		||||
      _plugins: Array,
 | 
			
		||||
      /**
 | 
			
		||||
        _offset: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 0,
 | 
			
		||||
        },
 | 
			
		||||
        _path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
          value: '/admin/plugins',
 | 
			
		||||
        },
 | 
			
		||||
        _plugins: Array,
 | 
			
		||||
        /**
 | 
			
		||||
       * Because  we request one more than the pluginsPerPage, _shownPlugins
 | 
			
		||||
       * maybe one less than _plugins.
 | 
			
		||||
       * */
 | 
			
		||||
      _shownPlugins: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: 'computeShownItems(_plugins)',
 | 
			
		||||
      },
 | 
			
		||||
      _pluginsPerPage: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 25,
 | 
			
		||||
      },
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _filter: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.ListViewBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _shownPlugins: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: 'computeShownItems(_plugins)',
 | 
			
		||||
        },
 | 
			
		||||
        _pluginsPerPage: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 25,
 | 
			
		||||
        },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _filter: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.fire('title-change', {title: 'Plugins'});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(params) {
 | 
			
		||||
      this._loading = true;
 | 
			
		||||
@@ -79,7 +86,7 @@
 | 
			
		||||
 | 
			
		||||
      return this._getPlugins(this._filter, this._pluginsPerPage,
 | 
			
		||||
          this._offset);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getPlugins(filter, pluginsPerPage, offset) {
 | 
			
		||||
      const errFn = response => {
 | 
			
		||||
@@ -99,14 +106,16 @@
 | 
			
		||||
                });
 | 
			
		||||
            this._loading = false;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _status(item) {
 | 
			
		||||
      return item.disabled === true ? 'Disabled' : 'Enabled';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePluginUrl(id) {
 | 
			
		||||
      return this.getUrl('/', id);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrPluginList.is, GrPluginList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -67,66 +67,77 @@
 | 
			
		||||
   */
 | 
			
		||||
  Defs.projectAccessInput;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-access',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.AccessMixin
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRepoAccess extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.AccessBehavior,
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-repo-access'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      repo: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_repoChanged',
 | 
			
		||||
      },
 | 
			
		||||
      // The current path
 | 
			
		||||
      path: String,
 | 
			
		||||
 | 
			
		||||
      _canUpload: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _inheritFromFilter: String,
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getInheritFromSuggestions.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        repo: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_repoChanged',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _ownerOf: Array,
 | 
			
		||||
      _capabilities: Object,
 | 
			
		||||
      _groups: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _inheritsFrom: Object,
 | 
			
		||||
      _labels: Object,
 | 
			
		||||
      _local: Object,
 | 
			
		||||
      _editing: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_handleEditingChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _modified: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _sections: Array,
 | 
			
		||||
      _weblinks: Array,
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        // The current path
 | 
			
		||||
        path: String,
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.AccessBehavior,
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _canUpload: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _inheritFromFilter: String,
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getInheritFromSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _ownerOf: Array,
 | 
			
		||||
        _capabilities: Object,
 | 
			
		||||
        _groups: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _inheritsFrom: Object,
 | 
			
		||||
        _labels: Object,
 | 
			
		||||
        _local: Object,
 | 
			
		||||
        _editing: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_handleEditingChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _modified: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _sections: Array,
 | 
			
		||||
        _weblinks: Array,
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'access-modified': '_handleAccessModified',
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('access-modified',
 | 
			
		||||
          () =>
 | 
			
		||||
            this._handleAccessModified());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAccessModified() {
 | 
			
		||||
      this._modified = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} repo
 | 
			
		||||
@@ -138,7 +149,7 @@
 | 
			
		||||
      if (!repo) { return Promise.resolve(); }
 | 
			
		||||
 | 
			
		||||
      return this._reload(repo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reload(repo) {
 | 
			
		||||
      const promises = [];
 | 
			
		||||
@@ -195,7 +206,7 @@
 | 
			
		||||
        this._sections = sections;
 | 
			
		||||
        this._loading = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUpdateInheritFrom(e) {
 | 
			
		||||
      if (!this._inheritsFrom) {
 | 
			
		||||
@@ -204,7 +215,7 @@
 | 
			
		||||
      this._inheritsFrom.id = e.detail.value;
 | 
			
		||||
      this._inheritsFrom.name = this._inheritFromFilter;
 | 
			
		||||
      this._handleAccessModified();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getInheritFromSuggestions() {
 | 
			
		||||
      return this.$.restAPI.getRepos(
 | 
			
		||||
@@ -221,33 +232,33 @@
 | 
			
		||||
            }
 | 
			
		||||
            return projects;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoadingClass(loading) {
 | 
			
		||||
      return loading ? 'loading' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEdit() {
 | 
			
		||||
      this._editing = !this._editing;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _editOrCancel(editing) {
 | 
			
		||||
      return editing ? 'Cancel' : 'Edit';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeWebLinkClass(weblinks) {
 | 
			
		||||
      return weblinks && weblinks.length ? 'show' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowInherit(inheritsFrom) {
 | 
			
		||||
      return inheritsFrom ? 'show' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAddedSectionRemoved(e) {
 | 
			
		||||
      const index = e.model.index;
 | 
			
		||||
      this._sections = this._sections.slice(0, index)
 | 
			
		||||
          .concat(this._sections.slice(index + 1, this._sections.length));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEditingChanged(editing, editingOld) {
 | 
			
		||||
      // Ignore when editing gets set initially.
 | 
			
		||||
@@ -266,7 +277,7 @@
 | 
			
		||||
          delete this._local[key];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Defs.projectAccessInput} addRemoveObj
 | 
			
		||||
@@ -296,7 +307,7 @@
 | 
			
		||||
        curPos = curPos[item];
 | 
			
		||||
      }
 | 
			
		||||
      return addRemoveObj;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Used to recursively remove any objects with a 'deleted' bit.
 | 
			
		||||
@@ -313,7 +324,7 @@
 | 
			
		||||
          this._recursivelyRemoveDeleted(obj[k]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _recursivelyUpdateAddRemoveObj(obj, addRemoveObj, path = []) {
 | 
			
		||||
      for (const k in obj) {
 | 
			
		||||
@@ -354,7 +365,7 @@
 | 
			
		||||
              path.concat(k));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns an object formatted for saving or submitting access changes for
 | 
			
		||||
@@ -388,7 +399,7 @@
 | 
			
		||||
        addRemoveObj.parent = inheritsFromId;
 | 
			
		||||
      }
 | 
			
		||||
      return addRemoveObj;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateSection() {
 | 
			
		||||
      let newRef = 'refs/for/*';
 | 
			
		||||
@@ -403,7 +414,7 @@
 | 
			
		||||
      Polymer.dom.flush();
 | 
			
		||||
      Polymer.dom(this.root).querySelector('gr-access-section:last-of-type')
 | 
			
		||||
          .editReference();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getObjforSave() {
 | 
			
		||||
      const addRemoveObj = this._computeAddAndRemove();
 | 
			
		||||
@@ -426,7 +437,7 @@
 | 
			
		||||
        obj.parent = addRemoveObj.parent;
 | 
			
		||||
      }
 | 
			
		||||
      return obj;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSave() {
 | 
			
		||||
      const obj = this._getObjforSave();
 | 
			
		||||
@@ -435,7 +446,7 @@
 | 
			
		||||
          .then(() => {
 | 
			
		||||
            this._reload(this.repo);
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveForReview() {
 | 
			
		||||
      const obj = this._getObjforSave();
 | 
			
		||||
@@ -444,15 +455,15 @@
 | 
			
		||||
          .then(change => {
 | 
			
		||||
            Gerrit.Nav.navigateToChange(change);
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSaveReviewBtnClass(canUpload) {
 | 
			
		||||
      return !canUpload ? 'invisible' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSaveBtnClass(ownerOf) {
 | 
			
		||||
      return ownerOf && ownerOf.length === 0 ? 'invisible' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeMainClass(ownerOf, canUpload, editing) {
 | 
			
		||||
      const classList = [];
 | 
			
		||||
@@ -463,11 +474,13 @@
 | 
			
		||||
        classList.push('editing');
 | 
			
		||||
      }
 | 
			
		||||
      return classList.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeParentHref(repoName) {
 | 
			
		||||
      return this.getBaseUrl() +
 | 
			
		||||
          `/admin/repos/${this.encodeURL(repoName, true)},access`;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoAccess.is, GrRepoAccess);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,14 +17,18 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-command',
 | 
			
		||||
  class GrRepoCommand extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-repo-command'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      title: String,
 | 
			
		||||
      disabled: Boolean,
 | 
			
		||||
      tooltip: String,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        title: String,
 | 
			
		||||
        disabled: Boolean,
 | 
			
		||||
        tooltip: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when command button is tapped.
 | 
			
		||||
@@ -35,6 +39,8 @@
 | 
			
		||||
    _onCommandTap() {
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('command-tap', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoCommand.is, GrRepoCommand);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -26,30 +26,36 @@
 | 
			
		||||
  const CREATE_CHANGE_FAILED_MESSAGE = 'Failed to create change.';
 | 
			
		||||
  const CREATE_CHANGE_SUCCEEDED_MESSAGE = 'Navigating to change';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-commands',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRepoCommands extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-repo-commands'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      params: Object,
 | 
			
		||||
      repo: String,
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _repoConfig: Object,
 | 
			
		||||
      _canCreate: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        params: Object,
 | 
			
		||||
        repo: String,
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _repoConfig: Object,
 | 
			
		||||
        _canCreate: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._loadRepo();
 | 
			
		||||
 | 
			
		||||
      this.fire('title-change', {title: 'Repo Commands'});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadRepo() {
 | 
			
		||||
      if (!this.repo) { return Promise.resolve(); }
 | 
			
		||||
@@ -65,15 +71,15 @@
 | 
			
		||||
            this._repoConfig = config;
 | 
			
		||||
            this._loading = false;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoadingClass(loading) {
 | 
			
		||||
      return loading ? 'loading' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isLoading() {
 | 
			
		||||
      return this._loading || this._loading === undefined;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRunningGC() {
 | 
			
		||||
      return this.$.restAPI.runRepoGC(this.repo).then(response => {
 | 
			
		||||
@@ -83,20 +89,20 @@
 | 
			
		||||
              {detail: {message: GC_MESSAGE}, bubbles: true, composed: true}));
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createNewChange() {
 | 
			
		||||
      this.$.createChangeOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateChange() {
 | 
			
		||||
      this.$.createNewChangeModal.handleCreateChange();
 | 
			
		||||
      this._handleCloseCreateChange();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseCreateChange() {
 | 
			
		||||
      this.$.createChangeOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEditRepoConfig() {
 | 
			
		||||
      return this.$.restAPI.createChange(this.repo, CONFIG_BRANCH,
 | 
			
		||||
@@ -112,6 +118,8 @@
 | 
			
		||||
        Gerrit.Nav.navigateToRelativeUrl(Gerrit.Nav.getEditUrlForDiff(
 | 
			
		||||
            change, CONFIG_PATH, INITIAL_PATCHSET));
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoCommands.is, GrRepoCommands);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,24 +17,29 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-dashboards',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRepoDashboards extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-repo-dashboards'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      repo: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_repoChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _dashboards: Array,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        repo: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_repoChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _dashboards: Array,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _repoChanged(repo) {
 | 
			
		||||
      this._loading = true;
 | 
			
		||||
@@ -70,24 +75,26 @@
 | 
			
		||||
        this._loading = false;
 | 
			
		||||
        Polymer.dom.flush();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getUrl(project, id) {
 | 
			
		||||
      if (!project || !id) { return ''; }
 | 
			
		||||
 | 
			
		||||
      return Gerrit.Nav.getUrlForRepoDashboard(project, id);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoadingClass(loading) {
 | 
			
		||||
      return loading ? 'loading' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeInheritedFrom(project, definingProject) {
 | 
			
		||||
      return project === definingProject ? '' : definingProject;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsDefault(isDefault) {
 | 
			
		||||
      return isDefault ? '✓' : '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoDashboards.is, GrRepoDashboards);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,75 +24,82 @@
 | 
			
		||||
 | 
			
		||||
  const PGP_START = '-----BEGIN PGP SIGNATURE-----';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-detail-list',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.ListViewMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRepoDetailList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.ListViewBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-repo-detail-list'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * The kind of detail we are displaying, possibilities are determined by
 | 
			
		||||
       * the const DETAIL_TYPES.
 | 
			
		||||
       */
 | 
			
		||||
      detailType: String,
 | 
			
		||||
        detailType: String,
 | 
			
		||||
 | 
			
		||||
      _editing: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _isOwner: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        _editing: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _isOwner: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * Offset of currently visible query results.
 | 
			
		||||
       */
 | 
			
		||||
      _offset: Number,
 | 
			
		||||
      _repo: Object,
 | 
			
		||||
      _items: Array,
 | 
			
		||||
      /**
 | 
			
		||||
        _offset: Number,
 | 
			
		||||
        _repo: Object,
 | 
			
		||||
        _items: Array,
 | 
			
		||||
        /**
 | 
			
		||||
       * Because  we request one more than the projectsPerPage, _shownProjects
 | 
			
		||||
       * maybe one less than _projects.
 | 
			
		||||
       */
 | 
			
		||||
      _shownItems: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: 'computeShownItems(_items)',
 | 
			
		||||
      },
 | 
			
		||||
      _itemsPerPage: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 25,
 | 
			
		||||
      },
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _filter: String,
 | 
			
		||||
      _refName: String,
 | 
			
		||||
      _hasNewItemName: Boolean,
 | 
			
		||||
      _isEditing: Boolean,
 | 
			
		||||
      _revisedRef: String,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.ListViewBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _shownItems: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: 'computeShownItems(_items)',
 | 
			
		||||
        },
 | 
			
		||||
        _itemsPerPage: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 25,
 | 
			
		||||
        },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _filter: String,
 | 
			
		||||
        _refName: String,
 | 
			
		||||
        _hasNewItemName: Boolean,
 | 
			
		||||
        _isEditing: Boolean,
 | 
			
		||||
        _revisedRef: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _determineIfOwner(repo) {
 | 
			
		||||
      return this.$.restAPI.getRepoAccess(repo)
 | 
			
		||||
          .then(access =>
 | 
			
		||||
            this._isOwner = access && !!access[repo].is_owner);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(params) {
 | 
			
		||||
      if (!params || !params.repo) { return; }
 | 
			
		||||
@@ -113,7 +120,7 @@
 | 
			
		||||
 | 
			
		||||
      return this._getItems(this._filter, this._repo,
 | 
			
		||||
          this._itemsPerPage, this._offset, this.detailType);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getItems(filter, repo, itemsPerPage, offset, detailType) {
 | 
			
		||||
      this._loading = true;
 | 
			
		||||
@@ -137,24 +144,24 @@
 | 
			
		||||
          this._loading = false;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getPath(repo) {
 | 
			
		||||
      return `/admin/repos/${this.encodeURL(repo, false)},` +
 | 
			
		||||
          `${this.detailType}`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeWeblink(repo) {
 | 
			
		||||
      if (!repo.web_links) { return ''; }
 | 
			
		||||
      const webLinks = repo.web_links;
 | 
			
		||||
      return webLinks.length ? webLinks : null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeMessage(message) {
 | 
			
		||||
      if (!message) { return; }
 | 
			
		||||
      // Strip PGP info.
 | 
			
		||||
      return message.split(PGP_START)[0];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _stripRefs(item, detailType) {
 | 
			
		||||
      if (detailType === DETAIL_TYPES.BRANCHES) {
 | 
			
		||||
@@ -162,33 +169,33 @@
 | 
			
		||||
      } else if (detailType === DETAIL_TYPES.TAGS) {
 | 
			
		||||
        return item.replace('refs/tags/', '');
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLoggedIn() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeEditingClass(isEditing) {
 | 
			
		||||
      return isEditing ? 'editing' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeCanEditClass(ref, detailType, isOwner) {
 | 
			
		||||
      return isOwner && this._stripRefs(ref, detailType) === 'HEAD' ?
 | 
			
		||||
        'canEdit' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEditRevision(e) {
 | 
			
		||||
      this._revisedRef = e.model.get('item.revision');
 | 
			
		||||
      this._isEditing = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelRevision() {
 | 
			
		||||
      this._isEditing = false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveRevision(e) {
 | 
			
		||||
      this._setRepoHead(this._repo, this._revisedRef, e);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setRepoHead(repo, ref, e) {
 | 
			
		||||
      return this.$.restAPI.setRepoHead(repo, ref).then(res => {
 | 
			
		||||
@@ -202,7 +209,7 @@
 | 
			
		||||
              this._offset, this.detailType);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeItemName(detailType) {
 | 
			
		||||
      if (detailType === DETAIL_TYPES.BRANCHES) {
 | 
			
		||||
@@ -210,7 +217,7 @@
 | 
			
		||||
      } else if (detailType === DETAIL_TYPES.TAGS) {
 | 
			
		||||
        return 'Tag';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDeleteItemConfirm() {
 | 
			
		||||
      this.$.overlay.close();
 | 
			
		||||
@@ -233,18 +240,18 @@
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmDialogCancel() {
 | 
			
		||||
      this.$.overlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDeleteItem(e) {
 | 
			
		||||
      const name = this._stripRefs(e.model.get('item.ref'), this.detailType);
 | 
			
		||||
      if (!name) { return; }
 | 
			
		||||
      this._refName = name;
 | 
			
		||||
      this.$.overlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideDeleteClass(owner, canDelete) {
 | 
			
		||||
      if (canDelete || owner) {
 | 
			
		||||
@@ -252,20 +259,20 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateItem() {
 | 
			
		||||
      this.$.createNewModal.handleCreateItem();
 | 
			
		||||
      this._handleCloseCreate();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseCreate() {
 | 
			
		||||
      this.$.createOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateClicked() {
 | 
			
		||||
      this.$.createOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hideIfBranch(type) {
 | 
			
		||||
      if (type === DETAIL_TYPES.BRANCHES) {
 | 
			
		||||
@@ -273,10 +280,12 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideTagger(tagger) {
 | 
			
		||||
      return tagger ? '' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoDetailList.is, GrRepoDetailList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,67 +17,73 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-list',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.ListViewMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRepoList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.ListViewBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-repo-list'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Offset of currently visible query results.
 | 
			
		||||
       */
 | 
			
		||||
      _offset: Number,
 | 
			
		||||
      _path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
        value: '/admin/repos',
 | 
			
		||||
      },
 | 
			
		||||
      _hasNewRepoName: Boolean,
 | 
			
		||||
      _createNewCapability: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _repos: Array,
 | 
			
		||||
        _offset: Number,
 | 
			
		||||
        _path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
          value: '/admin/repos',
 | 
			
		||||
        },
 | 
			
		||||
        _hasNewRepoName: Boolean,
 | 
			
		||||
        _createNewCapability: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _repos: Array,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Because  we request one more than the projectsPerPage, _shownProjects
 | 
			
		||||
       * maybe one less than _projects.
 | 
			
		||||
       * */
 | 
			
		||||
      _shownRepos: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: 'computeShownItems(_repos)',
 | 
			
		||||
      },
 | 
			
		||||
        _shownRepos: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: 'computeShownItems(_repos)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _reposPerPage: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 25,
 | 
			
		||||
      },
 | 
			
		||||
        _reposPerPage: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 25,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _filter: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.ListViewBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _filter: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._getCreateRepoCapability();
 | 
			
		||||
      this.fire('title-change', {title: 'Repos'});
 | 
			
		||||
      this._maybeOpenCreateOverlay(this.params);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(params) {
 | 
			
		||||
      this._loading = true;
 | 
			
		||||
@@ -86,7 +92,7 @@
 | 
			
		||||
 | 
			
		||||
      return this._getRepos(this._filter, this._reposPerPage,
 | 
			
		||||
          this._offset);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Opens the create overlay if the route has a hash 'create'
 | 
			
		||||
@@ -96,15 +102,15 @@
 | 
			
		||||
      if (params && params.openCreateModal) {
 | 
			
		||||
        this.$.createOverlay.open();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRepoUrl(name) {
 | 
			
		||||
      return this.getUrl(this._path + '/', name);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChangesLink(name) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForProjectChanges(name);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCreateRepoCapability() {
 | 
			
		||||
      return this.$.restAPI.getAccount().then(account => {
 | 
			
		||||
@@ -116,7 +122,7 @@
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRepos(filter, reposPerPage, offset) {
 | 
			
		||||
      this._repos = [];
 | 
			
		||||
@@ -127,36 +133,38 @@
 | 
			
		||||
            this._repos = repos;
 | 
			
		||||
            this._loading = false;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _refreshReposList() {
 | 
			
		||||
      this.$.restAPI.invalidateReposCache();
 | 
			
		||||
      return this._getRepos(this._filter, this._reposPerPage,
 | 
			
		||||
          this._offset);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateRepo() {
 | 
			
		||||
      this.$.createNewModal.handleCreateRepo().then(() => {
 | 
			
		||||
        this._refreshReposList();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseCreate() {
 | 
			
		||||
      this.$.createOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateClicked() {
 | 
			
		||||
      this.$.createOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _readOnly(item) {
 | 
			
		||||
      return item.state === 'READ_ONLY' ? 'Y' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeWeblink(repo) {
 | 
			
		||||
      if (!repo.web_links) { return ''; }
 | 
			
		||||
      const webLinks = repo.web_links;
 | 
			
		||||
      return webLinks.length ? webLinks : null;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoList.is, GrRepoList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,28 +17,32 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-plugin-config',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.RepoPluginConfigMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRepoPluginConfig extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.RepoPluginConfig,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-repo-plugin-config'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the plugin config changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @event plugin-config-changed
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      pluginData: Object,
 | 
			
		||||
      /** @type {Array} */
 | 
			
		||||
      _pluginConfigOptions: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computePluginConfigOptions(pluginData.*)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.RepoPluginConfig,
 | 
			
		||||
    ],
 | 
			
		||||
        pluginData: Object,
 | 
			
		||||
        /** @type {Array} */
 | 
			
		||||
        _pluginConfigOptions: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computePluginConfigOptions(pluginData.*)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePluginConfigOptions(dataRecord) {
 | 
			
		||||
      if (!dataRecord || !dataRecord.base || !dataRecord.base.config) {
 | 
			
		||||
@@ -46,34 +50,34 @@
 | 
			
		||||
      }
 | 
			
		||||
      const {config} = dataRecord.base;
 | 
			
		||||
      return Object.keys(config).map(_key => ({_key, info: config[_key]}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isArray(type) {
 | 
			
		||||
      return type === this.ENTRY_TYPES.ARRAY;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isBoolean(type) {
 | 
			
		||||
      return type === this.ENTRY_TYPES.BOOLEAN;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isList(type) {
 | 
			
		||||
      return type === this.ENTRY_TYPES.LIST;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isString(type) {
 | 
			
		||||
      // Treat numbers like strings for simplicity.
 | 
			
		||||
      return type === this.ENTRY_TYPES.STRING ||
 | 
			
		||||
          type === this.ENTRY_TYPES.INT ||
 | 
			
		||||
          type === this.ENTRY_TYPES.LONG;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDisabled(editable) {
 | 
			
		||||
      return editable === 'false';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChecked(value) {
 | 
			
		||||
      return JSON.parse(value);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleStringChange(e) {
 | 
			
		||||
      const el = Polymer.dom(e).localTarget;
 | 
			
		||||
@@ -81,7 +85,7 @@
 | 
			
		||||
      const configChangeInfo =
 | 
			
		||||
          this._buildConfigChangeInfo(el.value, _key);
 | 
			
		||||
      this._handleChange(configChangeInfo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleListChange(e) {
 | 
			
		||||
      const el = Polymer.dom(e).localTarget;
 | 
			
		||||
@@ -89,7 +93,7 @@
 | 
			
		||||
      const configChangeInfo =
 | 
			
		||||
          this._buildConfigChangeInfo(el.value, _key);
 | 
			
		||||
      this._handleChange(configChangeInfo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleBooleanChange(e) {
 | 
			
		||||
      const el = Polymer.dom(e).localTarget;
 | 
			
		||||
@@ -97,7 +101,7 @@
 | 
			
		||||
      const configChangeInfo =
 | 
			
		||||
          this._buildConfigChangeInfo(JSON.stringify(el.checked), _key);
 | 
			
		||||
      this._handleChange(configChangeInfo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _buildConfigChangeInfo(value, _key) {
 | 
			
		||||
      const info = this.pluginData.config[_key];
 | 
			
		||||
@@ -107,11 +111,11 @@
 | 
			
		||||
        info,
 | 
			
		||||
        notifyPath: `${_key}.value`,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleArrayChange({detail}) {
 | 
			
		||||
      this._handleChange(detail);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleChange({_key, info, notifyPath}) {
 | 
			
		||||
      const {name, config} = this.pluginData;
 | 
			
		||||
@@ -125,6 +129,8 @@
 | 
			
		||||
 | 
			
		||||
      this.dispatchEvent(new CustomEvent(
 | 
			
		||||
          this.PLUGIN_CONFIG_CHANGED, {detail, bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoPluginConfig.is, GrRepoPluginConfig);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -51,76 +51,84 @@
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRepo extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-repo'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      params: Object,
 | 
			
		||||
      repo: String,
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        params: Object,
 | 
			
		||||
        repo: String,
 | 
			
		||||
 | 
			
		||||
      _configChanged: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_loggedInChanged',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _repoConfig: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _pluginData: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computePluginData(_repoConfig.plugin_config.*)',
 | 
			
		||||
      },
 | 
			
		||||
      _readOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _states: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() {
 | 
			
		||||
          return Object.values(STATES);
 | 
			
		||||
        _configChanged: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _submitTypes: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() {
 | 
			
		||||
          return Object.values(SUBMIT_TYPES);
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _schemes: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
        computed: '_computeSchemes(_schemesObj)',
 | 
			
		||||
        observer: '_schemesChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _selectedCommand: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: 'Clone',
 | 
			
		||||
      },
 | 
			
		||||
      _selectedScheme: String,
 | 
			
		||||
      _schemesObj: Object,
 | 
			
		||||
    },
 | 
			
		||||
        _loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_loggedInChanged',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _repoConfig: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _pluginData: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computePluginData(_repoConfig.plugin_config.*)',
 | 
			
		||||
        },
 | 
			
		||||
        _readOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _states: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() {
 | 
			
		||||
            return Object.values(STATES);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _submitTypes: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() {
 | 
			
		||||
            return Object.values(SUBMIT_TYPES);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _schemes: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
          computed: '_computeSchemes(_schemesObj)',
 | 
			
		||||
          observer: '_schemesChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _selectedCommand: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: 'Clone',
 | 
			
		||||
        },
 | 
			
		||||
        _selectedScheme: String,
 | 
			
		||||
        _schemesObj: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_handleConfigChanged(_repoConfig.*)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_handleConfigChanged(_repoConfig.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._loadRepo();
 | 
			
		||||
 | 
			
		||||
      this.fire('title-change', {title: this.repo});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePluginData(configRecord) {
 | 
			
		||||
      if (!configRecord ||
 | 
			
		||||
@@ -129,7 +137,7 @@
 | 
			
		||||
      const pluginConfig = configRecord.base;
 | 
			
		||||
      return Object.keys(pluginConfig)
 | 
			
		||||
          .map(name => ({name, config: pluginConfig[name]}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadRepo() {
 | 
			
		||||
      if (!this.repo) { return Promise.resolve(); }
 | 
			
		||||
@@ -179,15 +187,15 @@
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return Promise.all(promises);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoadingClass(loading) {
 | 
			
		||||
      return loading ? 'loading' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideClass(arr) {
 | 
			
		||||
      return !arr || !arr.length ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loggedInChanged(_loggedIn) {
 | 
			
		||||
      if (!_loggedIn) { return; }
 | 
			
		||||
@@ -197,7 +205,7 @@
 | 
			
		||||
          this._selectedScheme = prefs.download_scheme.toLowerCase();
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _formatBooleanSelect(item) {
 | 
			
		||||
      if (!item) { return; }
 | 
			
		||||
@@ -218,7 +226,7 @@
 | 
			
		||||
          value: 'FALSE',
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _formatSubmitTypeSelect(projectConfig) {
 | 
			
		||||
      if (!projectConfig) { return; }
 | 
			
		||||
@@ -248,15 +256,15 @@
 | 
			
		||||
        },
 | 
			
		||||
        ...allValues,
 | 
			
		||||
      ];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isLoading() {
 | 
			
		||||
      return this._loading || this._loading === undefined;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLoggedIn() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _formatRepoConfigForSave(repoConfig) {
 | 
			
		||||
      const configInputObj = {};
 | 
			
		||||
@@ -278,38 +286,38 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return configInputObj;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveRepoConfig() {
 | 
			
		||||
      return this.$.restAPI.saveRepoConfig(this.repo,
 | 
			
		||||
          this._formatRepoConfigForSave(this._repoConfig)).then(() => {
 | 
			
		||||
        this._configChanged = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfigChanged() {
 | 
			
		||||
      if (this._isLoading()) { return; }
 | 
			
		||||
      this._configChanged = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeButtonDisabled(readOnly, configChanged) {
 | 
			
		||||
      return readOnly || !configChanged;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHeaderClass(configChanged) {
 | 
			
		||||
      return configChanged ? 'edited' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSchemes(schemesObj) {
 | 
			
		||||
      return Object.keys(schemesObj);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _schemesChanged(schemes) {
 | 
			
		||||
      if (schemes.length === 0) { return; }
 | 
			
		||||
      if (!schemes.includes(this._selectedScheme)) {
 | 
			
		||||
        this._selectedScheme = schemes.sort()[0];
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeCommands(repo, schemesObj, _selectedScheme) {
 | 
			
		||||
      if (!schemesObj || !repo || !_selectedScheme) {
 | 
			
		||||
@@ -331,19 +339,21 @@
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      return commands;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRepositoriesClass(config) {
 | 
			
		||||
      return config ? 'showConfig': '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChangesUrl(name) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForProjectChanges(name);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePluginConfigChanged({detail: {name, config, notifyPath}}) {
 | 
			
		||||
      this._repoConfig.plugin_config[name] = config;
 | 
			
		||||
      this.notifyPath('_repoConfig.plugin_config.' + notifyPath);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepo.is, GrRepo);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -63,62 +63,76 @@
 | 
			
		||||
    },
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-rule-editor',
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      hasRange: Boolean,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      label: Object,
 | 
			
		||||
      editing: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_handleEditingChanged',
 | 
			
		||||
      },
 | 
			
		||||
      groupId: String,
 | 
			
		||||
      groupName: String,
 | 
			
		||||
      permission: String,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      rule: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      section: String,
 | 
			
		||||
 | 
			
		||||
      _deleted: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _originalRuleValues: Object,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.AccessBehavior,
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      /**
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.AccessMixin
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRuleEditor extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.AccessBehavior,
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    /**
 | 
			
		||||
       * Unused in this element, but called by other elements in tests
 | 
			
		||||
       * e.g gr-permission_test.
 | 
			
		||||
       */
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-rule-editor'; }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_handleValueChange(rule.value.*)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        hasRange: Boolean,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        label: Object,
 | 
			
		||||
        editing: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_handleEditingChanged',
 | 
			
		||||
        },
 | 
			
		||||
        groupId: String,
 | 
			
		||||
        groupName: String,
 | 
			
		||||
        permission: String,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        rule: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        section: String,
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'access-saved': '_handleAccessSaved',
 | 
			
		||||
    },
 | 
			
		||||
        _deleted: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _originalRuleValues: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_handleValueChange(rule.value.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('access-saved',
 | 
			
		||||
          () => this._handleAccessSaved());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      // Called on ready rather than the observer because when new rules are
 | 
			
		||||
      // added, the observer is triggered prior to being ready.
 | 
			
		||||
      if (!this.rule) { return; } // Check needed for test purposes.
 | 
			
		||||
      this._setupValues(this.rule);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      if (!this.rule) { return; } // Check needed for test purposes.
 | 
			
		||||
      if (!this._originalRuleValues) {
 | 
			
		||||
        // Observer _handleValueChange is called after the ready()
 | 
			
		||||
@@ -126,13 +140,13 @@
 | 
			
		||||
        // avoid set .modified flag to true
 | 
			
		||||
        this._setOriginalRuleValues(this.rule.value);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setupValues(rule) {
 | 
			
		||||
      if (!rule.value) {
 | 
			
		||||
        this._setDefaultRuleValues();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeForce(permission, action) {
 | 
			
		||||
      if (this.permissionValues.push.id === permission &&
 | 
			
		||||
@@ -141,21 +155,21 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return this.permissionValues.editTopicName.id === permission;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeForceClass(permission, action) {
 | 
			
		||||
      return this._computeForce(permission, action) ? 'force' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroupPath(group) {
 | 
			
		||||
      return `${this.getBaseUrl()}/admin/groups/${this.encodeURL(group, true)}`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAccessSaved() {
 | 
			
		||||
      // Set a new 'original' value to keep track of after the value has been
 | 
			
		||||
      // saved.
 | 
			
		||||
      this._setOriginalRuleValues(this.rule.value);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEditingChanged(editing, editingOld) {
 | 
			
		||||
      // Ignore when editing gets set initially.
 | 
			
		||||
@@ -164,7 +178,7 @@
 | 
			
		||||
      if (!editing) {
 | 
			
		||||
        this._handleUndoChange();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSectionClass(editing, deleted) {
 | 
			
		||||
      const classList = [];
 | 
			
		||||
@@ -175,7 +189,7 @@
 | 
			
		||||
        classList.push('deleted');
 | 
			
		||||
      }
 | 
			
		||||
      return classList.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeForceOptions(permission, action) {
 | 
			
		||||
      if (permission === this.permissionValues.push.id) {
 | 
			
		||||
@@ -190,7 +204,7 @@
 | 
			
		||||
        return FORCE_EDIT_OPTIONS;
 | 
			
		||||
      }
 | 
			
		||||
      return [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getDefaultRuleValues(permission, label) {
 | 
			
		||||
      const ruleAction = Action.ALLOW;
 | 
			
		||||
@@ -207,19 +221,19 @@
 | 
			
		||||
      }
 | 
			
		||||
      value.action = DROPDOWN_OPTIONS[0];
 | 
			
		||||
      return value;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setDefaultRuleValues() {
 | 
			
		||||
      this.set('rule.value', this._getDefaultRuleValues(this.permission,
 | 
			
		||||
          this.label));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeOptions(permission) {
 | 
			
		||||
      if (permission === 'priority') {
 | 
			
		||||
        return PRIORITY_OPTIONS;
 | 
			
		||||
      }
 | 
			
		||||
      return DROPDOWN_OPTIONS;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRemoveRule() {
 | 
			
		||||
      if (this.rule.value.added) {
 | 
			
		||||
@@ -230,12 +244,12 @@
 | 
			
		||||
      this.rule.value.deleted = true;
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('access-modified', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUndoRemove() {
 | 
			
		||||
      this._deleted = false;
 | 
			
		||||
      delete this.rule.value.deleted;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUndoChange() {
 | 
			
		||||
      // gr-permission will take care of removing rules that were added but
 | 
			
		||||
@@ -245,7 +259,7 @@
 | 
			
		||||
      this._deleted = false;
 | 
			
		||||
      delete this.rule.value.deleted;
 | 
			
		||||
      delete this.rule.value.modified;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleValueChange() {
 | 
			
		||||
      if (!this._originalRuleValues) { return; }
 | 
			
		||||
@@ -253,10 +267,12 @@
 | 
			
		||||
      // Allows overall access page to know a change has been made.
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('access-modified', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setOriginalRuleValues(value) {
 | 
			
		||||
      this._originalRuleValues = Object.assign({}, value);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRuleEditor.is, GrRuleEditor);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,57 +24,67 @@
 | 
			
		||||
    LARGE: 1000,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-change-list-item',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.ChangeTableMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PathListMixin
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrChangeListItem extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.ChangeTableBehavior,
 | 
			
		||||
    Gerrit.PathListBehavior,
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-change-list-item'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      visibleChangeTableColumns: Array,
 | 
			
		||||
      labelNames: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
      },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        visibleChangeTableColumns: Array,
 | 
			
		||||
        labelNames: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      changeURL: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeChangeURL(change)',
 | 
			
		||||
      },
 | 
			
		||||
      statuses: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: 'changeStatuses(change)',
 | 
			
		||||
      },
 | 
			
		||||
      showStar: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      showNumber: Boolean,
 | 
			
		||||
      _changeSize: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeChangeSize(change)',
 | 
			
		||||
      },
 | 
			
		||||
      _dynamicCellEndpoints: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.ChangeTableBehavior,
 | 
			
		||||
      Gerrit.PathListBehavior,
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        change: Object,
 | 
			
		||||
        changeURL: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeChangeURL(change)',
 | 
			
		||||
        },
 | 
			
		||||
        statuses: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: 'changeStatuses(change)',
 | 
			
		||||
        },
 | 
			
		||||
        showStar: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        showNumber: Boolean,
 | 
			
		||||
        _changeSize: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeChangeSize(change)',
 | 
			
		||||
        },
 | 
			
		||||
        _dynamicCellEndpoints: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      Gerrit.awaitPluginsLoaded().then(() => {
 | 
			
		||||
        this._dynamicCellEndpoints = Gerrit._endpoints.getDynamicEndpoints(
 | 
			
		||||
            'change-list-item-cell');
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChangeURL(change) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForChange(change);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelTitle(change, labelName) {
 | 
			
		||||
      const label = change.labels[labelName];
 | 
			
		||||
@@ -85,7 +95,7 @@
 | 
			
		||||
        return labelName + '\nby ' + significantLabel.name;
 | 
			
		||||
      }
 | 
			
		||||
      return labelName;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelClass(change, labelName) {
 | 
			
		||||
      const label = change.labels[labelName];
 | 
			
		||||
@@ -112,7 +122,7 @@
 | 
			
		||||
        classes['u-gray-background'] = true;
 | 
			
		||||
      }
 | 
			
		||||
      return Object.keys(classes).sort().join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelValue(change, labelName) {
 | 
			
		||||
      const label = change.labels[labelName];
 | 
			
		||||
@@ -130,22 +140,22 @@
 | 
			
		||||
        return label.value;
 | 
			
		||||
      }
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRepoUrl(change) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForProjectChanges(change.project, true,
 | 
			
		||||
          change.internalHost);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRepoBranchURL(change) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForBranch(change.branch, change.project, null,
 | 
			
		||||
          change.internalHost);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeTopicURL(change) {
 | 
			
		||||
      if (!change.topic) { return ''; }
 | 
			
		||||
      return Gerrit.Nav.getUrlForTopic(change.topic, change.internalHost);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Computes the display string for the project column. If there is a host
 | 
			
		||||
@@ -162,7 +172,7 @@
 | 
			
		||||
      if (change.internalHost) { str += change.internalHost + '/'; }
 | 
			
		||||
      str += truncate ? this.truncatePath(change.project, 2) : change.project;
 | 
			
		||||
      return str;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSizeTooltip(change) {
 | 
			
		||||
      if (change.insertions + change.deletions === 0 ||
 | 
			
		||||
@@ -171,7 +181,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return `+${change.insertions}, -${change.deletions}`;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * TShirt sizing is based on the following paper:
 | 
			
		||||
@@ -193,7 +203,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return 'XL';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toggleReviewed() {
 | 
			
		||||
      const newVal = !this.change.reviewed;
 | 
			
		||||
@@ -203,6 +213,8 @@
 | 
			
		||||
        composed: true,
 | 
			
		||||
        detail: {change: this.change, reviewed: newVal},
 | 
			
		||||
      }));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrChangeListItem.is, GrChangeListItem);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -29,44 +29,49 @@
 | 
			
		||||
 | 
			
		||||
  const LIMIT_OPERATOR_PATTERN = /\blimit:(\d+)/i;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-change-list-view',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrChangeListView extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-change-list-view'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the title of the page should change.
 | 
			
		||||
     *
 | 
			
		||||
     * @event title-change
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * True when user is logged in.
 | 
			
		||||
       */
 | 
			
		||||
      _loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeLoggedIn(account)',
 | 
			
		||||
      },
 | 
			
		||||
        _loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeLoggedIn(account)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      account: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        account: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * State persisted across restamps of the element.
 | 
			
		||||
       *
 | 
			
		||||
       * Need sub-property declaration since it is used in template before
 | 
			
		||||
@@ -74,66 +79,71 @@
 | 
			
		||||
       * @type {{ selectedChangeIndex: (number|undefined) }}
 | 
			
		||||
       *
 | 
			
		||||
       */
 | 
			
		||||
      viewState: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        value() { return {}; },
 | 
			
		||||
      },
 | 
			
		||||
        viewState: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          value() { return {}; },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      preferences: Object,
 | 
			
		||||
        preferences: Object,
 | 
			
		||||
 | 
			
		||||
      _changesPerPage: Number,
 | 
			
		||||
        _changesPerPage: Number,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Currently active query.
 | 
			
		||||
       */
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Offset of currently visible query results.
 | 
			
		||||
       */
 | 
			
		||||
      _offset: Number,
 | 
			
		||||
        _offset: Number,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Change objects loaded from the server.
 | 
			
		||||
       */
 | 
			
		||||
      _changes: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        observer: '_changesChanged',
 | 
			
		||||
      },
 | 
			
		||||
        _changes: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          observer: '_changesChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * For showing a "loading..." string during ajax requests.
 | 
			
		||||
       */
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?String} */
 | 
			
		||||
      _userId: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        /** @type {?String} */
 | 
			
		||||
        _userId: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?String} */
 | 
			
		||||
      _repo: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        /** @type {?String} */
 | 
			
		||||
        _repo: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'next-page': '_handleNextPage',
 | 
			
		||||
      'previous-page': '_handlePreviousPage',
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('next-page',
 | 
			
		||||
          () => this._handleNextPage());
 | 
			
		||||
      this.addEventListener('previous-page',
 | 
			
		||||
          () => this._handlePreviousPage());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._loadPreferences();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(value) {
 | 
			
		||||
      if (value.view !== Gerrit.Nav.View.SEARCH) { return; }
 | 
			
		||||
@@ -170,7 +180,7 @@
 | 
			
		||||
        this._changes = changes;
 | 
			
		||||
        this._loading = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadPreferences() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn().then(loggedIn => {
 | 
			
		||||
@@ -182,20 +192,20 @@
 | 
			
		||||
          this.preferences = {};
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _replaceCurrentLocation(url) {
 | 
			
		||||
      window.location.replace(url);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChanges() {
 | 
			
		||||
      return this.$.restAPI.getChanges(this._changesPerPage, this._query,
 | 
			
		||||
          this._offset);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getPreferences() {
 | 
			
		||||
      return this.$.restAPI.getPreferences();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _limitFor(query, defaultLimit) {
 | 
			
		||||
      const match = query.match(LIMIT_OPERATOR_PATTERN);
 | 
			
		||||
@@ -203,7 +213,7 @@
 | 
			
		||||
        return defaultLimit;
 | 
			
		||||
      }
 | 
			
		||||
      return parseInt(match[1], 10);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeNavLink(query, offset, direction, changesPerPage) {
 | 
			
		||||
      // Offset could be a string when passed from the router.
 | 
			
		||||
@@ -211,32 +221,32 @@
 | 
			
		||||
      const limit = this._limitFor(query, changesPerPage);
 | 
			
		||||
      const newOffset = Math.max(0, offset + (limit * direction));
 | 
			
		||||
      return Gerrit.Nav.getUrlForSearchQuery(query, newOffset);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePrevArrowClass(offset) {
 | 
			
		||||
      return offset === 0 ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeNextArrowClass(changes) {
 | 
			
		||||
      const more = changes.length && changes[changes.length - 1]._more_changes;
 | 
			
		||||
      return more ? '' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeNavClass(loading) {
 | 
			
		||||
      return loading || !this._changes || !this._changes.length ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNextPage() {
 | 
			
		||||
      if (this.$.nextArrow.hidden) { return; }
 | 
			
		||||
      page.show(this._computeNavLink(
 | 
			
		||||
          this._query, this._offset, 1, this._changesPerPage));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePreviousPage() {
 | 
			
		||||
      if (this.$.prevArrow.hidden) { return; }
 | 
			
		||||
      page.show(this._computeNavLink(
 | 
			
		||||
          this._query, this._offset, -1, this._changesPerPage));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _changesChanged(changes) {
 | 
			
		||||
      this._userId = null;
 | 
			
		||||
@@ -255,28 +265,30 @@
 | 
			
		||||
      if (REPO_QUERY_PATTERN.test(this._query)) {
 | 
			
		||||
        this._repo = changes[0].project;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHeaderClass(id) {
 | 
			
		||||
      return id ? '' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePage(offset, changesPerPage) {
 | 
			
		||||
      return offset / changesPerPage + 1;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoggedIn(account) {
 | 
			
		||||
      return !!(account && Object.keys(account).length > 0);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleToggleStar(e) {
 | 
			
		||||
      this.$.restAPI.saveChangeStarred(e.detail.change._number,
 | 
			
		||||
          e.detail.starred);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleToggleReviewed(e) {
 | 
			
		||||
      this.$.restAPI.saveChangeReviewed(e.detail.change._number,
 | 
			
		||||
          e.detail.reviewed);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrChangeListView.is, GrChangeListView);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,25 @@
 | 
			
		||||
  const LABEL_PREFIX_INVALID_PROLOG = 'Invalid-Prolog-Rules-Label-Name--';
 | 
			
		||||
  const MAX_SHORTCUT_CHARS = 5;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-change-list',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.ChangeTableMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrChangeList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.ChangeTableBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-change-list'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when next page key shortcut was pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -37,28 +53,25 @@
 | 
			
		||||
     * @event previous-page
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    hostAttributes: {
 | 
			
		||||
      tabindex: 0,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * The logged-in user's account, or an empty object if no user is logged
 | 
			
		||||
       * in.
 | 
			
		||||
       */
 | 
			
		||||
      account: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        account: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * An array of ChangeInfo objects to render.
 | 
			
		||||
       * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-info
 | 
			
		||||
       */
 | 
			
		||||
      changes: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        observer: '_changesChanged',
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        changes: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          observer: '_changesChanged',
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * ChangeInfo objects grouped into arrays. The sections and changes
 | 
			
		||||
       * properties should not be used together.
 | 
			
		||||
       *
 | 
			
		||||
@@ -68,56 +81,46 @@
 | 
			
		||||
       *   results: !Array<!Object>
 | 
			
		||||
       * }>}
 | 
			
		||||
       */
 | 
			
		||||
      sections: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      labelNames: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeLabelNames(sections)',
 | 
			
		||||
      },
 | 
			
		||||
      _dynamicHeaderEndpoints: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
      },
 | 
			
		||||
      selectedIndex: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      showNumber: Boolean, // No default value to prevent flickering.
 | 
			
		||||
      showStar: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      showReviewedState: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      keyEventTarget: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return document.body; },
 | 
			
		||||
      },
 | 
			
		||||
      changeTableColumns: Array,
 | 
			
		||||
      visibleChangeTableColumns: Array,
 | 
			
		||||
      preferences: Object,
 | 
			
		||||
    },
 | 
			
		||||
        sections: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        labelNames: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeLabelNames(sections)',
 | 
			
		||||
        },
 | 
			
		||||
        _dynamicHeaderEndpoints: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
        },
 | 
			
		||||
        selectedIndex: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        showNumber: Boolean, // No default value to prevent flickering.
 | 
			
		||||
        showStar: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        showReviewedState: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        keyEventTarget: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return document.body; },
 | 
			
		||||
        },
 | 
			
		||||
        changeTableColumns: Array,
 | 
			
		||||
        visibleChangeTableColumns: Array,
 | 
			
		||||
        preferences: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.ChangeTableBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      keydown: '_scopedKeydownHandler',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_sectionsChanged(sections.*)',
 | 
			
		||||
      '_computePreferences(account, preferences)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_sectionsChanged(sections.*)',
 | 
			
		||||
        '_computePreferences(account, preferences)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    keyboardShortcuts() {
 | 
			
		||||
      return {
 | 
			
		||||
@@ -130,14 +133,26 @@
 | 
			
		||||
        [this.Shortcut.TOGGLE_CHANGE_STAR]: '_toggleChangeStar',
 | 
			
		||||
        [this.Shortcut.REFRESH_CHANGE_LIST]: '_refreshChangeList',
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('keydown',
 | 
			
		||||
          e => this._scopedKeydownHandler(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this._ensureAttribute('tabindex', 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      Gerrit.awaitPluginsLoaded().then(() => {
 | 
			
		||||
        this._dynamicHeaderEndpoints = Gerrit._endpoints.getDynamicEndpoints(
 | 
			
		||||
            'change-list-header');
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Iron-a11y-keys-behavior catches keyboard events globally. Some keyboard
 | 
			
		||||
@@ -151,11 +166,11 @@
 | 
			
		||||
        // Enter.
 | 
			
		||||
        this._openChange(e);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _lowerCase(column) {
 | 
			
		||||
      return column.toLowerCase();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePreferences(account, preferences) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -175,13 +190,13 @@
 | 
			
		||||
        this.showNumber = false;
 | 
			
		||||
        this.visibleChangeTableColumns = this.columnNames;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeColspan(changeTableColumns, labelNames) {
 | 
			
		||||
      if (!changeTableColumns || !labelNames) return;
 | 
			
		||||
      return changeTableColumns.length + labelNames.length +
 | 
			
		||||
          NUMBER_FIXED_COLUMNS;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelNames(sections) {
 | 
			
		||||
      if (!sections) { return []; }
 | 
			
		||||
@@ -198,7 +213,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return labels.sort();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelShortcut(labelName) {
 | 
			
		||||
      if (labelName.startsWith(LABEL_PREFIX_INVALID_PROLOG)) {
 | 
			
		||||
@@ -210,11 +225,11 @@
 | 
			
		||||
            return a + i[0].toUpperCase();
 | 
			
		||||
          }, '')
 | 
			
		||||
          .slice(0, MAX_SHORTCUT_CHARS);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _changesChanged(changes) {
 | 
			
		||||
      this.sections = changes ? [{results: changes}] : [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _processQuery(query) {
 | 
			
		||||
      let tokens = query.split(' ');
 | 
			
		||||
@@ -225,11 +240,11 @@
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      return tokens.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _sectionHref(query) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForSearchQuery(this._processQuery(query));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maps an index local to a particular section to the absolute index
 | 
			
		||||
@@ -245,19 +260,19 @@
 | 
			
		||||
        idx += this.sections[i].results.length;
 | 
			
		||||
      }
 | 
			
		||||
      return idx + localIndex;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeItemSelected(sectionIndex, index, selectedIndex) {
 | 
			
		||||
      const idx = this._computeItemAbsoluteIndex(sectionIndex, index);
 | 
			
		||||
      return idx == selectedIndex;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeItemNeedsReview(account, change, showReviewedState) {
 | 
			
		||||
      return showReviewedState && !change.reviewed &&
 | 
			
		||||
          !change.work_in_progress &&
 | 
			
		||||
          this.changeIsOpen(change) &&
 | 
			
		||||
          (!account || account._account_id != change.owner._account_id);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeItemHighlight(account, change) {
 | 
			
		||||
      // Do not show the assignee highlight if the change is not open.
 | 
			
		||||
@@ -267,7 +282,7 @@
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      return account._account_id === change.assignee._account_id;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _nextChange(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -275,7 +290,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.cursor.next();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _prevChange(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -283,7 +298,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.cursor.previous();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _openChange(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -291,7 +306,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      Gerrit.Nav.navigateToChange(this._changeForIndex(this.selectedIndex));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _nextPage(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -301,7 +316,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.fire('next-page');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _prevPage(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -311,7 +326,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.fire('previous-page');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _toggleChangeReviewed(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -319,7 +334,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._toggleReviewedForIndex(this.selectedIndex);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _toggleReviewedForIndex(index) {
 | 
			
		||||
      const changeEls = this._getListItems();
 | 
			
		||||
@@ -329,18 +344,18 @@
 | 
			
		||||
 | 
			
		||||
      const changeEl = changeEls[index];
 | 
			
		||||
      changeEl.toggleReviewed();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _refreshChangeList(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._reloadWindow();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reloadWindow() {
 | 
			
		||||
      window.location.reload();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _toggleChangeStar(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -348,7 +363,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._toggleStarForIndex(this.selectedIndex);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _toggleStarForIndex(index) {
 | 
			
		||||
      const changeEls = this._getListItems();
 | 
			
		||||
@@ -358,7 +373,7 @@
 | 
			
		||||
 | 
			
		||||
      const changeEl = changeEls[index];
 | 
			
		||||
      changeEl.$$('gr-change-star').toggleStar();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _changeForIndex(index) {
 | 
			
		||||
      const changeEls = this._getListItems();
 | 
			
		||||
@@ -366,12 +381,12 @@
 | 
			
		||||
        return changeEls[index].change;
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getListItems() {
 | 
			
		||||
      return Array.from(
 | 
			
		||||
          Polymer.dom(this.root).querySelectorAll('gr-change-list-item'));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _sectionsChanged() {
 | 
			
		||||
      // Flush DOM operations so that the list item elements will be loaded.
 | 
			
		||||
@@ -379,14 +394,16 @@
 | 
			
		||||
        this.$.cursor.stops = this._getListItems();
 | 
			
		||||
        this.$.cursor.moveToStart();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isOutgoing(section) {
 | 
			
		||||
      return !!section.isOutgoing;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isEmpty(section) {
 | 
			
		||||
      return !section.results.length;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrChangeList.is, GrChangeList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,8 +17,10 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-create-change-help',
 | 
			
		||||
  class GrCreateChangeHelp extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-create-change-help'; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the "Create change" button is tapped.
 | 
			
		||||
@@ -30,6 +32,8 @@
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('create-tap', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCreateChangeHelp.is, GrCreateChangeHelp);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -23,37 +23,43 @@
 | 
			
		||||
    PUSH_PREFIX: 'git push origin HEAD:refs/for/',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-create-commands-dialog',
 | 
			
		||||
  class GrCreateCommandsDialog extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-create-commands-dialog'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      branch: String,
 | 
			
		||||
      _createNewCommitCommand: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        readonly: true,
 | 
			
		||||
        value: Commands.CREATE,
 | 
			
		||||
      },
 | 
			
		||||
      _amendExistingCommitCommand: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        readonly: true,
 | 
			
		||||
        value: Commands.AMEND,
 | 
			
		||||
      },
 | 
			
		||||
      _pushCommand: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computePushCommand(branch)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        branch: String,
 | 
			
		||||
        _createNewCommitCommand: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          readonly: true,
 | 
			
		||||
          value: Commands.CREATE,
 | 
			
		||||
        },
 | 
			
		||||
        _amendExistingCommitCommand: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          readonly: true,
 | 
			
		||||
          value: Commands.AMEND,
 | 
			
		||||
        },
 | 
			
		||||
        _pushCommand: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computePushCommand(branch)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open() {
 | 
			
		||||
      this.$.commandsOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleClose() {
 | 
			
		||||
      this.$.commandsOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePushCommand(branch) {
 | 
			
		||||
      return Commands.PUSH_PREFIX + branch;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCreateCommandsDialog.is, GrCreateCommandsDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -23,37 +23,44 @@
 | 
			
		||||
   *
 | 
			
		||||
   * @event confirm
 | 
			
		||||
   */
 | 
			
		||||
  class GrCreateDestinationDialog extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-create-destination-dialog'; }
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-create-destination-dialog',
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        _repo: String,
 | 
			
		||||
        _branch: String,
 | 
			
		||||
        _repoAndBranchSelected: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          computed: '_computeRepoAndBranchSelected(_repo, _branch)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      _repo: String,
 | 
			
		||||
      _branch: String,
 | 
			
		||||
      _repoAndBranchSelected: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        computed: '_computeRepoAndBranchSelected(_repo, _branch)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    open() {
 | 
			
		||||
      this._repo = '';
 | 
			
		||||
      this._branch = '';
 | 
			
		||||
      this.$.createOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleClose() {
 | 
			
		||||
      this.$.createOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _pickerConfirm() {
 | 
			
		||||
      this.$.createOverlay.close();
 | 
			
		||||
      const detail = {repo: this._repo, branch: this._branch};
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('confirm', {detail, bubbles: false}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRepoAndBranchSelected(repo, branch) {
 | 
			
		||||
      return !!(repo && branch);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCreateDestinationDialog.is,
 | 
			
		||||
      GrCreateDestinationDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,65 +19,72 @@
 | 
			
		||||
 | 
			
		||||
  const PROJECT_PLACEHOLDER_PATTERN = /\$\{project\}/g;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-dashboard-view',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDashboardView extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-dashboard-view'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the title of the page should change.
 | 
			
		||||
     *
 | 
			
		||||
     * @event title-change
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      account: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
      preferences: Object,
 | 
			
		||||
      /** @type {{ selectedChangeIndex: number }} */
 | 
			
		||||
      viewState: Object,
 | 
			
		||||
 | 
			
		||||
      /** @type {{ project: string, user: string }} */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      createChangeTap: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._createChangeTap.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        account: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
        preferences: Object,
 | 
			
		||||
        /** @type {{ selectedChangeIndex: number }} */
 | 
			
		||||
        viewState: Object,
 | 
			
		||||
 | 
			
		||||
      _results: Array,
 | 
			
		||||
        /** @type {{ project: string, user: string }} */
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        createChangeTap: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._createChangeTap.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        _results: Array,
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
       * For showing a "loading..." string during ajax requests.
 | 
			
		||||
       */
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _showDraftsBanner: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
        _showDraftsBanner: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _showNewUserHelp: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _showNewUserHelp: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_paramsChanged(params.*)',
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_paramsChanged(params.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get options() {
 | 
			
		||||
      return this.listChangesOptionsToHex(
 | 
			
		||||
@@ -85,11 +92,12 @@
 | 
			
		||||
          this.ListChangesOption.DETAILED_ACCOUNTS,
 | 
			
		||||
          this.ListChangesOption.REVIEWED
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._loadPreferences();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadPreferences() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn().then(loggedIn => {
 | 
			
		||||
@@ -101,7 +109,7 @@
 | 
			
		||||
          this.preferences = {};
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getProjectDashboard(project, dashboard) {
 | 
			
		||||
      const errFn = response => {
 | 
			
		||||
@@ -124,18 +132,18 @@
 | 
			
		||||
          }),
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeTitle(user) {
 | 
			
		||||
      if (!user || user === 'self') {
 | 
			
		||||
        return 'My Reviews';
 | 
			
		||||
      }
 | 
			
		||||
      return 'Dashboard for ' + user;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isViewActive(params) {
 | 
			
		||||
      return params.view === Gerrit.Nav.View.DASHBOARD;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(paramsChangeRecord) {
 | 
			
		||||
      const params = paramsChangeRecord.base;
 | 
			
		||||
@@ -145,7 +153,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return this._reload();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reloads the element.
 | 
			
		||||
@@ -179,7 +187,7 @@
 | 
			
		||||
            });
 | 
			
		||||
            console.warn(err);
 | 
			
		||||
          }).then(() => { this._loading = false; });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetches the changes for each dashboard section and sets this._results
 | 
			
		||||
@@ -218,7 +226,7 @@
 | 
			
		||||
              !res.sections[i].hideIfEmpty ||
 | 
			
		||||
                section.results.length));
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSectionCountLabel(changes) {
 | 
			
		||||
      if (!changes || !changes.length || changes.length == 0) {
 | 
			
		||||
@@ -228,7 +236,7 @@
 | 
			
		||||
      const numChanges = changes.length;
 | 
			
		||||
      const andMore = more ? ' and more' : '';
 | 
			
		||||
      return `(${numChanges}${andMore})`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeUserHeaderClass(params) {
 | 
			
		||||
      if (!params || !!params.project || !params.user
 | 
			
		||||
@@ -236,17 +244,17 @@
 | 
			
		||||
        return 'hide';
 | 
			
		||||
      }
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleToggleStar(e) {
 | 
			
		||||
      this.$.restAPI.saveChangeStarred(e.detail.change._number,
 | 
			
		||||
          e.detail.starred);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleToggleReviewed(e) {
 | 
			
		||||
      this.$.restAPI.saveChangeReviewed(e.detail.change._number,
 | 
			
		||||
          e.detail.reviewed);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Banner is shown if a user is on their own dashboard and they have draft
 | 
			
		||||
@@ -265,15 +273,15 @@
 | 
			
		||||
      if (!closedChanges.length) { return; }
 | 
			
		||||
 | 
			
		||||
      this._showDraftsBanner = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeBannerClass(show) {
 | 
			
		||||
      return show ? '' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleOpenDeleteDialog() {
 | 
			
		||||
      this.$.confirmDeleteOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmDelete() {
 | 
			
		||||
      this.$.confirmDeleteDialog.disabled = true;
 | 
			
		||||
@@ -281,23 +289,25 @@
 | 
			
		||||
        this._closeConfirmDeleteOverlay();
 | 
			
		||||
        this._reload();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _closeConfirmDeleteOverlay() {
 | 
			
		||||
      this.$.confirmDeleteOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDraftsLink() {
 | 
			
		||||
      return Gerrit.Nav.getUrlForSearchQuery('has:draft -is:open');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createChangeTap(e) {
 | 
			
		||||
      this.$.destinationDialog.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDestinationConfirm(e) {
 | 
			
		||||
      this.$.commandsDialog.branch = e.detail.branch;
 | 
			
		||||
      this.$.commandsDialog.open();
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDashboardView.is, GrDashboardView);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,14 +17,20 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-embed-dashboard',
 | 
			
		||||
  class GrEmbedDashboard extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-embed-dashboard'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      account: Object,
 | 
			
		||||
      sections: Array,
 | 
			
		||||
      preferences: Object,
 | 
			
		||||
      showNewUserHelp: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        account: Object,
 | 
			
		||||
        sections: Array,
 | 
			
		||||
        preferences: Object,
 | 
			
		||||
        showNewUserHelp: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrEmbedDashboard.is, GrEmbedDashboard);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,18 +17,22 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-repo-header',
 | 
			
		||||
  class GrRepoHeader extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-repo-header'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?String} */
 | 
			
		||||
      repo: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_repoChanged',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {String|null} */
 | 
			
		||||
      _repoUrl: String,
 | 
			
		||||
    },
 | 
			
		||||
        repo: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_repoChanged',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {String|null} */
 | 
			
		||||
        _repoUrl: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _repoChanged(repoName) {
 | 
			
		||||
      if (!repoName) {
 | 
			
		||||
@@ -36,6 +40,8 @@
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      this._repoUrl = Gerrit.Nav.getUrlForRepo(repoName);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRepoHeader.is, GrRepoHeader);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,40 +17,44 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-user-header',
 | 
			
		||||
  class GrUserHeader extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-user-header'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?String} */
 | 
			
		||||
      userId: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_accountChanged',
 | 
			
		||||
      },
 | 
			
		||||
        userId: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_accountChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      showDashboardLink: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
        showDashboardLink: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
        loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {?{name: ?, email: ?, registered_on: ?}}
 | 
			
		||||
       */
 | 
			
		||||
      _accountDetails: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        _accountDetails: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?String} */
 | 
			
		||||
      _status: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        /** @type {?String} */
 | 
			
		||||
        _status: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _accountChanged(userId) {
 | 
			
		||||
      if (!userId) {
 | 
			
		||||
@@ -65,19 +69,19 @@
 | 
			
		||||
      this.$.restAPI.getAccountStatus(userId).then(status => {
 | 
			
		||||
        this._status = status;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDisplayClass(status) {
 | 
			
		||||
      return status ? ' ' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDetail(accountDetails, name) {
 | 
			
		||||
      return accountDetails ? accountDetails[name] : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeStatusClass(accountDetails) {
 | 
			
		||||
      return this._computeDetail(accountDetails, 'status') ? '' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDashboardUrl(accountDetails) {
 | 
			
		||||
      if (!accountDetails) { return null; }
 | 
			
		||||
@@ -85,11 +89,13 @@
 | 
			
		||||
      const email = accountDetails.email;
 | 
			
		||||
      if (!id && !email ) { return null; }
 | 
			
		||||
      return Gerrit.Nav.getUrlForUserDashboard(id ? id : email);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDashboardLinkClass(showDashboardLink, loggedIn) {
 | 
			
		||||
      return showDashboardLink && loggedIn ?
 | 
			
		||||
        'dashboardLink' : 'dashboardLink hide';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrUserHeader.is, GrUserHeader);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -48,105 +48,111 @@
 | 
			
		||||
    TRUSTED: 'TRUSTED',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-change-metadata',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrChangeMetadata extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-change-metadata'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the change topic is changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @event topic-changed
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      labels: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      account: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      revision: Object,
 | 
			
		||||
      commitInfo: Object,
 | 
			
		||||
      _mutable: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeIsMutable(account)',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      serverConfig: Object,
 | 
			
		||||
      parentIsCurrent: Boolean,
 | 
			
		||||
      _notCurrentMessage: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: NOT_CURRENT_MESSAGE,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
      },
 | 
			
		||||
      _topicReadOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeTopicReadOnly(_mutable, change)',
 | 
			
		||||
      },
 | 
			
		||||
      _hashtagReadOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeHashtagReadOnly(_mutable, change)',
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        change: Object,
 | 
			
		||||
        labels: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        account: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        revision: Object,
 | 
			
		||||
        commitInfo: Object,
 | 
			
		||||
        _mutable: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeIsMutable(account)',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        serverConfig: Object,
 | 
			
		||||
        parentIsCurrent: Boolean,
 | 
			
		||||
        _notCurrentMessage: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: NOT_CURRENT_MESSAGE,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
        },
 | 
			
		||||
        _topicReadOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeTopicReadOnly(_mutable, change)',
 | 
			
		||||
        },
 | 
			
		||||
        _hashtagReadOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeHashtagReadOnly(_mutable, change)',
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {Gerrit.PushCertificateValidation}
 | 
			
		||||
       */
 | 
			
		||||
      _pushCertificateValidation: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computePushCertificateValidation(serverConfig, change)',
 | 
			
		||||
      },
 | 
			
		||||
      _showRequirements: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeShowRequirements(change)',
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      _assignee: Array,
 | 
			
		||||
      _isWip: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeIsWip(change)',
 | 
			
		||||
      },
 | 
			
		||||
      _newHashtag: String,
 | 
			
		||||
 | 
			
		||||
      _settingTopic: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      _currentParents: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeParents(change)',
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _CHANGE_ROLE: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
        value: {
 | 
			
		||||
          OWNER: 'owner',
 | 
			
		||||
          UPLOADER: 'uploader',
 | 
			
		||||
          AUTHOR: 'author',
 | 
			
		||||
          COMMITTER: 'committer',
 | 
			
		||||
        _pushCertificateValidation: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computePushCertificateValidation(serverConfig, change)',
 | 
			
		||||
        },
 | 
			
		||||
        _showRequirements: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeShowRequirements(change)',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _assignee: Array,
 | 
			
		||||
        _isWip: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeIsWip(change)',
 | 
			
		||||
        },
 | 
			
		||||
        _newHashtag: String,
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_changeChanged(change)',
 | 
			
		||||
      '_labelsChanged(change.labels)',
 | 
			
		||||
      '_assigneeChanged(_assignee.*)',
 | 
			
		||||
    ],
 | 
			
		||||
        _settingTopic: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        _currentParents: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeParents(change)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _CHANGE_ROLE: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
          value: {
 | 
			
		||||
            OWNER: 'owner',
 | 
			
		||||
            UPLOADER: 'uploader',
 | 
			
		||||
            AUTHOR: 'author',
 | 
			
		||||
            COMMITTER: 'committer',
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_changeChanged(change)',
 | 
			
		||||
        '_labelsChanged(change.labels)',
 | 
			
		||||
        '_assigneeChanged(_assignee.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _labelsChanged(labels) {
 | 
			
		||||
      this.labels = Object.assign({}, labels) || null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _changeChanged(change) {
 | 
			
		||||
      this._assignee = change.assignee ? [change.assignee] : [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _assigneeChanged(assigneeRecord) {
 | 
			
		||||
      if (!this.change) { return; }
 | 
			
		||||
@@ -162,11 +168,11 @@
 | 
			
		||||
        this.set(['change', 'assignee'], undefined);
 | 
			
		||||
        this.$.restAPI.deleteAssignee(this.change._number);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideStrategy(change) {
 | 
			
		||||
      return !this.changeIsOpen(change);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} commitInfo
 | 
			
		||||
@@ -184,15 +190,15 @@
 | 
			
		||||
            config: serverConfig,
 | 
			
		||||
          });
 | 
			
		||||
      return weblinks.length ? weblinks : null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeStrategy(change) {
 | 
			
		||||
      return SubmitTypeLabel[change.submit_type];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelNames(labels) {
 | 
			
		||||
      return Object.keys(labels).sort();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTopicChanged(e, topic) {
 | 
			
		||||
      const lastTopic = this.change.topic;
 | 
			
		||||
@@ -207,19 +213,19 @@
 | 
			
		||||
                  'topic-changed', {bubbles: true, composed: true}));
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showAddTopic(changeRecord, settingTopic) {
 | 
			
		||||
      const hasTopic = !!changeRecord &&
 | 
			
		||||
          !!changeRecord.base && !!changeRecord.base.topic;
 | 
			
		||||
      return !hasTopic && !settingTopic;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showTopicChip(changeRecord, settingTopic) {
 | 
			
		||||
      const hasTopic = !!changeRecord &&
 | 
			
		||||
          !!changeRecord.base && !!changeRecord.base.topic;
 | 
			
		||||
      return hasTopic && !settingTopic;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleHashtagChanged(e) {
 | 
			
		||||
      const lastHashtag = this.change.hashtag;
 | 
			
		||||
@@ -234,7 +240,7 @@
 | 
			
		||||
              'hashtag-changed', {bubbles: true, composed: true}));
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeTopicReadOnly(mutable, change) {
 | 
			
		||||
      return !mutable ||
 | 
			
		||||
@@ -242,7 +248,7 @@
 | 
			
		||||
          !change.actions ||
 | 
			
		||||
          !change.actions.topic ||
 | 
			
		||||
          !change.actions.topic.enabled;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHashtagReadOnly(mutable, change) {
 | 
			
		||||
      return !mutable ||
 | 
			
		||||
@@ -250,7 +256,7 @@
 | 
			
		||||
          !change.actions ||
 | 
			
		||||
          !change.actions.hashtags ||
 | 
			
		||||
          !change.actions.hashtags.enabled;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeAssigneeReadOnly(mutable, change) {
 | 
			
		||||
      return !mutable ||
 | 
			
		||||
@@ -258,17 +264,17 @@
 | 
			
		||||
          !change.actions ||
 | 
			
		||||
          !change.actions.assignee ||
 | 
			
		||||
          !change.actions.assignee.enabled;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeTopicPlaceholder(_topicReadOnly) {
 | 
			
		||||
      // Action items in Material Design are uppercase -- placeholder label text
 | 
			
		||||
      // is sentence case.
 | 
			
		||||
      return _topicReadOnly ? 'No topic' : 'ADD TOPIC';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHashtagPlaceholder(_hashtagReadOnly) {
 | 
			
		||||
      return _hashtagReadOnly ? '' : HASHTAG_ADD_MESSAGE;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowRequirements(change) {
 | 
			
		||||
      if (change.status !== this.ChangeStatus.NEW) {
 | 
			
		||||
@@ -281,7 +287,7 @@
 | 
			
		||||
      const hasLabels = !!change.labels &&
 | 
			
		||||
          Object.keys(change.labels).length > 0;
 | 
			
		||||
      return hasRequirements || hasLabels || !!change.work_in_progress;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return {?Gerrit.PushCertificateValidation} object representing data for
 | 
			
		||||
@@ -326,7 +332,7 @@
 | 
			
		||||
        default:
 | 
			
		||||
          throw new Error(`unknown certificate status: ${key.status}`);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _problems(msg, key) {
 | 
			
		||||
      if (!key || !key.problems || key.problems.length === 0) {
 | 
			
		||||
@@ -334,26 +340,26 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return [msg + ':'].concat(key.problems).join('\n');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeProjectURL(project) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForProjectChanges(project);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeBranchURL(project, branch) {
 | 
			
		||||
      if (!this.change || !this.change.status) return '';
 | 
			
		||||
      return Gerrit.Nav.getUrlForBranch(branch, project,
 | 
			
		||||
          this.change.status == this.ChangeStatus.NEW ? 'open' :
 | 
			
		||||
            this.change.status.toLowerCase());
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeTopicURL(topic) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForTopic(topic);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHashtagURL(hashtag) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForHashtag(hashtag);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTopicRemoved(e) {
 | 
			
		||||
      const target = Polymer.dom(e).rootTarget;
 | 
			
		||||
@@ -367,7 +373,7 @@
 | 
			
		||||
        target.disabled = false;
 | 
			
		||||
        return;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleHashtagRemoved(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
@@ -382,15 +388,15 @@
 | 
			
		||||
            target.disabled = false;
 | 
			
		||||
            return;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsWip(change) {
 | 
			
		||||
      return !!change.work_in_progress;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowRoleClass(change, role) {
 | 
			
		||||
      return this._getNonOwnerRole(change, role) ? '' : 'hideDisplay';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the user with the specified role on the change. Returns null if the
 | 
			
		||||
@@ -427,7 +433,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeParents(change) {
 | 
			
		||||
      if (!change || !change.current_revision ||
 | 
			
		||||
@@ -436,11 +442,11 @@
 | 
			
		||||
        return undefined;
 | 
			
		||||
      }
 | 
			
		||||
      return change.revisions[change.current_revision].commit.parents;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeParentsLabel(parents) {
 | 
			
		||||
      return parents && parents.length > 1 ? 'Parents' : 'Parent';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeParentListClass(parents, parentIsCurrent) {
 | 
			
		||||
      // Undefined check for polymer 2
 | 
			
		||||
@@ -453,24 +459,26 @@
 | 
			
		||||
        parents && parents.length > 1 ? 'merge' : 'nonMerge',
 | 
			
		||||
        parentIsCurrent ? 'current' : 'notCurrent',
 | 
			
		||||
      ].join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsMutable(account) {
 | 
			
		||||
      return !!Object.keys(account).length;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    editTopic() {
 | 
			
		||||
      if (this._topicReadOnly || this.change.topic) { return; }
 | 
			
		||||
      // Cannot use `this.$.ID` syntax because the element exists inside of a
 | 
			
		||||
      // dom-if.
 | 
			
		||||
      this.$$('.topicEditableLabel').open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getReviewerSuggestionsProvider(change) {
 | 
			
		||||
      const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
 | 
			
		||||
          change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.ANY);
 | 
			
		||||
      provider.init();
 | 
			
		||||
      return provider;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrChangeMetadata.is, GrChangeMetadata);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,47 +17,54 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-change-requirements',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrChangeRequirements extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-change-requirements'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      account: Object,
 | 
			
		||||
      mutable: Boolean,
 | 
			
		||||
      _requirements: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeRequirements(change)',
 | 
			
		||||
      },
 | 
			
		||||
      _requiredLabels: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: () => [],
 | 
			
		||||
      },
 | 
			
		||||
      _optionalLabels: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: () => [],
 | 
			
		||||
      },
 | 
			
		||||
      _showWip: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeShowWip(change)',
 | 
			
		||||
      },
 | 
			
		||||
      _showOptionalLabels: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        change: Object,
 | 
			
		||||
        account: Object,
 | 
			
		||||
        mutable: Boolean,
 | 
			
		||||
        _requirements: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeRequirements(change)',
 | 
			
		||||
        },
 | 
			
		||||
        _requiredLabels: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: () => [],
 | 
			
		||||
        },
 | 
			
		||||
        _optionalLabels: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: () => [],
 | 
			
		||||
        },
 | 
			
		||||
        _showWip: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeShowWip(change)',
 | 
			
		||||
        },
 | 
			
		||||
        _showOptionalLabels: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_computeLabels(change.labels.*)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_computeLabels(change.labels.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowWip(change) {
 | 
			
		||||
      return change.work_in_progress;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRequirements(change) {
 | 
			
		||||
      const _requirements = [];
 | 
			
		||||
@@ -78,15 +85,15 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return _requirements;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRequirementClass(requirementStatus) {
 | 
			
		||||
      return requirementStatus ? 'approved' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRequirementIcon(requirementStatus) {
 | 
			
		||||
      return requirementStatus ? 'gr-icons:check' : 'gr-icons:hourglass';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabels(labelsRecord) {
 | 
			
		||||
      const labels = labelsRecord.base;
 | 
			
		||||
@@ -103,7 +110,7 @@
 | 
			
		||||
 | 
			
		||||
        this.push(path, {label, icon, style, labelInfo});
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} labelInfo
 | 
			
		||||
@@ -114,7 +121,7 @@
 | 
			
		||||
      if (labelInfo.approved) { return 'gr-icons:check'; }
 | 
			
		||||
      if (labelInfo.rejected) { return 'gr-icons:close'; }
 | 
			
		||||
      return 'gr-icons:hourglass';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} labelInfo
 | 
			
		||||
@@ -123,28 +130,30 @@
 | 
			
		||||
      if (labelInfo.approved) { return 'approved'; }
 | 
			
		||||
      if (labelInfo.rejected) { return 'rejected'; }
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowOptional(optionalFieldsRecord) {
 | 
			
		||||
      return optionalFieldsRecord.base.length ? '' : 'hidden';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelValue(value) {
 | 
			
		||||
      return (value > 0 ? '+' : '') + value;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowHideIcon(showOptionalLabels) {
 | 
			
		||||
      return showOptionalLabels ?
 | 
			
		||||
        'gr-icons:expand-less' :
 | 
			
		||||
        'gr-icons:expand-more';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSectionClass(show) {
 | 
			
		||||
      return show ? '' : 'hidden';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleShowHide(e) {
 | 
			
		||||
      this._showOptionalLabels = !this._showOptionalLabels;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrChangeRequirements.is, GrChangeRequirements);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -16,32 +16,40 @@
 | 
			
		||||
 */
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-comment-list',
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.PathListBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PathListMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrCommentList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.PathListBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-comment-list'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      changeNum: Number,
 | 
			
		||||
      comments: Object,
 | 
			
		||||
      patchNum: Number,
 | 
			
		||||
      projectName: String,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      projectConfig: Object,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        changeNum: Number,
 | 
			
		||||
        comments: Object,
 | 
			
		||||
        patchNum: Number,
 | 
			
		||||
        projectName: String,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        projectConfig: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeFilesFromComments(comments) {
 | 
			
		||||
      const arr = Object.keys(comments || {});
 | 
			
		||||
      return arr.sort(this.specialFilePathCompare);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isOnParent(comment) {
 | 
			
		||||
      return comment.side === 'PARENT';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDiffLineURL(file, changeNum, patchNum, comment) {
 | 
			
		||||
      const basePatchNum = comment.hasOwnProperty('parent') ?
 | 
			
		||||
@@ -49,13 +57,13 @@
 | 
			
		||||
      return Gerrit.Nav.getUrlForDiffById(this.changeNum, this.projectName,
 | 
			
		||||
          file, patchNum, basePatchNum, comment.line,
 | 
			
		||||
          this._isOnParent(comment));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeCommentsForFile(comments, file) {
 | 
			
		||||
      // Changes are not picked up by the dom-repeat due to the array instance
 | 
			
		||||
      // identity not changing even when it has elements added/removed from it.
 | 
			
		||||
      return (comments[file] || []).slice();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePatchDisplayName(comment) {
 | 
			
		||||
      if (this._isOnParent(comment)) {
 | 
			
		||||
@@ -65,6 +73,8 @@
 | 
			
		||||
        return `PS${comment.patch_set}, `;
 | 
			
		||||
      }
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCommentList.is, GrCommentList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,23 +17,27 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-commit-info',
 | 
			
		||||
  class GrCommitInfo extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-commit-info'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      change: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      commitInfo: Object,
 | 
			
		||||
      serverConfig: Object,
 | 
			
		||||
      _showWebLink: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeShowWebLink(change, commitInfo, serverConfig)',
 | 
			
		||||
      },
 | 
			
		||||
      _webLink: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeWebLink(change, commitInfo, serverConfig)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        change: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        commitInfo: Object,
 | 
			
		||||
        serverConfig: Object,
 | 
			
		||||
        _showWebLink: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeShowWebLink(change, commitInfo, serverConfig)',
 | 
			
		||||
        },
 | 
			
		||||
        _webLink: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeWebLink(change, commitInfo, serverConfig)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getWeblink(change, commitInfo, config) {
 | 
			
		||||
      return Gerrit.Nav.getPatchSetWeblink(
 | 
			
		||||
@@ -43,7 +47,7 @@
 | 
			
		||||
            weblinks: commitInfo.web_links,
 | 
			
		||||
            config,
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowWebLink(change, commitInfo, serverConfig) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -53,7 +57,7 @@
 | 
			
		||||
 | 
			
		||||
      const weblink = this._getWeblink(change, commitInfo, serverConfig);
 | 
			
		||||
      return !!weblink && !!weblink.url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeWebLink(change, commitInfo, serverConfig) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -63,12 +67,14 @@
 | 
			
		||||
 | 
			
		||||
      const {url} = this._getWeblink(change, commitInfo, serverConfig) || {};
 | 
			
		||||
      return url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShortHash(commitInfo) {
 | 
			
		||||
      const {name} =
 | 
			
		||||
            this._getWeblink(this.change, commitInfo, this.serverConfig) || {};
 | 
			
		||||
      return name;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCommitInfo.is, GrCommitInfo);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,17 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-abandon-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrConfirmAbandonDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-confirm-abandon-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -32,41 +40,42 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      message: String,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        message: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    keyBindings: {
 | 
			
		||||
      'ctrl+enter meta+enter': '_handleEnterKey',
 | 
			
		||||
    },
 | 
			
		||||
    get keyBindings() {
 | 
			
		||||
      return {
 | 
			
		||||
        'ctrl+enter meta+enter': '_handleEnterKey',
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resetFocus() {
 | 
			
		||||
      this.$.messageInput.textarea.focus();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEnterKey(e) {
 | 
			
		||||
      this._confirm();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this._confirm();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _confirm() {
 | 
			
		||||
      this.fire('confirm', {reason: this.message}, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmAbandonDialog.is, GrConfirmAbandonDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,15 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-cherrypick-conflict-dialog',
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrConfirmCherrypickConflictDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-confirm-cherrypick-conflict-dialog'; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
@@ -40,12 +43,15 @@
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('confirm', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmCherrypickConflictDialog.is,
 | 
			
		||||
      GrConfirmCherrypickConflictDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,15 @@
 | 
			
		||||
 | 
			
		||||
  const SUGGESTIONS_LIMIT = 15;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-cherrypick-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrConfirmCherrypickDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-confirm-cherrypick-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -34,29 +40,29 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      branch: String,
 | 
			
		||||
      baseCommit: String,
 | 
			
		||||
      changeStatus: String,
 | 
			
		||||
      commitMessage: String,
 | 
			
		||||
      commitNum: String,
 | 
			
		||||
      message: String,
 | 
			
		||||
      project: String,
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getProjectBranchesSuggestions.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        branch: String,
 | 
			
		||||
        baseCommit: String,
 | 
			
		||||
        changeStatus: String,
 | 
			
		||||
        commitMessage: String,
 | 
			
		||||
        commitNum: String,
 | 
			
		||||
        message: String,
 | 
			
		||||
        project: String,
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getProjectBranchesSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_computeMessage(changeStatus, commitNum, commitMessage)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_computeMessage(changeStatus, commitNum, commitMessage)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeMessage(changeStatus, commitNum, commitMessage) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -74,23 +80,23 @@
 | 
			
		||||
        newMessage += '(cherry picked from commit ' + commitNum + ')';
 | 
			
		||||
      }
 | 
			
		||||
      this.message = newMessage;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('confirm', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resetFocus() {
 | 
			
		||||
      this.$.branchInput.focus();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getProjectBranchesSuggestions(input) {
 | 
			
		||||
      if (input.startsWith('refs/heads/')) {
 | 
			
		||||
@@ -113,6 +119,9 @@
 | 
			
		||||
        }
 | 
			
		||||
        return branches;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmCherrypickDialog.is,
 | 
			
		||||
      GrConfirmCherrypickDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,15 @@
 | 
			
		||||
 | 
			
		||||
  const SUGGESTIONS_LIMIT = 15;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-move-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrConfirmMoveDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-confirm-move-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -34,33 +40,31 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      branch: String,
 | 
			
		||||
      message: String,
 | 
			
		||||
      project: String,
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getProjectBranchesSuggestions.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        branch: String,
 | 
			
		||||
        message: String,
 | 
			
		||||
        project: String,
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getProjectBranchesSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('confirm', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getProjectBranchesSuggestions(input) {
 | 
			
		||||
      if (input.startsWith('refs/heads/')) {
 | 
			
		||||
@@ -83,6 +87,8 @@
 | 
			
		||||
        }
 | 
			
		||||
        return branches;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmMoveDialog.is, GrConfirmMoveDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,10 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-rebase-dialog',
 | 
			
		||||
 | 
			
		||||
  class GrConfirmRebaseDialog extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-confirm-rebase-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -32,24 +33,28 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      branch: String,
 | 
			
		||||
      changeNumber: Number,
 | 
			
		||||
      hasParent: Boolean,
 | 
			
		||||
      rebaseOnCurrent: Boolean,
 | 
			
		||||
      _text: String,
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getChangeSuggestions.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        branch: String,
 | 
			
		||||
        changeNumber: Number,
 | 
			
		||||
        hasParent: Boolean,
 | 
			
		||||
        rebaseOnCurrent: Boolean,
 | 
			
		||||
        _text: String,
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getChangeSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _recentChanges: Array,
 | 
			
		||||
    },
 | 
			
		||||
        _recentChanges: Array,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_updateSelectedOption(rebaseOnCurrent, hasParent)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_updateSelectedOption(rebaseOnCurrent, hasParent)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This is called by gr-change-actions every time the rebase dialog is
 | 
			
		||||
    // re-opened. Unlike other autocompletes that make a request with each
 | 
			
		||||
@@ -71,36 +76,36 @@
 | 
			
		||||
            this._recentChanges = changes;
 | 
			
		||||
            return this._recentChanges;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRecentChanges() {
 | 
			
		||||
      if (this._recentChanges) {
 | 
			
		||||
        return Promise.resolve(this._recentChanges);
 | 
			
		||||
      }
 | 
			
		||||
      return this.fetchRecentChanges();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChangeSuggestions(input) {
 | 
			
		||||
      return this._getRecentChanges().then(changes =>
 | 
			
		||||
        this._filterChanges(input, changes));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _filterChanges(input, changes) {
 | 
			
		||||
      return changes.filter(change => change.name.includes(input) &&
 | 
			
		||||
          change.value !== this.changeNumber);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _displayParentOption(rebaseOnCurrent, hasParent) {
 | 
			
		||||
      return hasParent && rebaseOnCurrent;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _displayParentUpToDateMsg(rebaseOnCurrent, hasParent) {
 | 
			
		||||
      return hasParent && !rebaseOnCurrent;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _displayTipOption(rebaseOnCurrent, hasParent) {
 | 
			
		||||
      return !(!rebaseOnCurrent && !hasParent);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * There is a subtle but important difference between setting the base to an
 | 
			
		||||
@@ -115,7 +120,7 @@
 | 
			
		||||
      // Change numbers will have their description appended by the
 | 
			
		||||
      // autocomplete.
 | 
			
		||||
      return this._text.split(':')[0];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
@@ -123,22 +128,22 @@
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('confirm',
 | 
			
		||||
          {detail: {base: this._getSelectedBase()}}));
 | 
			
		||||
      this._text = '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('cancel'));
 | 
			
		||||
      this._text = '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRebaseOnOther() {
 | 
			
		||||
      this.$.parentInput.focus();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEnterChangeNumberClick() {
 | 
			
		||||
      this.$.rebaseOnOtherInput.checked = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the default radio button based on the state of the app and
 | 
			
		||||
@@ -157,6 +162,8 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$.rebaseOnOtherInput.checked = true;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmRebaseDialog.is, GrConfirmRebaseDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,15 @@
 | 
			
		||||
  const ERR_COMMIT_NOT_FOUND =
 | 
			
		||||
      'Unable to find the commit hash of this change.';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-revert-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrConfirmRevertDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-confirm-revert-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -35,13 +41,11 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      message: String,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        message: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    populateRevertMessage(message, commitHash) {
 | 
			
		||||
      // Figure out what the revert title should be.
 | 
			
		||||
@@ -55,18 +59,20 @@
 | 
			
		||||
 | 
			
		||||
      this.message = `${revertTitle}\n\n${revertCommitText}\n\n` +
 | 
			
		||||
          `Reason for revert: <INSERT REASONING HERE>\n`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('confirm', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmRevertDialog.is, GrConfirmRevertDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,15 @@
 | 
			
		||||
  const ERR_COMMIT_NOT_FOUND =
 | 
			
		||||
      'Unable to find the commit hash of this change.';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-revert-submission-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrConfirmRevertSubmissionDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-confirm-revert-submission-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -35,13 +41,11 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      message: String,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        message: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    populateRevertSubmissionMessage(message, commitHash) {
 | 
			
		||||
      // Follow the same convention of the revert
 | 
			
		||||
@@ -52,18 +56,21 @@
 | 
			
		||||
      }
 | 
			
		||||
      this.message = `${revertTitle}\n\n` +
 | 
			
		||||
          `Reason for revert: <INSERT REASONING HERE>\n`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('confirm', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmRevertSubmissionDialog.is,
 | 
			
		||||
      GrConfirmRevertSubmissionDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,10 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-confirm-submit-dialog',
 | 
			
		||||
 | 
			
		||||
  class GrConfirmSubmitDialog extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-confirm-submit-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the confirm button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
@@ -32,37 +33,41 @@
 | 
			
		||||
     * @event cancel
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * @type {{
 | 
			
		||||
       *    is_private: boolean,
 | 
			
		||||
       *    subject: string,
 | 
			
		||||
       *  }}
 | 
			
		||||
       */
 | 
			
		||||
      change: Object,
 | 
			
		||||
        change: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {{
 | 
			
		||||
       *    label: string,
 | 
			
		||||
       *  }}
 | 
			
		||||
       */
 | 
			
		||||
      action: Object,
 | 
			
		||||
    },
 | 
			
		||||
        action: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resetFocus(e) {
 | 
			
		||||
      this.$.dialog.resetFocus();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirmTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('confirm', {bubbles: false}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('cancel', {bubbles: false}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrConfirmSubmitDialog.is, GrConfirmSubmitDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,40 +17,47 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-download-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDownloadDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-download-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the user presses the close button.
 | 
			
		||||
     *
 | 
			
		||||
     * @event close
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {{ revisions: Array }} */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      patchNum: String,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      config: Object,
 | 
			
		||||
        change: Object,
 | 
			
		||||
        patchNum: String,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        config: Object,
 | 
			
		||||
 | 
			
		||||
      _schemes: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
        computed: '_computeSchemes(change, patchNum)',
 | 
			
		||||
        observer: '_schemesChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _selectedScheme: String,
 | 
			
		||||
    },
 | 
			
		||||
        _schemes: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
          computed: '_computeSchemes(change, patchNum)',
 | 
			
		||||
          observer: '_schemesChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _selectedScheme: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hostAttributes: {
 | 
			
		||||
      role: 'dialog',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this._ensureAttribute('role', 'dialog');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    focus() {
 | 
			
		||||
      if (this._schemes.length) {
 | 
			
		||||
@@ -58,7 +65,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$.download.focus();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getFocusStops() {
 | 
			
		||||
      const links = this.$$('#archives').querySelectorAll('a');
 | 
			
		||||
@@ -66,7 +73,7 @@
 | 
			
		||||
        start: this.$.closeButton,
 | 
			
		||||
        end: links[links.length - 1],
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDownloadCommands(change, patchNum, _selectedScheme) {
 | 
			
		||||
      let commandObj;
 | 
			
		||||
@@ -87,7 +94,7 @@
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      return commands;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} change
 | 
			
		||||
@@ -97,7 +104,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _computeZipDownloadLink(change, patchNum) {
 | 
			
		||||
      return this._computeDownloadLink(change, patchNum, true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} change
 | 
			
		||||
@@ -107,7 +114,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _computeZipDownloadFilename(change, patchNum) {
 | 
			
		||||
      return this._computeDownloadFilename(change, patchNum, true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} change
 | 
			
		||||
@@ -123,7 +130,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      return this.changeBaseURL(change.project, change._number, patchNum) +
 | 
			
		||||
          '/patch?' + (opt_zip ? 'zip' : 'download');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} change
 | 
			
		||||
@@ -146,7 +153,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return shortRev + '.diff.' + (opt_zip ? 'zip' : 'base64');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHidePatchFile(change, patchNum) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -161,7 +168,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeArchiveDownloadLink(change, patchNum, format) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -170,7 +177,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      return this.changeBaseURL(change.project, change._number, patchNum) +
 | 
			
		||||
          '/archive?format=' + format;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSchemes(change, patchNum) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -188,28 +195,30 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePatchSetQuantity(revisions) {
 | 
			
		||||
      if (!revisions) { return 0; }
 | 
			
		||||
      return Object.keys(revisions).length;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('close', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _schemesChanged(schemes) {
 | 
			
		||||
      if (schemes.length === 0) { return; }
 | 
			
		||||
      if (!schemes.includes(this._selectedScheme)) {
 | 
			
		||||
        this._selectedScheme = schemes.sort()[0];
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowDownloadCommands(schemes) {
 | 
			
		||||
      return schemes.length ? '' : 'hidden';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDownloadDialog.is, GrDownloadDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,17 @@
 | 
			
		||||
  const PATCH_DESC_MAX_LENGTH = 500;
 | 
			
		||||
  const MERGED_STATUS = 'MERGED';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-file-list-header',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrFileListHeader extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-file-list-header'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * @event expand-diffs
 | 
			
		||||
     */
 | 
			
		||||
@@ -48,72 +56,71 @@
 | 
			
		||||
     * @event open-upload-help-dialog
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      account: Object,
 | 
			
		||||
      allPatchSets: Array,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      changeNum: String,
 | 
			
		||||
      changeUrl: String,
 | 
			
		||||
      changeComments: Object,
 | 
			
		||||
      commitInfo: Object,
 | 
			
		||||
      editMode: Boolean,
 | 
			
		||||
      loggedIn: Boolean,
 | 
			
		||||
      serverConfig: Object,
 | 
			
		||||
      shownFileCount: Number,
 | 
			
		||||
      diffPrefs: Object,
 | 
			
		||||
      diffPrefsDisabled: Boolean,
 | 
			
		||||
      diffViewMode: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      patchNum: String,
 | 
			
		||||
      basePatchNum: String,
 | 
			
		||||
      filesExpanded: String,
 | 
			
		||||
      // Caps the number of files that can be shown and have the 'show diffs' /
 | 
			
		||||
      // 'hide diffs' buttons still be functional.
 | 
			
		||||
      _maxFilesForBulkActions: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
        value: 225,
 | 
			
		||||
      },
 | 
			
		||||
      _patchsetDescription: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
      showTitle: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _descriptionReadOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeDescriptionReadOnly(loggedIn, change, account)',
 | 
			
		||||
      },
 | 
			
		||||
      revisionInfo: Object,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        account: Object,
 | 
			
		||||
        allPatchSets: Array,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        change: Object,
 | 
			
		||||
        changeNum: String,
 | 
			
		||||
        changeUrl: String,
 | 
			
		||||
        changeComments: Object,
 | 
			
		||||
        commitInfo: Object,
 | 
			
		||||
        editMode: Boolean,
 | 
			
		||||
        loggedIn: Boolean,
 | 
			
		||||
        serverConfig: Object,
 | 
			
		||||
        shownFileCount: Number,
 | 
			
		||||
        diffPrefs: Object,
 | 
			
		||||
        diffPrefsDisabled: Boolean,
 | 
			
		||||
        diffViewMode: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        patchNum: String,
 | 
			
		||||
        basePatchNum: String,
 | 
			
		||||
        filesExpanded: String,
 | 
			
		||||
        // Caps the number of files that can be shown and have the 'show diffs' /
 | 
			
		||||
        // 'hide diffs' buttons still be functional.
 | 
			
		||||
        _maxFilesForBulkActions: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
          value: 225,
 | 
			
		||||
        },
 | 
			
		||||
        _patchsetDescription: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
        showTitle: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _descriptionReadOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeDescriptionReadOnly(loggedIn, change, account)',
 | 
			
		||||
        },
 | 
			
		||||
        revisionInfo: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_computePatchSetDescription(change, patchNum)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_computePatchSetDescription(change, patchNum)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setDiffViewMode(mode) {
 | 
			
		||||
      this.$.modeSelect.setMode(mode);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _expandAllDiffs() {
 | 
			
		||||
      this._expanded = true;
 | 
			
		||||
      this.fire('expand-diffs');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _collapseAllDiffs() {
 | 
			
		||||
      this._expanded = false;
 | 
			
		||||
      this.fire('collapse-diffs');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeExpandedClass(filesExpanded) {
 | 
			
		||||
      const classes = [];
 | 
			
		||||
@@ -125,11 +132,11 @@
 | 
			
		||||
        classes.push('openFile');
 | 
			
		||||
      }
 | 
			
		||||
      return classes.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDescriptionPlaceholder(readOnly) {
 | 
			
		||||
      return (readOnly ? 'No' : 'Add') + ' patchset description';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDescriptionReadOnly(loggedIn, change, account) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -138,7 +145,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return !(loggedIn && (account._account_id === change.owner._account_id));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePatchSetDescription(change, patchNum) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -149,11 +156,11 @@
 | 
			
		||||
      const rev = this.getRevisionByPatchNum(change.revisions, patchNum);
 | 
			
		||||
      this._patchsetDescription = (rev && rev.description) ?
 | 
			
		||||
        rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDescriptionRemoved(e) {
 | 
			
		||||
      return this._updateDescription('', e);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} revisions The revisions object keyed by revision hashes
 | 
			
		||||
@@ -167,12 +174,12 @@
 | 
			
		||||
          return rev;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDescriptionChanged(e) {
 | 
			
		||||
      const desc = e.detail.trim();
 | 
			
		||||
      this._updateDescription(desc, e);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update the patchset description with the rest API.
 | 
			
		||||
@@ -197,43 +204,43 @@
 | 
			
		||||
            if (target) { target.disabled = false; }
 | 
			
		||||
            return;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePrefsButtonHidden(prefs, diffPrefsDisabled) {
 | 
			
		||||
      return diffPrefsDisabled || !prefs;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _fileListActionsVisible(shownFileCount, maxFilesForBulkActions) {
 | 
			
		||||
      return shownFileCount <= maxFilesForBulkActions;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePatchChange(e) {
 | 
			
		||||
      const {basePatchNum, patchNum} = e.detail;
 | 
			
		||||
      if (this.patchNumEquals(basePatchNum, this.basePatchNum) &&
 | 
			
		||||
          this.patchNumEquals(patchNum, this.patchNum)) { return; }
 | 
			
		||||
      Gerrit.Nav.navigateToChange(this.change, patchNum, basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePrefsTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.fire('open-diff-prefs');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleIncludedInTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.fire('open-included-in-dialog');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDownloadTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('open-download-dialog', {bubbles: false}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeEditModeClass(editMode) {
 | 
			
		||||
      return editMode ? 'editMode' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePatchInfoClass(patchNum, allPatchSets) {
 | 
			
		||||
      const latestNum = this.computeLatestPatchNum(allPatchSets);
 | 
			
		||||
@@ -241,18 +248,18 @@
 | 
			
		||||
        return '';
 | 
			
		||||
      }
 | 
			
		||||
      return 'patchInfoOldPatchSet';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hideIncludedIn(change) {
 | 
			
		||||
      return change && change.status === MERGED_STATUS ? '' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUploadTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('open-upload-help-dialog', {bubbles: false}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeUploadHelpContainerClass(change, account) {
 | 
			
		||||
      const changeIsMerged = change && change.status === MERGED_STATUS;
 | 
			
		||||
@@ -262,6 +269,8 @@
 | 
			
		||||
      const userIsOwner = ownerId && userId && ownerId === userId;
 | 
			
		||||
      const hideContainer = !userIsOwner || changeIsMerged;
 | 
			
		||||
      return 'uploadContainer desktop' + (hideContainer ? ' hide' : '');
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrFileListHeader.is, GrFileListHeader);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -17,36 +17,40 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-included-in-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrIncludedInDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-included-in-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the user presses the close button.
 | 
			
		||||
     *
 | 
			
		||||
     * @event close
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      changeNum: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_resetData',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _includedIn: Object,
 | 
			
		||||
      _loaded: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _filterText: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        changeNum: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_resetData',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _includedIn: Object,
 | 
			
		||||
        _loaded: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _filterText: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadData() {
 | 
			
		||||
      if (!this.changeNum) { return; }
 | 
			
		||||
@@ -57,12 +61,12 @@
 | 
			
		||||
            this._includedIn = configs;
 | 
			
		||||
            this._loaded = true;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _resetData() {
 | 
			
		||||
      this._includedIn = null;
 | 
			
		||||
      this._loaded = false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeGroups(includedIn, filterText) {
 | 
			
		||||
      if (!includedIn) { return []; }
 | 
			
		||||
@@ -83,22 +87,24 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return groups.filter(g => g.items.length);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('close', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLoadingClass(loaded) {
 | 
			
		||||
      return loaded ? 'loading loaded' : 'loading';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onFilterChanged() {
 | 
			
		||||
      this.debounce('filter-change', () => {
 | 
			
		||||
        this._filterText = this.$.filterInput.bindValue;
 | 
			
		||||
      }, 100);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrIncludedInDialog.is, GrIncludedInDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,56 +17,59 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-label-score-row',
 | 
			
		||||
 | 
			
		||||
  class GrLabelScoreRow extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-label-score-row'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when any label is changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @event labels-changed
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * @type {{ name: string }}
 | 
			
		||||
       */
 | 
			
		||||
      label: Object,
 | 
			
		||||
      labels: Object,
 | 
			
		||||
      name: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      permittedLabels: Object,
 | 
			
		||||
      labelValues: Object,
 | 
			
		||||
      _selectedValueText: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: 'No value selected',
 | 
			
		||||
      },
 | 
			
		||||
      _items: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computePermittedLabelValues(permittedLabels, label.name)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        label: Object,
 | 
			
		||||
        labels: Object,
 | 
			
		||||
        name: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
        permittedLabels: Object,
 | 
			
		||||
        labelValues: Object,
 | 
			
		||||
        _selectedValueText: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: 'No value selected',
 | 
			
		||||
        },
 | 
			
		||||
        _items: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computePermittedLabelValues(permittedLabels, label.name)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get selectedItem() {
 | 
			
		||||
      if (!this._ironSelector) { return undefined; }
 | 
			
		||||
      return this._ironSelector.selectedItem;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get selectedValue() {
 | 
			
		||||
      if (!this._ironSelector) { return undefined; }
 | 
			
		||||
      return this._ironSelector.selected;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setSelectedValue(value) {
 | 
			
		||||
      // The selector may not be present if it’s not at the latest patch set.
 | 
			
		||||
      if (!this._ironSelector) { return; }
 | 
			
		||||
      this._ironSelector.select(value);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get _ironSelector() {
 | 
			
		||||
      return this.$ && this.$.labelSelector;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeBlankItems(permittedLabels, label, side) {
 | 
			
		||||
      if (!permittedLabels || !permittedLabels[label] ||
 | 
			
		||||
@@ -82,7 +85,7 @@
 | 
			
		||||
      const endPosition = this.labelValues[parseInt(
 | 
			
		||||
          permittedLabels[label][permittedLabels[label].length - 1], 10)];
 | 
			
		||||
      return new Array(Object.keys(this.labelValues).length - endPosition - 1);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLabelValue(labels, permittedLabels, label) {
 | 
			
		||||
      if (label.value) {
 | 
			
		||||
@@ -93,7 +96,7 @@
 | 
			
		||||
        return permittedLabels[label.name].find(
 | 
			
		||||
            value => parseInt(value, 10) === labels[label.name].default_value);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeButtonClass(value, index, totalItems) {
 | 
			
		||||
      const classes = [];
 | 
			
		||||
@@ -114,7 +117,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return classes.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelValue(labels, permittedLabels, label) {
 | 
			
		||||
      if ([labels, permittedLabels, label].some(arg => arg === undefined)) {
 | 
			
		||||
@@ -131,7 +134,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setSelectedValueText(e) {
 | 
			
		||||
      // Needed because when the selected item changes, it first changes to
 | 
			
		||||
@@ -145,17 +148,17 @@
 | 
			
		||||
      this.dispatchEvent(new CustomEvent(
 | 
			
		||||
          'labels-changed',
 | 
			
		||||
          {detail: {name, value}, bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeAnyPermittedLabelValues(permittedLabels, label) {
 | 
			
		||||
      return permittedLabels && permittedLabels.hasOwnProperty(label) &&
 | 
			
		||||
        permittedLabels[label].length;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHiddenClass(permittedLabels, label) {
 | 
			
		||||
      return !this._computeAnyPermittedLabelValues(permittedLabels, label) ?
 | 
			
		||||
        'hidden' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePermittedLabelValues(permittedLabels, label) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -164,12 +167,14 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return permittedLabels[label];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabelValueTitle(labels, label, value) {
 | 
			
		||||
      return labels[label] &&
 | 
			
		||||
        labels[label].values &&
 | 
			
		||||
        labels[label].values[value];
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrLabelScoreRow.is, GrLabelScoreRow);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,25 +17,29 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-label-scores',
 | 
			
		||||
  class GrLabelScores extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-label-scores'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      _labels: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeLabels(change.labels.*, account)',
 | 
			
		||||
      },
 | 
			
		||||
      permittedLabels: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_computeColumns',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      account: Object,
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        _labels: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeLabels(change.labels.*, account)',
 | 
			
		||||
        },
 | 
			
		||||
        permittedLabels: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_computeColumns',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        change: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        account: Object,
 | 
			
		||||
 | 
			
		||||
      _labelValues: Object,
 | 
			
		||||
    },
 | 
			
		||||
        _labelValues: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getLabelValues() {
 | 
			
		||||
      const labels = {};
 | 
			
		||||
@@ -61,7 +65,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return labels;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getStringLabelValue(labels, labelName, numberValue) {
 | 
			
		||||
      for (const k in labels[labelName].values) {
 | 
			
		||||
@@ -70,7 +74,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return numberValue;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getVoteForAccount(labels, labelName, account) {
 | 
			
		||||
      const votes = labels[labelName];
 | 
			
		||||
@@ -83,7 +87,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLabels(labelRecord, account) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -99,7 +103,7 @@
 | 
			
		||||
          value: this._getVoteForAccount(labelsObj, key, this.account),
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeColumns(permittedLabels) {
 | 
			
		||||
      const labels = Object.keys(permittedLabels);
 | 
			
		||||
@@ -118,11 +122,11 @@
 | 
			
		||||
        values[orderedValues[i]] = i;
 | 
			
		||||
      }
 | 
			
		||||
      this._labelValues = values;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _changeIsMerged(changeStatus) {
 | 
			
		||||
      return changeStatus === 'MERGED';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param label {string|undefined}
 | 
			
		||||
@@ -136,6 +140,8 @@
 | 
			
		||||
 | 
			
		||||
      return permittedLabels.hasOwnProperty(label) &&
 | 
			
		||||
        permittedLabels[label].length ? 'access' : 'no-access';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrLabelScores.is, GrLabelScores);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,15 @@
 | 
			
		||||
  const PATCH_SET_PREFIX_PATTERN = /^Patch Set \d+: /;
 | 
			
		||||
  const LABEL_TITLE_SCORE_PATTERN = /^([A-Za-z0-9-]+)([+-]\d+)$/;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-message',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrMessage extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-message'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when this message's reply link is tapped.
 | 
			
		||||
     *
 | 
			
		||||
@@ -35,90 +41,93 @@
 | 
			
		||||
     * @event message-anchor-tap
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      click: '_handleClick',
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        changeNum: Number,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        message: Object,
 | 
			
		||||
        author: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computeAuthor(message)',
 | 
			
		||||
        },
 | 
			
		||||
        comments: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_commentsChanged',
 | 
			
		||||
        },
 | 
			
		||||
        config: Object,
 | 
			
		||||
        hideAutomated: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        hidden: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeIsHidden(hideAutomated, isAutomated)',
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
        isAutomated: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeIsAutomated(message)',
 | 
			
		||||
        },
 | 
			
		||||
        showAvatar: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeShowAvatar(author, config)',
 | 
			
		||||
        },
 | 
			
		||||
        showOnBehalfOf: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeShowOnBehalfOf(message)',
 | 
			
		||||
        },
 | 
			
		||||
        showReplyButton: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeShowReplyButton(message, _loggedIn)',
 | 
			
		||||
        },
 | 
			
		||||
        projectName: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_projectNameChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      changeNum: Number,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      message: Object,
 | 
			
		||||
      author: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computeAuthor(message)',
 | 
			
		||||
      },
 | 
			
		||||
      comments: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_commentsChanged',
 | 
			
		||||
      },
 | 
			
		||||
      config: Object,
 | 
			
		||||
      hideAutomated: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      hidden: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeIsHidden(hideAutomated, isAutomated)',
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      isAutomated: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeIsAutomated(message)',
 | 
			
		||||
      },
 | 
			
		||||
      showAvatar: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeShowAvatar(author, config)',
 | 
			
		||||
      },
 | 
			
		||||
      showOnBehalfOf: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeShowOnBehalfOf(message)',
 | 
			
		||||
      },
 | 
			
		||||
      showReplyButton: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeShowReplyButton(message, _loggedIn)',
 | 
			
		||||
      },
 | 
			
		||||
      projectName: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_projectNameChanged',
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * A mapping from label names to objects representing the minimum and
 | 
			
		||||
       * maximum possible values for that label.
 | 
			
		||||
       */
 | 
			
		||||
      labelExtremes: Object,
 | 
			
		||||
        labelExtremes: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {{ commentlinks: Array }}
 | 
			
		||||
       */
 | 
			
		||||
      _projectConfig: Object,
 | 
			
		||||
      // Computed property needed to trigger Polymer value observing.
 | 
			
		||||
      _expanded: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computeExpanded(message.expanded)',
 | 
			
		||||
      },
 | 
			
		||||
      _loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _projectConfig: Object,
 | 
			
		||||
        // Computed property needed to trigger Polymer value observing.
 | 
			
		||||
        _expanded: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computeExpanded(message.expanded)',
 | 
			
		||||
        },
 | 
			
		||||
        _loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_updateExpandedClass(message.expanded)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_updateExpandedClass(message.expanded)',
 | 
			
		||||
    ],
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('click',
 | 
			
		||||
          e => this._handleClick(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this.$.restAPI.getConfig().then(config => {
 | 
			
		||||
        this.config = config;
 | 
			
		||||
      });
 | 
			
		||||
      this.$.restAPI.getLoggedIn().then(loggedIn => {
 | 
			
		||||
        this._loggedIn = loggedIn;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateExpandedClass(expanded) {
 | 
			
		||||
      if (expanded) {
 | 
			
		||||
@@ -126,30 +135,30 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.classList.remove('expanded');
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeAuthor(message) {
 | 
			
		||||
      return message.author || message.updated_by;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowAvatar(author, config) {
 | 
			
		||||
      return !!(author && config && config.plugin && config.plugin.has_avatars);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowOnBehalfOf(message) {
 | 
			
		||||
      const author = message.author || message.updated_by;
 | 
			
		||||
      return !!(author && message.real_author &&
 | 
			
		||||
          author._account_id != message.real_author._account_id);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowReplyButton(message, loggedIn) {
 | 
			
		||||
      return message && !!message.message && loggedIn &&
 | 
			
		||||
          !this._computeIsAutomated(message);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeExpanded(expanded) {
 | 
			
		||||
      return expanded;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If there is no value set on the message object as to whether _expanded
 | 
			
		||||
@@ -160,33 +169,33 @@
 | 
			
		||||
      if (this.message && this.message.expanded === undefined) {
 | 
			
		||||
        this.set('message.expanded', Object.keys(value || {}).length > 0);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleClick(e) {
 | 
			
		||||
      if (this.message.expanded) { return; }
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.set('message.expanded', true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAuthorClick(e) {
 | 
			
		||||
      if (!this.message.expanded) { return; }
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.set('message.expanded', false);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsAutomated(message) {
 | 
			
		||||
      return !!(message.reviewer ||
 | 
			
		||||
          this._computeIsReviewerUpdate(message) ||
 | 
			
		||||
          (message.tag && message.tag.startsWith('autogenerated')));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsHidden(hideAutomated, isAutomated) {
 | 
			
		||||
      return hideAutomated && isAutomated;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsReviewerUpdate(event) {
 | 
			
		||||
      return event.type === 'REVIEWER_UPDATE';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getScores(message) {
 | 
			
		||||
      if (!message.message) { return []; }
 | 
			
		||||
@@ -199,7 +208,7 @@
 | 
			
		||||
          .map(s => s.match(LABEL_TITLE_SCORE_PATTERN))
 | 
			
		||||
          .filter(ms => ms && ms.length === 3)
 | 
			
		||||
          .map(ms => ({label: ms[1], value: ms[2]}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeScoreClass(score, labelExtremes) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -222,14 +231,14 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return classes.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeClass(expanded, showAvatar, message) {
 | 
			
		||||
      const classes = [];
 | 
			
		||||
      classes.push(expanded ? 'expanded' : 'collapsed');
 | 
			
		||||
      classes.push(showAvatar ? 'showAvatar' : 'hideAvatar');
 | 
			
		||||
      return classes.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAnchorClick(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
@@ -238,26 +247,28 @@
 | 
			
		||||
        composed: true,
 | 
			
		||||
        detail: {id: this.message.id},
 | 
			
		||||
      }));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleReplyTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.fire('reply', {message: this.message});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _projectNameChanged(name) {
 | 
			
		||||
      this.$.restAPI.getProjectConfig(name).then(config => {
 | 
			
		||||
        this._projectConfig = config;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeExpandToggleIcon(expanded) {
 | 
			
		||||
      return expanded ? 'gr-icons:expand-less' : 'gr-icons:expand-more';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _toggleExpanded(e) {
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.set('message.expanded', !this.message.expanded);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrMessage.is, GrMessage);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -25,57 +25,61 @@
 | 
			
		||||
    SHOW_MORE: 'show-more-messages',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-messages-list',
 | 
			
		||||
  class GrMessagesList extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-messages-list'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      changeNum: Number,
 | 
			
		||||
      messages: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      reviewerUpdates: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      changeComments: Object,
 | 
			
		||||
      projectName: String,
 | 
			
		||||
      showReplyButtons: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      labels: Object,
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        changeNum: Number,
 | 
			
		||||
        messages: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        reviewerUpdates: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        changeComments: Object,
 | 
			
		||||
        projectName: String,
 | 
			
		||||
        showReplyButtons: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        labels: Object,
 | 
			
		||||
 | 
			
		||||
      _expanded: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_expandedChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _hideAutomated: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        _expanded: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_expandedChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _hideAutomated: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * The messages after processing and including merged reviewer updates.
 | 
			
		||||
       */
 | 
			
		||||
      _processedMessages: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeItems(messages, reviewerUpdates)',
 | 
			
		||||
        observer: '_processedMessagesChanged',
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        _processedMessages: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeItems(messages, reviewerUpdates)',
 | 
			
		||||
          observer: '_processedMessagesChanged',
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * The subset of _processedMessages that is visible to the user.
 | 
			
		||||
       */
 | 
			
		||||
      _visibleMessages: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
        _visibleMessages: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _labelExtremes: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computeLabelExtremes(labels.*)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _labelExtremes: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computeLabelExtremes(labels.*)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    scrollToMessage(messageID) {
 | 
			
		||||
      let el = this.$$('[data-message-id="' + messageID + '"]');
 | 
			
		||||
@@ -108,12 +112,12 @@
 | 
			
		||||
      }
 | 
			
		||||
      window.scrollTo(0, top);
 | 
			
		||||
      this._highlightEl(el);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isAutomated(message) {
 | 
			
		||||
      return !!(message.reviewer ||
 | 
			
		||||
          (message.tag && message.tag.startsWith('autogenerated')));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeItems(messages, reviewerUpdates) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -152,7 +156,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return result;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _expandedChanged(exp) {
 | 
			
		||||
      if (this._processedMessages) {
 | 
			
		||||
@@ -169,7 +173,7 @@
 | 
			
		||||
          this.notifyPath(`_visibleMessages.${i}.expanded`);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _highlightEl(el) {
 | 
			
		||||
      const highlightedEls =
 | 
			
		||||
@@ -183,23 +187,23 @@
 | 
			
		||||
      }
 | 
			
		||||
      el.addEventListener('animationend', handleAnimationEnd);
 | 
			
		||||
      el.classList.add('highlighted');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {boolean} expand
 | 
			
		||||
     */
 | 
			
		||||
    handleExpandCollapse(expand) {
 | 
			
		||||
      this._expanded = expand;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleExpandCollapseTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.handleExpandCollapse(!this._expanded);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAnchorClick(e) {
 | 
			
		||||
      this.scrollToMessage(e.detail.id);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hasAutomatedMessages(messages) {
 | 
			
		||||
      if (!messages) { return false; }
 | 
			
		||||
@@ -209,11 +213,11 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeExpandCollapseMessage(expanded) {
 | 
			
		||||
      return expanded ? 'Collapse all' : 'Expand all';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Computes message author's file comments for change's message.
 | 
			
		||||
@@ -268,7 +272,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return msgComments;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the number of messages to splice to the beginning of
 | 
			
		||||
@@ -293,7 +297,7 @@
 | 
			
		||||
        delta = msgsRemaining - i;
 | 
			
		||||
      }
 | 
			
		||||
      return Math.min(msgsRemaining, delta);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the number of messages that would be visible, but do not currently
 | 
			
		||||
@@ -309,20 +313,20 @@
 | 
			
		||||
            this._getHumanMessages(visibleMessages).length;
 | 
			
		||||
      }
 | 
			
		||||
      return messages.length - visibleMessages.length;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIncrementText(visibleMessages, messages, hideAutomated) {
 | 
			
		||||
      let delta = this._getDelta(visibleMessages, messages, hideAutomated);
 | 
			
		||||
      delta = Math.min(
 | 
			
		||||
          this._numRemaining(visibleMessages, messages, hideAutomated), delta);
 | 
			
		||||
      return 'Show ' + Math.min(MESSAGES_INCREMENT, delta) + ' more';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getHumanMessages(messages) {
 | 
			
		||||
      return messages.filter(msg => {
 | 
			
		||||
        return !this._isAutomated(msg);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowHideTextHidden(visibleMessages, messages,
 | 
			
		||||
        hideAutomated) {
 | 
			
		||||
@@ -335,12 +339,12 @@
 | 
			
		||||
        visibleMessages = this._getHumanMessages(visibleMessages);
 | 
			
		||||
      }
 | 
			
		||||
      return visibleMessages.length >= messages.length;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleShowAllTap() {
 | 
			
		||||
      this._visibleMessages = this._processedMessages;
 | 
			
		||||
      this.$.reporting.reportInteraction(ReportingEvent.SHOW_ALL);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleIncrementShownMessages() {
 | 
			
		||||
      const delta = this._getDelta(this._visibleMessages,
 | 
			
		||||
@@ -350,27 +354,27 @@
 | 
			
		||||
      // Add newMessages to the beginning of _visibleMessages
 | 
			
		||||
      this.splice(...['_visibleMessages', 0, 0].concat(newMessages));
 | 
			
		||||
      this.$.reporting.reportInteraction(ReportingEvent.SHOW_MORE);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _processedMessagesChanged(messages) {
 | 
			
		||||
      if (messages) {
 | 
			
		||||
        this._visibleMessages = messages.slice(-MAX_INITIAL_SHOWN_MESSAGES);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeNumMessagesText(visibleMessages, messages,
 | 
			
		||||
        hideAutomated) {
 | 
			
		||||
      const total =
 | 
			
		||||
          this._numRemaining(visibleMessages, messages, hideAutomated);
 | 
			
		||||
      return total === 1 ? 'Show 1 message' : 'Show all ' + total + ' messages';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIncrementHidden(visibleMessages, messages,
 | 
			
		||||
        hideAutomated) {
 | 
			
		||||
      const total =
 | 
			
		||||
          this._numRemaining(visibleMessages, messages, hideAutomated);
 | 
			
		||||
      return total <= this._getDelta(visibleMessages, messages, hideAutomated);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Compute a mapping from label name to objects representing the minimum and
 | 
			
		||||
@@ -389,6 +393,8 @@
 | 
			
		||||
        extremes[key] = {min: values[0], max: values[values.length - 1]};
 | 
			
		||||
      }
 | 
			
		||||
      return extremes;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrMessagesList.is, GrMessagesList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,19 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-related-changes-list',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRelatedChangesList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-related-changes-list'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when a new section is loaded so that the change view can determine
 | 
			
		||||
     * a show more button is needed, sometimes before all the sections finish
 | 
			
		||||
@@ -28,64 +38,62 @@
 | 
			
		||||
     * @event new-section-loaded
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      change: Object,
 | 
			
		||||
      hasParent: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      patchNum: String,
 | 
			
		||||
      parentChange: Object,
 | 
			
		||||
      hidden: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      mergeable: Boolean,
 | 
			
		||||
      _connectedRevisions: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeConnectedRevisions(change, patchNum, ' +
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        change: Object,
 | 
			
		||||
        hasParent: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        patchNum: String,
 | 
			
		||||
        parentChange: Object,
 | 
			
		||||
        hidden: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
        loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        mergeable: Boolean,
 | 
			
		||||
        _connectedRevisions: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeConnectedRevisions(change, patchNum, ' +
 | 
			
		||||
            '_relatedResponse.changes)',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _relatedResponse: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return {changes: []}; },
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _submittedTogether: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return {changes: []}; },
 | 
			
		||||
      },
 | 
			
		||||
      _conflicts: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _cherryPicks: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _sameTopic: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _relatedResponse: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return {changes: []}; },
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _submittedTogether: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return {changes: []}; },
 | 
			
		||||
        },
 | 
			
		||||
        _conflicts: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _cherryPicks: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _sameTopic: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_resultsChanged(_relatedResponse, _submittedTogether, ' +
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_resultsChanged(_relatedResponse, _submittedTogether, ' +
 | 
			
		||||
          '_conflicts, _cherryPicks, _sameTopic)',
 | 
			
		||||
    ],
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    clear() {
 | 
			
		||||
      this.loading = true;
 | 
			
		||||
@@ -96,7 +104,7 @@
 | 
			
		||||
      this._conflicts = [];
 | 
			
		||||
      this._cherryPicks = [];
 | 
			
		||||
      this._sameTopic = [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reload() {
 | 
			
		||||
      if (!this.change || !this.patchNum) {
 | 
			
		||||
@@ -144,7 +152,7 @@
 | 
			
		||||
      return Promise.all(promises).then(() => {
 | 
			
		||||
        this.loading = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _fireReloadEvent() {
 | 
			
		||||
      // The listener on the change computes height of the related changes
 | 
			
		||||
@@ -152,7 +160,7 @@
 | 
			
		||||
      // that requires a flush.
 | 
			
		||||
      Polymer.dom.flush();
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('new-section-loaded'));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines whether or not the given change has a parent change. If there
 | 
			
		||||
@@ -166,34 +174,34 @@
 | 
			
		||||
      return relatedChanges.length > 0 &&
 | 
			
		||||
          relatedChanges[relatedChanges.length - 1].change_id !==
 | 
			
		||||
          currentChangeId;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRelatedChanges() {
 | 
			
		||||
      return this.$.restAPI.getRelatedChanges(this.change._number,
 | 
			
		||||
          this.patchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getSubmittedTogether() {
 | 
			
		||||
      return this.$.restAPI.getChangesSubmittedTogether(this.change._number);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getServerConfig() {
 | 
			
		||||
      return this.$.restAPI.getConfig();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getConflicts() {
 | 
			
		||||
      return this.$.restAPI.getChangeConflicts(this.change._number);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCherryPicks() {
 | 
			
		||||
      return this.$.restAPI.getChangeCherryPicks(this.change.project,
 | 
			
		||||
          this.change.change_id, this.change._number);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChangesWithSameTopic() {
 | 
			
		||||
      return this.$.restAPI.getChangesWithSameTopic(this.change.topic,
 | 
			
		||||
          this.change._number);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {number} changeNum
 | 
			
		||||
@@ -203,7 +211,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _computeChangeURL(changeNum, project, opt_patchNum) {
 | 
			
		||||
      return Gerrit.Nav.getUrlForChangeById(changeNum, project, opt_patchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChangeContainerClass(currentChange, relatedChange) {
 | 
			
		||||
      const classes = ['changeContainer'];
 | 
			
		||||
@@ -214,7 +222,7 @@
 | 
			
		||||
        classes.push('thisChange');
 | 
			
		||||
      }
 | 
			
		||||
      return classes.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Do the given objects describe the same change? Compares the changes by
 | 
			
		||||
@@ -229,7 +237,7 @@
 | 
			
		||||
      const aNum = this._getChangeNumber(a);
 | 
			
		||||
      const bNum = this._getChangeNumber(b);
 | 
			
		||||
      return aNum === bNum;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the change number from either a ChangeInfo (such as those included in
 | 
			
		||||
@@ -251,7 +259,7 @@
 | 
			
		||||
        return change._change_number;
 | 
			
		||||
      }
 | 
			
		||||
      return change._number;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLinkClass(change) {
 | 
			
		||||
      const statuses = [];
 | 
			
		||||
@@ -262,7 +270,7 @@
 | 
			
		||||
        statuses.push('submittable');
 | 
			
		||||
      }
 | 
			
		||||
      return statuses.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChangeStatusClass(change) {
 | 
			
		||||
      const classes = ['status'];
 | 
			
		||||
@@ -276,7 +284,7 @@
 | 
			
		||||
        classes.push('hidden');
 | 
			
		||||
      }
 | 
			
		||||
      return classes.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChangeStatus(change) {
 | 
			
		||||
      switch (change.status) {
 | 
			
		||||
@@ -293,7 +301,7 @@
 | 
			
		||||
        return 'Submittable';
 | 
			
		||||
      }
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _resultsChanged(related, submittedTogether, conflicts,
 | 
			
		||||
        cherryPicks, sameTopic) {
 | 
			
		||||
@@ -323,11 +331,11 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.hidden = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isIndirectAncestor(change) {
 | 
			
		||||
      return !this._connectedRevisions.includes(change.commit.commit);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeConnectedRevisions(change, patchNum, relatedChanges) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -364,7 +372,7 @@
 | 
			
		||||
        --pos;
 | 
			
		||||
      }
 | 
			
		||||
      return connected;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSubmittedTogetherClass(submittedTogether) {
 | 
			
		||||
      if (!submittedTogether || (
 | 
			
		||||
@@ -373,11 +381,13 @@
 | 
			
		||||
        return 'hidden';
 | 
			
		||||
      }
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeNonVisibleChangesNote(n) {
 | 
			
		||||
      const noun = n === 1 ? 'change' : 'changes';
 | 
			
		||||
      return `(+ ${n} non-visible ${noun})`;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRelatedChangesList.is, GrRelatedChangesList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -52,9 +52,23 @@
 | 
			
		||||
 | 
			
		||||
  const SEND_REPLY_TIMING_LABEL = 'SendReply';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-reply-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrReplyDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-reply-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when a reply is successfully sent.
 | 
			
		||||
     *
 | 
			
		||||
@@ -93,156 +107,159 @@
 | 
			
		||||
      * @event send-disabled-changed
 | 
			
		||||
      */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    constructor() {
 | 
			
		||||
      super();
 | 
			
		||||
      this.FocusTarget = FocusTarget;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * @type {{ _number: number, removable_reviewers: Array }}
 | 
			
		||||
       */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      patchNum: String,
 | 
			
		||||
      canBeStarted: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      disabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      draft: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
        observer: '_draftChanged',
 | 
			
		||||
      },
 | 
			
		||||
      quote: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
      diffDrafts: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_handleHeightChanged',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {!Function} */
 | 
			
		||||
      filterReviewerSuggestion: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._filterReviewerSuggestionGenerator(false);
 | 
			
		||||
        change: Object,
 | 
			
		||||
        patchNum: String,
 | 
			
		||||
        canBeStarted: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {!Function} */
 | 
			
		||||
      filterCCSuggestion: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._filterReviewerSuggestionGenerator(true);
 | 
			
		||||
        disabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      permittedLabels: Object,
 | 
			
		||||
      /**
 | 
			
		||||
        draft: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
          observer: '_draftChanged',
 | 
			
		||||
        },
 | 
			
		||||
        quote: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
        diffDrafts: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_handleHeightChanged',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {!Function} */
 | 
			
		||||
        filterReviewerSuggestion: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._filterReviewerSuggestionGenerator(false);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {!Function} */
 | 
			
		||||
        filterCCSuggestion: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._filterReviewerSuggestionGenerator(true);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        permittedLabels: Object,
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {{ commentlinks: Array }}
 | 
			
		||||
       */
 | 
			
		||||
      projectConfig: Object,
 | 
			
		||||
      knownLatestState: String,
 | 
			
		||||
      underReview: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      _account: Object,
 | 
			
		||||
      _ccs: Array,
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _ccPendingConfirmation: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_reviewerPendingConfirmationUpdated',
 | 
			
		||||
      },
 | 
			
		||||
      _messagePlaceholder: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeMessagePlaceholder(canBeStarted)',
 | 
			
		||||
      },
 | 
			
		||||
      _owner: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _pendingConfirmationDetails: Object,
 | 
			
		||||
      _includeComments: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _reviewers: Array,
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _reviewerPendingConfirmation: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_reviewerPendingConfirmationUpdated',
 | 
			
		||||
      },
 | 
			
		||||
      _previewFormatting: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_handleHeightChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _reviewersPendingRemove: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: {
 | 
			
		||||
          CC: [],
 | 
			
		||||
          REVIEWER: [],
 | 
			
		||||
        projectConfig: Object,
 | 
			
		||||
        knownLatestState: String,
 | 
			
		||||
        underReview: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _sendButtonLabel: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeSendButtonLabel(canBeStarted)',
 | 
			
		||||
      },
 | 
			
		||||
      _savingComments: Boolean,
 | 
			
		||||
      _reviewersMutated: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _labelsChanged: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _saveTooltip: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: ButtonTooltips.SAVE,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
      },
 | 
			
		||||
      _pluginMessage: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
      _sendDisabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeSendButtonDisabled(_sendButtonLabel, ' +
 | 
			
		||||
 | 
			
		||||
        _account: Object,
 | 
			
		||||
        _ccs: Array,
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _ccPendingConfirmation: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_reviewerPendingConfirmationUpdated',
 | 
			
		||||
        },
 | 
			
		||||
        _messagePlaceholder: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeMessagePlaceholder(canBeStarted)',
 | 
			
		||||
        },
 | 
			
		||||
        _owner: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _pendingConfirmationDetails: Object,
 | 
			
		||||
        _includeComments: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _reviewers: Array,
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _reviewerPendingConfirmation: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_reviewerPendingConfirmationUpdated',
 | 
			
		||||
        },
 | 
			
		||||
        _previewFormatting: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_handleHeightChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _reviewersPendingRemove: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: {
 | 
			
		||||
            CC: [],
 | 
			
		||||
            REVIEWER: [],
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _sendButtonLabel: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeSendButtonLabel(canBeStarted)',
 | 
			
		||||
        },
 | 
			
		||||
        _savingComments: Boolean,
 | 
			
		||||
        _reviewersMutated: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _labelsChanged: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _saveTooltip: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: ButtonTooltips.SAVE,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
        },
 | 
			
		||||
        _pluginMessage: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
        _sendDisabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeSendButtonDisabled(_sendButtonLabel, ' +
 | 
			
		||||
            'diffDrafts, draft, _reviewersMutated, _labelsChanged, ' +
 | 
			
		||||
            '_includeComments, disabled)',
 | 
			
		||||
        observer: '_sendDisabledChanged',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
          observer: '_sendDisabledChanged',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FocusTarget,
 | 
			
		||||
    get keyBindings() {
 | 
			
		||||
      return {
 | 
			
		||||
        'esc': '_handleEscKey',
 | 
			
		||||
        'ctrl+enter meta+enter': '_handleEnterKey',
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    keyBindings: {
 | 
			
		||||
      'esc': '_handleEscKey',
 | 
			
		||||
      'ctrl+enter meta+enter': '_handleEnterKey',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_changeUpdated(change.reviewers.*, change.owner)',
 | 
			
		||||
      '_ccsChanged(_ccs.splices)',
 | 
			
		||||
      '_reviewersChanged(_reviewers.splices)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_changeUpdated(change.reviewers.*, change.owner)',
 | 
			
		||||
        '_ccsChanged(_ccs.splices)',
 | 
			
		||||
        '_reviewersChanged(_reviewers.splices)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._getAccount().then(account => {
 | 
			
		||||
        this._account = account || {};
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this.$.jsAPI.addElement(this.$.jsAPI.Element.REPLY_DIALOG, this);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open(opt_focusTarget) {
 | 
			
		||||
      this.knownLatestState = LatestPatchState.CHECKING;
 | 
			
		||||
@@ -268,11 +285,11 @@
 | 
			
		||||
          this._savingComments = false;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    focus() {
 | 
			
		||||
      this._focusOn(FocusTarget.ANY);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getFocusStops() {
 | 
			
		||||
      const end = this._sendDisabled ? this.$.cancelButton : this.$.sendButton;
 | 
			
		||||
@@ -280,14 +297,14 @@
 | 
			
		||||
        start: this.$.reviewers.focusStart,
 | 
			
		||||
        end,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setLabelValue(label, value) {
 | 
			
		||||
      const selectorEl =
 | 
			
		||||
          this.$.labelScores.$$(`gr-label-score-row[name="${label}"]`);
 | 
			
		||||
      if (!selectorEl) { return; }
 | 
			
		||||
      selectorEl.setSelectedValue(value);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getLabelValue(label) {
 | 
			
		||||
      const selectorEl =
 | 
			
		||||
@@ -295,23 +312,23 @@
 | 
			
		||||
      if (!selectorEl) { return null; }
 | 
			
		||||
 | 
			
		||||
      return selectorEl.selectedValue;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEscKey(e) {
 | 
			
		||||
      this.cancel();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEnterKey(e) {
 | 
			
		||||
      this._submit();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _ccsChanged(splices) {
 | 
			
		||||
      this._reviewerTypeChanged(splices, ReviewerTypes.CC);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reviewersChanged(splices) {
 | 
			
		||||
      this._reviewerTypeChanged(splices, ReviewerTypes.REVIEWER);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reviewerTypeChanged(splices, reviewerType) {
 | 
			
		||||
      if (splices && splices.indexSplices) {
 | 
			
		||||
@@ -342,7 +359,7 @@
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _processReviewerChange(indexSplices, type) {
 | 
			
		||||
      for (const splice of indexSplices) {
 | 
			
		||||
@@ -354,7 +371,7 @@
 | 
			
		||||
          this._reviewersPendingRemove[type].push(account);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets the state of the _reviewersPendingRemove object, and removes
 | 
			
		||||
@@ -380,7 +397,7 @@
 | 
			
		||||
          this._reviewersPendingRemove[type] = [];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes an account from the change, both on the backend and the client.
 | 
			
		||||
@@ -404,7 +421,7 @@
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _mapReviewer(reviewer) {
 | 
			
		||||
      let reviewerId;
 | 
			
		||||
@@ -416,7 +433,7 @@
 | 
			
		||||
        confirmed = reviewer.group.confirmed;
 | 
			
		||||
      }
 | 
			
		||||
      return {reviewer: reviewerId, confirmed};
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    send(includeComments, startReview) {
 | 
			
		||||
      this.$.reporting.time(SEND_REPLY_TIMING_LABEL);
 | 
			
		||||
@@ -479,7 +496,7 @@
 | 
			
		||||
        this.disabled = false;
 | 
			
		||||
        throw err;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _focusOn(section) {
 | 
			
		||||
      // Safeguard- always want to focus on something.
 | 
			
		||||
@@ -497,7 +514,7 @@
 | 
			
		||||
        const ccEntry = this.$.ccs.focusStart;
 | 
			
		||||
        ccEntry.async(ccEntry.focus);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _chooseFocusTarget() {
 | 
			
		||||
      // If we are the owner and the reviewers field is empty, focus on that.
 | 
			
		||||
@@ -509,7 +526,7 @@
 | 
			
		||||
 | 
			
		||||
      // Default to BODY.
 | 
			
		||||
      return FocusTarget.BODY;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handle400Error(response) {
 | 
			
		||||
      // A call to _saveReview could fail with a server error if erroneous
 | 
			
		||||
@@ -551,11 +568,11 @@
 | 
			
		||||
        this.fire('server-error', {response});
 | 
			
		||||
        return null; // Means that the error has been handled.
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHideDraftList(drafts) {
 | 
			
		||||
      return Object.keys(drafts || {}).length == 0;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDraftsTitle(drafts) {
 | 
			
		||||
      let total = 0;
 | 
			
		||||
@@ -567,13 +584,13 @@
 | 
			
		||||
      if (total == 0) { return ''; }
 | 
			
		||||
      if (total == 1) { return '1 Draft'; }
 | 
			
		||||
      if (total > 1) { return total + ' Drafts'; }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeMessagePlaceholder(canBeStarted) {
 | 
			
		||||
      return canBeStarted ?
 | 
			
		||||
        'Add a note for your reviewers...' :
 | 
			
		||||
        'Say something nice...';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _changeUpdated(changeRecord, owner) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -582,7 +599,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._rebuildReviewerArrays(changeRecord.base, owner);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _rebuildReviewerArrays(change, owner) {
 | 
			
		||||
      this._owner = owner;
 | 
			
		||||
@@ -614,11 +631,11 @@
 | 
			
		||||
 | 
			
		||||
      this._ccs = ccs;
 | 
			
		||||
      this._reviewers = reviewers;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _accountOrGroupKey(entry) {
 | 
			
		||||
      return entry.id || entry._account_id;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generates a function to filter out reviewer/CC entries. When isCCs is
 | 
			
		||||
@@ -650,23 +667,23 @@
 | 
			
		||||
        }
 | 
			
		||||
        return this._reviewers.find(finder) === undefined;
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getAccount() {
 | 
			
		||||
      return this.$.restAPI.getAccount();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _cancelTapHandler(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.cancel();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cancel() {
 | 
			
		||||
      this.fire('cancel', null, {bubbles: false});
 | 
			
		||||
      this.$.textarea.closeDropdown();
 | 
			
		||||
      this._purgeReviewersPendingRemove(true);
 | 
			
		||||
      this._rebuildReviewerArrays(this.change.reviewers, this._owner);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _saveTapHandler(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
@@ -678,12 +695,12 @@
 | 
			
		||||
      this.send(this._includeComments, false).then(keepReviewers => {
 | 
			
		||||
        this._purgeReviewersPendingRemove(false, keepReviewers);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _sendTapHandler(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._submit();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _submit() {
 | 
			
		||||
      if (!this.$.ccs.submitEntryText()) {
 | 
			
		||||
@@ -710,12 +727,12 @@
 | 
			
		||||
              detail: {message: `Error submitting review ${err}`},
 | 
			
		||||
            }));
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _saveReview(review, opt_errFn) {
 | 
			
		||||
      return this.$.restAPI.saveChangeReview(this.change._number, this.patchNum,
 | 
			
		||||
          review, opt_errFn);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reviewerPendingConfirmationUpdated(reviewer) {
 | 
			
		||||
      if (reviewer === null) {
 | 
			
		||||
@@ -725,7 +742,7 @@
 | 
			
		||||
            this._ccPendingConfirmation || this._reviewerPendingConfirmation;
 | 
			
		||||
        this.$.reviewerConfirmationOverlay.open();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _confirmPendingReviewer() {
 | 
			
		||||
      if (this._ccPendingConfirmation) {
 | 
			
		||||
@@ -735,7 +752,7 @@
 | 
			
		||||
        this.$.reviewers.confirmGroup(this._reviewerPendingConfirmation.group);
 | 
			
		||||
        this._focusOn(FocusTarget.REVIEWERS);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _cancelPendingReviewer() {
 | 
			
		||||
      this._ccPendingConfirmation = null;
 | 
			
		||||
@@ -744,7 +761,7 @@
 | 
			
		||||
      const target =
 | 
			
		||||
          this._ccPendingConfirmation ? FocusTarget.CCS : FocusTarget.REVIEWERS;
 | 
			
		||||
      this._focusOn(target);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getStorageLocation() {
 | 
			
		||||
      // Tests trigger this method without setting change.
 | 
			
		||||
@@ -754,12 +771,12 @@
 | 
			
		||||
        patchNum: '@change',
 | 
			
		||||
        path: '@change',
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadStoredDraft() {
 | 
			
		||||
      const draft = this.$.storage.getDraftComment(this._getStorageLocation());
 | 
			
		||||
      return draft ? draft.message : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAccountTextEntry() {
 | 
			
		||||
      // When either of the account entries has input added to the autocomplete,
 | 
			
		||||
@@ -767,7 +784,7 @@
 | 
			
		||||
      //
 | 
			
		||||
      // Note: if the text is removed, the save button will not get disabled.
 | 
			
		||||
      this._reviewersMutated = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _draftChanged(newDraft, oldDraft) {
 | 
			
		||||
      this.debounce('store', () => {
 | 
			
		||||
@@ -780,37 +797,37 @@
 | 
			
		||||
              this.draft);
 | 
			
		||||
        }
 | 
			
		||||
      }, STORAGE_DEBOUNCE_INTERVAL_MS);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleHeightChanged(e) {
 | 
			
		||||
      this.fire('autogrow');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleLabelsChanged() {
 | 
			
		||||
      this._labelsChanged = Object.keys(
 | 
			
		||||
          this.$.labelScores.getLabelValues()).length !== 0;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isState(knownLatestState, value) {
 | 
			
		||||
      return knownLatestState === value;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reload() {
 | 
			
		||||
      // Load the current change without any patch range.
 | 
			
		||||
      location.href = this.getBaseUrl() + '/c/' + this.change._number;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSendButtonLabel(canBeStarted) {
 | 
			
		||||
      return canBeStarted ? ButtonLabels.START_REVIEW : ButtonLabels.SEND;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSendButtonTooltip(canBeStarted) {
 | 
			
		||||
      return canBeStarted ? ButtonTooltips.START_REVIEW : ButtonTooltips.SEND;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSavingLabelClass(savingComments) {
 | 
			
		||||
      return savingComments ? 'saving' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSendButtonDisabled(buttonLabel, drafts, text, reviewersMutated,
 | 
			
		||||
        labelsChanged, includeComments, disabled) {
 | 
			
		||||
@@ -831,7 +848,7 @@
 | 
			
		||||
      if (buttonLabel === ButtonLabels.START_REVIEW) { return false; }
 | 
			
		||||
      const hasDrafts = includeComments && Object.keys(drafts).length;
 | 
			
		||||
      return !hasDrafts && !text.length && !reviewersMutated && !labelsChanged;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePatchSetWarning(patchNum, labelsChanged) {
 | 
			
		||||
      let str = `Patch ${patchNum} is not latest.`;
 | 
			
		||||
@@ -839,28 +856,30 @@
 | 
			
		||||
        str += ' Voting on a non-latest patch will have no effect.';
 | 
			
		||||
      }
 | 
			
		||||
      return str;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setPluginMessage(message) {
 | 
			
		||||
      this._pluginMessage = message;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _sendDisabledChanged(sendDisabled) {
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('send-disabled-changed'));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getReviewerSuggestionsProvider(change) {
 | 
			
		||||
      const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
 | 
			
		||||
          change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.REVIEWER);
 | 
			
		||||
      provider.init();
 | 
			
		||||
      return provider;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCcSuggestionsProvider(change) {
 | 
			
		||||
      const provider = GrReviewerSuggestionsProvider.create(this.$.restAPI,
 | 
			
		||||
          change._number, Gerrit.SUGGESTIONS_PROVIDERS_USERS_TYPES.CC);
 | 
			
		||||
      provider.init();
 | 
			
		||||
      return provider;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrReplyDialog.is, GrReplyDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,69 +17,75 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-reviewer-list',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrReviewerList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-reviewer-list'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the "Add reviewer..." button is tapped.
 | 
			
		||||
     *
 | 
			
		||||
     * @event show-reply-dialog
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      change: Object,
 | 
			
		||||
      disabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      mutable: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      reviewersOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      ccsOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      maxReviewersDisplayed: Number,
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        change: Object,
 | 
			
		||||
        disabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
        mutable: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        reviewersOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        ccsOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        maxReviewersDisplayed: Number,
 | 
			
		||||
 | 
			
		||||
      _displayedReviewers: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _reviewers: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _showInput: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _addLabel: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeAddLabel(ccsOnly)',
 | 
			
		||||
      },
 | 
			
		||||
      _hiddenReviewerCount: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        computed: '_computeHiddenCount(_reviewers, _displayedReviewers)',
 | 
			
		||||
      },
 | 
			
		||||
        _displayedReviewers: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _reviewers: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _showInput: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _addLabel: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeAddLabel(ccsOnly)',
 | 
			
		||||
        },
 | 
			
		||||
        _hiddenReviewerCount: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          computed: '_computeHiddenCount(_reviewers, _displayedReviewers)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      // Used for testing.
 | 
			
		||||
      _lastAutocompleteRequest: Object,
 | 
			
		||||
      _xhrPromise: Object,
 | 
			
		||||
    },
 | 
			
		||||
        // Used for testing.
 | 
			
		||||
        _lastAutocompleteRequest: Object,
 | 
			
		||||
        _xhrPromise: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_reviewersChanged(change.reviewers.*, change.owner)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_reviewersChanged(change.reviewers.*, change.owner)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts change.permitted_labels to an array of hashes of label keys to
 | 
			
		||||
@@ -100,7 +106,7 @@
 | 
			
		||||
        label,
 | 
			
		||||
        scores: labels[label].map(v => parseInt(v, 10)),
 | 
			
		||||
      }));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns hash of labels to max permitted score.
 | 
			
		||||
@@ -114,7 +120,7 @@
 | 
			
		||||
                .map(v => parseInt(v, 10))
 | 
			
		||||
                .reduce((a, b) => Math.max(a, b))}))
 | 
			
		||||
          .reduce((acc, i) => Object.assign(acc, i), {});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns max permitted score for reviewer.
 | 
			
		||||
@@ -139,7 +145,7 @@
 | 
			
		||||
        return 0;
 | 
			
		||||
      }
 | 
			
		||||
      return NaN;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeReviewerTooltip(reviewer, change) {
 | 
			
		||||
      if (!change || !change.labels) { return ''; }
 | 
			
		||||
@@ -160,7 +166,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return '';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reviewersChanged(changeRecord, owner) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -194,7 +200,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this._displayedReviewers = this._reviewers;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHiddenCount(reviewers, displayedReviewers) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -203,7 +209,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return reviewers.length - displayedReviewers.length;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeCanRemoveReviewer(reviewer, mutable) {
 | 
			
		||||
      if (!mutable) { return false; }
 | 
			
		||||
@@ -217,7 +223,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRemove(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
@@ -245,7 +251,7 @@
 | 
			
		||||
        this.disabled = false;
 | 
			
		||||
        throw err;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAddTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
@@ -257,18 +263,20 @@
 | 
			
		||||
        value.ccsOnly = true;
 | 
			
		||||
      }
 | 
			
		||||
      this.fire('show-reply-dialog', {value});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleViewAll(e) {
 | 
			
		||||
      this._displayedReviewers = this._reviewers;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _removeReviewer(id) {
 | 
			
		||||
      return this.$.restAPI.removeChangeReviewer(this.change._number, id);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeAddLabel(ccsOnly) {
 | 
			
		||||
      return ccsOnly ? 'Add CC' : 'Add reviewer';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrReviewerList.is, GrReviewerList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -22,39 +22,42 @@
 | 
			
		||||
   *
 | 
			
		||||
   * @event thread-list-modified
 | 
			
		||||
   */
 | 
			
		||||
  class GrThreadList extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-thread-list'; }
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-thread-list',
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      change: Object,
 | 
			
		||||
      threads: Array,
 | 
			
		||||
      changeNum: String,
 | 
			
		||||
      loggedIn: Boolean,
 | 
			
		||||
      _sortedThreads: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
      },
 | 
			
		||||
      _filteredThreads: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeFilteredThreads(_sortedThreads, ' +
 | 
			
		||||
        change: Object,
 | 
			
		||||
        threads: Array,
 | 
			
		||||
        changeNum: String,
 | 
			
		||||
        loggedIn: Boolean,
 | 
			
		||||
        _sortedThreads: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
        },
 | 
			
		||||
        _filteredThreads: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeFilteredThreads(_sortedThreads, ' +
 | 
			
		||||
            '_unresolvedOnly, _draftsOnly)',
 | 
			
		||||
      },
 | 
			
		||||
      _unresolvedOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _draftsOnly: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        },
 | 
			
		||||
        _unresolvedOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _draftsOnly: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: ['_computeSortedThreads(threads.*)'],
 | 
			
		||||
    static get observers() { return ['_computeSortedThreads(threads.*)']; }
 | 
			
		||||
 | 
			
		||||
    _computeShowDraftToggle(loggedIn) {
 | 
			
		||||
      return loggedIn ? 'show' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Order as follows:
 | 
			
		||||
@@ -68,7 +71,7 @@
 | 
			
		||||
      const threads = changeRecord.base;
 | 
			
		||||
      if (!threads) { return []; }
 | 
			
		||||
      this._updateSortedThreads(threads);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateSortedThreads(threads) {
 | 
			
		||||
      this._sortedThreads =
 | 
			
		||||
@@ -90,7 +93,7 @@
 | 
			
		||||
            }
 | 
			
		||||
            return dateCompare ? dateCompare : c1.id.localeCompare(c2.id);
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeFilteredThreads(sortedThreads, unresolvedOnly, draftsOnly) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -125,7 +128,7 @@
 | 
			
		||||
          return c;
 | 
			
		||||
        }
 | 
			
		||||
      }).map(threadInfo => threadInfo.thread);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getThreadWithSortInfo(thread) {
 | 
			
		||||
      const lastComment = thread.comments[thread.comments.length - 1] || {};
 | 
			
		||||
@@ -143,7 +146,7 @@
 | 
			
		||||
        hasDraft: !!lastComment.__draft,
 | 
			
		||||
        updated: lastComment.updated,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeThread(rootId) {
 | 
			
		||||
      for (let i = 0; i < this.threads.length; i++) {
 | 
			
		||||
@@ -154,11 +157,11 @@
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleThreadDiscard(e) {
 | 
			
		||||
      this.removeThread(e.detail.rootId);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCommentsChanged(e) {
 | 
			
		||||
      // Reset threads so thread computations occur on deep array changes to
 | 
			
		||||
@@ -167,10 +170,12 @@
 | 
			
		||||
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('thread-list-modified',
 | 
			
		||||
          {detail: {rootId: e.detail.rootId, path: e.detail.path}}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isOnParent(side) {
 | 
			
		||||
      return !!side;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrThreadList.is, GrThreadList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -27,41 +27,46 @@
 | 
			
		||||
    'pull',
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-upload-help-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrUploadHelpDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-upload-help-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the user presses the close button.
 | 
			
		||||
     *
 | 
			
		||||
     * @event close
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      revision: Object,
 | 
			
		||||
      targetBranch: String,
 | 
			
		||||
      _commitCommand: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: COMMIT_COMMAND,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
      },
 | 
			
		||||
      _fetchCommand: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeFetchCommand(revision, ' +
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        revision: Object,
 | 
			
		||||
        targetBranch: String,
 | 
			
		||||
        _commitCommand: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: COMMIT_COMMAND,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
        },
 | 
			
		||||
        _fetchCommand: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeFetchCommand(revision, ' +
 | 
			
		||||
            '_preferredDownloadCommand, _preferredDownloadScheme)',
 | 
			
		||||
      },
 | 
			
		||||
      _preferredDownloadCommand: String,
 | 
			
		||||
      _preferredDownloadScheme: String,
 | 
			
		||||
      _pushCommand: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computePushCommand(targetBranch)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        },
 | 
			
		||||
        _preferredDownloadCommand: String,
 | 
			
		||||
        _preferredDownloadScheme: String,
 | 
			
		||||
        _pushCommand: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computePushCommand(targetBranch)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.$.restAPI.getLoggedIn().then(loggedIn => {
 | 
			
		||||
        if (loggedIn) {
 | 
			
		||||
          return this.$.restAPI.getPreferences();
 | 
			
		||||
@@ -72,13 +77,13 @@
 | 
			
		||||
          this._preferredDownloadScheme = prefs.download_scheme;
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('close', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeFetchCommand(revision, preferredDownloadCommand,
 | 
			
		||||
        preferredDownloadScheme) {
 | 
			
		||||
@@ -126,10 +131,12 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return undefined;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePushCommand(targetBranch) {
 | 
			
		||||
      return PUSH_COMMAND_PREFIX + targetBranch;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrUploadHelpDialog.is, GrUploadHelpDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,29 +19,39 @@
 | 
			
		||||
 | 
			
		||||
  const INTERPOLATE_URL_PATTERN = /\$\{([\w]+)\}/g;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-account-dropdown',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.DisplayNameMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrAccountDropdown extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.DisplayNameBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-account-dropdown'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      account: Object,
 | 
			
		||||
      config: Object,
 | 
			
		||||
      links: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_getLinks(_switchAccountUrl, _path)',
 | 
			
		||||
      },
 | 
			
		||||
      topContent: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_getTopContent(account)',
 | 
			
		||||
      },
 | 
			
		||||
      _path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '/',
 | 
			
		||||
      },
 | 
			
		||||
      _hasAvatars: Boolean,
 | 
			
		||||
      _switchAccountUrl: String,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        account: Object,
 | 
			
		||||
        config: Object,
 | 
			
		||||
        links: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_getLinks(_switchAccountUrl, _path)',
 | 
			
		||||
        },
 | 
			
		||||
        topContent: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_getTopContent(account)',
 | 
			
		||||
        },
 | 
			
		||||
        _path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '/',
 | 
			
		||||
        },
 | 
			
		||||
        _hasAvatars: Boolean,
 | 
			
		||||
        _switchAccountUrl: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._handleLocationChange();
 | 
			
		||||
      this.listen(window, 'location-change', '_handleLocationChange');
 | 
			
		||||
      this.$.restAPI.getConfig().then(cfg => {
 | 
			
		||||
@@ -54,15 +64,12 @@
 | 
			
		||||
        }
 | 
			
		||||
        this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.DisplayNameBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      this.unlisten(window, 'location-change', '_handleLocationChange');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLinks(switchAccountUrl, path) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -78,30 +85,32 @@
 | 
			
		||||
      }
 | 
			
		||||
      links.push({name: 'Sign out', url: '/logout'});
 | 
			
		||||
      return links;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getTopContent(account) {
 | 
			
		||||
      return [
 | 
			
		||||
        {text: this._accountName(account), bold: true},
 | 
			
		||||
        {text: account.email ? account.email : ''},
 | 
			
		||||
      ];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleLocationChange() {
 | 
			
		||||
      this._path =
 | 
			
		||||
          window.location.pathname +
 | 
			
		||||
          window.location.search +
 | 
			
		||||
          window.location.hash;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _interpolateUrl(url, replacements) {
 | 
			
		||||
      return url.replace(INTERPOLATE_URL_PATTERN, (match, p1) => {
 | 
			
		||||
        return replacements[p1] || '';
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _accountName(account) {
 | 
			
		||||
      return this.getUserName(this.config, account, true);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrAccountDropdown.is, GrAccountDropdown);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,21 +17,26 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-error-dialog',
 | 
			
		||||
 | 
			
		||||
  class GrErrorDialog extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-error-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the dismiss button is pressed.
 | 
			
		||||
     *
 | 
			
		||||
     * @event dismiss
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      text: String,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        text: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleConfirm() {
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('dismiss'));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrErrorDialog.is, GrErrorDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -25,40 +25,47 @@
 | 
			
		||||
  const TOO_MANY_FILES = 'too many files to find conflicts';
 | 
			
		||||
  const AUTHENTICATION_REQUIRED = 'Authentication required\n';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-error-manager',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrErrorManager extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-error-manager'; }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * The ID of the account that was logged in when the app was launched. If
 | 
			
		||||
       * not set, then there was no account at launch.
 | 
			
		||||
       */
 | 
			
		||||
      knownAccountId: Number,
 | 
			
		||||
        knownAccountId: Number,
 | 
			
		||||
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _alertElement: Object,
 | 
			
		||||
      /** @type {?number} */
 | 
			
		||||
      _hideAlertHandle: Number,
 | 
			
		||||
      _refreshingCredentials: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _alertElement: Object,
 | 
			
		||||
        /** @type {?number} */
 | 
			
		||||
        _hideAlertHandle: Number,
 | 
			
		||||
        _refreshingCredentials: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * The time (in milliseconds) since the most recent credential check.
 | 
			
		||||
       */
 | 
			
		||||
      _lastCredentialCheck: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value() { return Date.now(); },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _lastCredentialCheck: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value() { return Date.now(); },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.listen(document, 'server-error', '_handleServerError');
 | 
			
		||||
      this.listen(document, 'network-error', '_handleNetworkError');
 | 
			
		||||
      this.listen(document, 'auth-error', '_handleAuthError');
 | 
			
		||||
@@ -66,9 +73,10 @@
 | 
			
		||||
      this.listen(document, 'show-error', '_handleShowErrorDialog');
 | 
			
		||||
      this.listen(document, 'visibilitychange', '_handleVisibilityChange');
 | 
			
		||||
      this.listen(document, 'show-auth-required', '_handleAuthRequired');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      this._clearHideAlertHandle();
 | 
			
		||||
      this.unlisten(document, 'server-error', '_handleServerError');
 | 
			
		||||
      this.unlisten(document, 'network-error', '_handleNetworkError');
 | 
			
		||||
@@ -76,20 +84,20 @@
 | 
			
		||||
      this.unlisten(document, 'show-auth-required', '_handleAuthRequired');
 | 
			
		||||
      this.unlisten(document, 'visibilitychange', '_handleVisibilityChange');
 | 
			
		||||
      this.unlisten(document, 'show-error', '_handleShowErrorDialog');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _shouldSuppressError(msg) {
 | 
			
		||||
      return msg.includes(TOO_MANY_FILES);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAuthRequired() {
 | 
			
		||||
      this._showAuthErrorAlert(
 | 
			
		||||
          'Log in is required to perform that action.', 'Log in.');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAuthError() {
 | 
			
		||||
      this._showAuthErrorAlert('Auth error', 'Refresh credentials.');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleServerError(e) {
 | 
			
		||||
      const {request, response} = e.detail;
 | 
			
		||||
@@ -113,7 +121,7 @@
 | 
			
		||||
            }
 | 
			
		||||
            console.error(errorText);
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _constructServerErrorMsg({errorText, status, statusText, url}) {
 | 
			
		||||
      let err = `Error ${status}`;
 | 
			
		||||
@@ -122,21 +130,21 @@
 | 
			
		||||
      if (errorText) { err += errorText; }
 | 
			
		||||
      if (url) { err += `\nEndpoint: ${url}`; }
 | 
			
		||||
      return err;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleShowAlert(e) {
 | 
			
		||||
      this._showAlert(e.detail.message, e.detail.action, e.detail.callback,
 | 
			
		||||
          e.detail.dismissOnNavigation);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNetworkError(e) {
 | 
			
		||||
      this._showAlert('Server unavailable');
 | 
			
		||||
      console.error(e.detail.error.message);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLoggedIn() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} text
 | 
			
		||||
@@ -161,7 +169,7 @@
 | 
			
		||||
      const el = this._createToastAlert();
 | 
			
		||||
      el.show(text, opt_actionText, opt_actionCallback);
 | 
			
		||||
      this._alertElement = el;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hideAlert() {
 | 
			
		||||
      if (!this._alertElement) { return; }
 | 
			
		||||
@@ -171,14 +179,14 @@
 | 
			
		||||
 | 
			
		||||
      // Remove listener for page navigation, if it exists.
 | 
			
		||||
      this.unlisten(document, 'location-change', '_hideAlert');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _clearHideAlertHandle() {
 | 
			
		||||
      if (this._hideAlertHandle != null) {
 | 
			
		||||
        this.cancelAsync(this._hideAlertHandle);
 | 
			
		||||
        this._hideAlertHandle = null;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showAuthErrorAlert(errorText, actionText) {
 | 
			
		||||
      // TODO(viktard): close alert if it's not for auth error.
 | 
			
		||||
@@ -193,13 +201,13 @@
 | 
			
		||||
      if (!document.hidden) {
 | 
			
		||||
        this._handleVisibilityChange();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createToastAlert() {
 | 
			
		||||
      const el = document.createElement('gr-alert');
 | 
			
		||||
      el.toast = true;
 | 
			
		||||
      return el;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleVisibilityChange() {
 | 
			
		||||
      // Ignore when the page is transitioning to hidden (or hidden is
 | 
			
		||||
@@ -216,12 +224,12 @@
 | 
			
		||||
        this._lastCredentialCheck = Date.now();
 | 
			
		||||
        this.$.restAPI.checkCredentials();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _requestCheckLoggedIn() {
 | 
			
		||||
      this.debounce(
 | 
			
		||||
          'checkLoggedIn', this._checkSignedIn, CHECK_SIGN_IN_INTERVAL_MS);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _checkSignedIn() {
 | 
			
		||||
      this.$.restAPI.checkCredentials().then(account => {
 | 
			
		||||
@@ -242,11 +250,11 @@
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _reloadPage() {
 | 
			
		||||
      window.location.reload();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createLoginPopup() {
 | 
			
		||||
      const left = window.screenLeft +
 | 
			
		||||
@@ -262,31 +270,33 @@
 | 
			
		||||
      window.open(this.getBaseUrl() +
 | 
			
		||||
          '/login/%3FcloseAfterLogin', '_blank', options.join(','));
 | 
			
		||||
      this.listen(window, 'focus', '_handleWindowFocus');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCredentialRefreshed() {
 | 
			
		||||
      this.unlisten(window, 'focus', '_handleWindowFocus');
 | 
			
		||||
      this._refreshingCredentials = false;
 | 
			
		||||
      this._hideAlert();
 | 
			
		||||
      this._showAlert('Credentials refreshed.');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleWindowFocus() {
 | 
			
		||||
      this.flushDebouncer('checkLoggedIn');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleShowErrorDialog(e) {
 | 
			
		||||
      this._showErrorDialog(e.detail.message);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDismissErrorDialog() {
 | 
			
		||||
      this.$.errorOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showErrorDialog(message) {
 | 
			
		||||
      this.$.reporting.reportErrorDialog(message);
 | 
			
		||||
      this.$.errorDialog.text = message;
 | 
			
		||||
      this.$.errorOverlay.open();
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrErrorManager.is, GrErrorManager);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,20 +17,26 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-key-binding-display',
 | 
			
		||||
  class GrKeyBindingDisplay extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-key-binding-display'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {Array<string>} */
 | 
			
		||||
      binding: Array,
 | 
			
		||||
    },
 | 
			
		||||
        binding: Array,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeModifiers(binding) {
 | 
			
		||||
      return binding.slice(0, binding.length - 1);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeKey(binding) {
 | 
			
		||||
      return binding[binding.length - 1];
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrKeyBindingDisplay.is, GrKeyBindingDisplay);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,60 +19,68 @@
 | 
			
		||||
 | 
			
		||||
  const {ShortcutSection} = window.Gerrit.KeyboardShortcutBinder;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-keyboard-shortcuts-dialog',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrKeyboardShortcutsDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-keyboard-shortcuts-dialog'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the user presses the close button.
 | 
			
		||||
     *
 | 
			
		||||
     * @event close
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      _left: Array,
 | 
			
		||||
      _right: Array,
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        _left: Array,
 | 
			
		||||
        _right: Array,
 | 
			
		||||
 | 
			
		||||
      _propertyBySection: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() {
 | 
			
		||||
          return {
 | 
			
		||||
            [ShortcutSection.EVERYWHERE]: '_everywhere',
 | 
			
		||||
            [ShortcutSection.NAVIGATION]: '_navigation',
 | 
			
		||||
            [ShortcutSection.DASHBOARD]: '_dashboard',
 | 
			
		||||
            [ShortcutSection.CHANGE_LIST]: '_changeList',
 | 
			
		||||
            [ShortcutSection.ACTIONS]: '_actions',
 | 
			
		||||
            [ShortcutSection.REPLY_DIALOG]: '_replyDialog',
 | 
			
		||||
            [ShortcutSection.FILE_LIST]: '_fileList',
 | 
			
		||||
            [ShortcutSection.DIFFS]: '_diffs',
 | 
			
		||||
          };
 | 
			
		||||
        _propertyBySection: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() {
 | 
			
		||||
            return {
 | 
			
		||||
              [ShortcutSection.EVERYWHERE]: '_everywhere',
 | 
			
		||||
              [ShortcutSection.NAVIGATION]: '_navigation',
 | 
			
		||||
              [ShortcutSection.DASHBOARD]: '_dashboard',
 | 
			
		||||
              [ShortcutSection.CHANGE_LIST]: '_changeList',
 | 
			
		||||
              [ShortcutSection.ACTIONS]: '_actions',
 | 
			
		||||
              [ShortcutSection.REPLY_DIALOG]: '_replyDialog',
 | 
			
		||||
              [ShortcutSection.FILE_LIST]: '_fileList',
 | 
			
		||||
              [ShortcutSection.DIFFS]: '_diffs',
 | 
			
		||||
            };
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    hostAttributes: {
 | 
			
		||||
      role: 'dialog',
 | 
			
		||||
    },
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this._ensureAttribute('role', 'dialog');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.addKeyboardShortcutDirectoryListener(
 | 
			
		||||
          this._onDirectoryUpdated.bind(this));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      this.removeKeyboardShortcutDirectoryListener(
 | 
			
		||||
          this._onDirectoryUpdated.bind(this));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('close', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onDirectoryUpdated(directory) {
 | 
			
		||||
      const left = [];
 | 
			
		||||
@@ -122,6 +130,9 @@
 | 
			
		||||
 | 
			
		||||
      this.set('_left', left);
 | 
			
		||||
      this.set('_right', right);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrKeyboardShortcutsDialog.is,
 | 
			
		||||
      GrKeyboardShortcutsDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -69,94 +69,107 @@
 | 
			
		||||
    'CUSTOM_EXTENSION',
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-main-header',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.AdminNavMixin
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.DocsUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrMainHeader extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.AdminNavBehavior,
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.DocsUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-main-header'; }
 | 
			
		||||
 | 
			
		||||
    hostAttributes: {
 | 
			
		||||
      role: 'banner',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      searchQuery: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _account: Object,
 | 
			
		||||
      _adminLinks: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _defaultLinks: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() {
 | 
			
		||||
          return DEFAULT_LINKS;
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        searchQuery: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _docBaseUrl: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
      _links: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeLinks(_defaultLinks, _userLinks, _adminLinks, ' +
 | 
			
		||||
        loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
        loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _account: Object,
 | 
			
		||||
        _adminLinks: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _defaultLinks: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() {
 | 
			
		||||
            return DEFAULT_LINKS;
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _docBaseUrl: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
        _links: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeLinks(_defaultLinks, _userLinks, _adminLinks, ' +
 | 
			
		||||
            '_topMenus, _docBaseUrl)',
 | 
			
		||||
      },
 | 
			
		||||
      _loginURL: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '/login',
 | 
			
		||||
      },
 | 
			
		||||
      _userLinks: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _topMenus: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _registerText: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: 'Sign up',
 | 
			
		||||
      },
 | 
			
		||||
      _registerURL: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        },
 | 
			
		||||
        _loginURL: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '/login',
 | 
			
		||||
        },
 | 
			
		||||
        _userLinks: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _topMenus: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _registerText: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: 'Sign up',
 | 
			
		||||
        },
 | 
			
		||||
        _registerURL: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.AdminNavBehavior,
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.DocsUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_accountLoaded(_account)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_accountLoaded(_account)',
 | 
			
		||||
    ],
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this._ensureAttribute('role', 'banner');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._loadAccount();
 | 
			
		||||
      this._loadConfig();
 | 
			
		||||
      this.listen(window, 'location-change', '_handleLocationChange');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      this.unlisten(window, 'location-change', '_handleLocationChange');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reload() {
 | 
			
		||||
      this._loadAccount();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleLocationChange(e) {
 | 
			
		||||
      const baseUrl = this.getBaseUrl();
 | 
			
		||||
@@ -173,11 +186,11 @@
 | 
			
		||||
            window.location.search +
 | 
			
		||||
            window.location.hash);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRelativeURL(path) {
 | 
			
		||||
      return '//' + window.location.host + this.getBaseUrl() + path;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLinks(defaultLinks, userLinks, adminLinks, topMenus, docBaseUrl) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -232,7 +245,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return links;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getDocLinks(docBaseUrl, docLinks) {
 | 
			
		||||
      if (!docBaseUrl || !docLinks) {
 | 
			
		||||
@@ -249,7 +262,7 @@
 | 
			
		||||
          target: '_blank',
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadAccount() {
 | 
			
		||||
      this.loading = true;
 | 
			
		||||
@@ -273,7 +286,7 @@
 | 
			
		||||
              this._adminLinks = res.links;
 | 
			
		||||
            });
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadConfig() {
 | 
			
		||||
      this.$.restAPI.getConfig()
 | 
			
		||||
@@ -282,7 +295,7 @@
 | 
			
		||||
            return this.getDocsBaseUrl(config, this.$.restAPI);
 | 
			
		||||
          })
 | 
			
		||||
          .then(docBaseUrl => { this._docBaseUrl = docBaseUrl; });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _accountLoaded(account) {
 | 
			
		||||
      if (!account) { return; }
 | 
			
		||||
@@ -290,7 +303,7 @@
 | 
			
		||||
      this.$.restAPI.getPreferences().then(prefs => {
 | 
			
		||||
        this._userLinks = prefs.my.map(this._fixCustomMenuItem);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _retrieveRegisterURL(config) {
 | 
			
		||||
      if (AUTH_TYPES_WITH_REGISTER_URL.has(config.auth.auth_type)) {
 | 
			
		||||
@@ -299,11 +312,11 @@
 | 
			
		||||
          this._registerText = config.auth.register_text;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsInvisible(registerURL) {
 | 
			
		||||
      return registerURL ? '' : 'invisible';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _fixCustomMenuItem(linkObj) {
 | 
			
		||||
      // Normalize all urls to PolyGerrit style.
 | 
			
		||||
@@ -321,17 +334,17 @@
 | 
			
		||||
      delete linkObj.target;
 | 
			
		||||
 | 
			
		||||
      return linkObj;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _generateSettingsLink() {
 | 
			
		||||
      return this.getBaseUrl() + '/settings/';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onMobileSearchTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('mobile-search', null, {bubbles: false});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeLinkGroupClass(linkGroup) {
 | 
			
		||||
      if (linkGroup && linkGroup.class) {
 | 
			
		||||
@@ -339,6 +352,8 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrMainHeader.is, GrMainHeader);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -207,38 +207,46 @@
 | 
			
		||||
    });
 | 
			
		||||
  })();
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-router',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrRouter extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-router'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      _app: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: app,
 | 
			
		||||
      },
 | 
			
		||||
      _isRedirecting: Boolean,
 | 
			
		||||
      // This variable is to differentiate between internal navigation (false)
 | 
			
		||||
      // and for first navigation in app after loaded from server (true).
 | 
			
		||||
      _isInitialLoad: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        _app: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: app,
 | 
			
		||||
        },
 | 
			
		||||
        _isRedirecting: Boolean,
 | 
			
		||||
        // This variable is to differentiate between internal navigation (false)
 | 
			
		||||
        // and for first navigation in app after loaded from server (true).
 | 
			
		||||
        _isInitialLoad: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    start() {
 | 
			
		||||
      if (!this._app) { return; }
 | 
			
		||||
      this._startRouter();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setParams(params) {
 | 
			
		||||
      this._appElement().params = params;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _appElement() {
 | 
			
		||||
      // In Polymer2 you have to reach through the shadow root of the app
 | 
			
		||||
@@ -248,12 +256,12 @@
 | 
			
		||||
      return document.getElementById('app-element') ||
 | 
			
		||||
          document.getElementById('app').shadowRoot.getElementById(
 | 
			
		||||
              'app-element');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _redirect(url) {
 | 
			
		||||
      this._isRedirecting = true;
 | 
			
		||||
      page.redirect(url);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -285,7 +293,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return base + url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _generateWeblinks(params) {
 | 
			
		||||
      const type = params.type;
 | 
			
		||||
@@ -299,7 +307,7 @@
 | 
			
		||||
        default:
 | 
			
		||||
          console.warn(`Unsupported weblink ${type}!`);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getPatchSetWeblink(params) {
 | 
			
		||||
      const {commit, options} = params;
 | 
			
		||||
@@ -311,7 +319,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return {name, url: weblink.url};
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _firstCodeBrowserWeblink(weblinks) {
 | 
			
		||||
      // This is an ordered whitelist of web link types that provide direct
 | 
			
		||||
@@ -323,7 +331,7 @@
 | 
			
		||||
        if (weblink) { return weblink; }
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getBrowseCommitWeblink(weblinks, config) {
 | 
			
		||||
      if (!weblinks) { return null; }
 | 
			
		||||
@@ -339,7 +347,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      if (!weblink) { return null; }
 | 
			
		||||
      return weblink;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChangeWeblinks({repo, commit, options: {weblinks, config}}) {
 | 
			
		||||
      if (!weblinks || !weblinks.length) return [];
 | 
			
		||||
@@ -348,11 +356,11 @@
 | 
			
		||||
        !commitWeblink ||
 | 
			
		||||
        !commitWeblink.name ||
 | 
			
		||||
        weblink.name !== commitWeblink.name);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getFileWebLinks({repo, commit, file, options: {weblinks}}) {
 | 
			
		||||
      return weblinks;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -399,7 +407,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return '/q/' + operators.join('+') + offsetExpr;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -423,7 +431,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return `/c/${params.changeNum}${suffix}`;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -448,7 +456,7 @@
 | 
			
		||||
        // User dashboard.
 | 
			
		||||
        return `/dashboard/${params.user || 'self'}`;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Array<!{name: string, query: string}>} sections
 | 
			
		||||
@@ -465,7 +473,7 @@
 | 
			
		||||
        return encodeURIComponent(section.name) + '=' +
 | 
			
		||||
            encodeURIComponent(query);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -491,7 +499,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return `/c/${params.changeNum}${suffix}`;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -505,7 +513,7 @@
 | 
			
		||||
        url += ',audit-log';
 | 
			
		||||
      }
 | 
			
		||||
      return url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -525,7 +533,7 @@
 | 
			
		||||
        url += ',dashboards';
 | 
			
		||||
      }
 | 
			
		||||
      return url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} params
 | 
			
		||||
@@ -533,7 +541,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _generateSettingsUrl(params) {
 | 
			
		||||
      return '/settings';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given an object of parameters, potentially including a `patchNum` or a
 | 
			
		||||
@@ -547,7 +555,7 @@
 | 
			
		||||
      if (params.patchNum) { range = '' + params.patchNum; }
 | 
			
		||||
      if (params.basePatchNum) { range = params.basePatchNum + '..' + range; }
 | 
			
		||||
      return range;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a set of params without a project, gets the project from the rest
 | 
			
		||||
@@ -571,7 +579,7 @@
 | 
			
		||||
            this._normalizePatchRangeParams(params);
 | 
			
		||||
            this._redirect(this._generateUrl(params));
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Normalizes the params object, and determines if the URL needs to be
 | 
			
		||||
@@ -600,7 +608,7 @@
 | 
			
		||||
        params.basePatchNum = null;
 | 
			
		||||
      }
 | 
			
		||||
      return needsRedirect;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Redirect the user to login using the given return-URL for redirection
 | 
			
		||||
@@ -611,7 +619,7 @@
 | 
			
		||||
      const basePath = this.getBaseUrl() || '';
 | 
			
		||||
      page(
 | 
			
		||||
          '/login/' + encodeURIComponent(returnUrl.substring(basePath.length)));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Hashes parsed by page.js exclude "inner" hashes, so a URL like "/a#b#c"
 | 
			
		||||
@@ -622,7 +630,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _getHashFromCanonicalPath(canonicalPath) {
 | 
			
		||||
      return canonicalPath.split('#').slice(1).join('#');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _parseLineAddress(hash) {
 | 
			
		||||
      const match = hash.match(LINE_ADDRESS_PATTERN);
 | 
			
		||||
@@ -631,7 +639,7 @@
 | 
			
		||||
        leftSide: !!match[1],
 | 
			
		||||
        lineNum: parseInt(match[2], 10),
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check to see if the user is logged in and return a promise that only
 | 
			
		||||
@@ -650,12 +658,12 @@
 | 
			
		||||
          return Promise.reject(new Error());
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**  Page.js middleware that warms the REST API's logged-in cache line. */
 | 
			
		||||
    _loadUserMiddleware(ctx, next) {
 | 
			
		||||
      this.$.restAPI.getLoggedIn().then(() => { next(); });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Map a route to a method on the router.
 | 
			
		||||
@@ -682,7 +690,7 @@
 | 
			
		||||
          this._redirectIfNotLoggedIn(data) : Promise.resolve();
 | 
			
		||||
        promise.then(() => { this[handlerName](data); });
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _startRouter() {
 | 
			
		||||
      const base = this.getBaseUrl();
 | 
			
		||||
@@ -878,7 +886,7 @@
 | 
			
		||||
      this._mapRoute(RoutePattern.DEFAULT, '_handleDefaultRoute');
 | 
			
		||||
 | 
			
		||||
      page.start();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} data
 | 
			
		||||
@@ -919,7 +927,7 @@
 | 
			
		||||
          this._redirect('/q/status:open');
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Decode an application/x-www-form-urlencoded string.
 | 
			
		||||
@@ -929,7 +937,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _decodeQueryString(qs) {
 | 
			
		||||
      return decodeURIComponent(qs.replace(PLUS_PATTERN, ' '));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse a query string (e.g. window.location.search) into an array of
 | 
			
		||||
@@ -961,7 +969,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      return params;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle dashboard routes. These may be user, or project dashboards.
 | 
			
		||||
@@ -986,7 +994,7 @@
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle custom dashboard routes.
 | 
			
		||||
@@ -1038,7 +1046,7 @@
 | 
			
		||||
      // Redirect /dashboard/ -> /dashboard/self.
 | 
			
		||||
      this._redirect('/dashboard/self');
 | 
			
		||||
      return Promise.resolve();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleProjectDashboardRoute(data) {
 | 
			
		||||
      const project = data.params[0];
 | 
			
		||||
@@ -1048,22 +1056,22 @@
 | 
			
		||||
        dashboard: decodeURIComponent(data.params[1]),
 | 
			
		||||
      });
 | 
			
		||||
      this.$.reporting.setRepoName(project);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupInfoRoute(data) {
 | 
			
		||||
      this._redirect('/admin/groups/' + encodeURIComponent(data.params[0]));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupSelfRedirectRoute(data) {
 | 
			
		||||
      this._redirect('/settings/#Groups');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
        view: Gerrit.Nav.View.GROUP,
 | 
			
		||||
        groupId: data.params[0],
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupAuditLogRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1071,7 +1079,7 @@
 | 
			
		||||
        detail: Gerrit.Nav.GroupDetailView.LOG,
 | 
			
		||||
        groupId: data.params[0],
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupMembersRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1079,7 +1087,7 @@
 | 
			
		||||
        detail: Gerrit.Nav.GroupDetailView.MEMBERS,
 | 
			
		||||
        groupId: data.params[0],
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupListOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1089,7 +1097,7 @@
 | 
			
		||||
        filter: null,
 | 
			
		||||
        openCreateModal: data.hash === 'create',
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupListFilterOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1098,7 +1106,7 @@
 | 
			
		||||
        offset: data.params.offset,
 | 
			
		||||
        filter: data.params.filter,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGroupListFilterRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1106,7 +1114,7 @@
 | 
			
		||||
        adminView: 'gr-admin-group-list',
 | 
			
		||||
        filter: data.params.filter || null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleProjectsOldRoute(data) {
 | 
			
		||||
      let params = '';
 | 
			
		||||
@@ -1119,7 +1127,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._redirect(`/admin/repos/${params}`);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRepoCommandsRoute(data) {
 | 
			
		||||
      const repo = data.params[0];
 | 
			
		||||
@@ -1129,7 +1137,7 @@
 | 
			
		||||
        repo,
 | 
			
		||||
      });
 | 
			
		||||
      this.$.reporting.setRepoName(repo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRepoAccessRoute(data) {
 | 
			
		||||
      const repo = data.params[0];
 | 
			
		||||
@@ -1139,7 +1147,7 @@
 | 
			
		||||
        repo,
 | 
			
		||||
      });
 | 
			
		||||
      this.$.reporting.setRepoName(repo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRepoDashboardsRoute(data) {
 | 
			
		||||
      const repo = data.params[0];
 | 
			
		||||
@@ -1149,7 +1157,7 @@
 | 
			
		||||
        repo,
 | 
			
		||||
      });
 | 
			
		||||
      this.$.reporting.setRepoName(repo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleBranchListOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1159,7 +1167,7 @@
 | 
			
		||||
        offset: data.params[2] || 0,
 | 
			
		||||
        filter: null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleBranchListFilterOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1169,7 +1177,7 @@
 | 
			
		||||
        offset: data.params.offset,
 | 
			
		||||
        filter: data.params.filter,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleBranchListFilterRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1178,7 +1186,7 @@
 | 
			
		||||
        repo: data.params.repo,
 | 
			
		||||
        filter: data.params.filter || null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTagListOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1188,7 +1196,7 @@
 | 
			
		||||
        offset: data.params[2] || 0,
 | 
			
		||||
        filter: null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTagListFilterOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1198,7 +1206,7 @@
 | 
			
		||||
        offset: data.params.offset,
 | 
			
		||||
        filter: data.params.filter,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTagListFilterRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1207,7 +1215,7 @@
 | 
			
		||||
        repo: data.params.repo,
 | 
			
		||||
        filter: data.params.filter || null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRepoListOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1217,7 +1225,7 @@
 | 
			
		||||
        filter: null,
 | 
			
		||||
        openCreateModal: data.hash === 'create',
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRepoListFilterOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1226,7 +1234,7 @@
 | 
			
		||||
        offset: data.params.offset,
 | 
			
		||||
        filter: data.params.filter,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRepoListFilterRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1234,19 +1242,19 @@
 | 
			
		||||
        adminView: 'gr-repo-list',
 | 
			
		||||
        filter: data.params.filter || null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateProjectRoute(data) {
 | 
			
		||||
      // Redirects the legacy route to the new route, which displays the project
 | 
			
		||||
      // list with a hash 'create'.
 | 
			
		||||
      this._redirect('/admin/repos#create');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateGroupRoute(data) {
 | 
			
		||||
      // Redirects the legacy route to the new route, which displays the group
 | 
			
		||||
      // list with a hash 'create'.
 | 
			
		||||
      this._redirect('/admin/groups#create');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRepoRoute(data) {
 | 
			
		||||
      const repo = data.params[0];
 | 
			
		||||
@@ -1255,7 +1263,7 @@
 | 
			
		||||
        repo,
 | 
			
		||||
      });
 | 
			
		||||
      this.$.reporting.setRepoName(repo);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePluginListOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1264,7 +1272,7 @@
 | 
			
		||||
        offset: data.params[1] || 0,
 | 
			
		||||
        filter: null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePluginListFilterOffsetRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1273,7 +1281,7 @@
 | 
			
		||||
        offset: data.params.offset,
 | 
			
		||||
        filter: data.params.filter,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePluginListFilterRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1281,14 +1289,14 @@
 | 
			
		||||
        adminView: 'gr-plugin-list',
 | 
			
		||||
        filter: data.params.filter || null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePluginListRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
        view: Gerrit.Nav.View.ADMIN,
 | 
			
		||||
        adminView: 'gr-plugin-list',
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleQueryRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
@@ -1296,15 +1304,15 @@
 | 
			
		||||
        query: data.params[0],
 | 
			
		||||
        offset: data.params[2],
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleQueryLegacySuffixRoute(ctx) {
 | 
			
		||||
      this._redirect(ctx.path.replace(LEGACY_QUERY_SUFFIX_PATTERN, ''));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleChangeNumberLegacyRoute(ctx) {
 | 
			
		||||
      this._redirect('/c/' + encodeURIComponent(ctx.params[0]));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleChangeRoute(ctx) {
 | 
			
		||||
      // Parameter order is based on the regex group number matched.
 | 
			
		||||
@@ -1318,7 +1326,7 @@
 | 
			
		||||
 | 
			
		||||
      this.$.reporting.setRepoName(params.project);
 | 
			
		||||
      this._redirectOrNavigate(params);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDiffRoute(ctx) {
 | 
			
		||||
      // Parameter order is based on the regex group number matched.
 | 
			
		||||
@@ -1338,7 +1346,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      this.$.reporting.setRepoName(params.project);
 | 
			
		||||
      this._redirectOrNavigate(params);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleChangeLegacyRoute(ctx) {
 | 
			
		||||
      // Parameter order is based on the regex group number matched.
 | 
			
		||||
@@ -1351,11 +1359,11 @@
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      this._normalizeLegacyRouteParams(params);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleLegacyLinenum(ctx) {
 | 
			
		||||
      this._redirect(ctx.path.replace(LEGACY_LINENUM_PATTERN, '#$1'));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDiffLegacyRoute(ctx) {
 | 
			
		||||
      // Parameter order is based on the regex group number matched.
 | 
			
		||||
@@ -1374,7 +1382,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._normalizeLegacyRouteParams(params);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDiffEditRoute(ctx) {
 | 
			
		||||
      // Parameter order is based on the regex group number matched.
 | 
			
		||||
@@ -1387,7 +1395,7 @@
 | 
			
		||||
        view: Gerrit.Nav.View.EDIT,
 | 
			
		||||
      });
 | 
			
		||||
      this.$.reporting.setRepoName(project);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleChangeEditRoute(ctx) {
 | 
			
		||||
      // Parameter order is based on the regex group number matched.
 | 
			
		||||
@@ -1400,7 +1408,7 @@
 | 
			
		||||
        edit: true,
 | 
			
		||||
      });
 | 
			
		||||
      this.$.reporting.setRepoName(project);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Normalize the patch range params for a the change or diff view and
 | 
			
		||||
@@ -1413,16 +1421,16 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this._setParams(params);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAgreementsRoute() {
 | 
			
		||||
      this._redirect('/settings/#Agreements');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNewAgreementsRoute(data) {
 | 
			
		||||
      data.params.view = Gerrit.Nav.View.AGREEMENTS;
 | 
			
		||||
      this._setParams(data.params);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSettingsLegacyRoute(data) {
 | 
			
		||||
      // email tokens may contain '+' but no space.
 | 
			
		||||
@@ -1433,11 +1441,11 @@
 | 
			
		||||
        view: Gerrit.Nav.View.SETTINGS,
 | 
			
		||||
        emailToken: token,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSettingsRoute(data) {
 | 
			
		||||
      this._setParams({view: Gerrit.Nav.View.SETTINGS});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRegisterRoute(ctx) {
 | 
			
		||||
      this._setParams({justRegistered: true});
 | 
			
		||||
@@ -1448,7 +1456,7 @@
 | 
			
		||||
 | 
			
		||||
      if (path[0] !== '/') { return; }
 | 
			
		||||
      this._redirect(this.getBaseUrl() + path);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handler for routes that should pass through the router and not be caught
 | 
			
		||||
@@ -1456,7 +1464,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _handlePassThroughRoute() {
 | 
			
		||||
      location.reload();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * URL may sometimes have /+/ encoded to / /.
 | 
			
		||||
@@ -1466,26 +1474,26 @@
 | 
			
		||||
      let hash = this._getHashFromCanonicalPath(ctx.canonicalPath);
 | 
			
		||||
      if (hash.length) { hash = '#' + hash; }
 | 
			
		||||
      this._redirect(`/c/${ctx.params[0]}/+/${ctx.params[1]}${hash}`);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePluginScreen(ctx) {
 | 
			
		||||
      const view = Gerrit.Nav.View.PLUGIN_SCREEN;
 | 
			
		||||
      const plugin = ctx.params[0];
 | 
			
		||||
      const screen = ctx.params[1];
 | 
			
		||||
      this._setParams({view, plugin, screen});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDocumentationSearchRoute(data) {
 | 
			
		||||
      this._setParams({
 | 
			
		||||
        view: Gerrit.Nav.View.DOCUMENTATION_SEARCH,
 | 
			
		||||
        filter: data.params.filter || null,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDocumentationSearchRedirectRoute(data) {
 | 
			
		||||
      this._redirect('/Documentation/q/filter:' +
 | 
			
		||||
          encodeURIComponent(data.params[0]));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDocumentationRedirectRoute(data) {
 | 
			
		||||
      if (data.params[1]) {
 | 
			
		||||
@@ -1494,7 +1502,7 @@
 | 
			
		||||
        // Redirect /Documentation to /Documentation/index.html
 | 
			
		||||
        this._redirect('/Documentation/index.html');
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Catchall route for when no other route is matched.
 | 
			
		||||
@@ -1507,7 +1515,7 @@
 | 
			
		||||
        // Route can be recognized by server, so we pass it to server.
 | 
			
		||||
        this._handlePassThroughRoute();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _show404() {
 | 
			
		||||
      // Note: the app's 404 display is tightly-coupled with catching 404
 | 
			
		||||
@@ -1515,6 +1523,8 @@
 | 
			
		||||
      // TODO: Decouple the gr-app error view from network responses.
 | 
			
		||||
      this._appElement().dispatchEvent(new CustomEvent('page-error',
 | 
			
		||||
          {detail: {response: {status: 404}}}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRouter.is, GrRouter);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -103,75 +103,80 @@
 | 
			
		||||
 | 
			
		||||
  const TOKENIZE_REGEX = /(?:[^\s"]+|"[^"]*")+\s*/g;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-search-bar',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    * @appliesMixin Gerrit.URLEncodingMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrSearchBar extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    Gerrit.URLEncodingBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-search-bar'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when a search is committed
 | 
			
		||||
     *
 | 
			
		||||
     * @event handle-search
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
      Gerrit.URLEncodingBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      value: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
        notify: true,
 | 
			
		||||
        observer: '_valueChanged',
 | 
			
		||||
      },
 | 
			
		||||
      keyEventTarget: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return document.body; },
 | 
			
		||||
      },
 | 
			
		||||
      query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._getSearchSuggestions.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        value: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
          notify: true,
 | 
			
		||||
          observer: '_valueChanged',
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      projectSuggestions: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return () => Promise.resolve([]);
 | 
			
		||||
        keyEventTarget: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return document.body; },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      groupSuggestions: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return () => Promise.resolve([]);
 | 
			
		||||
        query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._getSearchSuggestions.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      accountSuggestions: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return () => Promise.resolve([]);
 | 
			
		||||
        projectSuggestions: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return () => Promise.resolve([]);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _inputVal: String,
 | 
			
		||||
      _threshold: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 1,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        groupSuggestions: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return () => Promise.resolve([]);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        accountSuggestions: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return () => Promise.resolve([]);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        _inputVal: String,
 | 
			
		||||
        _threshold: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 1,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    keyboardShortcuts() {
 | 
			
		||||
      return {
 | 
			
		||||
        [this.Shortcut.SEARCH]: '_handleSearch',
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _valueChanged(value) {
 | 
			
		||||
      this._inputVal = value;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleInputCommit(e) {
 | 
			
		||||
      this._preventDefaultAndNavigateToInputVal(e);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This function is called in a few different cases:
 | 
			
		||||
@@ -205,7 +210,7 @@
 | 
			
		||||
          detail: {inputVal: this._inputVal},
 | 
			
		||||
        }));
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine what array of possible suggestions should be provided
 | 
			
		||||
@@ -247,7 +252,7 @@
 | 
			
		||||
              .filter(operator => operator.includes(input))
 | 
			
		||||
              .map(operator => ({text: operator})));
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the sorted, pruned list of suggestions for the current search query.
 | 
			
		||||
@@ -290,7 +295,7 @@
 | 
			
		||||
                  };
 | 
			
		||||
                });
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSearch(e) {
 | 
			
		||||
      const keyboardEvent = this.getKeyboardEvent(e);
 | 
			
		||||
@@ -300,6 +305,8 @@
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.searchInput.focus();
 | 
			
		||||
      this.$.searchInput.selectAll();
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrSearchBar.is, GrSearchBar);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -21,52 +21,58 @@
 | 
			
		||||
  const SELF_EXPRESSION = 'self';
 | 
			
		||||
  const ME_EXPRESSION = 'me';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-smart-search',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.DisplayNameMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrSmartSearch extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.DisplayNameBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-smart-search'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      searchQuery: String,
 | 
			
		||||
      _config: Object,
 | 
			
		||||
      _projectSuggestions: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._fetchProjects.bind(this);
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        searchQuery: String,
 | 
			
		||||
        _config: Object,
 | 
			
		||||
        _projectSuggestions: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._fetchProjects.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _groupSuggestions: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._fetchGroups.bind(this);
 | 
			
		||||
        _groupSuggestions: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._fetchGroups.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      _accountSuggestions: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._fetchAccounts.bind(this);
 | 
			
		||||
        _accountSuggestions: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._fetchAccounts.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.DisplayNameBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.$.restAPI.getConfig().then(cfg => {
 | 
			
		||||
        this._config = cfg;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSearch(e) {
 | 
			
		||||
      const input = e.detail.inputVal;
 | 
			
		||||
      if (input) {
 | 
			
		||||
        Gerrit.Nav.navigateToSearchQuery(input);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _accountOrAnon(name) {
 | 
			
		||||
      return this.getUserName(this._serverConfig, name, false);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch from the API the predicted projects.
 | 
			
		||||
@@ -86,7 +92,7 @@
 | 
			
		||||
            const keys = Object.keys(projects);
 | 
			
		||||
            return keys.map(key => ({text: predicate + ':' + key}));
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch from the API the predicted groups.
 | 
			
		||||
@@ -107,7 +113,7 @@
 | 
			
		||||
            const keys = Object.keys(groups);
 | 
			
		||||
            return keys.map(key => ({text: predicate + ':' + key}));
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch from the API the predicted accounts.
 | 
			
		||||
@@ -138,7 +144,7 @@
 | 
			
		||||
              return accounts;
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _mapAccountsHelper(accounts, predicate) {
 | 
			
		||||
      return accounts.map(account => ({
 | 
			
		||||
@@ -147,6 +153,8 @@
 | 
			
		||||
          `${predicate}:${account.email}` :
 | 
			
		||||
          `${predicate}:"${this._accountOrAnon(account)}"`,
 | 
			
		||||
      }));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrSmartSearch.is, GrSmartSearch);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -460,20 +460,27 @@
 | 
			
		||||
        this._isInRevisionOfPatchRange(comment, range);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-comment-api',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrCommentApi extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-comment-api'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      _changeComments: Object,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        _changeComments: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'reload-drafts': 'reloadDrafts',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('reload-drafts',
 | 
			
		||||
          changeNum => this.reloadDrafts(changeNum));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load all comments (with drafts and robot comments) for the given change
 | 
			
		||||
@@ -494,7 +501,7 @@
 | 
			
		||||
            robotComments, drafts, changeNum);
 | 
			
		||||
        return this._changeComments;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Re-initialize _changeComments with a new ChangeComments object, that
 | 
			
		||||
@@ -513,6 +520,8 @@
 | 
			
		||||
            this._changeComments.robotComments, drafts, changeNum);
 | 
			
		||||
        return this._changeComments;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCommentApi.is, GrCommentApi);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,20 +24,23 @@
 | 
			
		||||
    [Gerrit.CoverageType.NOT_INSTRUMENTED, 'Not instrumented by any tests.'],
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-coverage-layer',
 | 
			
		||||
  class GrCoverageLayer extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-coverage-layer'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * Must be sorted by code_range.start_line.
 | 
			
		||||
       * Must only contain ranges that match the side.
 | 
			
		||||
       *
 | 
			
		||||
       * @type {!Array<!Gerrit.CoverageRange>}
 | 
			
		||||
       */
 | 
			
		||||
      coverageRanges: Array,
 | 
			
		||||
      side: String,
 | 
			
		||||
        coverageRanges: Array,
 | 
			
		||||
        side: String,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * We keep track of the line number from the previous annotate() call,
 | 
			
		||||
       * and also of the index of the coverage range that had matched.
 | 
			
		||||
       * annotate() calls are coming in with increasing line numbers and
 | 
			
		||||
@@ -45,15 +48,16 @@
 | 
			
		||||
       * and efficient way for finding the coverage range that matches a given
 | 
			
		||||
       * line number.
 | 
			
		||||
       */
 | 
			
		||||
      _lineNumber: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 0,
 | 
			
		||||
      },
 | 
			
		||||
      _index: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 0,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _lineNumber: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 0,
 | 
			
		||||
        },
 | 
			
		||||
        _index: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 0,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Layer method to add annotations to a line.
 | 
			
		||||
@@ -102,6 +106,8 @@
 | 
			
		||||
        lineNumberEl.title = TOOLTIP_MAP.get(coverageRange.type);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrCoverageLayer.is, GrCoverageLayer);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -35,33 +35,36 @@
 | 
			
		||||
  const LEFT_SIDE_CLASS = 'target-side-left';
 | 
			
		||||
  const RIGHT_SIDE_CLASS = 'target-side-right';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-cursor',
 | 
			
		||||
  class GrDiffCursor extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-diff-cursor'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * Either DiffSides.LEFT or DiffSides.RIGHT.
 | 
			
		||||
       */
 | 
			
		||||
      side: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: DiffSides.RIGHT,
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {!HTMLElement|undefined} */
 | 
			
		||||
      diffRow: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        observer: '_rowChanged',
 | 
			
		||||
      },
 | 
			
		||||
        side: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: DiffSides.RIGHT,
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {!HTMLElement|undefined} */
 | 
			
		||||
        diffRow: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          observer: '_rowChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * The diff views to cursor through and listen to.
 | 
			
		||||
       */
 | 
			
		||||
      diffs: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
        diffs: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * If set, the cursor will attempt to move to the line number (instead of
 | 
			
		||||
       * the first chunk) the next time the diff renders. It is set back to null
 | 
			
		||||
       * when used. It should be only used if you want the line to be focused
 | 
			
		||||
@@ -71,56 +74,61 @@
 | 
			
		||||
       *
 | 
			
		||||
       * @type (?number)
 | 
			
		||||
       */
 | 
			
		||||
      initialLineNumber: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        initialLineNumber: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * The scroll behavior for the cursor. Values are 'never' and
 | 
			
		||||
       * 'keep-visible'. 'keep-visible' will only scroll if the cursor is beyond
 | 
			
		||||
       * the viewport.
 | 
			
		||||
       */
 | 
			
		||||
      _scrollBehavior: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: ScrollBehavior.KEEP_VISIBLE,
 | 
			
		||||
      },
 | 
			
		||||
        _scrollBehavior: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: ScrollBehavior.KEEP_VISIBLE,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _focusOnMove: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
        _focusOnMove: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _listeningForScroll: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
        _listeningForScroll: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_updateSideClass(side)',
 | 
			
		||||
      '_diffsChanged(diffs.splices)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_updateSideClass(side)',
 | 
			
		||||
        '_diffsChanged(diffs.splices)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      // Catch when users are scrolling as the view loads.
 | 
			
		||||
      this.listen(window, 'scroll', '_handleWindowScroll');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      this.unlisten(window, 'scroll', '_handleWindowScroll');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveLeft() {
 | 
			
		||||
      this.side = DiffSides.LEFT;
 | 
			
		||||
      if (this._isTargetBlank()) {
 | 
			
		||||
        this.moveUp();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveRight() {
 | 
			
		||||
      this.side = DiffSides.RIGHT;
 | 
			
		||||
      if (this._isTargetBlank()) {
 | 
			
		||||
        this.moveUp();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveDown() {
 | 
			
		||||
      if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) {
 | 
			
		||||
@@ -128,7 +136,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$.cursorManager.next();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveUp() {
 | 
			
		||||
      if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) {
 | 
			
		||||
@@ -136,7 +144,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$.cursorManager.previous();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveToNextChunk(opt_clipToTop) {
 | 
			
		||||
      this.$.cursorManager.next(this._isFirstRowOfChunk.bind(this),
 | 
			
		||||
@@ -144,22 +152,22 @@
 | 
			
		||||
            return target.parentNode.scrollHeight;
 | 
			
		||||
          }, opt_clipToTop);
 | 
			
		||||
      this._fixSide();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveToPreviousChunk() {
 | 
			
		||||
      this.$.cursorManager.previous(this._isFirstRowOfChunk.bind(this));
 | 
			
		||||
      this._fixSide();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveToNextCommentThread() {
 | 
			
		||||
      this.$.cursorManager.next(this._rowHasThread.bind(this));
 | 
			
		||||
      this._fixSide();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveToPreviousCommentThread() {
 | 
			
		||||
      this.$.cursorManager.previous(this._rowHasThread.bind(this));
 | 
			
		||||
      this._fixSide();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {number} number
 | 
			
		||||
@@ -172,7 +180,7 @@
 | 
			
		||||
        this.side = side;
 | 
			
		||||
        this.$.cursorManager.setCursor(row);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the line number element targeted by the cursor row and side.
 | 
			
		||||
@@ -190,7 +198,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return this.diffRow.querySelector(lineElSelector);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getTargetDiffElement() {
 | 
			
		||||
      if (!this.diffRow) return null;
 | 
			
		||||
@@ -202,12 +210,12 @@
 | 
			
		||||
        return hostOwner.host;
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    moveToFirstChunk() {
 | 
			
		||||
      this.$.cursorManager.moveToStart();
 | 
			
		||||
      this.moveToNextChunk(true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reInitCursor() {
 | 
			
		||||
      this._updateStops();
 | 
			
		||||
@@ -217,7 +225,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.moveToFirstChunk();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleWindowScroll() {
 | 
			
		||||
      if (this._listeningForScroll) {
 | 
			
		||||
@@ -225,7 +233,7 @@
 | 
			
		||||
        this._focusOnMove = false;
 | 
			
		||||
        this._listeningForScroll = false;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleDiffUpdate() {
 | 
			
		||||
      this._updateStops();
 | 
			
		||||
@@ -240,11 +248,11 @@
 | 
			
		||||
      this._scrollBehavior = ScrollBehavior.KEEP_VISIBLE;
 | 
			
		||||
      this._focusOnMove = true;
 | 
			
		||||
      this._listeningForScroll = false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDiffRenderStart() {
 | 
			
		||||
      this._listeningForScroll = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createCommentInPlace() {
 | 
			
		||||
      const diffWithRangeSelected = this.diffs.find(diff => {
 | 
			
		||||
@@ -258,7 +266,7 @@
 | 
			
		||||
          this.getTargetDiffElement().addDraftAtLine(line);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an object describing the location of the cursor. Such as
 | 
			
		||||
@@ -290,7 +298,7 @@
 | 
			
		||||
        leftSide: cell.matches('.left'),
 | 
			
		||||
        number: parseInt(number, 10),
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getViewMode() {
 | 
			
		||||
      if (!this.diffRow) {
 | 
			
		||||
@@ -302,24 +310,24 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return DiffViewMode.UNIFIED;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _rowHasSide(row) {
 | 
			
		||||
      const selector = (this.side === DiffSides.LEFT ? '.left' : '.right') +
 | 
			
		||||
          ' + .content';
 | 
			
		||||
      return !!row.querySelector(selector);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isFirstRowOfChunk(row) {
 | 
			
		||||
      const parentClassList = row.parentNode.classList;
 | 
			
		||||
      return parentClassList.contains('section') &&
 | 
			
		||||
          parentClassList.contains('delta') &&
 | 
			
		||||
          !row.previousSibling;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _rowHasThread(row) {
 | 
			
		||||
      return row.querySelector('.thread-group');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If we jumped to a row where there is no content on the current side then
 | 
			
		||||
@@ -331,7 +339,7 @@
 | 
			
		||||
        this.side = this.side === DiffSides.LEFT ?
 | 
			
		||||
          DiffSides.RIGHT : DiffSides.LEFT;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isTargetBlank() {
 | 
			
		||||
      if (!this.diffRow) {
 | 
			
		||||
@@ -341,14 +349,14 @@
 | 
			
		||||
      const actions = this._getActionsForRow();
 | 
			
		||||
      return (this.side === DiffSides.LEFT && !actions.left) ||
 | 
			
		||||
          (this.side === DiffSides.RIGHT && !actions.right);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _rowChanged(newRow, oldRow) {
 | 
			
		||||
      if (oldRow) {
 | 
			
		||||
        oldRow.classList.remove(LEFT_SIDE_CLASS, RIGHT_SIDE_CLASS);
 | 
			
		||||
      }
 | 
			
		||||
      this._updateSideClass();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateSideClass() {
 | 
			
		||||
      if (!this.diffRow) {
 | 
			
		||||
@@ -358,11 +366,11 @@
 | 
			
		||||
          this.diffRow);
 | 
			
		||||
      this.toggleClass(RIGHT_SIDE_CLASS, this.side === DiffSides.RIGHT,
 | 
			
		||||
          this.diffRow);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isActionType(type) {
 | 
			
		||||
      return type !== 'blank' && type !== 'contextControl';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getActionsForRow() {
 | 
			
		||||
      const actions = {left: false, right: false};
 | 
			
		||||
@@ -373,18 +381,18 @@
 | 
			
		||||
            this.diffRow.getAttribute('right-type'));
 | 
			
		||||
      }
 | 
			
		||||
      return actions;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getStops() {
 | 
			
		||||
      return this.diffs.reduce(
 | 
			
		||||
          (stops, diff) => {
 | 
			
		||||
            return stops.concat(diff.getCursorStops());
 | 
			
		||||
          }, []);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateStops() {
 | 
			
		||||
      this.$.cursorManager.stops = this._getStops();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Setup and tear down on-render listeners for any diffs that are added or
 | 
			
		||||
@@ -420,7 +428,7 @@
 | 
			
		||||
              'render-content', 'handleDiffUpdate');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _findRowByNumberAndFile(targetNumber, side, opt_path) {
 | 
			
		||||
      let stops;
 | 
			
		||||
@@ -437,6 +445,8 @@
 | 
			
		||||
          return stops[i];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffCursor.is, GrDiffCursor);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,24 +17,32 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-highlight',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDiffHighlight extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-diff-highlight'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {!Array<!Gerrit.HoveredRange>} */
 | 
			
		||||
      commentRanges: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      loggedIn: Boolean,
 | 
			
		||||
      /**
 | 
			
		||||
        commentRanges: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        loggedIn: Boolean,
 | 
			
		||||
        /**
 | 
			
		||||
       * querySelector can return null, so needs to be nullable.
 | 
			
		||||
       *
 | 
			
		||||
       * @type {?HTMLElement}
 | 
			
		||||
       * */
 | 
			
		||||
      _cachedDiffBuilder: Object,
 | 
			
		||||
        _cachedDiffBuilder: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Which range is currently selected by the user.
 | 
			
		||||
       * Stored in order to add a range-based comment
 | 
			
		||||
       * later.
 | 
			
		||||
@@ -42,21 +50,22 @@
 | 
			
		||||
       *
 | 
			
		||||
       * @type {{side: string, range: Gerrit.Range}|undefined}
 | 
			
		||||
       */
 | 
			
		||||
      selectedRange: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        selectedRange: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'comment-thread-mouseleave': '_handleCommentThreadMouseleave',
 | 
			
		||||
      'comment-thread-mouseenter': '_handleCommentThreadMouseenter',
 | 
			
		||||
      'create-comment-requested': '_handleRangeCommentRequest',
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('comment-thread-mouseleave',
 | 
			
		||||
          e => this._handleCommentThreadMouseleave(e));
 | 
			
		||||
      this.addEventListener('comment-thread-mouseenter',
 | 
			
		||||
          e => this._handleCommentThreadMouseenter(e));
 | 
			
		||||
      this.addEventListener('create-comment-requested',
 | 
			
		||||
          e => this._handleRangeCommentRequest(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get diffBuilder() {
 | 
			
		||||
      if (!this._cachedDiffBuilder) {
 | 
			
		||||
@@ -64,7 +73,7 @@
 | 
			
		||||
            Polymer.dom(this).querySelector('gr-diff-builder');
 | 
			
		||||
      }
 | 
			
		||||
      return this._cachedDiffBuilder;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determines side/line/range for a DOM selection and shows a tooltip.
 | 
			
		||||
@@ -93,7 +102,7 @@
 | 
			
		||||
      this.debounce(
 | 
			
		||||
          'selectionChange', () => this._handleSelection(selection, isMouseUp),
 | 
			
		||||
          10);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getThreadEl(e) {
 | 
			
		||||
      const path = Polymer.dom(e).path || [];
 | 
			
		||||
@@ -101,7 +110,7 @@
 | 
			
		||||
        if (pathEl.classList.contains('comment-thread')) return pathEl;
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCommentThreadMouseenter(e) {
 | 
			
		||||
      const threadEl = this._getThreadEl(e);
 | 
			
		||||
@@ -110,7 +119,7 @@
 | 
			
		||||
      if (index !== undefined) {
 | 
			
		||||
        this.set(['commentRanges', index, 'hovering'], true);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCommentThreadMouseleave(e) {
 | 
			
		||||
      const threadEl = this._getThreadEl(e);
 | 
			
		||||
@@ -119,7 +128,7 @@
 | 
			
		||||
      if (index !== undefined) {
 | 
			
		||||
        this.set(['commentRanges', index, 'hovering'], false);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _indexForThreadEl(threadEl) {
 | 
			
		||||
      const side = threadEl.getAttribute('comment-side');
 | 
			
		||||
@@ -128,7 +137,7 @@
 | 
			
		||||
      if (!range) return undefined;
 | 
			
		||||
 | 
			
		||||
      return this._indexOfCommentRange(side, range);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _indexOfCommentRange(side, range) {
 | 
			
		||||
      function rangesEqual(a, b) {
 | 
			
		||||
@@ -146,7 +155,7 @@
 | 
			
		||||
 | 
			
		||||
      return this.commentRanges.findIndex(commentRange =>
 | 
			
		||||
        commentRange.side === side && rangesEqual(commentRange.range, range));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current normalized selection.
 | 
			
		||||
@@ -184,7 +193,7 @@
 | 
			
		||||
          end: endRange.end,
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Normalize a specific DOM Range.
 | 
			
		||||
@@ -198,7 +207,7 @@
 | 
			
		||||
        end: this._normalizeSelectionSide(
 | 
			
		||||
            range.endContainer, range.endOffset),
 | 
			
		||||
      }, domRange);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adjust triple click selection for the whole line.
 | 
			
		||||
@@ -239,7 +248,7 @@
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
      return range;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert DOM Range selection to concrete numbers (line, column, side).
 | 
			
		||||
@@ -296,7 +305,7 @@
 | 
			
		||||
        line,
 | 
			
		||||
        column,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The only line in which add a comment tooltip is cut off is the first
 | 
			
		||||
@@ -312,7 +321,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      actionBox.positionBelow = true;
 | 
			
		||||
      actionBox.placeBelow(range);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isRangeValid(range) {
 | 
			
		||||
      if (!range || !range.start || !range.end) {
 | 
			
		||||
@@ -326,7 +335,7 @@
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      return true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSelection(selection, isMouseUp) {
 | 
			
		||||
      const normalizedRange = this._getNormalizedRange(selection);
 | 
			
		||||
@@ -398,12 +407,12 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this._positionActionBox(actionBox, start.line, start.node);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _fireCreateRangeComment(side, range) {
 | 
			
		||||
      this.fire('create-range-comment', {side, range});
 | 
			
		||||
      this._removeActionBox();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRangeCommentRequest(e) {
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
@@ -412,7 +421,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      const {side, range} = this.selectedRange;
 | 
			
		||||
      this._fireCreateRangeComment(side, range);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _removeActionBox() {
 | 
			
		||||
      this.selectedRange = undefined;
 | 
			
		||||
@@ -420,7 +429,7 @@
 | 
			
		||||
      if (actionBox) {
 | 
			
		||||
        Polymer.dom(this.root).removeChild(actionBox);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _convertOffsetToColumn(el, offset) {
 | 
			
		||||
      if (el instanceof Element && el.classList.contains('content')) {
 | 
			
		||||
@@ -436,7 +445,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return offset;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Traverse Element from right to left, call callback for each node.
 | 
			
		||||
@@ -461,7 +470,7 @@
 | 
			
		||||
        }
 | 
			
		||||
        node = nextNode;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get length of a node. If the node is a content node, then only give the
 | 
			
		||||
@@ -476,6 +485,8 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return GrAnnotation.getLength(node);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffHighlight.is, GrDiffHighlight);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -68,15 +68,23 @@
 | 
			
		||||
    RIGHT: 'right',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    */
 | 
			
		||||
  /**
 | 
			
		||||
   * Wrapper around gr-diff.
 | 
			
		||||
   *
 | 
			
		||||
   * Webcomponent fetching diffs and related data from restAPI and passing them
 | 
			
		||||
   * to the presentational gr-diff for rendering.
 | 
			
		||||
   */
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-host',
 | 
			
		||||
 | 
			
		||||
  class GrDiffHost extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-diff-host'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the user selects a line.
 | 
			
		||||
     * @event line-selected
 | 
			
		||||
@@ -94,182 +102,191 @@
 | 
			
		||||
     * @event diff-comments-modified
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      changeNum: String,
 | 
			
		||||
      noAutoRender: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      patchRange: Object,
 | 
			
		||||
      path: String,
 | 
			
		||||
      prefs: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
      },
 | 
			
		||||
      projectName: String,
 | 
			
		||||
      displayLine: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      isImageDiff: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeIsImageDiff(diff)',
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      commitRange: Object,
 | 
			
		||||
      filesWeblinks: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() {
 | 
			
		||||
          return {};
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        changeNum: String,
 | 
			
		||||
        noAutoRender: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        patchRange: Object,
 | 
			
		||||
        path: String,
 | 
			
		||||
        prefs: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
        },
 | 
			
		||||
        projectName: String,
 | 
			
		||||
        displayLine: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        isImageDiff: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeIsImageDiff(diff)',
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        commitRange: Object,
 | 
			
		||||
        filesWeblinks: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() {
 | 
			
		||||
            return {};
 | 
			
		||||
          },
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        hidden: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
        noRenderOnPrefsChange: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        comments: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_commentsChanged',
 | 
			
		||||
        },
 | 
			
		||||
        lineWrapping: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        viewMode: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: DiffViewMode.SIDE_BY_SIDE,
 | 
			
		||||
        },
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      hidden: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      noRenderOnPrefsChange: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      comments: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_commentsChanged',
 | 
			
		||||
      },
 | 
			
		||||
      lineWrapping: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      viewMode: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: DiffViewMode.SIDE_BY_SIDE,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Special line number which should not be collapsed into a shared region.
 | 
			
		||||
       * @type {{
 | 
			
		||||
       *  number: number,
 | 
			
		||||
       *  leftSide: {boolean}
 | 
			
		||||
       * }|null}
 | 
			
		||||
       */
 | 
			
		||||
      lineOfInterest: Object,
 | 
			
		||||
        lineOfInterest: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * If the diff fails to load, show the failure message in the diff rather
 | 
			
		||||
       * than bubbling the error up to the whole page. This is useful for when
 | 
			
		||||
       * loading inline diffs because one diff failing need not mark the whole
 | 
			
		||||
       * page with a failure.
 | 
			
		||||
       */
 | 
			
		||||
      showLoadFailure: Boolean,
 | 
			
		||||
        showLoadFailure: Boolean,
 | 
			
		||||
 | 
			
		||||
      isBlameLoaded: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        computed: '_computeIsBlameLoaded(_blame)',
 | 
			
		||||
      },
 | 
			
		||||
        isBlameLoaded: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          computed: '_computeIsBlameLoaded(_blame)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
        _loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?string} */
 | 
			
		||||
      _errorMessage: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        /** @type {?string} */
 | 
			
		||||
        _errorMessage: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _baseImage: Object,
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _revisionImage: Object,
 | 
			
		||||
      /**
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _baseImage: Object,
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _revisionImage: Object,
 | 
			
		||||
        /**
 | 
			
		||||
       * This is a DiffInfo object.
 | 
			
		||||
       */
 | 
			
		||||
      diff: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
        diff: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _blame: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _blame: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {!Array<!Gerrit.CoverageRange>}
 | 
			
		||||
       */
 | 
			
		||||
      _coverageRanges: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: () => [],
 | 
			
		||||
      },
 | 
			
		||||
        _coverageRanges: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: () => [],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _loadedWhitespaceLevel: String,
 | 
			
		||||
        _loadedWhitespaceLevel: String,
 | 
			
		||||
 | 
			
		||||
      _parentIndex: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        computed: '_computeParentIndex(patchRange.*)',
 | 
			
		||||
      },
 | 
			
		||||
        _parentIndex: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          computed: '_computeParentIndex(patchRange.*)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _syntaxHighlightingEnabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed:
 | 
			
		||||
        _syntaxHighlightingEnabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed:
 | 
			
		||||
          '_isSyntaxHighlightingEnabled(prefs.*, diff)',
 | 
			
		||||
      },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _layers: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: [],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _layers: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: [],
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      // These are named inconsistently for a reason:
 | 
			
		||||
      // The create-comment event is fired to indicate that we should
 | 
			
		||||
      // create a comment.
 | 
			
		||||
      // The comment-* events are just notifying that the comments did already
 | 
			
		||||
      // change in some way, and that we should update any models we may want
 | 
			
		||||
      // to keep in sync.
 | 
			
		||||
      'create-comment': '_handleCreateComment',
 | 
			
		||||
      'comment-discard': '_handleCommentDiscard',
 | 
			
		||||
      'comment-update': '_handleCommentUpdate',
 | 
			
		||||
      'comment-save': '_handleCommentSave',
 | 
			
		||||
 | 
			
		||||
      'render-start': '_handleRenderStart',
 | 
			
		||||
      'render-content': '_handleRenderContent',
 | 
			
		||||
 | 
			
		||||
      'normalize-range': '_handleNormalizeRange',
 | 
			
		||||
      'diff-context-expanded': '_handleDiffContextExpanded',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_whitespaceChanged(prefs.ignore_whitespace, _loadedWhitespaceLevel,' +
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_whitespaceChanged(prefs.ignore_whitespace, _loadedWhitespaceLevel,' +
 | 
			
		||||
          ' noRenderOnPrefsChange)',
 | 
			
		||||
      '_syntaxHighlightingChanged(noRenderOnPrefsChange, prefs.*)',
 | 
			
		||||
    ],
 | 
			
		||||
        '_syntaxHighlightingChanged(noRenderOnPrefsChange, prefs.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener(
 | 
			
		||||
          // These are named inconsistently for a reason:
 | 
			
		||||
          // The create-comment event is fired to indicate that we should
 | 
			
		||||
          // create a comment.
 | 
			
		||||
          // The comment-* events are just notifying that the comments did already
 | 
			
		||||
          // change in some way, and that we should update any models we may want
 | 
			
		||||
          // to keep in sync.
 | 
			
		||||
          'create-comment',
 | 
			
		||||
          e => this._handleCreateComment(e));
 | 
			
		||||
      this.addEventListener('comment-discard',
 | 
			
		||||
          e => this._handleCommentDiscard(e));
 | 
			
		||||
      this.addEventListener('comment-update',
 | 
			
		||||
          e => this._handleCommentUpdate(e));
 | 
			
		||||
      this.addEventListener('comment-save',
 | 
			
		||||
          e => this._handleCommentSave(e));
 | 
			
		||||
      this.addEventListener('render-start',
 | 
			
		||||
          () => this._handleRenderStart());
 | 
			
		||||
      this.addEventListener('render-content',
 | 
			
		||||
          () => this._handleRenderContent());
 | 
			
		||||
      this.addEventListener('normalize-range',
 | 
			
		||||
          event => this._handleNormalizeRange(event));
 | 
			
		||||
      this.addEventListener('diff-context-expanded',
 | 
			
		||||
          event => this._handleDiffContextExpanded(event));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      if (this._canReload()) {
 | 
			
		||||
        this.reload();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._getLoggedIn().then(loggedIn => {
 | 
			
		||||
        this._loggedIn = loggedIn;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {boolean=} shouldReportMetric indicate a new Diff Page. This is a
 | 
			
		||||
@@ -364,7 +381,7 @@
 | 
			
		||||
            console.warn('Error encountered loading diff:', err);
 | 
			
		||||
          })
 | 
			
		||||
          .then(() => { this._loading = false; });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getFilesWeblinks(diff) {
 | 
			
		||||
      if (!this.commitRange) {
 | 
			
		||||
@@ -378,30 +395,30 @@
 | 
			
		||||
            this.projectName, this.commitRange.commit, this.path,
 | 
			
		||||
            {weblinks: diff && diff.meta_b && diff.meta_b.web_links}),
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Cancel any remaining diff builder rendering work. */
 | 
			
		||||
    cancel() {
 | 
			
		||||
      this.$.diff.cancel();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {!Array<!HTMLElement>} */
 | 
			
		||||
    getCursorStops() {
 | 
			
		||||
      return this.$.diff.getCursorStops();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {boolean} */
 | 
			
		||||
    isRangeSelected() {
 | 
			
		||||
      return this.$.diff.isRangeSelected();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createRangeComment() {
 | 
			
		||||
      return this.$.diff.createRangeComment();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toggleLeftDiff() {
 | 
			
		||||
      this.$.diff.toggleLeftDiff();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load and display blame information for the base of the diff.
 | 
			
		||||
@@ -418,12 +435,12 @@
 | 
			
		||||
 | 
			
		||||
            this._blame = blame;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Unload blame information for the diff. */
 | 
			
		||||
    clearBlame() {
 | 
			
		||||
      this._blame = null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The thread elements in this diff, in no particular order.
 | 
			
		||||
@@ -432,31 +449,31 @@
 | 
			
		||||
    getThreadEls() {
 | 
			
		||||
      return Array.from(
 | 
			
		||||
          Polymer.dom(this.$.diff).querySelectorAll('.comment-thread'));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @param {HTMLElement} el */
 | 
			
		||||
    addDraftAtLine(el) {
 | 
			
		||||
      this.$.diff.addDraftAtLine(el);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    clearDiffContent() {
 | 
			
		||||
      this.$.diff.clearDiffContent();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expandAllContext() {
 | 
			
		||||
      this.$.diff.expandAllContext();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {!Promise} */
 | 
			
		||||
    _getLoggedIn() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {boolean}} */
 | 
			
		||||
    _canReload() {
 | 
			
		||||
      return !!this.changeNum && !!this.patchRange && !!this.path &&
 | 
			
		||||
          !this.noAutoRender;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {!Promise<!Object>} */
 | 
			
		||||
    _getDiff() {
 | 
			
		||||
@@ -472,7 +489,7 @@
 | 
			
		||||
            reject)
 | 
			
		||||
            .then(resolve);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleGetDiffError(response) {
 | 
			
		||||
      // Loading the diff may respond with 409 if the file is too large. In this
 | 
			
		||||
@@ -492,7 +509,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.fire('page-error', {response});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Report info about the diff response.
 | 
			
		||||
@@ -533,7 +550,7 @@
 | 
			
		||||
        this.$.reporting.reportInteraction(EVENT_NONZERO_REBASE,
 | 
			
		||||
            percentRebaseDelta);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} diff
 | 
			
		||||
@@ -550,7 +567,7 @@
 | 
			
		||||
        this._revisionImage = null;
 | 
			
		||||
        return Promise.resolve();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} diff
 | 
			
		||||
@@ -558,7 +575,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _computeIsImageDiff(diff) {
 | 
			
		||||
      return isImageDiff(diff);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _commentsChanged(newComments) {
 | 
			
		||||
      const allComments = [];
 | 
			
		||||
@@ -579,7 +596,7 @@
 | 
			
		||||
        const threadEl = this._createThreadElement(thread);
 | 
			
		||||
        this._attachThreadElement(threadEl);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Array<!Object>} comments
 | 
			
		||||
@@ -621,7 +638,7 @@
 | 
			
		||||
        threads.push(newThread);
 | 
			
		||||
      }
 | 
			
		||||
      return threads;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} blame
 | 
			
		||||
@@ -629,7 +646,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _computeIsBlameLoaded(blame) {
 | 
			
		||||
      return !!blame;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} diff
 | 
			
		||||
@@ -638,7 +655,7 @@
 | 
			
		||||
    _getImages(diff) {
 | 
			
		||||
      return this.$.restAPI.getImagesForDiff(this.changeNum, diff,
 | 
			
		||||
          this.patchRange);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @param {CustomEvent} e */
 | 
			
		||||
    _handleCreateComment(e) {
 | 
			
		||||
@@ -648,7 +665,7 @@
 | 
			
		||||
      threadEl.addOrEditDraft(lineNum, range);
 | 
			
		||||
 | 
			
		||||
      this.$.reporting.recordDraftInteraction();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets or creates a comment thread at a given location.
 | 
			
		||||
@@ -675,18 +692,18 @@
 | 
			
		||||
        this._attachThreadElement(threadEl);
 | 
			
		||||
      }
 | 
			
		||||
      return threadEl;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _attachThreadElement(threadEl) {
 | 
			
		||||
      Polymer.dom(this.$.diff).appendChild(threadEl);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _clearThreads() {
 | 
			
		||||
      for (const threadEl of this.getThreadEls()) {
 | 
			
		||||
        const parent = Polymer.dom(threadEl).parentNode;
 | 
			
		||||
        Polymer.dom(parent).removeChild(threadEl);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createThreadElement(thread) {
 | 
			
		||||
      const threadEl = document.createElement('gr-comment-thread');
 | 
			
		||||
@@ -717,7 +734,7 @@
 | 
			
		||||
      };
 | 
			
		||||
      threadEl.addEventListener('thread-discard', threadDiscardListener);
 | 
			
		||||
      return threadEl;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets a comment thread element at a given location.
 | 
			
		||||
@@ -746,7 +763,7 @@
 | 
			
		||||
      const filteredThreadEls = this._filterThreadElsForLocation(
 | 
			
		||||
          this.getThreadEls(), line, commentSide).filter(matchesRange);
 | 
			
		||||
      return filteredThreadEls.length ? filteredThreadEls[0] : null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Array<!HTMLElement>} threadEls
 | 
			
		||||
@@ -791,14 +808,14 @@
 | 
			
		||||
      }
 | 
			
		||||
      return threadEls.filter(threadEl =>
 | 
			
		||||
        matchers.some(matcher => matcher(threadEl)));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getIgnoreWhitespace() {
 | 
			
		||||
      if (!this.prefs || !this.prefs.ignore_whitespace) {
 | 
			
		||||
        return WHITESPACE_IGNORE_NONE;
 | 
			
		||||
      }
 | 
			
		||||
      return this.prefs.ignore_whitespace;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _whitespaceChanged(
 | 
			
		||||
        preferredWhitespaceLevel, loadedWhitespaceLevel,
 | 
			
		||||
@@ -816,7 +833,7 @@
 | 
			
		||||
          !noRenderOnPrefsChange) {
 | 
			
		||||
        this.reload();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _syntaxHighlightingChanged(noRenderOnPrefsChange, prefsChangeRecord) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -834,7 +851,7 @@
 | 
			
		||||
      if (!noRenderOnPrefsChange) {
 | 
			
		||||
        this.reload();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Object} patchRangeRecord
 | 
			
		||||
@@ -843,7 +860,7 @@
 | 
			
		||||
    _computeParentIndex(patchRangeRecord) {
 | 
			
		||||
      return this.isMergeParent(patchRangeRecord.base.basePatchNum) ?
 | 
			
		||||
        this.getParentIndex(patchRangeRecord.base.basePatchNum) : null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCommentSave(e) {
 | 
			
		||||
      const comment = e.detail.comment;
 | 
			
		||||
@@ -851,13 +868,13 @@
 | 
			
		||||
      const idx = this._findDraftIndex(comment, side);
 | 
			
		||||
      this.set(['comments', side, idx], comment);
 | 
			
		||||
      this._handleCommentSaveOrDiscard();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCommentDiscard(e) {
 | 
			
		||||
      const comment = e.detail.comment;
 | 
			
		||||
      this._removeComment(comment);
 | 
			
		||||
      this._handleCommentSaveOrDiscard();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Closure annotation for Polymer.prototype.push is off. Submitted PR:
 | 
			
		||||
@@ -878,17 +895,17 @@
 | 
			
		||||
      } else { // Create new draft.
 | 
			
		||||
        this.push(['comments', side], comment);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCommentSaveOrDiscard() {
 | 
			
		||||
      this.dispatchEvent(new CustomEvent(
 | 
			
		||||
          'diff-comments-modified', {bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _removeComment(comment) {
 | 
			
		||||
      const side = comment.__commentSide;
 | 
			
		||||
      this._removeCommentFromSide(comment, side);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _removeCommentFromSide(comment, side) {
 | 
			
		||||
      let idx = this._findCommentIndex(comment, side);
 | 
			
		||||
@@ -898,7 +915,7 @@
 | 
			
		||||
      if (idx !== -1) {
 | 
			
		||||
        this.splice('comments.' + side, idx, 1);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {number} */
 | 
			
		||||
    _findCommentIndex(comment, side) {
 | 
			
		||||
@@ -906,7 +923,7 @@
 | 
			
		||||
        return -1;
 | 
			
		||||
      }
 | 
			
		||||
      return this.comments[side].findIndex(item => item.id === comment.id);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {number} */
 | 
			
		||||
    _findDraftIndex(comment, side) {
 | 
			
		||||
@@ -915,7 +932,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      return this.comments[side].findIndex(
 | 
			
		||||
          item => item.__draftID === comment.__draftID);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isSyntaxHighlightingEnabled(preferenceChangeRecord, diff) {
 | 
			
		||||
      if (!preferenceChangeRecord ||
 | 
			
		||||
@@ -926,7 +943,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      return !this._anyLineTooLong(diff) &&
 | 
			
		||||
          this.$.diff.getDiffLength(diff) <= SYNTAX_MAX_DIFF_LENGTH;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return {boolean} whether any of the lines in diff are longer
 | 
			
		||||
@@ -940,7 +957,7 @@
 | 
			
		||||
          (section.a || []).concat(section.b || []);
 | 
			
		||||
        return lines.some(line => line.length >= SYNTAX_MAX_LINE_LENGTH);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _listenToViewportRender() {
 | 
			
		||||
      const renderUpdateListener = start => {
 | 
			
		||||
@@ -951,26 +968,28 @@
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      this.$.syntaxLayer.addListener(renderUpdateListener);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRenderStart() {
 | 
			
		||||
      this.$.reporting.time(TimingLabel.TOTAL);
 | 
			
		||||
      this.$.reporting.time(TimingLabel.CONTENT);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRenderContent() {
 | 
			
		||||
      this.$.reporting.timeEnd(TimingLabel.CONTENT);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNormalizeRange(event) {
 | 
			
		||||
      this.$.reporting.reportInteraction('normalize-range',
 | 
			
		||||
          `Modified invalid comment range on l. ${event.detail.lineNum}` +
 | 
			
		||||
          ` of the ${event.detail.side} side`);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDiffContextExpanded(event) {
 | 
			
		||||
      this.$.reporting.reportInteraction(
 | 
			
		||||
          'diff-context-expanded', event.detail.numLines);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffHost.is, GrDiffHost);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,34 +17,38 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-mode-selector',
 | 
			
		||||
  class GrDiffModeSelector extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-diff-mode-selector'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      mode: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        mode: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * If set to true, the user's preference will be updated every time a
 | 
			
		||||
       * button is tapped. Don't set to true if there is no user.
 | 
			
		||||
       */
 | 
			
		||||
      saveOnChange: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _VIEW_MODES: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
        value: {
 | 
			
		||||
          SIDE_BY_SIDE: 'SIDE_BY_SIDE',
 | 
			
		||||
          UNIFIED: 'UNIFIED_DIFF',
 | 
			
		||||
        saveOnChange: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _VIEW_MODES: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
          value: {
 | 
			
		||||
            SIDE_BY_SIDE: 'SIDE_BY_SIDE',
 | 
			
		||||
            UNIFIED: 'UNIFIED_DIFF',
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the mode. If save on change is enabled also update the preference.
 | 
			
		||||
@@ -54,18 +58,20 @@
 | 
			
		||||
        this.$.restAPI.savePreferences({diff_view: newMode});
 | 
			
		||||
      }
 | 
			
		||||
      this.mode = newMode;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSelectedClass(diffViewMode, buttonViewMode) {
 | 
			
		||||
      return buttonViewMode === diffViewMode ? 'selected' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSideBySideTap() {
 | 
			
		||||
      this.setMode(this._VIEW_MODES.SIDE_BY_SIDE);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUnifiedTap() {
 | 
			
		||||
      this.setMode(this._VIEW_MODES.UNIFIED);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffModeSelector.is, GrDiffModeSelector);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,39 +17,44 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-preferences-dialog',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDiffPreferencesDialog extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-diff-preferences-dialog'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      diffPrefs: Object,
 | 
			
		||||
        diffPrefs: Object,
 | 
			
		||||
 | 
			
		||||
      _diffPrefsChanged: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _diffPrefsChanged: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getFocusStops() {
 | 
			
		||||
      return {
 | 
			
		||||
        start: this.$.diffPreferences.$.contextSelect,
 | 
			
		||||
        end: this.$.saveButton,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resetFocus() {
 | 
			
		||||
      this.$.diffPreferences.$.contextSelect.focus();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHeaderClass(changed) {
 | 
			
		||||
      return changed ? 'edited' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCancelDiff(e) {
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.$.diffPrefsOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open() {
 | 
			
		||||
      this.$.diffPrefsOverlay.open().then(() => {
 | 
			
		||||
@@ -57,7 +62,7 @@
 | 
			
		||||
        this.$.diffPrefsOverlay.setFocusStops(focusStops);
 | 
			
		||||
        this.resetFocus();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveDiffPreferences() {
 | 
			
		||||
      this.$.diffPreferences.save().then(() => {
 | 
			
		||||
@@ -65,6 +70,8 @@
 | 
			
		||||
 | 
			
		||||
        this.$.diffPrefsOverlay.close();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffPreferencesDialog.is, GrDiffPreferencesDialog);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -64,71 +64,77 @@
 | 
			
		||||
   *    that the part that is within the context or has comments is shown, while
 | 
			
		||||
   *    the rest is not.
 | 
			
		||||
   */
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-processor',
 | 
			
		||||
  class GrDiffProcessor extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-diff-processor'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * The amount of context around collapsed groups.
 | 
			
		||||
       */
 | 
			
		||||
      context: Number,
 | 
			
		||||
        context: Number,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * The array of groups output by the processor.
 | 
			
		||||
       */
 | 
			
		||||
      groups: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
        groups: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Locations that should not be collapsed, including the locations of
 | 
			
		||||
       * comments.
 | 
			
		||||
       */
 | 
			
		||||
      keyLocations: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return {left: {}, right: {}}; },
 | 
			
		||||
      },
 | 
			
		||||
        keyLocations: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return {left: {}, right: {}}; },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * The maximum number of lines to process synchronously.
 | 
			
		||||
       */
 | 
			
		||||
      _asyncThreshold: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: 64,
 | 
			
		||||
      },
 | 
			
		||||
        _asyncThreshold: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: 64,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?number} */
 | 
			
		||||
      _nextStepHandle: Number,
 | 
			
		||||
      /**
 | 
			
		||||
        /** @type {?number} */
 | 
			
		||||
        _nextStepHandle: Number,
 | 
			
		||||
        /**
 | 
			
		||||
       * The promise last returned from `process()` while the asynchronous
 | 
			
		||||
       * processing is running - `null` otherwise. Provides a `cancel()`
 | 
			
		||||
       * method that rejects it with `{isCancelled: true}`.
 | 
			
		||||
       * @type {?Object}
 | 
			
		||||
       */
 | 
			
		||||
      _processPromise: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
      _isScrolling: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
        _processPromise: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
        _isScrolling: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.listen(window, 'scroll', '_handleWindowScroll');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      this.cancel();
 | 
			
		||||
      this.unlisten(window, 'scroll', '_handleWindowScroll');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleWindowScroll() {
 | 
			
		||||
      this._isScrolling = true;
 | 
			
		||||
      this.debounce('resetIsScrolling', () => {
 | 
			
		||||
        this._isScrolling = false;
 | 
			
		||||
      }, 50);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Asynchronously process the diff chunks into groups. As it processes, it
 | 
			
		||||
@@ -196,7 +202,7 @@
 | 
			
		||||
          }));
 | 
			
		||||
      return this._processPromise
 | 
			
		||||
          .finally(() => { this._processPromise = null; });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cancel any jobs that are running.
 | 
			
		||||
@@ -209,7 +215,7 @@
 | 
			
		||||
      if (this._processPromise) {
 | 
			
		||||
        this._processPromise.cancel();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Process the next uncollapsible chunk, or the next collapsible chunks.
 | 
			
		||||
@@ -236,15 +242,15 @@
 | 
			
		||||
 | 
			
		||||
      return this._processCollapsibleChunks(
 | 
			
		||||
          state, chunks, firstUncollapsibleChunkIndex);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _linesLeft(chunk) {
 | 
			
		||||
      return chunk.ab || chunk.a || [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _linesRight(chunk) {
 | 
			
		||||
      return chunk.ab || chunk.b || [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _firstUncollapsibleChunkIndex(chunks, offset) {
 | 
			
		||||
      let chunkIndex = offset;
 | 
			
		||||
@@ -253,11 +259,11 @@
 | 
			
		||||
        chunkIndex++;
 | 
			
		||||
      }
 | 
			
		||||
      return chunkIndex;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isCollapsibleChunk(chunk) {
 | 
			
		||||
      return (chunk.ab || chunk.common) && !chunk.keyLocation;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Process a stretch of collapsible chunks.
 | 
			
		||||
@@ -303,7 +309,7 @@
 | 
			
		||||
        groups,
 | 
			
		||||
        newChunkIndex: firstUncollapsibleChunkIndex,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _commonChunkLength(chunk) {
 | 
			
		||||
      console.assert(chunk.ab || chunk.common);
 | 
			
		||||
@@ -311,7 +317,7 @@
 | 
			
		||||
          !chunk.a || (chunk.b && chunk.a.length === chunk.b.length),
 | 
			
		||||
          `common chunk needs same number of a and b lines: `, chunk);
 | 
			
		||||
      return this._linesLeft(chunk).length;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Array<!Object>} chunks
 | 
			
		||||
@@ -327,7 +333,7 @@
 | 
			
		||||
        offsetRight += chunkLength;
 | 
			
		||||
        return group;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} chunk
 | 
			
		||||
@@ -343,7 +349,7 @@
 | 
			
		||||
      group.dueToRebase = chunk.due_to_rebase;
 | 
			
		||||
      group.ignoredWhitespaceOnly = chunk.common;
 | 
			
		||||
      return group;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _linesFromChunk(chunk, offsetLeft, offsetRight) {
 | 
			
		||||
      if (chunk.ab) {
 | 
			
		||||
@@ -366,7 +372,7 @@
 | 
			
		||||
            chunk[DiffHighlights.ADDED]));
 | 
			
		||||
      }
 | 
			
		||||
      return lines;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} lineType (GrDiffLine.Type)
 | 
			
		||||
@@ -380,7 +386,7 @@
 | 
			
		||||
        this._convertIntralineInfos(rows, opt_intralineInfos) : undefined;
 | 
			
		||||
      return rows.map((row, i) => this._lineFromRow(
 | 
			
		||||
          lineType, offset, offset, row, i, grDiffHighlights));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} type (GrDiffLine.Type)
 | 
			
		||||
@@ -403,14 +409,14 @@
 | 
			
		||||
        line.hasIntralineInfo = false;
 | 
			
		||||
      }
 | 
			
		||||
      return line;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _makeFileComments() {
 | 
			
		||||
      const line = new GrDiffLine(GrDiffLine.Type.BOTH);
 | 
			
		||||
      line.beforeNumber = GrDiffLine.FILE;
 | 
			
		||||
      line.afterNumber = GrDiffLine.FILE;
 | 
			
		||||
      return new GrDiffGroup(GrDiffGroup.Type.BOTH, [line]);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Split chunks into smaller chunks of the same kind.
 | 
			
		||||
@@ -452,7 +458,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return newChunks;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * In order to show key locations, such as comments, out of the bounds of
 | 
			
		||||
@@ -503,7 +509,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return result;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return {!Array<{offset: number, keyLocation: boolean}>} Offsets of the
 | 
			
		||||
@@ -534,7 +540,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return result;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _splitAtChunkEnds(lines, chunkEnds) {
 | 
			
		||||
      const result = [];
 | 
			
		||||
@@ -545,7 +551,7 @@
 | 
			
		||||
        lastChunkEndOffset = offset;
 | 
			
		||||
      }
 | 
			
		||||
      return result;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts `IntralineInfo`s return by the API to `GrLineHighlights` used
 | 
			
		||||
@@ -595,7 +601,7 @@
 | 
			
		||||
        normalized.push(lineHighlight);
 | 
			
		||||
      }
 | 
			
		||||
      return normalized;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If a group is an addition or a removal, break it down into smaller groups
 | 
			
		||||
@@ -625,7 +631,7 @@
 | 
			
		||||
            }
 | 
			
		||||
            return subChunk;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given an array and a size, return an array of arrays where no inner array
 | 
			
		||||
@@ -643,6 +649,8 @@
 | 
			
		||||
      const tail = array.slice(array.length - size);
 | 
			
		||||
 | 
			
		||||
      return this._breakdown(head, size).concat([tail]);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffProcessor.is, GrDiffProcessor);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -30,35 +30,46 @@
 | 
			
		||||
 | 
			
		||||
  const getNewCache = () => { return {left: null, right: null}; };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-selection',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.DomUtilMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDiffSelection extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.DomUtilBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-diff-selection'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      diff: Object,
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      _cachedDiffBuilder: Object,
 | 
			
		||||
      _linesCache: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: getNewCache(),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        diff: Object,
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        _cachedDiffBuilder: Object,
 | 
			
		||||
        _linesCache: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: getNewCache(),
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_diffChanged(diff)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_diffChanged(diff)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      copy: '_handleCopy',
 | 
			
		||||
      down: '_handleDown',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.DomUtilBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('copy',
 | 
			
		||||
          e => this._handleCopy(e));
 | 
			
		||||
      Polymer.Gestures.addListener(this, 'down',
 | 
			
		||||
          e => this._handleDown(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.classList.add(SelectionClass.RIGHT);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get diffBuilder() {
 | 
			
		||||
      if (!this._cachedDiffBuilder) {
 | 
			
		||||
@@ -66,11 +77,11 @@
 | 
			
		||||
            Polymer.dom(this).querySelector('gr-diff-builder');
 | 
			
		||||
      }
 | 
			
		||||
      return this._cachedDiffBuilder;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _diffChanged() {
 | 
			
		||||
      this._linesCache = getNewCache();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDownOnRangeComment(node) {
 | 
			
		||||
      if (node &&
 | 
			
		||||
@@ -85,7 +96,7 @@
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDown(e) {
 | 
			
		||||
      // Handle the down event on comment thread in Polymer 2
 | 
			
		||||
@@ -115,7 +126,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this._setClasses(targetClasses);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the provided list of classes on the element, to the exclusion of all
 | 
			
		||||
@@ -138,11 +149,11 @@
 | 
			
		||||
          this.classList.add(_class);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCopyEventTarget(e) {
 | 
			
		||||
      return Polymer.dom(e).rootTarget;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Utility function to determine whether an element is a descendant of
 | 
			
		||||
@@ -155,7 +166,7 @@
 | 
			
		||||
    _elementDescendedFromClass(element, className) {
 | 
			
		||||
      return this.descendedFromClass(element, className,
 | 
			
		||||
          this.diffBuilder.diffElement);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCopy(e) {
 | 
			
		||||
      let commentSelected = false;
 | 
			
		||||
@@ -175,7 +186,7 @@
 | 
			
		||||
        e.clipboardData.setData('Text', text);
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * For Polymer 2, use shadowRoot.getSelection instead.
 | 
			
		||||
@@ -186,7 +197,7 @@
 | 
			
		||||
        diffHost.shadowRoot &&
 | 
			
		||||
        diffHost.shadowRoot.getSelection();
 | 
			
		||||
      return selection ? selection: window.getSelection();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the text of the current selection. If commentSelected is
 | 
			
		||||
@@ -225,7 +236,7 @@
 | 
			
		||||
 | 
			
		||||
      return this._getRangeFromDiff(startLineNum, range.startOffset, endLineNum,
 | 
			
		||||
          range.endOffset, side);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Query the diff object for the selected lines.
 | 
			
		||||
@@ -247,7 +258,7 @@
 | 
			
		||||
        lines[0] = lines[0].substring(startOffset);
 | 
			
		||||
      }
 | 
			
		||||
      return lines.join('\n');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Query the diff object for the lines from a particular side.
 | 
			
		||||
@@ -270,7 +281,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      this._linesCache[side] = lines;
 | 
			
		||||
      return lines;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Query the diffElement for comments and check whether they lie inside the
 | 
			
		||||
@@ -308,7 +319,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return content.join('\n');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a DOM node, a selection, and a selection range, recursively get all
 | 
			
		||||
@@ -338,6 +349,8 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return text;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffSelection.is, GrDiffSelection);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,23 @@
 | 
			
		||||
    UNIFIED: 'UNIFIED_DIFF',
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff-view',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PathListMixin
 | 
			
		||||
    * @appliesMixin Gerrit.RESTClientMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDiffView extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
    Gerrit.PathListBehavior,
 | 
			
		||||
    Gerrit.RESTClientBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-diff-view'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the title of the page should change.
 | 
			
		||||
     *
 | 
			
		||||
@@ -48,154 +62,152 @@
 | 
			
		||||
     * @event show-alert
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
      keyEventTarget: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return document.body; },
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
        keyEventTarget: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return document.body; },
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {{ diffMode: (string|undefined) }}
 | 
			
		||||
       */
 | 
			
		||||
      changeViewState: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        value() { return {}; },
 | 
			
		||||
        observer: '_changeViewStateChanged',
 | 
			
		||||
      },
 | 
			
		||||
      disableDiffPrefs: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _diffPrefsDisabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _patchRange: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _commitRange: Object,
 | 
			
		||||
      /**
 | 
			
		||||
        changeViewState: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          value() { return {}; },
 | 
			
		||||
          observer: '_changeViewStateChanged',
 | 
			
		||||
        },
 | 
			
		||||
        disableDiffPrefs: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _diffPrefsDisabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeDiffPrefsDisabled(disableDiffPrefs, _loggedIn)',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _patchRange: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _commitRange: Object,
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {{
 | 
			
		||||
       *  subject: string,
 | 
			
		||||
       *  project: string,
 | 
			
		||||
       *  revisions: string,
 | 
			
		||||
       * }}
 | 
			
		||||
       */
 | 
			
		||||
      _change: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _changeComments: Object,
 | 
			
		||||
      _changeNum: String,
 | 
			
		||||
      /**
 | 
			
		||||
        _change: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _changeComments: Object,
 | 
			
		||||
        _changeNum: String,
 | 
			
		||||
        /**
 | 
			
		||||
       * This is a DiffInfo object.
 | 
			
		||||
       * This is retrieved and owned by a child component.
 | 
			
		||||
       */
 | 
			
		||||
      _diff: Object,
 | 
			
		||||
      // An array specifically formatted to be used in a gr-dropdown-list
 | 
			
		||||
      // element for selected a file to view.
 | 
			
		||||
      _formattedFiles: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_formatFilesForDropdown(_fileList, ' +
 | 
			
		||||
        _diff: Object,
 | 
			
		||||
        // An array specifically formatted to be used in a gr-dropdown-list
 | 
			
		||||
        // element for selected a file to view.
 | 
			
		||||
        _formattedFiles: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_formatFilesForDropdown(_fileList, ' +
 | 
			
		||||
            '_patchRange.patchNum, _changeComments)',
 | 
			
		||||
      },
 | 
			
		||||
      // An sorted array of files, as returned by the rest API.
 | 
			
		||||
      _fileList: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_pathChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _fileNum: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        computed: '_computeFileNum(_path, _formattedFiles)',
 | 
			
		||||
      },
 | 
			
		||||
      _loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _prefs: Object,
 | 
			
		||||
      _localPrefs: Object,
 | 
			
		||||
      _projectConfig: Object,
 | 
			
		||||
      _userPrefs: Object,
 | 
			
		||||
      _diffMode: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_getDiffViewMode(changeViewState.diffMode, _userPrefs)',
 | 
			
		||||
      },
 | 
			
		||||
      _isImageDiff: Boolean,
 | 
			
		||||
      _filesWeblinks: Object,
 | 
			
		||||
        },
 | 
			
		||||
        // An sorted array of files, as returned by the rest API.
 | 
			
		||||
        _fileList: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_pathChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _fileNum: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          computed: '_computeFileNum(_path, _formattedFiles)',
 | 
			
		||||
        },
 | 
			
		||||
        _loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _prefs: Object,
 | 
			
		||||
        _localPrefs: Object,
 | 
			
		||||
        _projectConfig: Object,
 | 
			
		||||
        _userPrefs: Object,
 | 
			
		||||
        _diffMode: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_getDiffViewMode(changeViewState.diffMode, _userPrefs)',
 | 
			
		||||
        },
 | 
			
		||||
        _isImageDiff: Boolean,
 | 
			
		||||
        _filesWeblinks: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Map of paths in the current change and patch range that have comments
 | 
			
		||||
       * or drafts or robot comments.
 | 
			
		||||
       */
 | 
			
		||||
      _commentMap: Object,
 | 
			
		||||
        _commentMap: Object,
 | 
			
		||||
 | 
			
		||||
      _commentsForDiff: Object,
 | 
			
		||||
        _commentsForDiff: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Object to contain the path of the next and previous file in the current
 | 
			
		||||
       * change and patch range that has comments.
 | 
			
		||||
       */
 | 
			
		||||
      _commentSkips: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computeCommentSkips(_commentMap, _fileList, _path)',
 | 
			
		||||
      },
 | 
			
		||||
      _panelFloatingDisabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: () => { return window.PANEL_FLOATING_DISABLED; },
 | 
			
		||||
      },
 | 
			
		||||
      _editMode: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        computed: '_computeEditMode(_patchRange.*)',
 | 
			
		||||
      },
 | 
			
		||||
      _isBlameLoaded: Boolean,
 | 
			
		||||
      _isBlameLoading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _allPatchSets: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: 'computeAllPatchSets(_change, _change.revisions.*)',
 | 
			
		||||
      },
 | 
			
		||||
      _revisionInfo: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_getRevisionInfo(_change)',
 | 
			
		||||
      },
 | 
			
		||||
      _reviewedFiles: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: () => new Set(),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _commentSkips: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computeCommentSkips(_commentMap, _fileList, _path)',
 | 
			
		||||
        },
 | 
			
		||||
        _panelFloatingDisabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: () => { return window.PANEL_FLOATING_DISABLED; },
 | 
			
		||||
        },
 | 
			
		||||
        _editMode: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          computed: '_computeEditMode(_patchRange.*)',
 | 
			
		||||
        },
 | 
			
		||||
        _isBlameLoaded: Boolean,
 | 
			
		||||
        _isBlameLoading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _allPatchSets: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: 'computeAllPatchSets(_change, _change.revisions.*)',
 | 
			
		||||
        },
 | 
			
		||||
        _revisionInfo: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_getRevisionInfo(_change)',
 | 
			
		||||
        },
 | 
			
		||||
        _reviewedFiles: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: () => new Set(),
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
      Gerrit.PathListBehavior,
 | 
			
		||||
      Gerrit.RESTClientBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_getProjectConfig(_change.project)',
 | 
			
		||||
        '_getFiles(_changeNum, _patchRange.*)',
 | 
			
		||||
        '_setReviewedObserver(_loggedIn, params.*, _prefs)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_getProjectConfig(_change.project)',
 | 
			
		||||
      '_getFiles(_changeNum, _patchRange.*)',
 | 
			
		||||
      '_setReviewedObserver(_loggedIn, params.*, _prefs)',
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    keyBindings: {
 | 
			
		||||
      esc: '_handleEscKey',
 | 
			
		||||
    },
 | 
			
		||||
    get keyBindings() {
 | 
			
		||||
      return {
 | 
			
		||||
        esc: '_handleEscKey',
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    keyboardShortcuts() {
 | 
			
		||||
      return {
 | 
			
		||||
@@ -230,37 +242,38 @@
 | 
			
		||||
        [this.Shortcut.EXPAND_ALL_COMMENT_THREADS]: null,
 | 
			
		||||
        [this.Shortcut.COLLAPSE_ALL_COMMENT_THREADS]: null,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._getLoggedIn().then(loggedIn => {
 | 
			
		||||
        this._loggedIn = loggedIn;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      this.$.cursor.push('diffs', this.$.diffHost);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLoggedIn() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getProjectConfig(project) {
 | 
			
		||||
      return this.$.restAPI.getProjectConfig(project).then(
 | 
			
		||||
          config => {
 | 
			
		||||
            this._projectConfig = config;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChangeDetail(changeNum) {
 | 
			
		||||
      return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => {
 | 
			
		||||
        this._change = change;
 | 
			
		||||
        return change;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChangeEdit(changeNum) {
 | 
			
		||||
      return this.$.restAPI.getChangeEdit(this._changeNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getFiles(changeNum, patchRangeRecord) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -274,25 +287,25 @@
 | 
			
		||||
          changeNum, patchRange).then(files => {
 | 
			
		||||
        this._fileList = files;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getDiffPreferences() {
 | 
			
		||||
      return this.$.restAPI.getDiffPreferences().then(prefs => {
 | 
			
		||||
        this._prefs = prefs;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getPreferences() {
 | 
			
		||||
      return this.$.restAPI.getPreferences();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getWindowWidth() {
 | 
			
		||||
      return window.innerWidth;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleReviewedChange(e) {
 | 
			
		||||
      this._setReviewed(Polymer.dom(e).rootTarget.checked);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setReviewed(reviewed) {
 | 
			
		||||
      if (this._editMode) { return; }
 | 
			
		||||
@@ -301,12 +314,12 @@
 | 
			
		||||
        this.fire('show-alert', {message: ERR_REVIEW_STATUS});
 | 
			
		||||
        throw err;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _saveReviewedState(reviewed) {
 | 
			
		||||
      return this.$.restAPI.saveFileReviewed(this._changeNum,
 | 
			
		||||
          this._patchRange.patchNum, this._path, reviewed);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleToggleFileReviewed(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -314,7 +327,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._setReviewed(!this.$.reviewed.checked);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleEscKey(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -322,21 +335,21 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.diffHost.displayLine = false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleLeftPane(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.cursor.moveLeft();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRightPane(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.cursor.moveRight();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePrevLineOrFileWithComments(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
@@ -350,7 +363,7 @@
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.diffHost.displayLine = true;
 | 
			
		||||
      this.$.cursor.moveUp();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNextLineOrFileWithComments(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
@@ -364,7 +377,7 @@
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.diffHost.displayLine = true;
 | 
			
		||||
      this.$.cursor.moveDown();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _moveToPreviousFileWithComment() {
 | 
			
		||||
      if (!this._commentSkips) { return; }
 | 
			
		||||
@@ -378,7 +391,7 @@
 | 
			
		||||
 | 
			
		||||
      Gerrit.Nav.navigateToDiff(this._change, this._commentSkips.previous,
 | 
			
		||||
          this._patchRange.patchNum, this._patchRange.basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _moveToNextFileWithComment() {
 | 
			
		||||
      if (!this._commentSkips) { return; }
 | 
			
		||||
@@ -391,14 +404,14 @@
 | 
			
		||||
 | 
			
		||||
      Gerrit.Nav.navigateToDiff(this._change, this._commentSkips.next,
 | 
			
		||||
          this._patchRange.patchNum, this._patchRange.basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNewComment(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
          this.modifierPressed(e)) { return; }
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.cursor.createCommentInPlace();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePrevFile(e) {
 | 
			
		||||
      // Check for meta key to avoid overriding native chrome shortcut.
 | 
			
		||||
@@ -407,7 +420,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._navToFile(this._path, this._fileList, -1);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNextFile(e) {
 | 
			
		||||
      // Check for meta key to avoid overriding native chrome shortcut.
 | 
			
		||||
@@ -416,7 +429,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._navToFile(this._path, this._fileList, 1);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNextChunkOrCommentThread(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
@@ -428,7 +441,7 @@
 | 
			
		||||
        if (this.modifierPressed(e)) { return; }
 | 
			
		||||
        this.$.cursor.moveToNextChunk();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePrevChunkOrCommentThread(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
@@ -440,7 +453,7 @@
 | 
			
		||||
        if (this.modifierPressed(e)) { return; }
 | 
			
		||||
        this.$.cursor.moveToPreviousChunk();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleOpenReplyDialogOrToggleLeftPane(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
@@ -458,7 +471,7 @@
 | 
			
		||||
      this.set('changeViewState.showReplyDialog', true);
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._navToChangeView();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleUpToChange(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -466,7 +479,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this._navToChangeView();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCommaKey(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -475,7 +488,7 @@
 | 
			
		||||
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.diffPreferencesDialog.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleToggleDiffMode(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e) ||
 | 
			
		||||
@@ -487,7 +500,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$.modeSelect.setMode(DiffViewMode.SIDE_BY_SIDE);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _navToChangeView() {
 | 
			
		||||
      if (!this._changeNum || !this._patchRange.patchNum) { return; }
 | 
			
		||||
@@ -495,7 +508,7 @@
 | 
			
		||||
          this._change,
 | 
			
		||||
          this._patchRange,
 | 
			
		||||
          this._change && this._change.revisions);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _navToFile(path, fileList, direction) {
 | 
			
		||||
      const newPath = this._getNavLinkPath(path, fileList, direction);
 | 
			
		||||
@@ -511,7 +524,7 @@
 | 
			
		||||
 | 
			
		||||
      Gerrit.Nav.navigateToDiff(this._change, newPath.path,
 | 
			
		||||
          this._patchRange.patchNum, this._patchRange.basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {?string} path The path of the current file being shown.
 | 
			
		||||
@@ -535,7 +548,7 @@
 | 
			
		||||
            this._change && this._change.revisions);
 | 
			
		||||
      }
 | 
			
		||||
      return this._getDiffUrl(this._change, this._patchRange, newPath.path);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gives an object representing the target of navigating either left or
 | 
			
		||||
@@ -575,7 +588,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return {path: fileList[idx]};
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getReviewedFiles(changeNum, patchNum) {
 | 
			
		||||
      return this.$.restAPI.getReviewedFiles(changeNum, patchNum)
 | 
			
		||||
@@ -583,13 +596,13 @@
 | 
			
		||||
            this._reviewedFiles = new Set(files);
 | 
			
		||||
            return this._reviewedFiles;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getReviewedStatus(editMode, changeNum, patchNum, path) {
 | 
			
		||||
      if (editMode) { return Promise.resolve(false); }
 | 
			
		||||
      return this._getReviewedFiles(changeNum, patchNum)
 | 
			
		||||
          .then(files => files.has(path));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(value) {
 | 
			
		||||
      if (value.view !== Gerrit.Nav.View.DIFF) { return; }
 | 
			
		||||
@@ -676,7 +689,7 @@
 | 
			
		||||
        // If diff view displayed has not ended yet, it ends here.
 | 
			
		||||
        this.$.reporting.diffViewDisplayed();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _changeViewStateChanged(changeViewState) {
 | 
			
		||||
      if (changeViewState.diffMode === null) {
 | 
			
		||||
@@ -685,7 +698,7 @@
 | 
			
		||||
          this.set('changeViewState.diffMode', prefs.default_diff_view);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _setReviewedObserver(_loggedIn, paramsRecord, _prefs) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -709,7 +722,7 @@
 | 
			
		||||
      if (params.view === Gerrit.Nav.View.DIFF) {
 | 
			
		||||
        this._setReviewed(true);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If the params specify a diff address then configure the diff cursor.
 | 
			
		||||
@@ -722,14 +735,14 @@
 | 
			
		||||
        this.$.cursor.side = DiffSides.RIGHT;
 | 
			
		||||
      }
 | 
			
		||||
      this.$.cursor.initialLineNumber = params.lineNum;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLineOfInterest(params) {
 | 
			
		||||
      // If there is a line number specified, pass it along to the diff so that
 | 
			
		||||
      // it will not get collapsed.
 | 
			
		||||
      if (!params.lineNum) { return null; }
 | 
			
		||||
      return {number: params.lineNum, leftSide: params.leftSide};
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _pathChanged(path) {
 | 
			
		||||
      if (path) {
 | 
			
		||||
@@ -741,7 +754,7 @@
 | 
			
		||||
 | 
			
		||||
      this.set('changeViewState.selectedFileIndex',
 | 
			
		||||
          this._fileList.indexOf(path));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getDiffUrl(change, patchRange, path) {
 | 
			
		||||
      if ([change, patchRange, path].some(arg => arg === undefined)) {
 | 
			
		||||
@@ -749,7 +762,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      return Gerrit.Nav.getUrlForDiff(change, path, patchRange.patchNum,
 | 
			
		||||
          patchRange.basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _patchRangeStr(patchRange) {
 | 
			
		||||
      let patchStr = patchRange.patchNum;
 | 
			
		||||
@@ -758,7 +771,7 @@
 | 
			
		||||
        patchStr = patchRange.basePatchNum + '..' + patchRange.patchNum;
 | 
			
		||||
      }
 | 
			
		||||
      return patchStr;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When the latest patch of the change is selected (and there is no base
 | 
			
		||||
@@ -782,7 +795,7 @@
 | 
			
		||||
        basePatchNum = patchRange.basePatchNum;
 | 
			
		||||
      }
 | 
			
		||||
      return {patchNum, basePatchNum};
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChangePath(change, patchRange, revisions) {
 | 
			
		||||
      if ([change, patchRange].some(arg => arg === undefined)) {
 | 
			
		||||
@@ -791,16 +804,16 @@
 | 
			
		||||
      const range = this._getChangeUrlRange(patchRange, revisions);
 | 
			
		||||
      return Gerrit.Nav.getUrlForChange(change, range.patchNum,
 | 
			
		||||
          range.basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _navigateToChange(change, patchRange, revisions) {
 | 
			
		||||
      const range = this._getChangeUrlRange(patchRange, revisions);
 | 
			
		||||
      Gerrit.Nav.navigateToChange(change, range.patchNum, range.basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeChangePath(change, patchRangeRecord, revisions) {
 | 
			
		||||
      return this._getChangePath(change, patchRangeRecord.base, revisions);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _formatFilesForDropdown(fileList, patchNum, changeComments) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -824,7 +837,7 @@
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      return dropdownContent;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeCommentString(changeComments, patchNum, path) {
 | 
			
		||||
      const unresolvedCount = changeComments.computeUnresolvedNum(patchNum,
 | 
			
		||||
@@ -840,11 +853,11 @@
 | 
			
		||||
          (commentString && unresolvedString ? ', ' : '') +
 | 
			
		||||
          // Add parentheses around unresolved if it exists.
 | 
			
		||||
          (unresolvedString ? `${unresolvedString}` : '');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePrefsButtonHidden(prefs, prefsDisabled) {
 | 
			
		||||
      return prefsDisabled || !prefs;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleFileChange(e) {
 | 
			
		||||
      // This is when it gets set initially.
 | 
			
		||||
@@ -855,7 +868,7 @@
 | 
			
		||||
 | 
			
		||||
      Gerrit.Nav.navigateToDiff(this._change, path, this._patchRange.patchNum,
 | 
			
		||||
          this._patchRange.basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleFileTap(e) {
 | 
			
		||||
      // async is needed so that that the click event is fired before the
 | 
			
		||||
@@ -863,7 +876,7 @@
 | 
			
		||||
      this.async(() => {
 | 
			
		||||
        this.$.dropdown.close();
 | 
			
		||||
      }, 1);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePatchChange(e) {
 | 
			
		||||
      const {basePatchNum, patchNum} = e.detail;
 | 
			
		||||
@@ -871,12 +884,12 @@
 | 
			
		||||
          this.patchNumEquals(patchNum, this._patchRange.patchNum)) { return; }
 | 
			
		||||
      Gerrit.Nav.navigateToDiff(
 | 
			
		||||
          this._change, this._path, patchNum, basePatchNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePrefsTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      this.$.diffPreferencesDialog.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * _getDiffViewMode: Get the diff view (side-by-side or unified) based on
 | 
			
		||||
@@ -901,11 +914,11 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        return 'SIDE_BY_SIDE';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeModeSelectHideClass(isImageDiff) {
 | 
			
		||||
      return isImageDiff ? 'hide' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _onLineSelected(e, detail) {
 | 
			
		||||
      this.$.cursor.moveToLineNumber(detail.number, detail.side);
 | 
			
		||||
@@ -917,7 +930,7 @@
 | 
			
		||||
          this._change.project, this._path, this._patchRange.patchNum,
 | 
			
		||||
          this._patchRange.basePatchNum, number, leftSide);
 | 
			
		||||
      history.replaceState(null, '', url);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDownloadDropdownLinks(
 | 
			
		||||
        project, changeNum, patchRange, path, diff) {
 | 
			
		||||
@@ -956,7 +969,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return links;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDownloadFileLink(
 | 
			
		||||
        project, changeNum, patchRange, path, isBase) {
 | 
			
		||||
@@ -976,13 +989,13 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDownloadPatchLink(project, changeNum, patchRange, path) {
 | 
			
		||||
      let url = this.changeBaseURL(project, changeNum, patchRange.patchNum);
 | 
			
		||||
      url += '/patch?zip&path=' + encodeURIComponent(path);
 | 
			
		||||
      return url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadComments() {
 | 
			
		||||
      return this.$.commentAPI.loadAll(this._changeNum).then(comments => {
 | 
			
		||||
@@ -992,20 +1005,20 @@
 | 
			
		||||
        this._commentsForDiff = this._getCommentsForPath(this._path,
 | 
			
		||||
            this._patchRange, this._projectConfig);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getPaths(patchRange) {
 | 
			
		||||
      return this._changeComments.getPaths(patchRange);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCommentsForPath(path, patchRange, projectConfig) {
 | 
			
		||||
      return this._changeComments.getCommentsBySideForPath(path, patchRange,
 | 
			
		||||
          projectConfig);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getDiffDrafts() {
 | 
			
		||||
      return this.$.restAPI.getDiffDrafts(this._changeNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeCommentSkips(commentMap, fileList, path) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -1038,13 +1051,13 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return skips;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDiffClass(panelFloatingDisabled) {
 | 
			
		||||
      if (panelFloatingDisabled) {
 | 
			
		||||
        return 'noOverflow';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} patchRangeRecord
 | 
			
		||||
@@ -1052,19 +1065,19 @@
 | 
			
		||||
    _computeEditMode(patchRangeRecord) {
 | 
			
		||||
      const patchRange = patchRangeRecord.base || {};
 | 
			
		||||
      return this.patchNumEquals(patchRange.patchNum, this.EDIT_NAME);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {boolean} editMode
 | 
			
		||||
     */
 | 
			
		||||
    _computeContainerClass(editMode) {
 | 
			
		||||
      return editMode ? 'editMode' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeBlameToggleLabel(loaded, loading) {
 | 
			
		||||
      if (loaded) { return 'Hide blame'; }
 | 
			
		||||
      return 'Show blame';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load and display blame information if it has not already been loaded.
 | 
			
		||||
@@ -1086,15 +1099,15 @@
 | 
			
		||||
          .catch(() => {
 | 
			
		||||
            this._isBlameLoading = false;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeBlameLoaderClass(isImageDiff) {
 | 
			
		||||
      return !isImageDiff ? 'show' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRevisionInfo(change) {
 | 
			
		||||
      return new Gerrit.RevisionInfo(change);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeFileNum(file, files) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -1103,7 +1116,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return files.findIndex(({value}) => value === file) + 1;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {number} fileNum
 | 
			
		||||
@@ -1115,16 +1128,16 @@
 | 
			
		||||
        return 'show';
 | 
			
		||||
      }
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleExpandAllDiffContext(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
      this.$.diffHost.expandAllContext();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeDiffPrefsDisabled(disableDiffPrefs, loggedIn) {
 | 
			
		||||
      return disableDiffPrefs || !loggedIn;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleNextUnreviewedFile(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
@@ -1135,10 +1148,12 @@
 | 
			
		||||
          .filter(file =>
 | 
			
		||||
            (file === this._path || !this._reviewedFiles.has(file)));
 | 
			
		||||
      this._navToFile(this._path, unreviewedFiles, 1);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleReloadingDiffPreference() {
 | 
			
		||||
      this._getDiffPreferences();
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiffView.is, GrDiffView);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -91,9 +91,17 @@
 | 
			
		||||
 | 
			
		||||
  const RENDER_DIFF_TABLE_DEBOUNCE_NAME = 'renderDiffTable';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-diff',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDiff extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-diff'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the user selects a line.
 | 
			
		||||
     * @event line-selected
 | 
			
		||||
@@ -126,89 +134,90 @@
 | 
			
		||||
     * @event diff-context-expanded
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      changeNum: String,
 | 
			
		||||
      noAutoRender: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      patchRange: Object,
 | 
			
		||||
      path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_pathObserver',
 | 
			
		||||
      },
 | 
			
		||||
      prefs: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_prefsObserver',
 | 
			
		||||
      },
 | 
			
		||||
      projectName: String,
 | 
			
		||||
      displayLine: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      isImageDiff: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
      },
 | 
			
		||||
      commitRange: Object,
 | 
			
		||||
      hidden: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        reflectToAttribute: true,
 | 
			
		||||
      },
 | 
			
		||||
      noRenderOnPrefsChange: Boolean,
 | 
			
		||||
      /** @type {!Array<!Gerrit.HoveredRange>} */
 | 
			
		||||
      _commentRanges: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: () => [],
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {!Array<!Gerrit.CoverageRange>} */
 | 
			
		||||
      coverageRanges: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: () => [],
 | 
			
		||||
      },
 | 
			
		||||
      lineWrapping: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_lineWrappingObserver',
 | 
			
		||||
      },
 | 
			
		||||
      viewMode: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: DiffViewMode.SIDE_BY_SIDE,
 | 
			
		||||
        observer: '_viewModeObserver',
 | 
			
		||||
      },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        changeNum: String,
 | 
			
		||||
        noAutoRender: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        patchRange: Object,
 | 
			
		||||
        path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_pathObserver',
 | 
			
		||||
        },
 | 
			
		||||
        prefs: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_prefsObserver',
 | 
			
		||||
        },
 | 
			
		||||
        projectName: String,
 | 
			
		||||
        displayLine: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        isImageDiff: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
        },
 | 
			
		||||
        commitRange: Object,
 | 
			
		||||
        hidden: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          reflectToAttribute: true,
 | 
			
		||||
        },
 | 
			
		||||
        noRenderOnPrefsChange: Boolean,
 | 
			
		||||
        /** @type {!Array<!Gerrit.HoveredRange>} */
 | 
			
		||||
        _commentRanges: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: () => [],
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {!Array<!Gerrit.CoverageRange>} */
 | 
			
		||||
        coverageRanges: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: () => [],
 | 
			
		||||
        },
 | 
			
		||||
        lineWrapping: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_lineWrappingObserver',
 | 
			
		||||
        },
 | 
			
		||||
        viewMode: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: DiffViewMode.SIDE_BY_SIDE,
 | 
			
		||||
          observer: '_viewModeObserver',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type ?Gerrit.LineOfInterest */
 | 
			
		||||
      lineOfInterest: Object,
 | 
			
		||||
        /** @type ?Gerrit.LineOfInterest */
 | 
			
		||||
        lineOfInterest: Object,
 | 
			
		||||
 | 
			
		||||
      loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
        observer: '_loadingChanged',
 | 
			
		||||
      },
 | 
			
		||||
        loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
          observer: '_loadingChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      loggedIn: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      diff: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_diffChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _diffHeaderItems: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: [],
 | 
			
		||||
        computed: '_computeDiffHeaderItems(diff.*)',
 | 
			
		||||
      },
 | 
			
		||||
      _diffTableClass: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      baseImage: Object,
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      revisionImage: Object,
 | 
			
		||||
        loggedIn: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        diff: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_diffChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _diffHeaderItems: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: [],
 | 
			
		||||
          computed: '_computeDiffHeaderItems(diff.*)',
 | 
			
		||||
        },
 | 
			
		||||
        _diffTableClass: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        baseImage: Object,
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        revisionImage: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Whether the safety check for large diffs when whole-file is set has
 | 
			
		||||
       * been bypassed. If the value is null, then the safety has not been
 | 
			
		||||
       * bypassed. If the value is a number, then that number represents the
 | 
			
		||||
@@ -216,83 +225,86 @@
 | 
			
		||||
       *
 | 
			
		||||
       * @type (number|null)
 | 
			
		||||
       */
 | 
			
		||||
      _safetyBypass: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        _safetyBypass: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _showWarning: Boolean,
 | 
			
		||||
        _showWarning: Boolean,
 | 
			
		||||
 | 
			
		||||
      /** @type {?string} */
 | 
			
		||||
      errorMessage: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        /** @type {?string} */
 | 
			
		||||
        errorMessage: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /** @type {?Object} */
 | 
			
		||||
      blame: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
        observer: '_blameChanged',
 | 
			
		||||
      },
 | 
			
		||||
        /** @type {?Object} */
 | 
			
		||||
        blame: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
          observer: '_blameChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      parentIndex: Number,
 | 
			
		||||
        parentIndex: Number,
 | 
			
		||||
 | 
			
		||||
      _newlineWarning: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computeNewlineWarning(diff)',
 | 
			
		||||
      },
 | 
			
		||||
        _newlineWarning: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computeNewlineWarning(diff)',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _diffLength: Number,
 | 
			
		||||
        _diffLength: Number,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Observes comment nodes added or removed after the initial render.
 | 
			
		||||
       * Can be used to unregister when the entire diff is (re-)rendered or upon
 | 
			
		||||
       * detachment.
 | 
			
		||||
       * @type {?PolymerDomApi.ObserveHandle}
 | 
			
		||||
       */
 | 
			
		||||
      _incrementalNodeObserver: Object,
 | 
			
		||||
        _incrementalNodeObserver: Object,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * Observes comment nodes added or removed at any point.
 | 
			
		||||
       * Can be used to unregister upon detachment.
 | 
			
		||||
       * @type {?PolymerDomApi.ObserveHandle}
 | 
			
		||||
       */
 | 
			
		||||
      _nodeObserver: Object,
 | 
			
		||||
        _nodeObserver: Object,
 | 
			
		||||
 | 
			
		||||
      /** Set by Polymer. */
 | 
			
		||||
      isAttached: Boolean,
 | 
			
		||||
      layers: Array,
 | 
			
		||||
    },
 | 
			
		||||
        /** Set by Polymer. */
 | 
			
		||||
        isAttached: Boolean,
 | 
			
		||||
        layers: Array,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_enableSelectionObserver(loggedIn, isAttached)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'create-range-comment': '_handleCreateRangeComment',
 | 
			
		||||
      'render-content': '_handleRenderContent',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_enableSelectionObserver(loggedIn, isAttached)',
 | 
			
		||||
    ],
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('create-range-comment',
 | 
			
		||||
          e => this._handleCreateRangeComment(e));
 | 
			
		||||
      this.addEventListener('render-content',
 | 
			
		||||
          () => this._handleRenderContent());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._observeNodes();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      this._unobserveIncrementalNodes();
 | 
			
		||||
      this._unobserveNodes();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    showNoChangeMessage(loading, prefs, diffLength) {
 | 
			
		||||
      return !loading &&
 | 
			
		||||
        prefs && prefs.ignore_whitespace !== 'IGNORE_NONE'
 | 
			
		||||
        && diffLength === 0;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _enableSelectionObserver(loggedIn, isAttached) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -307,7 +319,7 @@
 | 
			
		||||
        this.unlisten(document, 'selectionchange', '_handleSelectionChange');
 | 
			
		||||
        this.unlisten(document, 'mouseup', '_handleMouseUp');
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSelectionChange() {
 | 
			
		||||
      // Because of shadow DOM selections, we handle the selectionchange here,
 | 
			
		||||
@@ -315,7 +327,7 @@
 | 
			
		||||
      // corresponding range is determined and normalized.
 | 
			
		||||
      const selection = this._getShadowOrDocumentSelection();
 | 
			
		||||
      this.$.highlights.handleSelectionChange(selection, false);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleMouseUp(e) {
 | 
			
		||||
      // To handle double-click outside of text creating comments, we check on
 | 
			
		||||
@@ -323,7 +335,7 @@
 | 
			
		||||
      // can't do that on selection change since the user may still be dragging.
 | 
			
		||||
      const selection = this._getShadowOrDocumentSelection();
 | 
			
		||||
      this.$.highlights.handleSelectionChange(selection, true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Gets the current selection, preferring the shadow DOM selection. */
 | 
			
		||||
    _getShadowOrDocumentSelection() {
 | 
			
		||||
@@ -334,7 +346,7 @@
 | 
			
		||||
      return this.root.getSelection ?
 | 
			
		||||
        this.root.getSelection() :
 | 
			
		||||
        document.getSelection();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _observeNodes() {
 | 
			
		||||
      this._nodeObserver = Polymer.dom(this).observeNodes(info => {
 | 
			
		||||
@@ -343,7 +355,7 @@
 | 
			
		||||
        this._updateRanges(addedThreadEls, removedThreadEls);
 | 
			
		||||
        this._redispatchHoverEvents(addedThreadEls);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateRanges(addedThreadEls, removedThreadEls) {
 | 
			
		||||
      function commentRangeFromThreadEl(threadEl) {
 | 
			
		||||
@@ -369,7 +381,7 @@
 | 
			
		||||
      if (addedCommentRanges && addedCommentRanges.length) {
 | 
			
		||||
        this.push('_commentRanges', ...addedCommentRanges);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The key locations based on the comments and line of interests,
 | 
			
		||||
@@ -400,7 +412,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return keyLocations;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Dispatch events that are handled by the gr-diff-highlight.
 | 
			
		||||
    _redispatchHoverEvents(addedThreadEls) {
 | 
			
		||||
@@ -414,13 +426,13 @@
 | 
			
		||||
              'comment-thread-mouseleave', {bubbles: true, composed: true}));
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Cancel any remaining diff builder rendering work. */
 | 
			
		||||
    cancel() {
 | 
			
		||||
      this.$.diffBuilder.cancel();
 | 
			
		||||
      this.cancelDebouncer(RENDER_DIFF_TABLE_DEBOUNCE_NAME);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {!Array<!HTMLElement>} */
 | 
			
		||||
    getCursorStops() {
 | 
			
		||||
@@ -430,16 +442,16 @@
 | 
			
		||||
 | 
			
		||||
      return Array.from(
 | 
			
		||||
          Polymer.dom(this.root).querySelectorAll('.diff-row'));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {boolean} */
 | 
			
		||||
    isRangeSelected() {
 | 
			
		||||
      return !!this.$.highlights.selectedRange;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toggleLeftDiff() {
 | 
			
		||||
      this.toggleClass('no-left');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _blameChanged(newValue) {
 | 
			
		||||
      this.$.diffBuilder.setBlame(newValue);
 | 
			
		||||
@@ -448,7 +460,7 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        this.classList.remove('showBlame');
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {string} */
 | 
			
		||||
    _computeContainerClass(loggedIn, viewMode, displayLine) {
 | 
			
		||||
@@ -473,7 +485,7 @@
 | 
			
		||||
        classes.push('displayLine');
 | 
			
		||||
      }
 | 
			
		||||
      return classes.join(' ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTap(e) {
 | 
			
		||||
      const el = Polymer.dom(e).localTarget;
 | 
			
		||||
@@ -491,7 +503,7 @@
 | 
			
		||||
        const target = this.$.diffBuilder.getLineElByChild(el);
 | 
			
		||||
        if (target) { this._selectLine(target); }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _selectLine(el) {
 | 
			
		||||
      this.fire('line-selected', {
 | 
			
		||||
@@ -499,7 +511,7 @@
 | 
			
		||||
        number: el.getAttribute('data-value'),
 | 
			
		||||
        path: this.path,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addDraftAtLine(el) {
 | 
			
		||||
      this._selectLine(el);
 | 
			
		||||
@@ -515,7 +527,7 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this._createComment(el, lineNum);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createRangeComment() {
 | 
			
		||||
      if (!this.isRangeSelected()) {
 | 
			
		||||
@@ -523,7 +535,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      const {side, range} = this.$.highlights.selectedRange;
 | 
			
		||||
      this._createCommentForSelection(side, range);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createCommentForSelection(side, range) {
 | 
			
		||||
      const lineNum = range.end_line;
 | 
			
		||||
@@ -531,13 +543,13 @@
 | 
			
		||||
      if (this._isValidElForComment(lineEl)) {
 | 
			
		||||
        this._createComment(lineEl, lineNum, side, range);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCreateRangeComment(e) {
 | 
			
		||||
      const range = e.detail.range;
 | 
			
		||||
      const side = e.detail.side;
 | 
			
		||||
      this._createCommentForSelection(side, range);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {boolean} */
 | 
			
		||||
    _isValidElForComment(el) {
 | 
			
		||||
@@ -561,7 +573,7 @@
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      return true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} lineEl
 | 
			
		||||
@@ -589,11 +601,11 @@
 | 
			
		||||
          range,
 | 
			
		||||
        },
 | 
			
		||||
      }));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getThreadGroupForLine(contentEl) {
 | 
			
		||||
      return contentEl.querySelector('.thread-group');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets or creates a comment thread group for a specific line and side on a
 | 
			
		||||
@@ -612,7 +624,7 @@
 | 
			
		||||
        contentEl.appendChild(threadGroupEl);
 | 
			
		||||
      }
 | 
			
		||||
      return threadGroupEl;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The value to be used for the patch number of new comments created at the
 | 
			
		||||
@@ -638,7 +650,7 @@
 | 
			
		||||
        patchNum = this.patchRange.basePatchNum;
 | 
			
		||||
      }
 | 
			
		||||
      return patchNum;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {boolean} */
 | 
			
		||||
    _getIsParentCommentByLineAndContent(lineEl, contentEl) {
 | 
			
		||||
@@ -649,7 +661,7 @@
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {string} */
 | 
			
		||||
    _getCommentSideByLineAndContent(lineEl, contentEl) {
 | 
			
		||||
@@ -659,7 +671,7 @@
 | 
			
		||||
        side = 'left';
 | 
			
		||||
      }
 | 
			
		||||
      return side;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _prefsObserver(newPrefs, oldPrefs) {
 | 
			
		||||
      // Scan the preference objects one level deep to see if they differ.
 | 
			
		||||
@@ -675,16 +687,16 @@
 | 
			
		||||
      if (differ) {
 | 
			
		||||
        this._prefsChanged(newPrefs);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _pathObserver() {
 | 
			
		||||
      // Call _prefsChanged(), because line-limit style value depends on path.
 | 
			
		||||
      this._prefsChanged(this.prefs);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _viewModeObserver() {
 | 
			
		||||
      this._prefsChanged(this.prefs);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @param {boolean} newValue */
 | 
			
		||||
    _loadingChanged(newValue) {
 | 
			
		||||
@@ -695,11 +707,11 @@
 | 
			
		||||
        this._showWarning = false;
 | 
			
		||||
        this.clearDiffContent();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _lineWrappingObserver() {
 | 
			
		||||
      this._prefsChanged(this.prefs);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _prefsChanged(prefs) {
 | 
			
		||||
      if (!prefs) { return; }
 | 
			
		||||
@@ -730,14 +742,14 @@
 | 
			
		||||
      if (this.diff && !this.noRenderOnPrefsChange) {
 | 
			
		||||
        this._debounceRenderDiffTable();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _diffChanged(newValue) {
 | 
			
		||||
      if (newValue) {
 | 
			
		||||
        this._diffLength = this.getDiffLength(newValue);
 | 
			
		||||
        this._debounceRenderDiffTable();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When called multiple times from the same microtask, will call
 | 
			
		||||
@@ -752,7 +764,7 @@
 | 
			
		||||
    _debounceRenderDiffTable() {
 | 
			
		||||
      this.debounce(
 | 
			
		||||
          RENDER_DIFF_TABLE_DEBOUNCE_NAME, () => this._renderDiffTable());
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _renderDiffTable() {
 | 
			
		||||
      this._unobserveIncrementalNodes();
 | 
			
		||||
@@ -782,7 +794,7 @@
 | 
			
		||||
                  detail: {contentRendered: true},
 | 
			
		||||
                }));
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRenderContent() {
 | 
			
		||||
      this._incrementalNodeObserver = Polymer.dom(this).observeNodes(info => {
 | 
			
		||||
@@ -821,19 +833,19 @@
 | 
			
		||||
          lastEl.replaceWith(lastEl);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _unobserveIncrementalNodes() {
 | 
			
		||||
      if (this._incrementalNodeObserver) {
 | 
			
		||||
        Polymer.dom(this).unobserveNodes(this._incrementalNodeObserver);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _unobserveNodes() {
 | 
			
		||||
      if (this._nodeObserver) {
 | 
			
		||||
        Polymer.dom(this).unobserveNodes(this._nodeObserver);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the preferences object including the safety bypass context (if any).
 | 
			
		||||
@@ -843,12 +855,12 @@
 | 
			
		||||
        return Object.assign({}, this.prefs, {context: this._safetyBypass});
 | 
			
		||||
      }
 | 
			
		||||
      return this.prefs;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    clearDiffContent() {
 | 
			
		||||
      this._unobserveIncrementalNodes();
 | 
			
		||||
      this.$.diffTable.innerHTML = null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {!Array} */
 | 
			
		||||
    _computeDiffHeaderItems(diffInfoRecord) {
 | 
			
		||||
@@ -861,27 +873,27 @@
 | 
			
		||||
            item.startsWith('--- ') ||
 | 
			
		||||
            item === 'Binary files differ');
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {boolean} */
 | 
			
		||||
    _computeDiffHeaderHidden(items) {
 | 
			
		||||
      return items.length === 0;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleFullBypass() {
 | 
			
		||||
      this._safetyBypass = FULL_CONTEXT;
 | 
			
		||||
      this._debounceRenderDiffTable();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleLimitedBypass() {
 | 
			
		||||
      this._safetyBypass = LIMITED_CONTEXT;
 | 
			
		||||
      this._debounceRenderDiffTable();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @return {string} */
 | 
			
		||||
    _computeWarningClass(showWarning) {
 | 
			
		||||
      return showWarning ? 'warn' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} errorMessage
 | 
			
		||||
@@ -889,11 +901,11 @@
 | 
			
		||||
     */
 | 
			
		||||
    _computeErrorClass(errorMessage) {
 | 
			
		||||
      return errorMessage ? 'showError' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expandAllContext() {
 | 
			
		||||
      this._handleFullBypass();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find the last chunk for the given side.
 | 
			
		||||
@@ -929,7 +941,7 @@
 | 
			
		||||
      if (chunkIndex === -1) { return null; }
 | 
			
		||||
 | 
			
		||||
      return chunk;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether the specified side of the diff has a trailing newline.
 | 
			
		||||
@@ -950,7 +962,7 @@
 | 
			
		||||
        lines = leftSide ? chunk.a : chunk.b;
 | 
			
		||||
      }
 | 
			
		||||
      return lines[lines.length - 1] === '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Object} diff
 | 
			
		||||
@@ -968,7 +980,7 @@
 | 
			
		||||
      }
 | 
			
		||||
      if (!messages.length) { return null; }
 | 
			
		||||
      return messages.join(' — ');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} warning
 | 
			
		||||
@@ -978,7 +990,7 @@
 | 
			
		||||
    _computeNewlineWarningClass(warning, loading) {
 | 
			
		||||
      if (loading || !warning) { return 'newlineWarning hidden'; }
 | 
			
		||||
      return 'newlineWarning';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the approximate length of the diff as the sum of the maximum
 | 
			
		||||
@@ -997,6 +1009,8 @@
 | 
			
		||||
              sec.hasOwnProperty('b') ? sec.b.length : 0);
 | 
			
		||||
        }
 | 
			
		||||
      }, 0);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDiff.is, GrDiff);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,9 @@
 | 
			
		||||
  // Maximum length for patch set descriptions.
 | 
			
		||||
  const PATCH_DESC_MAX_LENGTH = 500;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    */
 | 
			
		||||
  /**
 | 
			
		||||
   * Fired when the patch range changes
 | 
			
		||||
   *
 | 
			
		||||
@@ -28,44 +31,47 @@
 | 
			
		||||
   * @property {string} patchNum
 | 
			
		||||
   * @property {string} basePatchNum
 | 
			
		||||
   */
 | 
			
		||||
  class GrPatchRangeSelect extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-patch-range-select'; }
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-patch-range-select',
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      availablePatches: Array,
 | 
			
		||||
      _baseDropdownContent: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computeBaseDropdownContent(availablePatches, patchNum,' +
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        availablePatches: Array,
 | 
			
		||||
        _baseDropdownContent: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computeBaseDropdownContent(availablePatches, patchNum,' +
 | 
			
		||||
            '_sortedRevisions, changeComments, revisionInfo)',
 | 
			
		||||
      },
 | 
			
		||||
      _patchDropdownContent: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        computed: '_computePatchDropdownContent(availablePatches,' +
 | 
			
		||||
        },
 | 
			
		||||
        _patchDropdownContent: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          computed: '_computePatchDropdownContent(availablePatches,' +
 | 
			
		||||
            'basePatchNum, _sortedRevisions, changeComments)',
 | 
			
		||||
      },
 | 
			
		||||
      changeNum: String,
 | 
			
		||||
      changeComments: Object,
 | 
			
		||||
      /** @type {{ meta_a: !Array, meta_b: !Array}} */
 | 
			
		||||
      filesWeblinks: Object,
 | 
			
		||||
      patchNum: String,
 | 
			
		||||
      basePatchNum: String,
 | 
			
		||||
      revisions: Object,
 | 
			
		||||
      revisionInfo: Object,
 | 
			
		||||
      _sortedRevisions: Array,
 | 
			
		||||
    },
 | 
			
		||||
        },
 | 
			
		||||
        changeNum: String,
 | 
			
		||||
        changeComments: Object,
 | 
			
		||||
        /** @type {{ meta_a: !Array, meta_b: !Array}} */
 | 
			
		||||
        filesWeblinks: Object,
 | 
			
		||||
        patchNum: String,
 | 
			
		||||
        basePatchNum: String,
 | 
			
		||||
        revisions: Object,
 | 
			
		||||
        revisionInfo: Object,
 | 
			
		||||
        _sortedRevisions: Array,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_updateSortedRevisions(revisions.*)',
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_updateSortedRevisions(revisions.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getShaForPatch(patch) {
 | 
			
		||||
      return patch.sha.substring(0, 10);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeBaseDropdownContent(availablePatches, patchNum, _sortedRevisions,
 | 
			
		||||
        changeComments, revisionInfo) {
 | 
			
		||||
@@ -113,13 +119,13 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return dropdownContent;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeMobileText(patchNum, changeComments, revisions) {
 | 
			
		||||
      return `${patchNum}` +
 | 
			
		||||
          `${this._computePatchSetCommentsString(changeComments, patchNum)}` +
 | 
			
		||||
          `${this._computePatchSetDescription(revisions, patchNum, true)}`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePatchDropdownContent(availablePatches, basePatchNum,
 | 
			
		||||
        _sortedRevisions, changeComments) {
 | 
			
		||||
@@ -145,13 +151,13 @@
 | 
			
		||||
        }));
 | 
			
		||||
      }
 | 
			
		||||
      return dropdownContent;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeText(patchNum, prefix, changeComments, sha) {
 | 
			
		||||
      return `${prefix}${patchNum}` +
 | 
			
		||||
        `${this._computePatchSetCommentsString(changeComments, patchNum)}`
 | 
			
		||||
          + (` | ${sha}`);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createDropdownEntry(patchNum, prefix, sortedRevisions, changeComments,
 | 
			
		||||
        sha) {
 | 
			
		||||
@@ -169,12 +175,12 @@
 | 
			
		||||
        entry['date'] = date;
 | 
			
		||||
      }
 | 
			
		||||
      return entry;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateSortedRevisions(revisionsRecord) {
 | 
			
		||||
      const revisions = revisionsRecord.base;
 | 
			
		||||
      this._sortedRevisions = this.sortRevisions(Object.values(revisions));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The basePatchNum should always be <= patchNum -- because sortedRevisions
 | 
			
		||||
@@ -187,7 +193,7 @@
 | 
			
		||||
    _computeLeftDisabled(basePatchNum, patchNum, sortedRevisions) {
 | 
			
		||||
      return this.findSortedIndex(basePatchNum, sortedRevisions) <=
 | 
			
		||||
          this.findSortedIndex(patchNum, sortedRevisions);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The basePatchNum should always be <= patchNum -- because sortedRevisions
 | 
			
		||||
@@ -216,7 +222,7 @@
 | 
			
		||||
 | 
			
		||||
      return this.findSortedIndex(basePatchNum, sortedRevisions) <=
 | 
			
		||||
          this.findSortedIndex(patchNum, sortedRevisions);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePatchSetCommentsString(changeComments, patchNum) {
 | 
			
		||||
      if (!changeComments) { return; }
 | 
			
		||||
@@ -237,7 +243,7 @@
 | 
			
		||||
          // Add a comma + space if both comments and unresolved
 | 
			
		||||
          (commentString && unresolvedString ? ', ' : '') +
 | 
			
		||||
          `${unresolvedString})`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Array} revisions
 | 
			
		||||
@@ -249,7 +255,7 @@
 | 
			
		||||
      return (rev && rev.description) ?
 | 
			
		||||
        (opt_addFrontSpace ? ' ' : '') +
 | 
			
		||||
          rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Array} revisions
 | 
			
		||||
@@ -258,7 +264,7 @@
 | 
			
		||||
    _computePatchSetDate(revisions, patchNum) {
 | 
			
		||||
      const rev = this.getRevisionByPatchNum(revisions, patchNum);
 | 
			
		||||
      return rev ? rev.created : undefined;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Catches value-change events from the patchset dropdowns and determines
 | 
			
		||||
@@ -276,6 +282,8 @@
 | 
			
		||||
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('patch-range-change', {detail, bubbles: false}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrPatchRangeSelect.is, GrPatchRangeSelect);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,10 @@
 | 
			
		||||
  const RANGE_HIGHLIGHT = 'style-scope gr-diff range';
 | 
			
		||||
  const HOVER_HIGHLIGHT = 'style-scope gr-diff rangeHighlight';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-ranged-comment-layer',
 | 
			
		||||
 | 
			
		||||
  class GrRangedCommentLayer extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-ranged-comment-layer'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the range in a range comment was malformed and had to be
 | 
			
		||||
     * normalized.
 | 
			
		||||
@@ -35,26 +36,30 @@
 | 
			
		||||
     * @event normalize-range
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /** @type {!Array<!Gerrit.HoveredRange>} */
 | 
			
		||||
      commentRanges: Array,
 | 
			
		||||
      _listeners: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _rangesMap: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return {left: {}, right: {}}; },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        commentRanges: Array,
 | 
			
		||||
        _listeners: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _rangesMap: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return {left: {}, right: {}}; },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_handleCommentRangesChange(commentRanges.*)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_handleCommentRangesChange(commentRanges.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get styleModuleName() {
 | 
			
		||||
      return 'gr-ranged-comment-styles';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Layer method to add annotations to a line.
 | 
			
		||||
@@ -81,7 +86,7 @@
 | 
			
		||||
            range.end - range.start,
 | 
			
		||||
            range.hovering ? HOVER_HIGHLIGHT : RANGE_HIGHLIGHT);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Register a listener for layer updates.
 | 
			
		||||
@@ -91,7 +96,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    addListener(fn) {
 | 
			
		||||
      this._listeners.push(fn);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notify Layer listeners of changes to annotations.
 | 
			
		||||
@@ -103,7 +108,7 @@
 | 
			
		||||
      for (const listener of this._listeners) {
 | 
			
		||||
        listener(start, end, side);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle change in the ranges by updating the ranges maps and by
 | 
			
		||||
@@ -161,7 +166,7 @@
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateRangesMap(side, range, hovering, operation) {
 | 
			
		||||
      const forSide = this._rangesMap[side] || (this._rangesMap[side] = {});
 | 
			
		||||
@@ -172,7 +177,7 @@
 | 
			
		||||
        operation(forLine, start, end, hovering);
 | 
			
		||||
      }
 | 
			
		||||
      this._notifyUpdateRange(range.start_line, range.end_line, side);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getRangesForLine(line, side) {
 | 
			
		||||
      const lineNum = side === 'left' ? line.beforeNumber : line.afterNumber;
 | 
			
		||||
@@ -200,6 +205,8 @@
 | 
			
		||||
          })
 | 
			
		||||
          // Sort the ranges so that hovering highlights are on top.
 | 
			
		||||
          .sort((a, b) => a.hovering && !b.hovering ? 1 : 0);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrRangedCommentLayer.is, GrRangedCommentLayer);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,31 +17,38 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-selection-action-box',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrSelectionActionBox extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-selection-action-box'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the comment creation action was taken (click).
 | 
			
		||||
     *
 | 
			
		||||
     * @event create-comment-requested
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      keyEventTarget: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return document.body; },
 | 
			
		||||
      },
 | 
			
		||||
      positionBelow: Boolean,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        keyEventTarget: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return document.body; },
 | 
			
		||||
        },
 | 
			
		||||
        positionBelow: Boolean,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      // See https://crbug.com/gerrit/4767
 | 
			
		||||
      mousedown: '_handleMouseDown',
 | 
			
		||||
    },
 | 
			
		||||
      this.addEventListener('mousedown',
 | 
			
		||||
          e => this._handleMouseDown(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    placeAbove(el) {
 | 
			
		||||
      Polymer.dom.flush();
 | 
			
		||||
@@ -52,7 +59,7 @@
 | 
			
		||||
          rect.top - parentRect.top - boxRect.height - 6 + 'px';
 | 
			
		||||
      this.style.left =
 | 
			
		||||
          rect.left - parentRect.left + (rect.width - boxRect.width) / 2 + 'px';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    placeBelow(el) {
 | 
			
		||||
      Polymer.dom.flush();
 | 
			
		||||
@@ -63,14 +70,14 @@
 | 
			
		||||
      rect.top - parentRect.top + boxRect.height - 6 + 'px';
 | 
			
		||||
      this.style.left =
 | 
			
		||||
      rect.left - parentRect.left + (rect.width - boxRect.width) / 2 + 'px';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getParentBoundingClientRect() {
 | 
			
		||||
      // With native shadow DOM, the parent is the shadow root, not the gr-diff
 | 
			
		||||
      // element
 | 
			
		||||
      const parent = this.parentElement || this.parentNode.host;
 | 
			
		||||
      return parent.getBoundingClientRect();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getTargetBoundingRect(el) {
 | 
			
		||||
      let rect;
 | 
			
		||||
@@ -83,13 +90,15 @@
 | 
			
		||||
        rect = el.getBoundingClientRect();
 | 
			
		||||
      }
 | 
			
		||||
      return rect;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleMouseDown(e) {
 | 
			
		||||
      if (e.button !== 0) { return; } // 0 = main button
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this.fire('create-comment-requested');
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrSelectionActionBox.is, GrSelectionActionBox);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -130,54 +130,58 @@
 | 
			
		||||
  const GO_BACKSLASH_LITERAL = '\'\\\\\'';
 | 
			
		||||
  const GLOBAL_LT_PATTERN = /</g;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-syntax-layer',
 | 
			
		||||
  class GrSyntaxLayer extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-syntax-layer'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      diff: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_diffChanged',
 | 
			
		||||
      },
 | 
			
		||||
      enabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _baseRanges: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _revisionRanges: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _baseLanguage: String,
 | 
			
		||||
      _revisionLanguage: String,
 | 
			
		||||
      _listeners: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?number} */
 | 
			
		||||
      _processHandle: Number,
 | 
			
		||||
      /**
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        diff: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_diffChanged',
 | 
			
		||||
        },
 | 
			
		||||
        enabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _baseRanges: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _revisionRanges: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _baseLanguage: String,
 | 
			
		||||
        _revisionLanguage: String,
 | 
			
		||||
        _listeners: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?number} */
 | 
			
		||||
        _processHandle: Number,
 | 
			
		||||
        /**
 | 
			
		||||
       * The promise last returned from `process()` while the asynchronous
 | 
			
		||||
       * processing is running - `null` otherwise. Provides a `cancel()`
 | 
			
		||||
       * method that rejects it with `{isCancelled: true}`.
 | 
			
		||||
       * @type {?Object}
 | 
			
		||||
       */
 | 
			
		||||
      _processPromise: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
      _hljs: Object,
 | 
			
		||||
    },
 | 
			
		||||
        _processPromise: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
        _hljs: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addListener(fn) {
 | 
			
		||||
      this.push('_listeners', fn);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeListener(fn) {
 | 
			
		||||
      this._listeners = this._listeners.filter(f => f != fn);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Annotation layer method to add syntax annotations to the given element
 | 
			
		||||
@@ -214,14 +218,14 @@
 | 
			
		||||
        GrAnnotation.annotateElement(
 | 
			
		||||
            el, range.start, range.length, range.className);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLanguage(diffFileMetaInfo) {
 | 
			
		||||
      // The Gerrit API provides only content-type, but for other users of
 | 
			
		||||
      // gr-diff it may be more convenient to specify the language directly.
 | 
			
		||||
      return diffFileMetaInfo.language ||
 | 
			
		||||
          LANGUAGE_MAP[diffFileMetaInfo.content_type];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start processing syntax for the loaded diff and notify layer listeners
 | 
			
		||||
@@ -298,7 +302,7 @@
 | 
			
		||||
          }));
 | 
			
		||||
      return this._processPromise
 | 
			
		||||
          .finally(() => { this._processPromise = null; });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cancel any asynchronous syntax processing jobs.
 | 
			
		||||
@@ -311,13 +315,13 @@
 | 
			
		||||
      if (this._processPromise) {
 | 
			
		||||
        this._processPromise.cancel();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _diffChanged() {
 | 
			
		||||
      this._cancel();
 | 
			
		||||
      this._baseRanges = [];
 | 
			
		||||
      this._revisionRanges = [];
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Take a string of HTML with the (potentially nested) syntax markers
 | 
			
		||||
@@ -339,7 +343,7 @@
 | 
			
		||||
      const ranges = this._rangesFromElement(div, 0);
 | 
			
		||||
      rangesCache.set(str, ranges);
 | 
			
		||||
      return ranges;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _rangesFromElement(elem, offset) {
 | 
			
		||||
      let result = [];
 | 
			
		||||
@@ -362,7 +366,7 @@
 | 
			
		||||
        offset += nodeLength;
 | 
			
		||||
      }
 | 
			
		||||
      return result;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * For a given state, process the syntax for the next line (or pair of
 | 
			
		||||
@@ -412,7 +416,7 @@
 | 
			
		||||
            this._rangesFromString(result.value, rangesCache));
 | 
			
		||||
        state.revisionContext = result.top;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Ad hoc fixes for HLJS parsing bugs. Rewrite lines of code in constrained
 | 
			
		||||
@@ -479,7 +483,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return line;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Tells whether the state has exhausted its current section.
 | 
			
		||||
@@ -494,7 +498,7 @@
 | 
			
		||||
        return (!section.a || state.lineIndex >= section.a.length) &&
 | 
			
		||||
            (!section.b || state.lineIndex >= section.b.length);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * For a given state, notify layer listeners of any processed line ranges
 | 
			
		||||
@@ -516,18 +520,20 @@
 | 
			
		||||
            'right');
 | 
			
		||||
        state.lastNotify.right = state.lineNums.right;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _notifyRange(start, end, side) {
 | 
			
		||||
      for (const fn of this._listeners) {
 | 
			
		||||
        fn(start, end, side);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _loadHLJS() {
 | 
			
		||||
      return this.$.libLoader.getHLJS().then(hljs => {
 | 
			
		||||
        this._hljs = hljs;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrSyntaxLayer.is, GrSyntaxLayer);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,50 +17,56 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-documentation-search',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.ListViewMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrDocumentationSearch extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.ListViewBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-documentation-search'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        readOnly: true,
 | 
			
		||||
        value: '/Documentation',
 | 
			
		||||
      },
 | 
			
		||||
      _documentationSearches: Array,
 | 
			
		||||
        _path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          readOnly: true,
 | 
			
		||||
          value: '/Documentation',
 | 
			
		||||
        },
 | 
			
		||||
        _documentationSearches: Array,
 | 
			
		||||
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
      },
 | 
			
		||||
      _filter: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.ListViewBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
        },
 | 
			
		||||
        _filter: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.dispatchEvent(
 | 
			
		||||
          new CustomEvent('title-change', {title: 'Documentation Search'}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(params) {
 | 
			
		||||
      this._loading = true;
 | 
			
		||||
      this._filter = this.getFilterValue(params);
 | 
			
		||||
 | 
			
		||||
      return this._getDocumentationSearches(this._filter);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getDocumentationSearches(filter) {
 | 
			
		||||
      this._documentationSearches = [];
 | 
			
		||||
@@ -71,11 +77,13 @@
 | 
			
		||||
            this._documentationSearches = searches;
 | 
			
		||||
            this._loading = false;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSearchUrl(url) {
 | 
			
		||||
      if (!url) { return ''; }
 | 
			
		||||
      return this.getBaseUrl() + '/' + url;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDocumentationSearch.is, GrDocumentationSearch);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,23 +17,28 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-default-editor',
 | 
			
		||||
 | 
			
		||||
  class GrDefaultEditor extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-default-editor'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the content of the editor changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @event content-change
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      fileContent: String,
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        fileContent: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTextareaInput(e) {
 | 
			
		||||
      this.dispatchEvent(new CustomEvent(
 | 
			
		||||
          'content-change',
 | 
			
		||||
          {detail: {value: e.target.value}, bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrDefaultEditor.is, GrDefaultEditor);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,47 +17,52 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-edit-controls',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrEditControls extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-edit-controls'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      change: Object,
 | 
			
		||||
      patchNum: String,
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        change: Object,
 | 
			
		||||
        patchNum: String,
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * TODO(kaspern): by default, the RESTORE action should be hidden in the
 | 
			
		||||
       * file-list as it is a per-file action only. Remove this default value
 | 
			
		||||
       * when the Actions dictionary is moved to a shared constants file and
 | 
			
		||||
       * use the hiddenActions property in the parent component.
 | 
			
		||||
       */
 | 
			
		||||
      hiddenActions: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return [GrEditConstants.Actions.RESTORE.id]; },
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      _actions: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return Object.values(GrEditConstants.Actions); },
 | 
			
		||||
      },
 | 
			
		||||
      _path: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
      _newPath: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
      _query: {
 | 
			
		||||
        type: Function,
 | 
			
		||||
        value() {
 | 
			
		||||
          return this._queryFiles.bind(this);
 | 
			
		||||
        hiddenActions: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return [GrEditConstants.Actions.RESTORE.id]; },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
        _actions: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return Object.values(GrEditConstants.Actions); },
 | 
			
		||||
        },
 | 
			
		||||
        _path: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
        _newPath: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
        _query: {
 | 
			
		||||
          type: Function,
 | 
			
		||||
          value() {
 | 
			
		||||
            return this._queryFiles.bind(this);
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
@@ -76,7 +81,7 @@
 | 
			
		||||
          this.openRestoreDialog();
 | 
			
		||||
          return;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string=} opt_path
 | 
			
		||||
@@ -84,7 +89,7 @@
 | 
			
		||||
    openOpenDialog(opt_path) {
 | 
			
		||||
      if (opt_path) { this._path = opt_path; }
 | 
			
		||||
      return this._showDialog(this.$.openDialog);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string=} opt_path
 | 
			
		||||
@@ -92,7 +97,7 @@
 | 
			
		||||
    openDeleteDialog(opt_path) {
 | 
			
		||||
      if (opt_path) { this._path = opt_path; }
 | 
			
		||||
      return this._showDialog(this.$.deleteDialog);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string=} opt_path
 | 
			
		||||
@@ -100,7 +105,7 @@
 | 
			
		||||
    openRenameDialog(opt_path) {
 | 
			
		||||
      if (opt_path) { this._path = opt_path; }
 | 
			
		||||
      return this._showDialog(this.$.renameDialog);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string=} opt_path
 | 
			
		||||
@@ -108,7 +113,7 @@
 | 
			
		||||
    openRestoreDialog(opt_path) {
 | 
			
		||||
      if (opt_path) { this._path = opt_path; }
 | 
			
		||||
      return this._showDialog(this.$.restoreDialog);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a path string, checks that it is a valid file path.
 | 
			
		||||
@@ -118,11 +123,11 @@
 | 
			
		||||
    _isValidPath(path) {
 | 
			
		||||
      // Double negation needed for strict boolean return type.
 | 
			
		||||
      return !!path.length && !path.endsWith('/');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeRenameDisabled(path, newPath) {
 | 
			
		||||
      return this._isValidPath(path) && this._isValidPath(newPath);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a dom event, gets the dialog that lies along this event path.
 | 
			
		||||
@@ -134,7 +139,7 @@
 | 
			
		||||
        if (!element.classList) { return false; }
 | 
			
		||||
        return element.classList.contains('dialog');
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showDialog(dialog) {
 | 
			
		||||
      // Some dialogs may not fire their on-close event when closed in certain
 | 
			
		||||
@@ -148,12 +153,12 @@
 | 
			
		||||
        if (autocomplete) { autocomplete.focus(); }
 | 
			
		||||
        this.async(() => { this.$.overlay.center(); }, 1);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hideAllDialogs() {
 | 
			
		||||
      const dialogs = Polymer.dom(this.root).querySelectorAll('.dialog');
 | 
			
		||||
      for (const dialog of dialogs) { this._closeDialog(dialog); }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {Element|undefined} dialog
 | 
			
		||||
@@ -175,18 +180,18 @@
 | 
			
		||||
 | 
			
		||||
      dialog.classList.toggle('invisible', true);
 | 
			
		||||
      return this.$.overlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDialogCancel(e) {
 | 
			
		||||
      this._closeDialog(this._getDialogFromEvent(e));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleOpenConfirm(e) {
 | 
			
		||||
      const url = Gerrit.Nav.getEditUrlForDiff(this.change, this._path,
 | 
			
		||||
          this.patchNum);
 | 
			
		||||
      Gerrit.Nav.navigateToRelativeUrl(url);
 | 
			
		||||
      this._closeDialog(this._getDialogFromEvent(e), true);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleDeleteConfirm(e) {
 | 
			
		||||
      // Get the dialog before the api call as the event will change during bubbling
 | 
			
		||||
@@ -198,7 +203,7 @@
 | 
			
		||||
            this._closeDialog(dialog, true);
 | 
			
		||||
            Gerrit.Nav.navigateToChange(this.change);
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRestoreConfirm(e) {
 | 
			
		||||
      const dialog = this._getDialogFromEvent(e);
 | 
			
		||||
@@ -208,7 +213,7 @@
 | 
			
		||||
            this._closeDialog(dialog, true);
 | 
			
		||||
            Gerrit.Nav.navigateToChange(this.change);
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRenameConfirm(e) {
 | 
			
		||||
      const dialog = this._getDialogFromEvent(e);
 | 
			
		||||
@@ -218,17 +223,19 @@
 | 
			
		||||
        this._closeDialog(dialog, true);
 | 
			
		||||
        Gerrit.Nav.navigateToChange(this.change);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _queryFiles(input) {
 | 
			
		||||
      return this.$.restAPI.queryChangeFiles(this.change._number,
 | 
			
		||||
          this.patchNum, input).then(res => res.map(file => {
 | 
			
		||||
        return {name: file};
 | 
			
		||||
      }));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeIsInvisible(id, hiddenActions) {
 | 
			
		||||
      return hiddenActions.includes(id) ? 'invisible' : '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrEditControls.is, GrEditControls);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,38 +17,41 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-edit-file-controls',
 | 
			
		||||
 | 
			
		||||
  class GrEditFileControls extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-edit-file-controls'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when an action in the overflow menu is tapped.
 | 
			
		||||
     *
 | 
			
		||||
     * @event file-action-tap
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      filePath: String,
 | 
			
		||||
      _allFileActions: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value: () => Object.values(GrEditConstants.Actions),
 | 
			
		||||
      },
 | 
			
		||||
      _fileActions: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        computed: '_computeFileActions(_allFileActions)',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        filePath: String,
 | 
			
		||||
        _allFileActions: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value: () => Object.values(GrEditConstants.Actions),
 | 
			
		||||
        },
 | 
			
		||||
        _fileActions: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          computed: '_computeFileActions(_allFileActions)',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleActionTap(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      this._dispatchFileAction(e.detail.id, this.filePath);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _dispatchFileAction(action, path) {
 | 
			
		||||
      this.dispatchEvent(new CustomEvent(
 | 
			
		||||
          'file-action-tap',
 | 
			
		||||
          {detail: {action, path}, bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeFileActions(actions) {
 | 
			
		||||
      // TODO(kaspern): conditionally disable some actions based on file status.
 | 
			
		||||
@@ -58,6 +61,8 @@
 | 
			
		||||
          id: action.id,
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrEditFileControls.is, GrEditFileControls);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -24,9 +24,21 @@
 | 
			
		||||
 | 
			
		||||
  const STORAGE_DEBOUNCE_INTERVAL_MS = 100;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-editor-view',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PatchSetMixin
 | 
			
		||||
    * @appliesMixin Gerrit.PathListMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrEditorView extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    Gerrit.PatchSetBehavior,
 | 
			
		||||
    Gerrit.PathListBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-editor-view'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the title of the page should change.
 | 
			
		||||
     *
 | 
			
		||||
@@ -39,69 +51,69 @@
 | 
			
		||||
     * @event show-alert
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * URL params passed from the router.
 | 
			
		||||
       */
 | 
			
		||||
      params: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_paramsChanged',
 | 
			
		||||
      },
 | 
			
		||||
        params: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_paramsChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _change: Object,
 | 
			
		||||
      _changeEditDetail: Object,
 | 
			
		||||
      _changeNum: String,
 | 
			
		||||
      _patchNum: String,
 | 
			
		||||
      _path: String,
 | 
			
		||||
      _type: String,
 | 
			
		||||
      _content: String,
 | 
			
		||||
      _newContent: String,
 | 
			
		||||
      _saving: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _successfulSave: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _saveDisabled: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: true,
 | 
			
		||||
        computed: '_computeSaveDisabled(_content, _newContent, _saving)',
 | 
			
		||||
      },
 | 
			
		||||
      _prefs: Object,
 | 
			
		||||
    },
 | 
			
		||||
        _change: Object,
 | 
			
		||||
        _changeEditDetail: Object,
 | 
			
		||||
        _changeNum: String,
 | 
			
		||||
        _patchNum: String,
 | 
			
		||||
        _path: String,
 | 
			
		||||
        _type: String,
 | 
			
		||||
        _content: String,
 | 
			
		||||
        _newContent: String,
 | 
			
		||||
        _saving: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _successfulSave: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _saveDisabled: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: true,
 | 
			
		||||
          computed: '_computeSaveDisabled(_content, _newContent, _saving)',
 | 
			
		||||
        },
 | 
			
		||||
        _prefs: Object,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
      Gerrit.PatchSetBehavior,
 | 
			
		||||
      Gerrit.PathListBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    get keyBindings() {
 | 
			
		||||
      return {
 | 
			
		||||
        'ctrl+s meta+s': '_handleSaveShortcut',
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'content-change': '_handleContentChange',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    keyBindings: {
 | 
			
		||||
      'ctrl+s meta+s': '_handleSaveShortcut',
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this.addEventListener('content-change',
 | 
			
		||||
          e => this._handleContentChange(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._getEditPrefs().then(prefs => { this._prefs = prefs; });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get storageKey() {
 | 
			
		||||
      return `c${this._changeNum}_ps${this._patchNum}_${this._path}`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getLoggedIn() {
 | 
			
		||||
      return this.$.restAPI.getLoggedIn();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getEditPrefs() {
 | 
			
		||||
      return this.$.restAPI.getEditPreferences();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(value) {
 | 
			
		||||
      if (value.view !== Gerrit.Nav.View.EDIT) {
 | 
			
		||||
@@ -126,13 +138,13 @@
 | 
			
		||||
      promises.push(
 | 
			
		||||
          this._getFileData(this._changeNum, this._path, this._patchNum));
 | 
			
		||||
      return Promise.all(promises);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getChangeDetail(changeNum) {
 | 
			
		||||
      return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => {
 | 
			
		||||
        this._change = change;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePathChanged(e) {
 | 
			
		||||
      const path = e.detail;
 | 
			
		||||
@@ -146,13 +158,13 @@
 | 
			
		||||
        this._successfulSave = true;
 | 
			
		||||
        this._viewEditInChangeView();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _viewEditInChangeView() {
 | 
			
		||||
      const patch = this._successfulSave ? this.EDIT_NAME : this._patchNum;
 | 
			
		||||
      Gerrit.Nav.navigateToChange(this._change, patch, null,
 | 
			
		||||
          patch !== this.EDIT_NAME);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getFileData(changeNum, path, patchNum) {
 | 
			
		||||
      const storedContent =
 | 
			
		||||
@@ -183,7 +195,7 @@
 | 
			
		||||
              this._type = '';
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _saveEdit() {
 | 
			
		||||
      this._saving = true;
 | 
			
		||||
@@ -198,7 +210,7 @@
 | 
			
		||||
        this._content = this._newContent;
 | 
			
		||||
        this._successfulSave = true;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showAlert(message) {
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('show-alert', {
 | 
			
		||||
@@ -206,7 +218,7 @@
 | 
			
		||||
        bubbles: true,
 | 
			
		||||
        composed: true,
 | 
			
		||||
      }));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeSaveDisabled(content, newContent, saving) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -222,12 +234,12 @@
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
      return content === newContent;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleCloseTap() {
 | 
			
		||||
      // TODO(kaspern): Add a confirm dialog if there are unsaved changes.
 | 
			
		||||
      this._viewEditInChangeView();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleContentChange(e) {
 | 
			
		||||
      this.debounce('store', () => {
 | 
			
		||||
@@ -239,13 +251,15 @@
 | 
			
		||||
          this.$.storage.eraseEditableContentItem(this.storageKey);
 | 
			
		||||
        }
 | 
			
		||||
      }, STORAGE_DEBOUNCE_INTERVAL_MS);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveShortcut(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      if (!this._saveDisabled) {
 | 
			
		||||
        this._saveEdit();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrEditorView.is, GrEditorView);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,90 +17,90 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-app-element',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.KeyboardShortcutMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrAppElement extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-app-element'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when the URL location changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @event location-change
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
      /**
 | 
			
		||||
       * @type {{ query: string, view: string, screen: string }}
 | 
			
		||||
       */
 | 
			
		||||
      params: Object,
 | 
			
		||||
      keyEventTarget: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        value() { return document.body; },
 | 
			
		||||
      },
 | 
			
		||||
        params: Object,
 | 
			
		||||
        keyEventTarget: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          value() { return document.body; },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _account: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_accountChanged',
 | 
			
		||||
      },
 | 
			
		||||
        _account: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_accountChanged',
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * The last time the g key was pressed in milliseconds (or a keydown event
 | 
			
		||||
       * was handled if the key is held down).
 | 
			
		||||
       * @type {number|null}
 | 
			
		||||
       */
 | 
			
		||||
      _lastGKeyPressTimestamp: {
 | 
			
		||||
        type: Number,
 | 
			
		||||
        value: null,
 | 
			
		||||
      },
 | 
			
		||||
        _lastGKeyPressTimestamp: {
 | 
			
		||||
          type: Number,
 | 
			
		||||
          value: null,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
        /**
 | 
			
		||||
       * @type {{ plugin: Object }}
 | 
			
		||||
       */
 | 
			
		||||
      _serverConfig: Object,
 | 
			
		||||
      _version: String,
 | 
			
		||||
      _showChangeListView: Boolean,
 | 
			
		||||
      _showDashboardView: Boolean,
 | 
			
		||||
      _showChangeView: Boolean,
 | 
			
		||||
      _showDiffView: Boolean,
 | 
			
		||||
      _showSettingsView: Boolean,
 | 
			
		||||
      _showAdminView: Boolean,
 | 
			
		||||
      _showCLAView: Boolean,
 | 
			
		||||
      _showEditorView: Boolean,
 | 
			
		||||
      _showPluginScreen: Boolean,
 | 
			
		||||
      _showDocumentationSearch: Boolean,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _viewState: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _lastError: Object,
 | 
			
		||||
      _lastSearchPage: String,
 | 
			
		||||
      _path: String,
 | 
			
		||||
      _pluginScreenName: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        computed: '_computePluginScreenName(params)',
 | 
			
		||||
      },
 | 
			
		||||
      _settingsUrl: String,
 | 
			
		||||
      _feedbackUrl: String,
 | 
			
		||||
      // Used to allow searching on mobile
 | 
			
		||||
      mobileSearch: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _serverConfig: Object,
 | 
			
		||||
        _version: String,
 | 
			
		||||
        _showChangeListView: Boolean,
 | 
			
		||||
        _showDashboardView: Boolean,
 | 
			
		||||
        _showChangeView: Boolean,
 | 
			
		||||
        _showDiffView: Boolean,
 | 
			
		||||
        _showSettingsView: Boolean,
 | 
			
		||||
        _showAdminView: Boolean,
 | 
			
		||||
        _showCLAView: Boolean,
 | 
			
		||||
        _showEditorView: Boolean,
 | 
			
		||||
        _showPluginScreen: Boolean,
 | 
			
		||||
        _showDocumentationSearch: Boolean,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _viewState: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _lastError: Object,
 | 
			
		||||
        _lastSearchPage: String,
 | 
			
		||||
        _path: String,
 | 
			
		||||
        _pluginScreenName: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          computed: '_computePluginScreenName(params)',
 | 
			
		||||
        },
 | 
			
		||||
        _settingsUrl: String,
 | 
			
		||||
        _feedbackUrl: String,
 | 
			
		||||
        // Used to allow searching on mobile
 | 
			
		||||
        mobileSearch: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    listeners: {
 | 
			
		||||
      'page-error': '_handlePageError',
 | 
			
		||||
      'title-change': '_handleTitleChange',
 | 
			
		||||
      'location-change': '_handleLocationChange',
 | 
			
		||||
      'rpc-log': '_handleRpcLog',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_viewChanged(params.view)',
 | 
			
		||||
      '_paramsChanged(params.*)',
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.KeyboardShortcutBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_viewChanged(params.view)',
 | 
			
		||||
        '_paramsChanged(params.*)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    keyboardShortcuts() {
 | 
			
		||||
      return {
 | 
			
		||||
@@ -111,13 +111,23 @@
 | 
			
		||||
        [this.Shortcut.GO_TO_ABANDONED_CHANGES]: '_goToAbandonedChanges',
 | 
			
		||||
        [this.Shortcut.GO_TO_WATCHED_CHANGES]: '_goToWatchedChanges',
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    created() {
 | 
			
		||||
      super.created();
 | 
			
		||||
      this._bindKeyboardShortcuts();
 | 
			
		||||
    },
 | 
			
		||||
      this.addEventListener('page-error',
 | 
			
		||||
          e => this._handlePageError(e));
 | 
			
		||||
      this.addEventListener('title-change',
 | 
			
		||||
          e => this._handleTitleChange(e));
 | 
			
		||||
      this.addEventListener('location-change',
 | 
			
		||||
          e => this._handleLocationChange(e));
 | 
			
		||||
      this.addEventListener('rpc-log',
 | 
			
		||||
          e => this._handleRpcLog(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      this.$.reporting.appStarted();
 | 
			
		||||
      this.$.router.start();
 | 
			
		||||
 | 
			
		||||
@@ -165,7 +175,7 @@
 | 
			
		||||
          selectedChangeIndex: 0,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _bindKeyboardShortcuts() {
 | 
			
		||||
      this.bindShortcut(this.Shortcut.SEND_REPLY,
 | 
			
		||||
@@ -287,7 +297,7 @@
 | 
			
		||||
 | 
			
		||||
      this.bindShortcut(
 | 
			
		||||
          this.Shortcut.SEARCH, '/');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _accountChanged(account) {
 | 
			
		||||
      if (!account) { return; }
 | 
			
		||||
@@ -298,7 +308,7 @@
 | 
			
		||||
      this.$.restAPI.getEditPreferences();
 | 
			
		||||
      this.$.errorManager.knownAccountId =
 | 
			
		||||
          this._account && this._account._account_id || null;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _viewChanged(view) {
 | 
			
		||||
      this.$.errorView.classList.remove('show');
 | 
			
		||||
@@ -328,7 +338,7 @@
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      this.$.header.unfloat();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handlePageError(e) {
 | 
			
		||||
      const props = [
 | 
			
		||||
@@ -356,7 +366,7 @@
 | 
			
		||||
          this._lastError = err;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleLocationChange(e) {
 | 
			
		||||
      const hash = e.detail.hash.substring(1);
 | 
			
		||||
@@ -365,7 +375,7 @@
 | 
			
		||||
        pathname += '@' + hash;
 | 
			
		||||
      }
 | 
			
		||||
      this.set('_path', pathname);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _paramsChanged(paramsRecord) {
 | 
			
		||||
      const params = paramsRecord.base;
 | 
			
		||||
@@ -373,7 +383,7 @@
 | 
			
		||||
      if (viewsToCheck.includes(params.view)) {
 | 
			
		||||
        this.set('_lastSearchPage', location.pathname);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleTitleChange(e) {
 | 
			
		||||
      if (e.detail.title) {
 | 
			
		||||
@@ -381,54 +391,54 @@
 | 
			
		||||
      } else {
 | 
			
		||||
        document.title = '';
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _showKeyboardShortcuts(e) {
 | 
			
		||||
      if (this.shouldSuppressKeyboardShortcut(e)) { return; }
 | 
			
		||||
      this.$.keyboardShortcuts.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleKeyboardShortcutDialogClose() {
 | 
			
		||||
      this.$.keyboardShortcuts.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleAccountDetailUpdate(e) {
 | 
			
		||||
      this.$.mainHeader.reload();
 | 
			
		||||
      if (this.params.view === Gerrit.Nav.View.SETTINGS) {
 | 
			
		||||
        this.$$('gr-settings-view').reloadAccountDetail();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleRegistrationDialogClose(e) {
 | 
			
		||||
      this.params.justRegistered = false;
 | 
			
		||||
      this.$.registrationOverlay.close();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _goToOpenedChanges() {
 | 
			
		||||
      Gerrit.Nav.navigateToStatusSearch('open');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _goToUserDashboard() {
 | 
			
		||||
      Gerrit.Nav.navigateToUserDashboard();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _goToMergedChanges() {
 | 
			
		||||
      Gerrit.Nav.navigateToStatusSearch('merged');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _goToAbandonedChanges() {
 | 
			
		||||
      Gerrit.Nav.navigateToStatusSearch('abandoned');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _goToWatchedChanges() {
 | 
			
		||||
      // The query is hardcoded, and doesn't respect custom menu entries
 | 
			
		||||
      Gerrit.Nav.navigateToSearchQuery('is:watched is:open');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computePluginScreenName({plugin, screen}) {
 | 
			
		||||
      if (!plugin || !screen) return '';
 | 
			
		||||
      return `${plugin}-screen-${screen}`;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _logWelcome() {
 | 
			
		||||
      console.group('Runtime Info');
 | 
			
		||||
@@ -441,7 +451,7 @@
 | 
			
		||||
        console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`);
 | 
			
		||||
      }
 | 
			
		||||
      console.groupEnd();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Intercept RPC log events emitted by REST API interfaces.
 | 
			
		||||
@@ -451,17 +461,19 @@
 | 
			
		||||
    _handleRpcLog(e) {
 | 
			
		||||
      this.$.reporting.reportRpcTiming(e.detail.anonymizedUrl,
 | 
			
		||||
          e.detail.elapsed);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _mobileSearchToggle(e) {
 | 
			
		||||
      this.mobileSearch = !this.mobileSearch;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getThemeEndpoint() {
 | 
			
		||||
      // For now, we only have dark mode and light mode
 | 
			
		||||
      return window.localStorage.getItem('dark-theme') ?
 | 
			
		||||
        'app-theme-dark' :
 | 
			
		||||
        'app-theme-light';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrAppElement.is, GrAppElement);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,11 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-app',
 | 
			
		||||
  });
 | 
			
		||||
  class GrApp extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-app'; }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrApp.is, GrApp);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -19,33 +19,38 @@
 | 
			
		||||
 | 
			
		||||
  const INIT_PROPERTIES_TIMEOUT_MS = 10000;
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-endpoint-decorator',
 | 
			
		||||
  class GrEndpointDecorator extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-endpoint-decorator'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      name: String,
 | 
			
		||||
      /** @type {!Map} */
 | 
			
		||||
      _domHooks: {
 | 
			
		||||
        type: Map,
 | 
			
		||||
        value() { return new Map(); },
 | 
			
		||||
      },
 | 
			
		||||
      /**
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        name: String,
 | 
			
		||||
        /** @type {!Map} */
 | 
			
		||||
        _domHooks: {
 | 
			
		||||
          type: Map,
 | 
			
		||||
          value() { return new Map(); },
 | 
			
		||||
        },
 | 
			
		||||
        /**
 | 
			
		||||
       * This map prevents importing the same endpoint twice.
 | 
			
		||||
       * Without caching, if a plugin is loaded after the loaded plugins
 | 
			
		||||
       * callback fires, it will be imported twice and appear twice on the page.
 | 
			
		||||
       * @type {!Map}
 | 
			
		||||
       */
 | 
			
		||||
      _initializedPlugins: {
 | 
			
		||||
        type: Map,
 | 
			
		||||
        value() { return new Map(); },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _initializedPlugins: {
 | 
			
		||||
          type: Map,
 | 
			
		||||
          value() { return new Map(); },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    detached() {
 | 
			
		||||
      super.detached();
 | 
			
		||||
      for (const [el, domHook] of this._domHooks) {
 | 
			
		||||
        domHook.handleInstanceDetached(el);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @suppress {checkTypes}
 | 
			
		||||
@@ -54,7 +59,7 @@
 | 
			
		||||
      return new Promise((resolve, reject) => {
 | 
			
		||||
        (this.importHref || Polymer.importHref)(url, resolve, reject);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _initDecoration(name, plugin) {
 | 
			
		||||
      const el = document.createElement(name);
 | 
			
		||||
@@ -62,7 +67,7 @@
 | 
			
		||||
          this.getContentChildren().find(
 | 
			
		||||
              el => el.nodeName !== 'GR-ENDPOINT-PARAM'))
 | 
			
		||||
          .then(el => this._appendChild(el));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _initReplacement(name, plugin) {
 | 
			
		||||
      this.getContentChildNodes()
 | 
			
		||||
@@ -71,12 +76,12 @@
 | 
			
		||||
      const el = document.createElement(name);
 | 
			
		||||
      return this._initProperties(el, plugin).then(
 | 
			
		||||
          el => this._appendChild(el));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getEndpointParams() {
 | 
			
		||||
      return Array.from(
 | 
			
		||||
          Polymer.dom(this).querySelectorAll('gr-endpoint-param'));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {!Element} el
 | 
			
		||||
@@ -109,11 +114,11 @@
 | 
			
		||||
            clearTimeout(timeoutId);
 | 
			
		||||
            return el;
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _appendChild(el) {
 | 
			
		||||
      return Polymer.dom(this.root).appendChild(el);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _initModule({moduleName, plugin, type, domHook}) {
 | 
			
		||||
      const name = plugin.getPluginName() + '.' + moduleName;
 | 
			
		||||
@@ -137,9 +142,10 @@
 | 
			
		||||
        domHook.handleInstanceAttached(el);
 | 
			
		||||
        this._domHooks.set(el, domHook);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      Gerrit._endpoints.onNewEndpoint(this.name, this._initModule.bind(this));
 | 
			
		||||
      Gerrit.awaitPluginsLoaded().then(() => Promise.all(
 | 
			
		||||
          Gerrit._endpoints.getPlugins(this.name).map(
 | 
			
		||||
@@ -149,6 +155,8 @@
 | 
			
		||||
            .getDetails(this.name)
 | 
			
		||||
            .forEach(this._initModule, this)
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrEndpointDecorator.is, GrEndpointDecorator);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,17 +17,21 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-endpoint-param',
 | 
			
		||||
  class GrEndpointParam extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-endpoint-param'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      name: String,
 | 
			
		||||
      value: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        observer: '_valueChanged',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        name: String,
 | 
			
		||||
        value: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          observer: '_valueChanged',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _valueChanged(newValue, oldValue) {
 | 
			
		||||
      /* In polymer 2 the following change was made:
 | 
			
		||||
@@ -42,6 +46,8 @@
 | 
			
		||||
        value: newValue,
 | 
			
		||||
      };
 | 
			
		||||
      this.dispatchEvent(new CustomEvent('value-changed', {detail}));
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrEndpointParam.is, GrEndpointParam);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,20 +17,24 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-external-style',
 | 
			
		||||
  class GrExternalStyle extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-external-style'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      name: String,
 | 
			
		||||
      _urlsImported: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
      _stylesApplied: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        value() { return []; },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        name: String,
 | 
			
		||||
        _urlsImported: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
        _stylesApplied: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          value() { return []; },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @suppress {checkTypes}
 | 
			
		||||
@@ -41,7 +45,7 @@
 | 
			
		||||
      return new Promise((resolve, reject) => {
 | 
			
		||||
        (this.importHref || Polymer.importHref)(url, resolve, reject);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _applyStyle(name) {
 | 
			
		||||
      if (this._stylesApplied.includes(name)) { return; }
 | 
			
		||||
@@ -56,7 +60,7 @@
 | 
			
		||||
      const topEl = document.getElementsByTagName('body')[0];
 | 
			
		||||
      topEl.insertBefore(cs, topEl.firstChild);
 | 
			
		||||
      Polymer.updateStyles();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _importAndApply() {
 | 
			
		||||
      Promise.all(Gerrit._endpoints.getPlugins(this.name).map(
 | 
			
		||||
@@ -67,14 +71,18 @@
 | 
			
		||||
          this._applyStyle(name);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this._importAndApply();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ready() {
 | 
			
		||||
      super.ready();
 | 
			
		||||
      Gerrit.awaitPluginsLoaded().then(() => this._importAndApply());
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrExternalStyle.is, GrExternalStyle);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,19 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-plugin-host',
 | 
			
		||||
  class GrPluginHost extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-plugin-host'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      config: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        observer: '_configChanged',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        config: {
 | 
			
		||||
          type: Object,
 | 
			
		||||
          observer: '_configChanged',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _configChanged(config) {
 | 
			
		||||
      const plugins = config.plugin;
 | 
			
		||||
@@ -50,7 +54,7 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Gerrit._loadPlugins(pluginsPending, pluginOpts);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Omit .js plugins that have .html counterparts.
 | 
			
		||||
@@ -61,6 +65,8 @@
 | 
			
		||||
        const counterpart = url.replace(/\.js$/, '.html');
 | 
			
		||||
        return !htmlPlugins.includes(counterpart);
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrPluginHost.is, GrPluginHost);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,19 +17,23 @@
 | 
			
		||||
(function(window) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-plugin-popup',
 | 
			
		||||
  class GrPluginPopup extends Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element)) {
 | 
			
		||||
    static get is() { return 'gr-plugin-popup'; }
 | 
			
		||||
 | 
			
		||||
    get opened() {
 | 
			
		||||
      return this.$.overlay.opened;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open() {
 | 
			
		||||
      return this.$.overlay.open();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close() {
 | 
			
		||||
      this.$.overlay.close();
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrPluginPopup.is, GrPluginPopup);
 | 
			
		||||
})(window);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,65 +17,71 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-account-info',
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrAccountInfo extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-account-info'; }
 | 
			
		||||
    /**
 | 
			
		||||
     * Fired when account details are changed.
 | 
			
		||||
     *
 | 
			
		||||
     * @event account-detail-update
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      usernameMutable: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        computed: '_computeUsernameMutable(_serverConfig, _account.username)',
 | 
			
		||||
      },
 | 
			
		||||
      nameMutable: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        computed: '_computeNameMutable(_serverConfig)',
 | 
			
		||||
      },
 | 
			
		||||
      hasUnsavedChanges: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
        computed: '_computeHasUnsavedChanges(_hasNameChange, ' +
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        usernameMutable: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          computed: '_computeUsernameMutable(_serverConfig, _account.username)',
 | 
			
		||||
        },
 | 
			
		||||
        nameMutable: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          computed: '_computeNameMutable(_serverConfig)',
 | 
			
		||||
        },
 | 
			
		||||
        hasUnsavedChanges: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
          computed: '_computeHasUnsavedChanges(_hasNameChange, ' +
 | 
			
		||||
            '_hasUsernameChange, _hasStatusChange)',
 | 
			
		||||
      },
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      _hasNameChange: Boolean,
 | 
			
		||||
      _hasUsernameChange: Boolean,
 | 
			
		||||
      _hasStatusChange: Boolean,
 | 
			
		||||
      _loading: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _saving: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _account: Object,
 | 
			
		||||
      _serverConfig: Object,
 | 
			
		||||
      _username: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        observer: '_usernameChanged',
 | 
			
		||||
      },
 | 
			
		||||
      _avatarChangeUrl: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        value: '',
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
        _hasNameChange: Boolean,
 | 
			
		||||
        _hasUsernameChange: Boolean,
 | 
			
		||||
        _hasStatusChange: Boolean,
 | 
			
		||||
        _loading: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _saving: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _account: Object,
 | 
			
		||||
        _serverConfig: Object,
 | 
			
		||||
        _username: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          observer: '_usernameChanged',
 | 
			
		||||
        },
 | 
			
		||||
        _avatarChangeUrl: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          value: '',
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    observers: [
 | 
			
		||||
      '_nameChanged(_account.name)',
 | 
			
		||||
      '_statusChanged(_account.status)',
 | 
			
		||||
    ],
 | 
			
		||||
    static get observers() {
 | 
			
		||||
      return [
 | 
			
		||||
        '_nameChanged(_account.name)',
 | 
			
		||||
        '_statusChanged(_account.status)',
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadData() {
 | 
			
		||||
      const promises = [];
 | 
			
		||||
@@ -104,7 +110,7 @@
 | 
			
		||||
      return Promise.all(promises).then(() => {
 | 
			
		||||
        this._loading = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    save() {
 | 
			
		||||
      if (!this.hasUnsavedChanges) {
 | 
			
		||||
@@ -123,29 +129,29 @@
 | 
			
		||||
            this._saving = false;
 | 
			
		||||
            this.fire('account-detail-update');
 | 
			
		||||
          });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _maybeSetName() {
 | 
			
		||||
      return this._hasNameChange && this.nameMutable ?
 | 
			
		||||
        this.$.restAPI.setAccountName(this._account.name) :
 | 
			
		||||
        Promise.resolve();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _maybeSetUsername() {
 | 
			
		||||
      return this._hasUsernameChange && this.usernameMutable ?
 | 
			
		||||
        this.$.restAPI.setAccountUsername(this._username) :
 | 
			
		||||
        Promise.resolve();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _maybeSetStatus() {
 | 
			
		||||
      return this._hasStatusChange ?
 | 
			
		||||
        this.$.restAPI.setAccountStatus(this._account.status) :
 | 
			
		||||
        Promise.resolve();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeHasUnsavedChanges(nameChanged, usernameChanged, statusChanged) {
 | 
			
		||||
      return nameChanged || usernameChanged || statusChanged;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeUsernameMutable(config, username) {
 | 
			
		||||
      // Polymer 2: check for undefined
 | 
			
		||||
@@ -159,34 +165,34 @@
 | 
			
		||||
      // Username may not be changed once it is set.
 | 
			
		||||
      return config.auth.editable_account_fields.includes('USER_NAME') &&
 | 
			
		||||
          !username;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeNameMutable(config) {
 | 
			
		||||
      return config.auth.editable_account_fields.includes('FULL_NAME');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _statusChanged() {
 | 
			
		||||
      if (this._loading) { return; }
 | 
			
		||||
      this._hasStatusChange = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _usernameChanged() {
 | 
			
		||||
      if (this._loading || !this._account) { return; }
 | 
			
		||||
      this._hasUsernameChange =
 | 
			
		||||
          (this._account.username || '') !== (this._username || '');
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _nameChanged() {
 | 
			
		||||
      if (this._loading) { return; }
 | 
			
		||||
      this._hasNameChange = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleKeydown(e) {
 | 
			
		||||
      if (e.keyCode === 13) { // Enter
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
        this.save();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hideAvatarChangeUrl(avatarChangeUrl) {
 | 
			
		||||
      if (!avatarChangeUrl) {
 | 
			
		||||
@@ -194,6 +200,8 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return '';
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrAccountInfo.is, GrAccountInfo);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,33 +17,41 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-agreements-list',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrAgreementsList extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-agreements-list'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      _agreements: Array,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        _agreements: Array,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.loadData();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadData() {
 | 
			
		||||
      return this.$.restAPI.getAccountAgreements().then(agreements => {
 | 
			
		||||
        this._agreements = agreements;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getUrl() {
 | 
			
		||||
      return this.getBaseUrl() + '/settings/new-agreement';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getUrlBase(item) {
 | 
			
		||||
      return this.getBaseUrl() + '/' + item;
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrAgreementsList.is, GrAgreementsList);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,23 +17,28 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-change-table-editor',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.ChangeTableMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrChangeTableEditor extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.ChangeTableBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-change-table-editor'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      displayedColumns: {
 | 
			
		||||
        type: Array,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
      showNumber: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        notify: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.ChangeTableBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        displayedColumns: {
 | 
			
		||||
          type: Array,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
        showNumber: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          notify: true,
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the list of enabled column names from whichever checkboxes are
 | 
			
		||||
@@ -45,7 +50,7 @@
 | 
			
		||||
          .querySelectorAll('.checkboxContainer input:not([name=number])'))
 | 
			
		||||
          .filter(checkbox => checkbox.checked)
 | 
			
		||||
          .map(checkbox => checkbox.name);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle a click on a checkbox container and relay the click to the checkbox it
 | 
			
		||||
@@ -55,7 +60,7 @@
 | 
			
		||||
      const checkbox = Polymer.dom(e.target).querySelector('input');
 | 
			
		||||
      if (!checkbox) { return; }
 | 
			
		||||
      checkbox.click();
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle a click on the number checkbox and update the showNumber property
 | 
			
		||||
@@ -63,7 +68,7 @@
 | 
			
		||||
     */
 | 
			
		||||
    _handleNumberCheckboxClick(e) {
 | 
			
		||||
      this.showNumber = Polymer.dom(e).rootTarget.checked;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle a click on a displayed column checkboxes (excluding number) and
 | 
			
		||||
@@ -71,6 +76,8 @@
 | 
			
		||||
     */
 | 
			
		||||
    _handleTargetClick(e) {
 | 
			
		||||
      this.set('displayedColumns', this._getDisplayedColumns());
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrChangeTableEditor.is, GrChangeTableEditor);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -17,33 +17,40 @@
 | 
			
		||||
(function() {
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  Polymer({
 | 
			
		||||
    is: 'gr-cla-view',
 | 
			
		||||
  /**
 | 
			
		||||
    * @appliesMixin Gerrit.BaseUrlMixin
 | 
			
		||||
    * @appliesMixin Gerrit.FireMixin
 | 
			
		||||
    */
 | 
			
		||||
  class GrClaView extends Polymer.mixinBehaviors( [
 | 
			
		||||
    Gerrit.BaseUrlBehavior,
 | 
			
		||||
    Gerrit.FireBehavior,
 | 
			
		||||
  ], Polymer.GestureEventListeners(
 | 
			
		||||
      Polymer.LegacyElementMixin(
 | 
			
		||||
          Polymer.Element))) {
 | 
			
		||||
    static get is() { return 'gr-cla-view'; }
 | 
			
		||||
 | 
			
		||||
    properties: {
 | 
			
		||||
      _groups: Object,
 | 
			
		||||
      /** @type {?} */
 | 
			
		||||
      _serverConfig: Object,
 | 
			
		||||
      _agreementsText: String,
 | 
			
		||||
      _agreementName: String,
 | 
			
		||||
      _signedAgreements: Array,
 | 
			
		||||
      _showAgreements: {
 | 
			
		||||
        type: Boolean,
 | 
			
		||||
        value: false,
 | 
			
		||||
      },
 | 
			
		||||
      _agreementsUrl: String,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    behaviors: [
 | 
			
		||||
      Gerrit.BaseUrlBehavior,
 | 
			
		||||
      Gerrit.FireBehavior,
 | 
			
		||||
    ],
 | 
			
		||||
    static get properties() {
 | 
			
		||||
      return {
 | 
			
		||||
        _groups: Object,
 | 
			
		||||
        /** @type {?} */
 | 
			
		||||
        _serverConfig: Object,
 | 
			
		||||
        _agreementsText: String,
 | 
			
		||||
        _agreementName: String,
 | 
			
		||||
        _signedAgreements: Array,
 | 
			
		||||
        _showAgreements: {
 | 
			
		||||
          type: Boolean,
 | 
			
		||||
          value: false,
 | 
			
		||||
        },
 | 
			
		||||
        _agreementsUrl: String,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    attached() {
 | 
			
		||||
      super.attached();
 | 
			
		||||
      this.loadData();
 | 
			
		||||
 | 
			
		||||
      this.fire('title-change', {title: 'New Contributor Agreement'});
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadData() {
 | 
			
		||||
      const promises = [];
 | 
			
		||||
@@ -62,7 +69,7 @@
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return Promise.all(promises);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getAgreementsUrl(configUrl) {
 | 
			
		||||
      let url;
 | 
			
		||||
@@ -76,14 +83,14 @@
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return url;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleShowAgreement(e) {
 | 
			
		||||
      this._agreementName = e.target.getAttribute('data-name');
 | 
			
		||||
      this._agreementsUrl =
 | 
			
		||||
          this._getAgreementsUrl(e.target.getAttribute('data-url'));
 | 
			
		||||
      this._showAgreements = true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _handleSaveAgreements(e) {
 | 
			
		||||
      this._createToast('Agreement saving...');
 | 
			
		||||
@@ -99,16 +106,16 @@
 | 
			
		||||
        this._agreementsText = '';
 | 
			
		||||
        this._showAgreements = false;
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _createToast(message) {
 | 
			
		||||
      this.dispatchEvent(new CustomEvent(
 | 
			
		||||
          'show-alert', {detail: {message}, bubbles: true, composed: true}));
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _computeShowAgreementsClass(agreements) {
 | 
			
		||||
      return agreements ? 'show' : '';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _disableAggreements(item, groups, signedAgreements) {
 | 
			
		||||
      for (const group of groups) {
 | 
			
		||||
@@ -119,16 +126,16 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _hideAggreements(item, groups, signedAgreements) {
 | 
			
		||||
      return this._disableAggreements(item, groups, signedAgreements) ?
 | 
			
		||||
        '' : 'hide';
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _disableAgreementsText(text) {
 | 
			
		||||
      return text.toLowerCase() === 'i agree' ? false : true;
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This checks for auto_verify_group,
 | 
			
		||||
    // if specified it returns 'hideAgreementsTextBox' which
 | 
			
		||||
@@ -148,6 +155,8 @@
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  customElements.define(GrClaView.is, GrClaView);
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user