|  | /** | 
|  | * @license | 
|  | * Copyright (C) 2016 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | * http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  | (function() { | 
|  | 'use strict'; | 
|  |  | 
|  | Polymer({ | 
|  | is: 'gr-reviewer-list', | 
|  | _legacyUndefinedCheck: true, | 
|  |  | 
|  | /** | 
|  | * 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, | 
|  |  | 
|  | _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, | 
|  | }, | 
|  |  | 
|  | behaviors: [ | 
|  | Gerrit.FireBehavior, | 
|  | ], | 
|  |  | 
|  | observers: [ | 
|  | '_reviewersChanged(change.reviewers.*, change.owner)', | 
|  | ], | 
|  |  | 
|  | /** | 
|  | * Converts change.permitted_labels to an array of hashes of label keys to | 
|  | * numeric scores. | 
|  | * Example: | 
|  | * [{ | 
|  | *   'Code-Review': ['-1', ' 0', '+1'] | 
|  | * }] | 
|  | * will be converted to | 
|  | * [{ | 
|  | *   label: 'Code-Review', | 
|  | *   scores: [-1, 0, 1] | 
|  | * }] | 
|  | */ | 
|  | _permittedLabelsToNumericScores(labels) { | 
|  | if (!labels) return []; | 
|  | return Object.keys(labels).map(label => ({ | 
|  | label, | 
|  | scores: labels[label].map(v => parseInt(v, 10)), | 
|  | })); | 
|  | }, | 
|  |  | 
|  | /** | 
|  | * Returns hash of labels to max permitted score. | 
|  | * @param {!Object} change | 
|  | * @returns {!Object} labels to max permitted scores hash | 
|  | */ | 
|  | _getMaxPermittedScores(change) { | 
|  | return this._permittedLabelsToNumericScores(change.permitted_labels) | 
|  | .map(({label, scores}) => ({ | 
|  | [label]: scores | 
|  | .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. | 
|  | * @param {!Object} reviewer | 
|  | * @param {!Object} change | 
|  | * @param {string} label | 
|  | * @return {number} | 
|  | */ | 
|  | _getReviewerPermittedScore(reviewer, change, label) { | 
|  | // Note (issue 7874): sometimes the "all" list is not included in change | 
|  | // detail responses, even when DETAILED_LABELS is included in options. | 
|  | if (!change.labels[label].all) { return NaN; } | 
|  | const detailed = change.labels[label].all.filter( | 
|  | ({_account_id}) => reviewer._account_id === _account_id).pop(); | 
|  | if (!detailed) { | 
|  | return NaN; | 
|  | } | 
|  | if (detailed.hasOwnProperty('permitted_voting_range')) { | 
|  | return detailed.permitted_voting_range.max; | 
|  | } else if (detailed.hasOwnProperty('value')) { | 
|  | // If preset, user can vote on the label. | 
|  | return 0; | 
|  | } | 
|  | return NaN; | 
|  | }, | 
|  |  | 
|  | _computeReviewerTooltip(reviewer, change) { | 
|  | if (!change || !change.labels) { return ''; } | 
|  | const maxScores = []; | 
|  | const maxPermitted = this._getMaxPermittedScores(change); | 
|  | for (const label of Object.keys(change.labels)) { | 
|  | const maxScore = | 
|  | this._getReviewerPermittedScore(reviewer, change, label); | 
|  | if (isNaN(maxScore) || maxScore < 0) { continue; } | 
|  | if (maxScore > 0 && maxScore === maxPermitted[label]) { | 
|  | maxScores.push(`${label}: +${maxScore}`); | 
|  | } else { | 
|  | maxScores.push(`${label}`); | 
|  | } | 
|  | } | 
|  | if (maxScores.length) { | 
|  | return 'Votable: ' + maxScores.join(', '); | 
|  | } else { | 
|  | return ''; | 
|  | } | 
|  | }, | 
|  |  | 
|  | _reviewersChanged(changeRecord, owner) { | 
|  | // Polymer 2: check for undefined | 
|  | if ([changeRecord, owner].some(arg => arg === undefined)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let result = []; | 
|  | const reviewers = changeRecord.base; | 
|  | for (const key in reviewers) { | 
|  | if (this.reviewersOnly && key !== 'REVIEWER') { | 
|  | continue; | 
|  | } | 
|  | if (this.ccsOnly && key !== 'CC') { | 
|  | continue; | 
|  | } | 
|  | if (key === 'REVIEWER' || key === 'CC') { | 
|  | result = result.concat(reviewers[key]); | 
|  | } | 
|  | } | 
|  | this._reviewers = result.filter(reviewer => { | 
|  | return reviewer._account_id != owner._account_id; | 
|  | }); | 
|  |  | 
|  | // If there is one more than the max reviewers, don't show the 'show | 
|  | // more' button, because it takes up just as much space. | 
|  | if (this.maxReviewersDisplayed && | 
|  | this._reviewers.length > this.maxReviewersDisplayed + 1) { | 
|  | this._displayedReviewers = | 
|  | this._reviewers.slice(0, this.maxReviewersDisplayed); | 
|  | } else { | 
|  | this._displayedReviewers = this._reviewers; | 
|  | } | 
|  | }, | 
|  |  | 
|  | _computeHiddenCount(reviewers, displayedReviewers) { | 
|  | // Polymer 2: check for undefined | 
|  | if ([reviewers, displayedReviewers].some(arg => arg === undefined)) { | 
|  | return undefined; | 
|  | } | 
|  |  | 
|  | return reviewers.length - displayedReviewers.length; | 
|  | }, | 
|  |  | 
|  | _computeCanRemoveReviewer(reviewer, mutable) { | 
|  | if (!mutable) { return false; } | 
|  |  | 
|  | let current; | 
|  | for (let i = 0; i < this.change.removable_reviewers.length; i++) { | 
|  | current = this.change.removable_reviewers[i]; | 
|  | if (current._account_id === reviewer._account_id || | 
|  | (!reviewer._account_id && current.email === reviewer.email)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | }, | 
|  |  | 
|  | _handleRemove(e) { | 
|  | e.preventDefault(); | 
|  | const target = Polymer.dom(e).rootTarget; | 
|  | if (!target.account) { return; } | 
|  | const accountID = target.account._account_id || target.account.email; | 
|  | this.disabled = true; | 
|  | this._xhrPromise = this._removeReviewer(accountID).then(response => { | 
|  | this.disabled = false; | 
|  | if (!response.ok) { return response; } | 
|  |  | 
|  | const reviewers = this.change.reviewers; | 
|  |  | 
|  | for (const type of ['REVIEWER', 'CC']) { | 
|  | reviewers[type] = reviewers[type] || []; | 
|  | for (let i = 0; i < reviewers[type].length; i++) { | 
|  | if (reviewers[type][i]._account_id == accountID || | 
|  | reviewers[type][i].email == accountID) { | 
|  | this.splice('change.reviewers.' + type, i, 1); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | }).catch(err => { | 
|  | this.disabled = false; | 
|  | throw err; | 
|  | }); | 
|  | }, | 
|  |  | 
|  | _handleAddTap(e) { | 
|  | e.preventDefault(); | 
|  | const value = {}; | 
|  | if (this.reviewersOnly) { | 
|  | value.reviewersOnly = true; | 
|  | } | 
|  | if (this.ccsOnly) { | 
|  | 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'; | 
|  | }, | 
|  | }); | 
|  | })(); |