b1575fa321
Dialogs (with a refresh option) appear whenever the SignalR client disconnects or encounters an error. Nonsensical error messages replaced. Page refresh technique changed to allow for urls containing fragment hashes.
282 lines
11 KiB
Plaintext
282 lines
11 KiB
Plaintext
@model string
|
|
@{
|
|
Html.BundleDeferred("~/ClientScripts/Modules/Knockout");
|
|
Html.BundleDeferred("~/ClientScripts/Modules/jQuery-SignalR");
|
|
}
|
|
<div style="min-height: 300px;">
|
|
<div id="Logging_Task_Status" class="form" style="width: 520px;" data-bind="visible: Initialized">
|
|
<h2 data-bind="text: TaskName"> </h2>
|
|
<table>
|
|
<tr data-bind="visible: IsRunning">
|
|
<th class="process" data-bind="text: CurrentProcess">
|
|
</th>
|
|
</tr>
|
|
<tr data-bind="visible: IsRunning">
|
|
<td class="description" data-bind="text: CurrentDescription">
|
|
</td>
|
|
</tr>
|
|
<tr data-bind="visible: IsRunning">
|
|
<td class="progress">
|
|
<div data-bind="progressValue: Progress">
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr data-bind="visible: FinishedTimestamp">
|
|
<td class="finishedTimestamp">
|
|
<h3>Finished: <span data-bind="text: FinishedTimestampFormatted"></span>
|
|
</h3>
|
|
</td>
|
|
</tr>
|
|
<tr data-bind="visible: FinishedTimestamp() && !TaskExceptionMessage()">
|
|
<td class="finishedMessage" data-bind="css: { finishedRedirect: FinishedUrl }">
|
|
<span data-bind="text: FinishedMessage"></span>
|
|
<i class="fa fa-lg fa-cog fa-spin"></i>
|
|
</td>
|
|
</tr>
|
|
<tr data-bind="visible: TaskExceptionMessage">
|
|
<td class="exception">Last Error:
|
|
<div class="code" data-bind="text: TaskExceptionMessage">
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr data-bind="visible: NextScheduledTimestamp">
|
|
<td class="nextScheduledTimestamp">Next Scheduled: <span data-bind="text: NextScheduledTimestampFormatted"></span>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<script type="text/javascript">
|
|
ko.bindingHandlers.progressValue = {
|
|
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
var $element = $(element);
|
|
if (!$element.is('.ui-progressbar'))
|
|
$element.progressbar();
|
|
},
|
|
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
var v = ko.utils.unwrapObservable(valueAccessor());
|
|
var vInt = parseInt(v);
|
|
if (vInt >= 0) {
|
|
$(element).progressbar('option', 'value', vInt);
|
|
}
|
|
}
|
|
};
|
|
//* http://webcloud.se/log/JavaScript-and-ISO-8601/
|
|
Date.prototype.setISO8601 = function (string) {
|
|
var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
|
|
"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
|
|
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
|
|
var d = string.match(new RegExp(regexp));
|
|
|
|
var offset = 0;
|
|
var date = new Date(d[1], 0, 1);
|
|
|
|
if (d[3]) { date.setMonth(d[3] - 1); }
|
|
if (d[5]) { date.setDate(d[5]); }
|
|
if (d[7]) { date.setHours(d[7]); }
|
|
if (d[8]) { date.setMinutes(d[8]); }
|
|
if (d[10]) { date.setSeconds(d[10]); }
|
|
if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
|
|
if (d[14]) {
|
|
offset = (Number(d[16]) * 60) + Number(d[17]);
|
|
offset *= ((d[15] == '-') ? 1 : -1);
|
|
}
|
|
|
|
offset -= date.getTimezoneOffset();
|
|
time = (Number(date) + (offset * 60 * 1000));
|
|
this.setTime(Number(time));
|
|
return this;
|
|
}
|
|
</script>
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
var sessionId = '@(Model)';
|
|
|
|
var view = $('#Logging_Task_Status');
|
|
var vm = null;
|
|
|
|
var notificationsHub = null;
|
|
|
|
var statusViewModel = function (sessionId) {
|
|
var self = this;
|
|
|
|
self.FullUpdateToken = null;
|
|
self.Initialized = ko.observable(false);
|
|
|
|
self.TimestampParse = function (timestamp) {
|
|
if (timestamp) {
|
|
if (timestamp.indexOf("\/Date(") == 0)
|
|
return new Date(parseInt(timestamp.substr(6)));
|
|
else
|
|
return (new Date()).setISO8601(timestamp);
|
|
}
|
|
return new Date();
|
|
}
|
|
self.TimestampFormat = function (timestamp) {
|
|
var addZero = function (v) { v = v + ''; if (v.length == 1) v = '0' + v; return v; }
|
|
return timestamp.getFullYear() + '/' + addZero((1 + timestamp.getMonth())) + '/' + addZero(timestamp.getDate()) + ' ' + addZero(timestamp.getHours()) + ':' + addZero(timestamp.getMinutes()) + ':' + addZero(timestamp.getSeconds());
|
|
}
|
|
|
|
self.SessionId = sessionId;
|
|
self.TaskName = ko.observable(null);
|
|
|
|
self.Progress = ko.observable(0);
|
|
self.CurrentProcess = ko.observable(null);
|
|
self.CurrentDescription = ko.observable(null);
|
|
|
|
self.IsRunning = ko.observable(null);
|
|
|
|
self.TaskExceptionMessage = ko.observable(null);
|
|
|
|
self.FinishedTimestamp = ko.observable(null);
|
|
self.NextScheduledTimestamp = ko.observable(null)
|
|
|
|
self.NextScheduledTimestampFormatted = ko.computed(function () {
|
|
return self.TimestampFormat(self.TimestampParse(self.NextScheduledTimestamp()));
|
|
});
|
|
self.FinishedTimestampFormatted = ko.computed(function () {
|
|
return self.TimestampFormat(self.TimestampParse(self.FinishedTimestamp()));
|
|
});
|
|
|
|
self.FinishedUrl = ko.observable(null);
|
|
self.FinishedMessage = ko.observable(null);
|
|
|
|
self.Finished = function () {
|
|
if (self.FinishedTimestamp()) {
|
|
if (self.FinishedUrl() && !self.TaskExceptionMessage()) {
|
|
|
|
if (self.FullUpdateToken)
|
|
window.clearTimeout(self.FullUpdateToken);
|
|
$.connection.hub.stop();
|
|
|
|
if (self.FinishedMessage())
|
|
window.setTimeout(function () { window.location.href = self.FinishedUrl(); }, 3000);
|
|
else
|
|
window.location.href = self.FinishedUrl();
|
|
}
|
|
}
|
|
}
|
|
|
|
self.Initialize = function (taskStatus) {
|
|
self.TaskName(taskStatus.TaskName);
|
|
self.FinishedUrl(taskStatus.FinishedUrl);
|
|
|
|
self.Progress(taskStatus.Progress);
|
|
self.CurrentProcess(taskStatus.CurrentProcess);
|
|
self.CurrentDescription(taskStatus.CurrentDescription);
|
|
|
|
self.IsRunning(taskStatus.IsRunning);
|
|
|
|
self.TaskExceptionMessage(taskStatus.TaskExceptionMessage);
|
|
|
|
self.FinishedTimestamp(taskStatus.FinishedTimestamp);
|
|
self.NextScheduledTimestamp(taskStatus.NextScheduledTimestamp);
|
|
|
|
self.FinishedMessage(taskStatus.FinishedMessage);
|
|
|
|
self.Initialized(true);
|
|
|
|
self.Finished();
|
|
}
|
|
self.Update = function (taskStatus) {
|
|
if (!self.Initialized())
|
|
return;
|
|
|
|
if (self.FullUpdateToken)
|
|
window.clearTimeout(self.FullUpdateToken);
|
|
|
|
if (taskStatus) {
|
|
$.each(taskStatus, function (key, value) {
|
|
switch (key) {
|
|
case 'Progress':
|
|
self.Progress(value);
|
|
break;
|
|
case 'CurrentProcess':
|
|
self.CurrentProcess(value);
|
|
break;
|
|
case 'CurrentDescription':
|
|
self.CurrentDescription(value);
|
|
break;
|
|
case 'IsRunning':
|
|
self.IsRunning(value);
|
|
break;
|
|
case 'TaskExceptionMessage':
|
|
self.TaskExceptionMessage(value);
|
|
break;
|
|
case 'NextScheduledTimestamp':
|
|
self.NextScheduledTimestamp(value);
|
|
break;
|
|
case 'FinishedUrl':
|
|
self.FinishedUrl(value);
|
|
break;
|
|
case 'FinishedMessage':
|
|
self.FinishedMessage(value);
|
|
break;
|
|
case 'FinishedTimestamp':
|
|
self.FinishedTimestamp(value);
|
|
window.setTimeout(self.Finished, 1);
|
|
break;
|
|
default:
|
|
// Ignore
|
|
}
|
|
});
|
|
}
|
|
|
|
if (!self.FinishedTimestamp())
|
|
self.FullUpdateToken = window.setTimeout(self.FullUpdate, 2000);
|
|
}
|
|
|
|
self.FullUpdate = function () {
|
|
self.FullUpdateToken = null;
|
|
|
|
if (!self.FinishedTimestamp())
|
|
notificationsHub.server.getStatus()
|
|
.done(self.Initialize);
|
|
}
|
|
}
|
|
|
|
vm = new statusViewModel(sessionId);
|
|
ko.applyBindings(vm, view[0]);
|
|
|
|
// Start Live Connection
|
|
updateWithLive();
|
|
|
|
function updateWithLive() {
|
|
notificationsHub = $.connection.scheduledTaskNotifications;
|
|
notificationsHub.client.initializeTaskStatus = vm.Initialize;
|
|
notificationsHub.client.updateTaskStatus = vm.Update;
|
|
|
|
$.connection.hub.qs = { TaskSessionId: sessionId };
|
|
$.connection.hub.error(onHubFailed);
|
|
|
|
$.connection.hub.start()
|
|
.fail(onHubFailed);
|
|
}
|
|
|
|
function onHubFailed(error) {
|
|
// Show Dialog Message
|
|
if ($('.disconnected-dialog').length == 0) {
|
|
$('<div>')
|
|
.addClass('dialog disconnected-dialog')
|
|
.html('<h3><span class="fa-stack fa-lg"><i class="fa fa-wifi fa-stack-1x"></i><i class="fa fa-ban fa-stack-2x error"></i></span>Disconnected from the Disco ICT Server</h3><div>This page is not receiving live updates. Please ensure you are connected to the server, then refresh this page to enable features.</div>')
|
|
.dialog({
|
|
resizable: false,
|
|
title: 'Disconnected',
|
|
width: 400,
|
|
modal: true,
|
|
buttons: {
|
|
'Refresh Now': function () {
|
|
$(this).dialog('option', 'buttons', null);
|
|
window.location.reload(true);
|
|
},
|
|
'Close': function () {
|
|
$(this).dialog('destroy');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
});
|
|
</script>
|