diff --git a/Duplicati/Server/webroot/scripts/app-functions.js b/Duplicati/Server/webroot/scripts/app-functions.js
index 227aa2f9e..b46836ef7 100644
--- a/Duplicati/Server/webroot/scripts/app-functions.js
+++ b/Duplicati/Server/webroot/scripts/app-functions.js
@@ -19,8 +19,16 @@
}
else
return v || false;
- }
+ };
+ APP_SCOPE.toJsDate = function(str) {
+ if (str == null)
+ return null;
+
+ var strtmp = str.replace(/\/Date\((-?\d+(((\+|\-)\d+)|Z|z)?)\)\//gi, "new Date($1)");
+ return eval(strtmp);
+ };
+
APP_SCOPE.createHtmlItems = function(html) {
if (ls.creator_div == null) {
ls.creator_div = document.createElement("div");
@@ -44,23 +52,17 @@
}
};
-
- APP_SCOPE.applyRoundedCornerClass = function() {
- $('.rounded-corner-box').each(function(index, el) {
- if (!$(el).hasClass('created-round-corner-box')) {
- $(el).removeClass('rounded-corner-box');
- var x = el.innerHTML;
- el.innerHTML = '
';
- }
- });
- }
-
+
APP_SCOPE.getActionUrl = function(action) {
//During the design phase, we emulate the server with a folder
//return './server-responses/' + encodeURIComponent(action) + '.txt';
return APP_SCOPE.ServerURL + 'control.cgi?action=' + encodeURIComponent(action);
}
+ APP_SCOPE.getCurrentState = function(callback) {
+ $.getJSON(this.getActionUrl('get-current-state'), callback);
+ };
+
APP_SCOPE.getApplicationSettings = function(callback) {
$.getJSON(this.getActionUrl('list-application-settings'), callback);
};
@@ -83,7 +85,7 @@
callback(APP_SCOPE.CompressionModules);
}
};
-
+
APP_SCOPE.getInstalledGenericModules = function(callback) {
if (APP_SCOPE.CompressionModules == null) {
$.getJSON(this.getActionUrl('list-installed-generic-modules'), function(resp) {
@@ -119,7 +121,21 @@
};
$(document).ready(function(){
- if (window.external && window.external.AddSearch == null && window.external.AddSearchProvider == null) {
+ ads = null;
+ adsp = null;
+ extmin = null;
+
+ if (window.external)
+ {
+ try { ads = window.external.AddSearch; }
+ catch (e) {}
+ try { adsp = window.external.AddSearchProvider; }
+ catch (e) {}
+ try { extmin = window.external.setMinSiz; }
+ catch (e) {}
+ }
+
+ if (window.external && ads == null && adsp == null && extmin != null) {
APP_SCOPE.external = window.external;
APP_SCOPE.activateFunction = function(code) {
APP_SCOPE.external.openWindow(code);
@@ -129,8 +145,6 @@
alert("Unimplemented " + code);
}
}
-
- APP_SCOPE.applyRoundedCornerClass();
});
})();
\ No newline at end of file
diff --git a/Duplicati/Server/webroot/scripts/jquery.timeago.js b/Duplicati/Server/webroot/scripts/jquery.timeago.js
new file mode 100644
index 000000000..bd9efbb1a
--- /dev/null
+++ b/Duplicati/Server/webroot/scripts/jquery.timeago.js
@@ -0,0 +1,150 @@
+/**
+ * Timeago is a jQuery plugin that makes it easy to support automatically
+ * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
+ *
+ * @name timeago
+ * @version 0.11.2
+ * @requires jQuery v1.2.3+
+ * @author Ryan McGeary
+ * @license MIT License - http://www.opensource.org/licenses/mit-license.php
+ *
+ * For usage and examples, visit:
+ * http://timeago.yarp.com/
+ *
+ * Copyright (c) 2008-2012, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
+ */
+(function($) {
+ $.timeago = function(timestamp) {
+ if (timestamp instanceof Date) {
+ return inWords(timestamp);
+ } else if (typeof timestamp === "string") {
+ return inWords($.timeago.parse(timestamp));
+ } else {
+ return inWords($.timeago.datetime(timestamp));
+ }
+ };
+ var $t = $.timeago;
+
+ $.extend($.timeago, {
+ settings: {
+ refreshMillis: 60000,
+ allowFuture: false,
+ strings: {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "ago",
+ suffixFromNow: "from now",
+ seconds: "less than a minute",
+ minute: "about a minute",
+ minutes: "%d minutes",
+ hour: "about an hour",
+ hours: "about %d hours",
+ day: "a day",
+ days: "%d days",
+ month: "about a month",
+ months: "%d months",
+ year: "about a year",
+ years: "%d years",
+ wordSeparator: " ",
+ numbers: []
+ }
+ },
+ inWords: function(distanceMillis) {
+ var $l = this.settings.strings;
+ var prefix = $l.prefixAgo;
+ var suffix = $l.suffixAgo;
+ if (this.settings.allowFuture) {
+ if (distanceMillis < 0) {
+ prefix = $l.prefixFromNow;
+ suffix = $l.suffixFromNow;
+ }
+ }
+
+ var seconds = Math.abs(distanceMillis) / 1000;
+ var minutes = seconds / 60;
+ var hours = minutes / 60;
+ var days = hours / 24;
+ var years = days / 365;
+
+ function substitute(stringOrFunction, number) {
+ var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
+ var value = ($l.numbers && $l.numbers[number]) || number;
+ return string.replace(/%d/i, value);
+ }
+
+ var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
+ seconds < 90 && substitute($l.minute, 1) ||
+ minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
+ minutes < 90 && substitute($l.hour, 1) ||
+ hours < 24 && substitute($l.hours, Math.round(hours)) ||
+ hours < 42 && substitute($l.day, 1) ||
+ days < 30 && substitute($l.days, Math.round(days)) ||
+ days < 45 && substitute($l.month, 1) ||
+ days < 365 && substitute($l.months, Math.round(days / 30)) ||
+ years < 1.5 && substitute($l.year, 1) ||
+ substitute($l.years, Math.round(years));
+
+ var separator = $l.wordSeparator === undefined ? " " : $l.wordSeparator;
+ return $.trim([prefix, words, suffix].join(separator));
+ },
+ parse: function(iso8601) {
+ var s = $.trim(iso8601);
+ s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
+ s = s.replace(/-/,"/").replace(/-/,"/");
+ s = s.replace(/T/," ").replace(/Z/," UTC");
+ s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
+ return new Date(s);
+ },
+ datetime: function(elem) {
+ var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
+ return $t.parse(iso8601);
+ },
+ isTime: function(elem) {
+ // jQuery's `is()` doesn't play well with HTML5 in IE
+ return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
+ }
+ });
+
+ $.fn.timeago = function() {
+ var self = this;
+ self.each(refresh);
+
+ var $s = $t.settings;
+ if ($s.refreshMillis > 0) {
+ setInterval(function() { self.each(refresh); }, $s.refreshMillis);
+ }
+ return self;
+ };
+
+ function refresh() {
+ var data = prepareData(this);
+ if (!isNaN(data.datetime)) {
+ $(this).text(inWords(data.datetime));
+ }
+ return this;
+ }
+
+ function prepareData(element) {
+ element = $(element);
+ if (!element.data("timeago")) {
+ element.data("timeago", { datetime: $t.datetime(element) });
+ var text = $.trim(element.text());
+ if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
+ element.attr("title", text);
+ }
+ }
+ return element.data("timeago");
+ }
+
+ function inWords(date) {
+ return $t.inWords(distance(date));
+ }
+
+ function distance(date) {
+ return (new Date().getTime() - date.getTime());
+ }
+
+ // fix for IE6 suckage
+ document.createElement("abbr");
+ document.createElement("time");
+}(jQuery));
diff --git a/Duplicati/Server/webroot/scripts/status-window.js b/Duplicati/Server/webroot/scripts/status-window.js
index c92368d9d..18ba8d300 100644
--- a/Duplicati/Server/webroot/scripts/status-window.js
+++ b/Duplicati/Server/webroot/scripts/status-window.js
@@ -1,8 +1,8 @@
$(document).ready(function(){
- APP_SCOPE.applyRoundedCornerClass();
$('#appname').each(function(i, e) { e.innerHTML = APP_SCOPE.AppName;});
-
+ jQuery.timeago.settings.allowFuture = true;
+
//Request some initial data for making the display
// we execute these in parallel for efficiency
APP_SCOPE.getApplicationSettings(function(data) {
@@ -14,21 +14,49 @@ $(document).ready(function(){
APP_SCOPE.backupSchedules = data;
APP_SCOPE.afterInitialRequest();
});
+
+ APP_SCOPE.getCurrentState(function(data) {
+ APP_SCOPE.currentState = data;
+ APP_SCOPE.afterInitialRequest();
+ });
- if (window.external)
- window.external.setMinSize(775, 200);
+ if (APP_SCOPE.external)
+ APP_SCOPE.external.setMinSize(775, 200);
APP_SCOPE.add_new_backup = function()
{
window.activateFunction("new-backup");
}
+
+ APP_SCOPE.scheduleHtmlTemplate = '' +
+ '' +
+ '
' +
+ '
%schedule%
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
%metadata%
' +
+ '
%labels%
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
';
+
+
});
(function(){
APP_SCOPE.afterInitialRequest = function() {
//Check that all required data is loaded, this prevents
- if (this.backupSchedules != null && this.applicationSettings != null) {
+ if (this.backupSchedules != null && this.applicationSettings != null && this.currentState != null) {
$('#modal-cover').hide();
$('#progress-loader').hide();
this.buildBackupDisplay();
@@ -44,20 +72,63 @@ $(document).ready(function(){
var sc = APP_SCOPE.backupSchedules[i];
var id = 'backup-schedule-' + sc.ID;
var oddClass = (i % 2) == 1 ? 'odd' : 'even';
- html += '';
- html += '
';
- html += '
' + sc.Name + '
';
- if (sc.Tags != null && sc.Tags != '')
- html += '
' + sc.Tags + '
';
- if (sc.Path != null && sc.Path != '')
- html += '
' + sc.Path + '
';
- html += '
';
- html += '
';
+
+ var templateFilled = APP_SCOPE.scheduleHtmlTemplate.replace("%name%", sc.Name).replace("%id%", "id").replace("%oddorevenclass%", oddClass);
+
+ var labels = '';
+ if ((sc.Tags != null && sc.Tags != '') || (sc.Path != null && sc.Path != ''))
+ {
+ labels = 'Labels: ';
+ if (sc.Tags != null && sc.Tags != '')
+ labels += sc.Tags;
+ if (sc.Path != null && sc.Path != '')
+ labels += sc.Path;
+ }
+
+ //TODO: Figure out what the scheduler state is
+ var schedule = '';
+
+ //Is the backup running?
+ if (sc.ID == APP_SCOPE.currentState.ActiveScheduleId) {
+ schedule = 'Running
Cancel
';
+ } else {
+
+ //Is the backup in queue?
+ var queueIndex = -1;
+ if (APP_SCOPE.currentState.SchedulerQueueIds != null)
+ for(var sci = 0; sci < APP_SCOPE.currentState.SchedulerQueueIds.length; sci++)
+ if (APP_SCOPE.currentState.SchedulerQueueIds[sci] == sc.ID)
+ {
+ queueIndex = sci;
+ break;
+ }
+
+ if (queueIndex >= 0) {
+ schedule = "No. " + (queueIndex + 1) + " in queue";
+ } else {
+ //Is the backup scheduled?
+ if (sc.NextScheduledTime != null)
+ {
+ var scdate = APP_SCOPE.toJsDate(sc.NextScheduledTime);
+ schedule = '' + jQuery.timeago(scdate) + '';
+ }
+ }
+
+ }
+
+ //TODO: Make some logic to extract the last error message
+ var error = 'placeholder for error message';
+
+ //TODO: Extract some metadata
+ var metadata = 'placeholder for metadata';
+
+ templateFilled = templateFilled.replace("%labels%", labels).replace("%schedule%", schedule).replace("%error%", error).replace("%metadata%", metadata);
+
+ html += templateFilled;
}
- container.innerHTML = html;
- this.applyRoundedCornerClass();
+ container.innerHTML = html;
};
}());
diff --git a/Duplicati/Server/webroot/status-window.html b/Duplicati/Server/webroot/status-window.html
index aa373cf9b..32e73bce4 100644
--- a/Duplicati/Server/webroot/status-window.html
+++ b/Duplicati/Server/webroot/status-window.html
@@ -8,6 +8,7 @@
+
@@ -16,14 +17,13 @@
-
Loading ...