tapis_job-1.4.1-alpha1/js/jobpage.js
js/jobpage.js
(function ($, Drupal, drupalSettings) {
Drupal.behaviors.jobStatusRefresh = {
attach: function (context, settings) {
const $jobStatusContainer = $('#job-status-container', context);
once('jobStatusRefresh', $jobStatusContainer, context).forEach(function (element) {
/**
* Possible job statuses, according to Tapis:
*
* PENDING - Job processing beginning
* PROCESSING_INPUTS - Identifying input files for staging
* STAGING_INPUTS - Transferring job input data to execution system
* STAGING_JOB - Staging runtime assets to execution system
* SUBMITTING_JOB - Submitting job to execution system
* QUEUED - Job queued to execution system queue
* RUNNING - Job running on execution system
* ARCHIVING - Transferring job output to archive system
* BLOCKED - Job blocked
* PAUSED - Job processing suspended
* FINISHED - Job completed successfully
* CANCELLED - Job execution intentionally stopped
* FAILED - Job failed
*/
var refreshJobStatuses = ["TERMINATING", "PENDING", "PROCESSING_INPUTS", "STAGING_INPUTS", "STAGING_JOB", "SUBMITTING_JOB", "QUEUED", "ARCHIVING"];
var PAGE_REFRESH_TIMEOUT_MS = 5000; // number of milliseconds before page refresh
var jobStatus = settings.tapis_job.status;
let progressJobStatus = '';
// The class form-item-refresh-button is available in the DOM.
if ($('.job-status-refresh-form .button').length > 0) {
// Check if the job status is "PENDING"
if (refreshJobStatuses.includes(jobStatus) || settings.tapis_job.jobProxyURLFlag === true) {
$('.job--terminate--action').show();
// Ensure the message container exists and sits ABOVE the Open App Session button
function ensureProxyStatusContainer() {
var $jobButtonsInner = $('.job-buttons .inner');
if ($jobButtonsInner.length === 0) return;
var $msg = $jobButtonsInner.find('#proxy-status-message');
if ($msg.length === 0) {
$msg = $('<div>', {
id: 'proxy-status-message',
class: 'proxy-status-message visually-hidden', // base class from CSS
'aria-live': 'polite'
});
// Place it near where buttons show up; we will reposition below if needed.
$msg.appendTo($jobButtonsInner);
}
// If the Open App Session button exists, ensure the message is directly above it
var $openBtn = $jobButtonsInner.find('[data-role="open-app-session"]').first();
if ($openBtn.length > 0) {
if (!$msg.next().is($openBtn)) {
$msg.insertBefore($openBtn);
}
} else {
// Otherwise, keep it ABOVE the terminate button (or at top as a fallback)
var $terminate = $jobButtonsInner.find('.job--terminate--action').first();
if ($terminate.length > 0) {
if (!$msg.prev().is($terminate)) {
$msg.insertBefore($terminate);
}
} else {
// No terminate button yet; ensure it stays at the top for visibility
if ($jobButtonsInner.children().first().attr('id') !== 'proxy-status-message') {
$msg.prependTo($jobButtonsInner);
}
}
}
}
// Call this once after DOM ready or after job buttons render
ensureProxyStatusContainer();
function updateProxyMessage(settings) {
const jobStatus = (settings.tapis_job && settings.tapis_job.status) || '';
const proxyNotAlive = !!(settings.tapis_job && settings.tapis_job.jobProxyURLFlag);
const $container = $('.job-buttons .inner');
const hasOpenSessionBtn = $container.find('[data-role="open-app-session"]').length > 0;
// Ensure container exists and is positioned relative to the button
ensureProxyStatusContainer();
var $msg = $container.find('#proxy-status-message');
let text = '';
let shouldShow = false;
if (!hasOpenSessionBtn && jobStatus === 'RUNNING' && proxyNotAlive) {
// While waiting for proxy/button, show loading message above where it will appear
text = "Connecting to app session.";
shouldShow = true;
}
if (shouldShow) {
if ($msg.hasClass('visually-hidden') || $msg.text() !== text) {
$msg.removeClass('visually-hidden')
.addClass('notice')
.text(text);
}
} else {
if (!$msg.hasClass('visually-hidden') || $msg.text() !== '') {
$msg.addClass('visually-hidden')
.removeClass('notice')
.empty();
}
}
}
// Hide the static status
let jobStatusSection = $('.job-status-section');
jobStatusSection.addClass('hidden');
// Display progress section
let progressSection = $('.job-progress-section');
progressSection.removeClass('hidden');
let progress = progressSection.find('.oak-progress');
// Add a step to the progress
progressJobStatus = jobStatus;
let jobStatusFormatted = jobStatus.replace('_', ' ').toLowerCase();
jobStatusFormatted = jobStatusFormatted.charAt(0).toUpperCase() + jobStatusFormatted.slice(1);
let step = $('<li class="working"><div class="inner"><div class="step-marker"><div class="oak-progress-loader"></div></div><span>' + jobStatusFormatted + '</span></div></li>');
step.appendTo(progress);
step.delay(4).queue(function () {
$(this).addClass("show");
});
// Variable to store the interval ID
var refreshIntervalId;
// Function to start the interval
function startRefreshInterval() {
// Periodically click the hidden button to refresh the form.
refreshIntervalId = setInterval(function () {
$('.job-status-refresh-form .button').trigger('click');
}, PAGE_REFRESH_TIMEOUT_MS);
}
// Start the interval initially
startRefreshInterval();
$(document).ajaxComplete(function (event, xhr, settings) {
if (settings.url.indexOf('/tapis/job/') !== -1) {
// Assuming the response body contains the updated settings
var responseBody = JSON.parse(xhr.responseText);
responseBody.forEach(obj => {
if (obj.command === 'settings' && obj.settings && obj.settings.tapis_job && obj.settings.tapis_job.status) {
settings.tapis_job = obj.settings.tapis_job;
// Add a step to the progress (if changed)
if (progressJobStatus !== obj.settings.tapis_job.status) {
progressJobStatus = obj.settings.tapis_job.status;
let jobStatusFormatted = obj.settings.tapis_job.status.replace('_', ' ').toLowerCase();
jobStatusFormatted = jobStatusFormatted.charAt(0).toUpperCase() + jobStatusFormatted.slice(1);
let step = $('<li><div class="inner"><div class="step-marker"><div class="oak-progress-loader"></div></div><span>' + jobStatusFormatted + '</span></div></li>');
step.appendTo(progress);
// reset all working
progress.find('.working').removeClass('working').addClass('completed');
step.addClass("working");
// See if there is a terminate button to render
if (obj.settings.terminate_job_button && obj.settings.terminate_job_button['#markup']) {
let terminateJobButton = $(obj.settings.terminate_job_button['#markup']);
let jobButtonsSection = $('.job-buttons');
let terminateJobButtonContainer = jobButtonsSection.find('.inner');
terminateJobButton.appendTo(terminateJobButtonContainer);
jobButtonsSection.removeClass('hidden');
}
// Check if the job status is on of those completed
if (!refreshJobStatuses.includes(settings.tapis_job.status)) {
step.removeClass("working");
step.addClass("completed");
if(settings.tapis_job.status === "BLOCKED" || settings.tapis_job.status === "FAILED") {
step.addClass("error");
}
else if(settings.tapis_job.status === "PAUSED" || settings.tapis_job.status === "CANCELLED") {
step.addClass("warning");
}
else {
step.addClass("success");
}
}
step.delay(4).queue(function () {
$(this).addClass("show");
});
// See if there is an open button to render
if (obj.settings.job_button && obj.settings.job_button['#markup']) {
let jobButton = $(obj.settings.job_button['#markup']);
let jobButtonsSection = $('.job-buttons');
let jobButtonsContainer = jobButtonsSection.find('.inner');
jobButton.prependTo(jobButtonsContainer);
jobButtonsSection.removeClass('hidden');
}
updateProxyMessage(obj.settings);
} else {
// See if there is an open button to render
if (obj.settings.job_button && obj.settings.job_button['#markup']) {
let jobButton = $(obj.settings.job_button['#markup']);
let jobButtonsSection = $('.job-buttons');
let jobButtonsContainer = jobButtonsSection.find('.inner');
jobButton.prependTo(jobButtonsContainer);
jobButtonsSection.removeClass('hidden');
}
updateProxyMessage(obj.settings);
}
}
});
// Check if the job status is "finished"
if (settings.tapis_job.jobProxyURLFlag === false) {
if (!refreshJobStatuses.includes(settings.tapis_job.status)) {
// Clear the interval
clearInterval(refreshIntervalId);
if (settings.tapis_job.status !== "RUNNING" ||
$('.job--terminate--open--session--action').length > 0) {
$('.job--terminate--action').hide();
}
}
} else {
updateProxyMessage(settings);
}
}
});
} else {
if (settings.tapis_job.status !== "RUNNING" ||
$('.job--terminate--open--session--action').length > 0) {
$('.job--terminate--action').hide();
}
}
} else {
console.log('The class form-item-refresh-button is not available in the DOM.');
}
})
}
};
})(jQuery, Drupal, drupalSettings);
