blob: 1802a4a7507d6af6a5c75e17f7d29379dba6df77 [file] [log] [blame]
/**
* @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';
const EventType = {
HISTORY: 'history',
LABEL_CHANGE: 'labelchange',
SHOW_CHANGE: 'showchange',
SUBMIT_CHANGE: 'submitchange',
COMMIT_MSG_EDIT: 'commitmsgedit',
COMMENT: 'comment',
REVERT: 'revert',
REVERT_SUBMISSION: 'revert_submission',
POST_REVERT: 'postrevert',
ANNOTATE_DIFF: 'annotatediff',
ADMIN_MENU_LINKS: 'admin-menu-links',
HIGHLIGHTJS_LOADED: 'highlightjs-loaded',
};
const Element = {
CHANGE_ACTIONS: 'changeactions',
REPLY_DIALOG: 'replydialog',
};
/**
* @appliesMixin Gerrit.PatchSetMixin
*/
class GrJsApiInterface extends Polymer.mixinBehaviors( [
Gerrit.PatchSetBehavior,
], Polymer.GestureEventListeners(
Polymer.LegacyElementMixin(
Polymer.Element))) {
static get is() { return 'gr-js-api-interface'; }
constructor() {
super();
this.Element = Element;
this.EventType = EventType;
}
static get properties() {
return {
_elements: {
type: Object,
value: {}, // Shared across all instances.
},
_eventCallbacks: {
type: Object,
value: {}, // Shared across all instances.
},
};
}
handleEvent(type, detail) {
Gerrit.awaitPluginsLoaded().then(() => {
switch (type) {
case EventType.HISTORY:
this._handleHistory(detail);
break;
case EventType.SHOW_CHANGE:
this._handleShowChange(detail);
break;
case EventType.COMMENT:
this._handleComment(detail);
break;
case EventType.LABEL_CHANGE:
this._handleLabelChange(detail);
break;
case EventType.HIGHLIGHTJS_LOADED:
this._handleHighlightjsLoaded(detail);
break;
default:
console.warn('handleEvent called with unsupported event type:',
type);
break;
}
});
}
addElement(key, el) {
this._elements[key] = el;
}
getElement(key) {
return this._elements[key];
}
addEventCallback(eventName, callback) {
if (!this._eventCallbacks[eventName]) {
this._eventCallbacks[eventName] = [];
}
this._eventCallbacks[eventName].push(callback);
}
canSubmitChange(change, revision) {
const submitCallbacks = this._getEventCallbacks(EventType.SUBMIT_CHANGE);
const cancelSubmit = submitCallbacks.some(callback => {
try {
return callback(change, revision) === false;
} catch (err) {
console.error(err);
}
return false;
});
return !cancelSubmit;
}
_removeEventCallbacks() {
for (const k in EventType) {
if (!EventType.hasOwnProperty(k)) { continue; }
this._eventCallbacks[EventType[k]] = [];
}
}
_handleHistory(detail) {
for (const cb of this._getEventCallbacks(EventType.HISTORY)) {
try {
cb(detail.path);
} catch (err) {
console.error(err);
}
}
}
_handleShowChange(detail) {
// Note (issue 8221) Shallow clone the change object and add a mergeable
// getter with deprecation warning. This makes the change detail appear as
// though SKIP_MERGEABLE was not set, so that plugins that expect it can
// still access.
//
// This clone and getter can be removed after plugins migrate to use
// info.mergeable.
const change = Object.assign({
get mergeable() {
console.warn('Accessing change.mergeable from SHOW_CHANGE is ' +
'deprecated! Use info.mergeable instead.');
return detail.info.mergeable;
},
}, detail.change);
const patchNum = detail.patchNum;
const info = detail.info;
let revision;
for (const rev of Object.values(change.revisions || {})) {
if (this.patchNumEquals(rev._number, patchNum)) {
revision = rev;
break;
}
}
for (const cb of this._getEventCallbacks(EventType.SHOW_CHANGE)) {
try {
cb(change, revision, info);
} catch (err) {
console.error(err);
}
}
}
handleCommitMessage(change, msg) {
for (const cb of this._getEventCallbacks(EventType.COMMIT_MSG_EDIT)) {
try {
cb(change, msg);
} catch (err) {
console.error(err);
}
}
}
_handleComment(detail) {
for (const cb of this._getEventCallbacks(EventType.COMMENT)) {
try {
cb(detail.node);
} catch (err) {
console.error(err);
}
}
}
_handleLabelChange(detail) {
for (const cb of this._getEventCallbacks(EventType.LABEL_CHANGE)) {
try {
cb(detail.change);
} catch (err) {
console.error(err);
}
}
}
_handleHighlightjsLoaded(detail) {
for (const cb of this._getEventCallbacks(EventType.HIGHLIGHTJS_LOADED)) {
try {
cb(detail.hljs);
} catch (err) {
console.error(err);
}
}
}
modifyRevertMsg(change, revertMsg, origMsg) {
for (const cb of this._getEventCallbacks(EventType.REVERT)) {
try {
revertMsg = cb(change, revertMsg, origMsg);
} catch (err) {
console.error(err);
}
}
return revertMsg;
}
modifyRevertSubmissionMsg(change, revertSubmissionMsg, origMsg) {
for (const cb of this._getEventCallbacks(EventType.REVERT_SUBMISSION)) {
try {
revertSubmissionMsg = cb(change, revertSubmissionMsg, origMsg);
} catch (err) {
console.error(err);
}
}
return revertSubmissionMsg;
}
getDiffLayers(path, changeNum, patchNum) {
const layers = [];
for (const annotationApi of
this._getEventCallbacks(EventType.ANNOTATE_DIFF)) {
try {
const layer = annotationApi.getLayer(path, changeNum, patchNum);
layers.push(layer);
} catch (err) {
console.error(err);
}
}
return layers;
}
/**
* Retrieves coverage data possibly provided by a plugin.
*
* Will wait for plugins to be loaded. If multiple plugins offer a coverage
* provider, the first one is used. If no plugin offers a coverage provider,
* will resolve to [].
*
* @param {string|number} changeNum
* @param {string} path
* @param {string|number} basePatchNum
* @param {string|number} patchNum
* @return {!Promise<!Array<!Gerrit.CoverageRange>>}
*/
getCoverageRanges(changeNum, path, basePatchNum, patchNum) {
return Gerrit.awaitPluginsLoaded().then(() => {
for (const annotationApi of
this._getEventCallbacks(EventType.ANNOTATE_DIFF)) {
const provider = annotationApi.getCoverageProvider();
// Only one coverage provider makes sense. If there are more, then we
// simply ignore them.
if (provider) {
return provider(changeNum, path, basePatchNum, patchNum);
}
}
return [];
});
}
getAdminMenuLinks() {
const links = [];
for (const adminApi of
this._getEventCallbacks(EventType.ADMIN_MENU_LINKS)) {
links.push(...adminApi.getMenuLinks());
}
return links;
}
getLabelValuesPostRevert(change) {
let labels = {};
for (const cb of this._getEventCallbacks(EventType.POST_REVERT)) {
try {
labels = cb(change);
} catch (err) {
console.error(err);
}
}
return labels;
}
_getEventCallbacks(type) {
return this._eventCallbacks[type] || [];
}
}
customElements.define(GrJsApiInterface.is, GrJsApiInterface);
})();