+
+ if (aa["slot"] !== "" && bb["slot"] !== "") {
+ if (aa["slot"] > bb["slot"]) {
+ return 1;
+ }
+ if (aa["slot"] < bb["slot"]) {
+ return -1;
+ }
+ }
+
+ return sortById(a, b, opt);
+}
+
+
+function dumbPluralize(n, s, p) {
+ if (typeof p === 'undefined') {
+ p = "s";
+ }
+ if (n == 0 || n > 1) {
+ return n + " " + (s + p);
+ } else {
+ return n + " " + s;
+ }
+}
+
+function generateJobOverview(id, logViewer, taskState) {
+ var html = "";
+
+ if (logViewer.items.length > 2) {
+ var first = logViewer.items[1];
+ var last = logViewer.items[logViewer.items.length-1];
+ var duration = (last.values().ts.getTime() - first.values().ts.getTime()) / 1000;
+
+ var hours = 0;
+ var minutes = 0;
+ var seconds;
+
+ if (duration >= 3600) {
+ hours = Math.floor(duration / 3600);
+ duration -= (hours * 3600);
+ }
+ if (duration >= 60) {
+ minutes = Math.floor(duration / 60);
+ duration -= (minutes * 60);
+ }
+ seconds = duration;
+
+ var tcount = taskState.task_count;
+
+ html += "<p>";
+ html += "Started at " + first.values().timestamp + ". ";
+ html += "Ran " + dumbPluralize(tcount, " task") + " over ";
+ if (hours > 0) {
+ html += dumbPluralize(hours, " hour");
+ }
+ if (minutes > 0) {
+ html += " " + dumbPluralize(minutes, " minute");
+ }
+ if (seconds > 0) {
+ html += " " + dumbPluralize(seconds, " second");
+ }
+
+ html += " using " + dumbPluralize(taskState.nodes.length, " node");
+
+ html += ". " + dumbPluralize(taskState.complete_count, "task") + " completed";
+ html += ", " + dumbPluralize(taskState.incomplete_count, "task") + " incomplete";
+ html += " (" + dumbPluralize(taskState.failure_count, " failure") + ")";
+
+ html += ". Finished at " + last.values().timestamp + ".";
+ html += "</p>";
+ } else {
+ html = "<p>Job log is empty or failed to load.</p>";
+ }
+
+ $(id).html(html);
+}
+
+function gotoPage(n, logViewer, page, id) {
+ if (n < 0) { return; }
+ if (n*page > logViewer.matchingItems.length) { return; }
+ logViewer.page_offset = n;
+ logViewer.show(n*page, page);
+}
+
+function updatePaging(id, logViewer, page) {
+ var p = "";
+ var i = logViewer.matchingItems.length;
+ var n;
+ for (n = 0; (n*page) < i; n += 1) {
+ if (n == logViewer.page_offset) {
+ p += "<span class='log-viewer-page-num'>" + (n+1) + "</span> ";
+ } else {
+ p += "<a href=\"#\" class='log-viewer-page-num log-viewer-page-" + n + "'>" + (n+1) + "</a> ";
+ }
+ }
+ $(id).html(p);
+ for (n = 0; (n*page) < i; n += 1) {
+ (function(n) {
+ $(".log-viewer-page-" + n).on("click", function() {
+ gotoPage(n, logViewer, page, id);
+ return false;
+ });
+ })(n);
+ }
+
+ if (logViewer.page_offset == 0) {
+ $(".log-viewer-page-up").addClass("text-muted");
+ } else {
+ $(".log-viewer-page-up").removeClass("text-muted");
+ }
+
+ if (logViewer.page_offset == (n-1)) {
+ $(".log-viewer-page-down").addClass("text-muted");
+ } else {
+ $(".log-viewer-page-down").removeClass("text-muted");
+ }
+}
+
+function nextPage(logViewer, page, id) {
+ gotoPage(logViewer.page_offset+1, logViewer, page, id);
+}
+
+function prevPage(logViewer, page, id) {
+ gotoPage(logViewer.page_offset-1, logViewer, page, id);