arch-repo-manager/srv/static/js/buildactionspage.js

898 lines
39 KiB
JavaScript

function initBuildActionsForm()
{
const buildActionsForm = document.getElementById('build-action-form');
if (buildActionsForm.dataset.initialized) {
return true;
}
queryBuildActions();
handleBuildActionTypeChange();
buildActionsForm.dataset.initialized = true;
return true;
}
function queryBuildActions()
{
queryRoute('GET', '/build-action', showBuildActions);
return true;
}
function queryBuildActionDetails(ids)
{
queryRoute('GET', '/build-action/details?' + makeIdParams(ids), showBuildActionDetails);
return true;
}
function cloneBuildAction(ids)
{
queryRoute('POST', '/build-action/clone?' + makeIdParams(ids), function (xhr, success) {
if (!success) {
return showAjaxError(xhr, 'clone build action');
}
const cloneIDs = JSON.parse(xhr.responseText);
if (cloneIDs.length === 1 && typeof cloneIDs[0] === 'number') {
if (window.confirm('Build action cloned as ' + cloneIDs[0] + '. Show cloned action?')) {
queryBuildActionDetails(cloneIDs[0]);
}
} else {
window.alert('Build action(s) have been cloned.');
}
});
return true;
}
function deleteBuildAction(ids)
{
queryRoute('DELETE', '/build-action?' + makeIdParams(ids), function (xhr, success) {
success ? window.alert('Build action has been deleted.') : showAjaxError(xhr, 'delete build action');
});
return true;
}
function stopBuildAction(ids)
{
queryRoute('POST', '/build-action/stop?' + makeIdParams(ids), function (xhr, success) {
success ? window.alert('Build action has been stopped.') : showAjaxError(xhr, 'stop build action');
});
return true;
}
function startBuildAction(ids)
{
queryRoute('POST', '/build-action/start?' + makeIdParams(ids), function (xhr, success) {
success ? window.alert('Build action has been started.') : showAjaxError(xhr, 'start build action');
});
return true;
}
function selectOptions(selectElement, values)
{
Array.from(selectElement.options).forEach(function(option) {
option.selected = values.includes(option.value);
});
}
function prepareBuildActionBasedOnExistingOne(existingBuildAction)
{
if (!globalInfo) {
window.alert('Global info needs to be retrieved first.');
return;
}
const typeId = existingBuildAction.type;
const typeInfo = globalInfo.buildActionTypes[typeId];
const typeName = typeInfo.type;
document.getElementById('build-action-task-none').selected = true;
document.getElementById('build-action-type').value = typeName;
handleBuildActionPresetChange();
handleBuildActionTypeChange();
document.getElementById('build-action-directory').value = existingBuildAction.directory;
document.getElementById('build-action-package-names').value = existingBuildAction.packageNames.join(', ');
selectOptions(document.getElementById('build-action-source-repo'), existingBuildAction.sourceDbs);
selectOptions(document.getElementById('build-action-destination-repo'), existingBuildAction.destinationDbs);
const presentFlags = existingBuildAction.flags;
typeInfo.flags.forEach(function (flag) {
const input = document.getElementById('build-action-' + typeName + '-' + flag.param);
input.checked = parseInt(input.dataset.id) & presentFlags;
});
typeInfo.settings.forEach(function (setting) {
document.getElementById('build-action-' + typeName + '-' + setting.param).value = existingBuildAction.settings[setting.param] || '';
});
}
function handleBuildActionTypeChange()
{
if (!globalInfo) {
return;
}
const buildActionType = document.getElementById('build-action-type').value;
const typeInfo = globalInfo.buildActionTypesByParam[buildActionType];
document.getElementById('build-action-directory').disabled = !typeInfo.directory;
document.getElementById('build-action-source-repo').disabled = !typeInfo.sourceDb;
document.getElementById('build-action-destination-repo').disabled = !typeInfo.destinationDb;
document.getElementById('build-action-package-names').disabled = !typeInfo.packageNames;
Array.from(document.getElementById('build-action-flags').getElementsByTagName('label')).forEach(function(label) {
const relevantType = label.dataset.relevantType;
if (!relevantType) {
return;
}
label.control.style.display = label.style.display = buildActionType === relevantType ? 'inline' : 'none';
});
Array.from(document.getElementById('build-action-settings').getElementsByTagName('tr')).forEach(function(tr) {
const relevantType = tr.dataset.relevantType;
if (!relevantType) {
return;
}
const isRelevant = buildActionType === relevantType;
tr.style.display = isRelevant ? 'table-row' : 'none';
Array.from(tr.getElementsByTagName('input')).forEach(function(input) {
input.style.display = isRelevant ? 'inline' : 'none';
});
});
}
function handleBuildActionPresetChange()
{
if (!globalInfo) {
return;
}
const task = document.getElementById('build-action-task').value;
const taskInfo = globalInfo.presets.tasks[task];
const taskInfoElement = getAndEmptyElement('build-action-task-info');
const actionSelect = document.getElementById('build-action-type');
if (!taskInfo) {
actionSelect.disabled = false;
taskInfoElement.style.fontStyle = 'italic';
taskInfoElement.appendChild(document.createTextNode('Start a single action (no predefined task selected)'));
handleBuildActionTypeChange();
return;
}
actionSelect.disabled = true;
taskInfoElement.style.fontStyle = 'normal';
taskInfoElement.appendChild(document.createTextNode(taskInfo.desc || taskInfo.name));
document.getElementById('build-action-directory').disabled = false;
document.getElementById('build-action-source-repo').disabled = true;
document.getElementById('build-action-destination-repo').disabled = true;
document.getElementById('build-action-package-names').disabled = false;
Array.from(document.getElementById('build-action-flags').getElementsByTagName('label')).forEach(function(label) {
label.control.style.display = label.style.display = 'none';
});
Array.from(document.getElementById('build-action-settings').getElementsByTagName('tr')).forEach(function(tr) {
tr.style.display = 'none';
Array.from(tr.getElementsByTagName('input')).forEach(function(input) {
input.style.display = 'none';
});
});
}
function renderBuildActionActions(actionValue, buildAction, refresh)
{
const container = document.createElement('span');
if (!refresh) {
container.className = 'table-row-actions';
}
const id = buildAction.id;
container.appendChild(renderIconLink(refresh ? 'table-refresh' : 'magnify', buildAction, function() {
queryBuildActionDetails(id);
return false;
}, refresh ? 'Refresh details table' : 'Show details', undefined, '#build-action-details-section?' + id));
container.appendChild(renderIconLink('restart', buildAction, function() {
if (window.confirm('Do you really want to clone/restart action ' + id + '?')) {
cloneBuildAction(id);
}
}, 'Clone ' + id));
container.appendChild(renderIconLink('plus', buildAction, function() {
prepareBuildActionBasedOnExistingOne(buildAction);
switchToBuildActions();
}, 'Create new build action based on ' + id));
container.appendChild(renderIconLink('delete', buildAction, function() {
if (window.confirm('Do you really want to delete action ' + id + '?')) {
deleteBuildAction(id);
}
}, 'Delete ' + id));
if (buildAction.status !== 0 && buildAction.status !== 4) {
container.appendChild(renderIconLink('stop', buildAction, function() {
if (window.confirm('Do you really want to stop/decline action ' + id + '?')) {
stopBuildAction(id);
}
}, 'Stop/decline ' + id));
}
if (buildAction.status === 0) {
container.appendChild(renderIconLink('play', buildAction, function() {
if (window.confirm('Do you really want to start action ' + id + '?')) {
startBuildAction(id);
}
}, 'Start ' + id));
}
return container;
}
function showBuildActions(ajaxRequest)
{
if (!window.globalInfo) {
window.functionsPostponedUntilGlobalInfo.push(showBuildActions.bind(this, ...arguments));
return;
}
const buildActionsList = getAndEmptyElement('build-actions-list');
if (ajaxRequest.status !== 200) {
buildActionsList.appendChild(document.createTextNode('Unable to load build actions: ' + ajaxRequest.responseText));
buildActionsList.appendChild(renderReloadButton(queryBuildActions));
return;
}
const responseJson = JSON.parse(ajaxRequest.responseText);
if (!Array.isArray(responseJson)) {
buildActionsList.appendChild(document.createTextNode('Unable to load build actions: response is no array'));
buildActionsList.appendChild(renderReloadButton(queryBuildActions));
return;
}
// compute clusters of actions which run in sequence
const buildActionsById = {};
responseJson.forEach(function(buildAction) {
buildActionsById[buildAction.id] = buildAction;
});
responseJson.forEach(function(buildAction) {
if (!Array.isArray(buildAction.startAfter)) {
return;
}
const currentActionId = buildAction.id;
let clusterId = buildAction._clusterId;
let clusterActions = buildAction._clusterActions;
buildAction.startAfter.forEach(function(previousActionId) {
const previousAction = buildActionsById[previousActionId];
if (!previousAction) {
return;
}
const previousActionClusterId = previousAction._clusterId;
const previousClusterActions = previousAction._clusterActions;
if (clusterId !== undefined) {
if (previousActionClusterId === undefined) {
clusterActions.splice(clusterActions.indexOf(buildAction), 0, previousAction);
previousAction._clusterId = clusterId;
previousAction._clusterActions = clusterActions;
} else if (previousActionClusterId !== clusterId) {
clusterActions.splice(clusterActions.indexOf(buildAction), 0, ...previousClusterActions);
previousClusterActions.forEach(function(previousClusterAction) {
previousClusterAction._clusterId = clusterId;
previousClusterAction._clusterActions = clusterActions;
});
}
} else if (previousActionClusterId !== undefined) {
clusterId = buildAction._clusterId = previousAction._clusterId;
clusterActions = buildAction._clusterActions = previousAction._clusterActions;
clusterActions.push(buildAction);
} else {
clusterId = buildAction._clusterId = previousAction._clusterId = currentActionId;
clusterActions = buildAction._clusterActions = previousAction._clusterActions = [previousAction, buildAction];
}
});
});
// compute cluster offsets and make up a color for each cluster
responseJson.forEach(function(buildAction) {
const clusterActions = buildAction._clusterActions;
const clusterOffset = buildAction._clusterOffset = clusterActions ? clusterActions.indexOf(buildAction) : 0;
buildAction._cc = buildAction.created + ':' + clusterOffset.toString().padStart(4, '0');
if (!clusterActions) { return; }
if (buildAction._clusterColor) {
return;
}
const clusterColor = '#' + Math.floor((Math.abs(Math.sin(buildAction._clusterId) * 16777215)) % 16777215).toString(16);
clusterActions.forEach(a => a._clusterColor = clusterColor);
});
// define function to toggle a row
const toggleRow = function(trElement) {
trElement.cells[0].getElementsByTagName('input')[0].click();
};
// render the table
const container = renderTableFromJsonArray({
rows: responseJson,
rowsPerPage: 10,
columnHeaders: ['', 'ID', 'Task', 'Type', 'Status', 'Result', 'Created', 'Started', 'Runtime', 'Directory', 'Source repo', 'Destination repo', 'Packages', 'Actions'],
columnAccessors: ['checkbox', 'id', 'taskName', 'type', 'status', 'result', 'created', 'started', 'finished', 'directory', 'sourceDbs', 'destinationDbs', 'packageNames', 'actions'],
columnSortAccessors: [null, null, null, null, null, null, '_cc'],
customRenderer: {
checkbox: function(value, row) {
return renderCheckBoxForTableRow(value, row, function(row) {
return row.name;
});
},
actions: renderBuildActionActions,
id: function(value, row) {
return renderLink(value, row, function() {
queryBuildActionDetails(row.id);
return false;
}, 'Show details', undefined, '#build-action-details-section?' + row.id);
},
taskName: function (value) {
if (!value) {
return renderNoneInGrey();
}
return document.createTextNode(getProperty(globalInfo.presets.tasks[value], 'name', value));
},
status: function(value) {
return document.createTextNode(getProperty(globalInfo.buildActionStates[value], 'name', 'Invalid/unknown'));
},
result: function(value) {
return renderNoneInGrey(getProperty(globalInfo.buildActionResults[value], 'name', 'Invalid/unknown'));
},
type: function(value) {
return document.createTextNode(getProperty(globalInfo.buildActionTypes[value], 'name', 'Invalid/debugging'));
},
created: renderShortTimeStamp,
started: renderShortTimeStamp,
finished: function (value, row) {
return renderTimeSpan(row.started, value);
},
sourceDbs: renderArrayElidedAsCommaSeparatedString,
destinationDbs: renderArrayElidedAsCommaSeparatedString,
packageNames: function(value) {
return renderNoneInGrey(!Array.isArray(value) || value.length <= 0 ? 'none' : value.length);
},
note: function(rows) {
const note = document.createElement('p');
note.appendChild(document.createTextNode(rows.length + ' build actions present '));
note.appendChild(renderReloadButton(queryBuildActions));
return note;
},
},
rowHandler: function(row, tr) {
// toggle checkbox on click; toggle checkbox for whole cluster when clicking edge
const dataset = tr.dataset;
dataset.id = row.id;
if (!dataset.initialized) {
dataset.initialized = true;
tr.onclick = function(e) {
if (e.defaultPrevented || e.target.type === 'checkbox') { return; }
const dataset = tr.dataset;
const clusterClass = e.clientX <= 30 ? dataset.clusterClass : undefined;
if (!clusterClass) { return toggleRow(tr); }
const relevantAction = buildActionsById[dataset.id];
const newSelected = !relevantAction.selected;
const clusterActions = relevantAction._clusterActions || [];
clusterActions.forEach(clusterAction => clusterAction.selected = newSelected);
Array.from(tr.parentElement.getElementsByClassName(clusterClass)).forEach(toggleRow);
};
}
// apply highlighting of clusters
const oldClusterClass = dataset.clusterClass;
const clusterId = row._clusterId;
const className = clusterId === undefined ? undefined : 'ba-tr-cluster-' + clusterId;
if (oldClusterClass === className) {
return;
}
if (oldClusterClass) {
delete tr.dataset.clusterClass;
tr.classList.remove(oldClusterClass);
tr.firstChild.style.borderLeft = '';
}
if (clusterId === undefined) {
return;
}
tr.dataset.clusterClass = className;
tr.classList.add(className);
tr.firstChild.style.borderLeft = '10px solid ' + row._clusterColor;
if (oldClusterClass) {
return;
}
const updateRowHighlighting = function(row, highlight) {
const clusterClass = row.dataset.clusterClass;
if (!clusterClass) {
return;
}
const fn = highlight ? 'add' : 'remove';
Array.from(document.getElementsByClassName(clusterClass)).forEach(function(tr) {
tr.classList[fn]('table-row-highlighted');
});
}
tr.addEventListener('mouseover', function() { updateRowHighlighting(this, true); });
tr.addEventListener('mouseout', function() { updateRowHighlighting(this, false); });
},
});
container.table.sort('_cc', true);
buildActionsList.appendChild(container);
}
function switchToBuildActionDetails(buildActionIds)
{
sections['build-action-details'].state.ids = buildActionIds;
updateHashPreventingSectionInitializer(!Array.isArray(buildActionIds) || buildActionIds.length === 0
? '#build-action-details-section'
: '#build-action-details-section?' + encodeURIComponent(buildActionIds.join(',')));
}
function switchToBuildActions()
{
updateHashPreventingSectionInitializer('#build-action-section');
}
function showBuildActionDetails(ajaxRequest)
{
if (!window.globalInfo) {
window.functionsPostponedUntilGlobalInfo.push(showBuildActionDetails.bind(this, ...arguments));
return;
}
if (checkForAjaxError(ajaxRequest, 'show build action')) {
return;
}
const responseJSON = JSON.parse(ajaxRequest.responseText);
return showBuildActionDetails2(Array.isArray(responseJSON) ? responseJSON : [responseJSON]);
}
function showBuildActionDetails2(buildActions)
{
const buildActionResults = getAndEmptyElement('build-action-results');
let buildActionActions = getAndEmptyElement('build-action-details-actions');
buildActions.forEach(function (buildActionDetails) {
if (!buildActionActions) {
buildActionActions = document.createElement('span');
buildActionActions.className = 'heading-actions';
buildActionResults.appendChild(buildActionActions);
}
buildActionResults.appendChild(renderBuildActionDetailsTable(buildActionDetails));
buildActionActions.appendChild(renderBuildActionActions(undefined, buildActionDetails, true));
buildActionActions = undefined;
});
switchToBuildActionDetails(buildActions.map(buildAction => buildAction.id));
}
function renderBuildActionDetailsTable(buildActionDetails)
{
return renderTableFromJsonObject({
data: buildActionDetails,
displayLabels: ['ID', 'Task', 'Type', 'Status', 'Result', 'Result data', 'Created', 'Started', 'Finished', 'Start after', 'Directory', 'Source repo', 'Destination repo', 'Packages', 'Flags', 'Settings', 'Log files', 'Artefacts', 'Output'],
fieldAccessors: ['id', 'taskName', 'type', 'status', 'result', 'resultData', 'created', 'started', 'finished', 'startAfter', 'directory', 'sourceDbs', 'destinationDbs', 'packageNames', 'flags', 'settings', 'logfiles', 'artefacts', 'output'],
customRenderer: {
taskName: function (value) {
if (!value) {
return renderNoneInGrey();
}
return document.createTextNode(getProperty(globalInfo.presets.tasks[value], 'name', value));
},
status: function(value) {
return document.createTextNode(getProperty(globalInfo.buildActionStates[value], 'name', 'Invalid/unknown'));
},
result: function(value) {
return renderNoneInGrey(getProperty(globalInfo.buildActionResults[value], 'name', 'Invalid/unknown'));
},
type: function(value) {
return document.createTextNode(getProperty(globalInfo.buildActionTypes[value], 'name', 'Invalid/debugging'));
},
created: renderTimeStamp,
started: renderTimeStamp,
finished: renderTimeStamp,
startAfter: renderArrayAsCommaSeparatedString,
sourceDbs: renderArrayAsCommaSeparatedString,
destinationDbs: renderArrayAsCommaSeparatedString,
packageNames: renderArrayAsCommaSeparatedString,
flags: function(value, row) {
const flagNames = [];
const typeInfo = globalInfo.buildActionTypes[row.type];
typeInfo.flags.forEach(function(flag) {
if (flag.id & value) {
flagNames.push(flag.name);
}
});
return renderArrayAsCommaSeparatedString(flagNames);
},
settings: function(value, row) {
const typeInfo = globalInfo.buildActionTypes[row.type];
if (typeInfo.settingNames.length === 0) {
return renderNoneInGrey();
}
return renderTableFromJsonObject({
data: value,
displayLabels: typeInfo.settingNames,
fieldAccessors: typeInfo.settingParams,
defaultRenderer: function (arg1, arg2, arg3) {
return renderNoneInGrey(arg1, arg2, arg3, 'default/none');
},
});
},
resultData: function(value, row) {
switch(value.index) {
case 3: { // update info
const formElement = document.createElement('form');
formElement.className = 'update-info-form';
formElement.appendChild(renderTableFromJsonObject({
data: value.data,
relatedRow: row,
displayLabels: ['Version updates', 'Package updates', 'Downgrades', 'Orphans'],
fieldAccessors: ['versionUpdates', 'packageUpdates', 'downgrades', 'orphans'],
customRenderer: {
orphans: renderOrphanPackage,
versionUpdates: renderUpdateOrDowngrade,
packageUpdates: renderUpdateOrDowngrade,
downgrades: renderUpdateOrDowngrade,
},
}));
const addSelectedInput = document.createElement('input');
addSelectedInput.type = 'button';
addSelectedInput.value = 'Add selected packages for new build action';
addSelectedInput.onclick = function () {
const buildActionForm = document.getElementById('build-action-form');
const packageNamesTextArea = buildActionForm['package-names'];
const elements = formElement.elements;
const packageNames = [];
for (let i = 0, count = elements.length; i != count; ++i) {
const element = elements[i];
if (element.type === 'checkbox' && element.checked) {
packageNames.push(element.value);
}
}
if (packageNamesTextArea.value !== '') {
packageNamesTextArea.value += ' ';
}
packageNamesTextArea.value += packageNames.join(' ');
};
formElement.appendChild(addSelectedInput);
return formElement;
}
case 4: // build preparation info
return renderTableFromJsonObject({
data: value.data,
displayLabels: ['Error', 'Warnings', 'Database config', 'Batches', 'Cyclic leftovers', 'Build data'],
fieldAccessors: ['error', 'warnings', 'dbConfig', 'batches', 'cyclicLeftovers', 'buildData'],
customRenderer: {
buildData: renderBuildPreparationResultData,
cyclicLeftovers: renderArrayAsCommaSeparatedString,
batches: function(batch) {
return renderCustomList(batch, renderArrayAsCommaSeparatedString);
},
},
});
case 7: { // repository problems
const container = document.createElement('div');
container.className = 'repo-problems';
for (const [database, problems] of Object.entries(value.data)) {
const table = renderTableFromJsonArray({
rows: problems,
columnHeaders: ['Related package', 'Problem description'],
columnAccessors: ['pkg', 'desc'],
customRenderer: {
note: problems.length + ' problems in repository ' + database,
desc: function(value) {
switch(value.index) {
case 1:
return renderTableFromJsonObject({
data: value.data,
displayLabels: ['Missing dependencies', 'Missing libraries'],
fieldAccessors: ['deps', 'libs'],
customRenderer: {
deps: renderDependency,
},
});
default:
return renderStandardTableCell(value.data);
}
},
},
});
table.table.sort('pkg', false);
container.appendChild(table);
}
return container;
}
default:
return renderStandardTableCell(value.data);
}
},
logfiles: renderBuildActionLogFiles,
artefacts: renderBuildActionArtefacts,
output: function(value, row) {
const isFinished = row.status === 4;
if (!value && isFinished) {
return renderNoneInGrey();
}
const targetElement = document.createElement('div');
if (isFinished) {
const terminal = makeTerminal();
setupTerminalLater(terminal, targetElement, value);
return targetElement;
}
const streamingSetup = setupTerminalForStreaming({
id: 'output-' + row.id,
targetElement: targetElement,
path: '/build-action/output?id=' + encodeURIComponent(row.id) + '&offset=' + encodeURIComponent(value.length),
});
streamingSetup.startStreaming();
const terminal = streamingSetup.terminal();
terminal.setOption('convertEol', true);
terminal.write(value);
return streamingSetup.elements;
},
},
});
}
function setupTerminalForStreaming(args)
{
const id = args.id; // a page-unique ID to make up DOM element IDs as needed
const targetElement = args.targetElement; // the DOM element to stream contents into
const path = args.path; // the path to GET contents from via streamRouteIntoTerminal()
let terminal;
let ajaxRequest;
return {
elements: [targetElement],
startStreaming: function () {
terminal = makeTerminal();
ajaxRequest = streamRouteIntoTerminal('GET', path, terminal);
setupTerminalLater(terminal, targetElement);
},
stopStreaming: function () {
ajaxRequest.abort();
ajaxRequest = undefined;
terminal.dispose();
terminal = undefined;
},
isStreaming: function () {
return ajaxRequest !== undefined;
},
terminal: function () {
return terminal;
},
};
}
function fillBuildActionFromPackageSearch()
{
const packageNamesTextArea = document.getElementById('build-action-form')['package-names'];
const data = getFormTableData('package-results-form');
if (data === undefined) {
return;
}
packageNamesTextArea.value = getSelectedRowProperties(data, 'name').join(' ');
location.hash = '#build-action-section';
}
function submitBuildAction()
{
startFormQuery('build-action-form', handleBuildActionResponse);
}
function handleBuildActionResponse(ajaxRequest)
{
const results = getAndEmptyElement('build-action-results');
if (ajaxRequest.status !== 200) {
results.appendChild(document.createTextNode('unable to create build action: ' + ajaxRequest.responseText));
switchToBuildActionDetails();
return;
}
showBuildActionDetails(ajaxRequest);
}
function renderBuildActionLogFiles(array, obj)
{
return renderCustomList(array, function(arrayElement) {
const params = 'id=' + encodeURIComponent(obj.id) + '&name=' + encodeURIComponent(arrayElement);
const logFilePath = '/build-action/logfile?' + params;
const newWindowPath = 'log.html#' + params;
const targetElement = document.createElement('div');
const streamingSetup = setupTerminalForStreaming({
id: 'logfile-' + obj.id + '-' + arrayElement,
targetElement: targetElement,
path: logFilePath,
});
const basicElements = streamingSetup.elements;
const openInNewWindowLinkElement = renderIconLink('dock-window', obj, function() { window.open(newWindowPath); }, 'Open in new window');
const downloadLinkElement = renderIconLink('download', obj, function() { window.open(apiPrefix + logFilePath); }, 'Download log');
const stopStreamingLinkElement = renderIconLink('stop', obj, function() {
if (!streamingSetup.isStreaming()) {
return;
}
streamingSetup.stopStreaming();
targetElement.style.display = stopStreamingLinkElement.style.display = 'none';
emptyDomElement(targetElement);
}, 'Close log');
const startStreamingLinkElement = renderLink(arrayElement, obj, function() {
if (streamingSetup.isStreaming()) {
return;
}
emptyDomElement(targetElement);
streamingSetup.startStreaming();
targetElement.style.display = 'block';
stopStreamingLinkElement.style.display = 'inline-block';
}, 'Show log file', apiPrefix + logFilePath);
targetElement.style.display = stopStreamingLinkElement.style.display = 'none';
[downloadLinkElement, stopStreamingLinkElement].forEach(function(element) {
element.classList.add('streaming-link');
});
return [startStreamingLinkElement, stopStreamingLinkElement, downloadLinkElement, openInNewWindowLinkElement, ...basicElements];
});
}
function renderBuildActionArtefacts(array, obj)
{
return renderCustomList(array, function(arrayElement) {
const path = apiPrefix + '/build-action/artefact?id=' + encodeURIComponent(obj.id) + '&name=' + encodeURIComponent(arrayElement);
return renderLink(arrayElement, obj, function() {
window.open(path);
}, 'Download artefact', path);
});
}
function renderUpdateInfoWithCheckbox(id, packageName, newPackageName, versionInfo, sourceDbs, newVersion)
{
const inputElement = document.createElement('input');
inputElement.type = 'checkbox';
inputElement.id = id;
inputElement.value = packageName;
const labelElement = document.createElement('label');
labelElement.htmlFor = id;
if (newVersion && newPackageName) {
const packageNameLink = document.createElement('a');
let from = newVersion.db;
if (newVersion.db === 'aur') {
packageNameLink.href = 'https://aur.archlinux.org/packages/' + encodeURIComponent(newPackageName);
packageNameLink.target = '_blank';
from = 'AUR';
} else {
packageNameLink.href = '#package-details-section?' + encodeURIComponent(newVersion.db + '@' + newVersion.arch + '/' + newPackageName);
}
packageNameLink.appendChild(document.createTextNode(newPackageName));
if (newPackageName !== packageName) {
labelElement.appendChild(document.createTextNode(packageName + ' ('));
}
labelElement.appendChild(packageNameLink);
if (newPackageName !== packageName) {
labelElement.appendChild(document.createTextNode(sourceDbs.length ? ' from ' + from + ')' : ')'));
} else if (sourceDbs.length) {
labelElement.appendChild(document.createTextNode(' from ' + from));
}
labelElement.appendChild(document.createTextNode(': ' + versionInfo));
} else if (newPackageName && packageName !== newPackageName) {
labelElement.appendChild(document.createTextNode(packageName + ' (' + newPackageName + '): ' + versionInfo));
} else {
labelElement.appendChild(document.createTextNode(packageName + ': ' + versionInfo));
}
return [inputElement, labelElement];
}
function renderPackageList(packageList)
{
return renderCustomList(packageList, renderPackage);
}
function renderBuildPreparationBuildData(buildDataForPackage)
{
return renderTableFromJsonObject({
data: buildDataForPackage,
displayLabels: ['Error', 'Warnings', 'Has source', 'Source directory', 'Original/local source directory', 'New packages', 'Existing packages', 'Specified index'],
fieldAccessors: ['error', 'warnings', 'hasSource', 'sourceDirectory', 'originalSourceDirectory', 'packages', 'existingPackages', 'specifiedIndex'],
customRenderer: {
packages: renderPackageList,
existingPackages: renderPackageList,
},
});
}
function makeVersionsString(packages)
{
const versions = packages.map(package => package.version);
if (versions.length === 0) {
return '?';
} else if (versions.length === 1) {
return versions[0];
} else {
return '(' + versions.join(', ') + ')';
}
}
function renderBuildPreparationResultData(buildPreparationData)
{
const elements = [];
for (const [packageName, buildDataForPackage] of Object.entries(buildPreparationData)) {
const heading = document.createElement('h4');
let table;
heading.className = 'compact-heading';
heading.appendChild(renderLink(packageName, undefined, function() {
if (table === undefined) {
table = renderBuildPreparationBuildData(buildDataForPackage);
heading.insertAdjacentElement('afterEnd', table);
} else {
table.style.display = table.style.display === 'none' ? 'table' : 'none';
}
}));
const versionSpan = document.createElement('span');
const newVersions = makeVersionsString(buildDataForPackage.packages);
const existingVersions = makeVersionsString(buildDataForPackage.existingPackages);
versionSpan.style.fontWeight = 'lighter';
versionSpan.style.float = 'right';
versionSpan.appendChild(document.createTextNode(' ' + existingVersions + ' → ' + newVersions));
heading.appendChild(versionSpan);
elements.push(heading);
}
return elements;
}
function renderOrphanPackage(value, obj, level, row)
{
return renderCustomList(value, function(package) {
const packageName = package.name;
return renderUpdateInfoWithCheckbox(
'update-info-checkbox-' + packageName + '-' + package.version,
packageName,
undefined,
package.version,
row.sourceDbs,
);
}, function(package1, package2) {
return package1.name.localeCompare(package2.name);
});
}
function renderUpdateOrDowngrade(value, obj, level, row)
{
return renderCustomList(value, function(updateInfo) {
const oldVersion = updateInfo.oldVersion;
const newVersion = updateInfo.newVersion;
const packageName = oldVersion.name;
return renderUpdateInfoWithCheckbox(
'update-info-checkbox-' + packageName + '-' + oldVersion.version + '-' + newVersion.version,
packageName,
newVersion.name,
oldVersion.version + ' → ' + newVersion.version,
row.sourceDbs,
newVersion
);
}, function(updateInfo1, updateInfo2) {
return updateInfo1.oldVersion.name.localeCompare(updateInfo2.oldVersion.name);
});
}
function triggerToolbarAction(toolbarElement)
{
const confirmQuestion = toolbarElement.dataset.confirmation;
if (confirmQuestion !== undefined && !window.confirm(confirmQuestion)) {
return false;
}
toolbarElement.disabled = true;
queryRoute(toolbarElement.dataset.method, toolbarElement.dataset.action, function(ajaxRequest) {
window.alert(ajaxRequest.responseText);
toolbarElement.disabled = false;
});
return false;
}
function deleteSelectedActions()
{
const data = getFormTableData('build-actions-list');
if (data === undefined) {
return;
}
const ids = getSelectedRowProperties(data, 'id');
if (ids.length && window.confirm('Do you really want to delete the build action(s) ' + ids.join(', ') + '?')) {
deleteBuildAction(ids);
}
}
function showSelectedActions()
{
const data = getFormTableData('build-actions-list');
if (data === undefined) {
return;
}
const ids = getSelectedRowProperties(data, 'id');
if (ids.length) {
queryBuildActionDetails(ids);
}
}
function initBuildActionDetails(sectionElement, sectionData, newHashParts)
{
const currentBuildActionIds = sectionData.state.ids;
const hasCurrentlyBuildActions = Array.isArray(currentBuildActionIds) && currentBuildActionIds.length !== 0;
if (!newHashParts.length) {
if (hasCurrentlyBuildActions) {
updateHashPreventingChangeHandler('#build-action-details-section?' + encodeURIComponent(currentBuildActionIds.join(',')));
}
return true;
}
const newBuildActionIds = newHashParts[0].split(',');
if (!hasCurrentlyBuildActions || newBuildActionIds.some(id => currentBuildActionIds.find(currentId => id == currentId) === undefined)) { // possible type conversion wanted
queryBuildActionDetails(newBuildActionIds);
}
return true;
}