2014-11-26 13:39:59 +01:00
|
|
|
angular.module('syncthing.core')
|
2015-03-22 16:35:08 +01:00
|
|
|
.config(function($locationProvider) {
|
2016-03-28 12:46:51 +02:00
|
|
|
$locationProvider.html5Mode({enabled: true, requireBase: false}).hashPrefix('!');
|
2015-03-22 16:35:08 +01:00
|
|
|
})
|
2015-11-18 10:57:11 +01:00
|
|
|
.controller('SyncthingController', function ($scope, $http, $location, LocaleService, Events, $filter) {
|
2014-11-26 13:39:59 +01:00
|
|
|
'use strict';
|
|
|
|
|
2014-11-28 19:39:33 +01:00
|
|
|
// private/helper definitions
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
var prevDate = 0;
|
|
|
|
var navigatingAway = false;
|
|
|
|
var online = false;
|
|
|
|
var restarting = false;
|
|
|
|
|
2014-11-28 19:39:33 +01:00
|
|
|
function initController() {
|
|
|
|
LocaleService.autoConfigLocale();
|
|
|
|
setInterval($scope.refresh, 10000);
|
2015-06-14 16:17:48 +02:00
|
|
|
Events.start();
|
2014-11-28 19:39:33 +01:00
|
|
|
}
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
// public/scope definitions
|
2014-11-28 19:39:33 +01:00
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.completion = {};
|
|
|
|
$scope.config = {};
|
|
|
|
$scope.configInSync = true;
|
|
|
|
$scope.connections = {};
|
|
|
|
$scope.errors = [];
|
|
|
|
$scope.model = {};
|
|
|
|
$scope.myID = '';
|
|
|
|
$scope.devices = [];
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.deviceRejections = {};
|
|
|
|
$scope.folderRejections = {};
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.protocolChanged = false;
|
|
|
|
$scope.reportData = {};
|
|
|
|
$scope.reportPreview = false;
|
|
|
|
$scope.folders = {};
|
|
|
|
$scope.seenError = '';
|
|
|
|
$scope.upgradeInfo = null;
|
2014-12-07 21:21:12 +01:00
|
|
|
$scope.deviceStats = {};
|
|
|
|
$scope.folderStats = {};
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.progress = {};
|
2015-04-09 11:32:54 +02:00
|
|
|
$scope.version = {};
|
2015-04-25 23:53:44 +02:00
|
|
|
$scope.needed = [];
|
|
|
|
$scope.neededTotal = 0;
|
|
|
|
$scope.neededCurrentPage = 1;
|
|
|
|
$scope.neededPageSize = 10;
|
2015-06-26 14:22:52 +02:00
|
|
|
$scope.failed = {};
|
|
|
|
$scope.failedCurrentPage = 1;
|
|
|
|
$scope.failedCurrentFolder = undefined;
|
|
|
|
$scope.failedPageSize = 10;
|
2015-08-27 00:49:06 +02:00
|
|
|
$scope.scanProgress = {};
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
$scope.localStateTotal = {
|
|
|
|
bytes: 0,
|
|
|
|
files: 0
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$(window).bind('beforeunload', function () {
|
|
|
|
navigatingAway = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.$on("$locationChangeSuccess", function () {
|
2014-11-28 19:39:33 +01:00
|
|
|
LocaleService.useLocale($location.search().lang);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
$scope.needActions = {
|
|
|
|
'rm': 'Del',
|
|
|
|
'rmdir': 'Del (dir)',
|
|
|
|
'sync': 'Sync',
|
|
|
|
'touch': 'Update'
|
|
|
|
};
|
|
|
|
$scope.needIcons = {
|
2015-08-10 11:22:37 +02:00
|
|
|
'rm': 'trash-o',
|
|
|
|
'rmdir': 'trash-o',
|
|
|
|
'sync': 'arrow-circle-o-down',
|
2014-11-26 13:39:59 +01:00
|
|
|
'touch': 'asterisk'
|
|
|
|
};
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.ONLINE, function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (online && !restarting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('UIOnline');
|
2015-01-12 12:14:24 +01:00
|
|
|
|
|
|
|
refreshSystem();
|
|
|
|
refreshConfig();
|
|
|
|
refreshConnectionStats();
|
|
|
|
refreshDeviceStats();
|
|
|
|
refreshFolderStats();
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/version').success(function (data) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if ($scope.version.version && $scope.version.version !== data.version) {
|
2015-08-25 15:38:28 +02:00
|
|
|
// We already have a version response, but it differs from
|
|
|
|
// the new one. Reload the full GUI in case it's changed.
|
|
|
|
document.location.reload(true);
|
|
|
|
}
|
|
|
|
|
2015-04-09 11:32:54 +02:00
|
|
|
$scope.version = data;
|
2016-04-05 08:36:53 +02:00
|
|
|
$scope.version.isDevelopmentVersion = data.version.indexOf('-')>0;
|
2015-01-12 12:14:24 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
2015-04-07 13:32:33 +02:00
|
|
|
$http.get(urlbase + '/svc/report').success(function (data) {
|
2015-01-12 12:14:24 +01:00
|
|
|
$scope.reportData = data;
|
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/upgrade').success(function (data) {
|
2015-01-12 12:14:24 +01:00
|
|
|
$scope.upgradeInfo = data;
|
|
|
|
}).error(function () {
|
|
|
|
$scope.upgradeInfo = null;
|
|
|
|
});
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
online = true;
|
|
|
|
restarting = false;
|
|
|
|
$('#networkError').modal('hide');
|
|
|
|
$('#restarting').modal('hide');
|
|
|
|
$('#shutdown').modal('hide');
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.OFFLINE, function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (navigatingAway || !online) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('UIOffline');
|
|
|
|
online = false;
|
|
|
|
if (!restarting) {
|
|
|
|
$('#networkError').modal();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-01-05 15:42:27 +01:00
|
|
|
$scope.$on('HTTPError', function (event, arg) {
|
|
|
|
// Emitted when a HTTP call fails. We use the status code to try
|
|
|
|
// to figure out what's wrong.
|
|
|
|
|
|
|
|
if (navigatingAway || !online) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('HTTPError', arg);
|
|
|
|
online = false;
|
|
|
|
if (!restarting) {
|
|
|
|
if (arg.status === 0) {
|
|
|
|
// A network error, not an HTTP error
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$emit(Events.OFFLINE);
|
2015-01-05 15:42:27 +01:00
|
|
|
} else if (arg.status >= 400 && arg.status <= 599) {
|
|
|
|
// A genuine HTTP error
|
|
|
|
$('#networkError').modal('hide');
|
|
|
|
$('#restarting').modal('hide');
|
|
|
|
$('#shutdown').modal('hide');
|
|
|
|
$('#httpError').modal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.STATE_CHANGED, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var data = arg.data;
|
|
|
|
if ($scope.model[data.folder]) {
|
|
|
|
$scope.model[data.folder].state = data.to;
|
2015-06-13 20:10:11 +02:00
|
|
|
$scope.model[data.folder].error = data.error;
|
2015-06-26 14:22:52 +02:00
|
|
|
|
|
|
|
// If a folder has started syncing, then any old list of
|
|
|
|
// errors is obsolete. We may get a new list of errors very
|
|
|
|
// shortly though.
|
|
|
|
if (data.to === 'syncing') {
|
|
|
|
$scope.failed[data.folder] = [];
|
|
|
|
}
|
2015-08-27 00:49:06 +02:00
|
|
|
|
|
|
|
// If a folder has started scanning, then any scan progress is
|
|
|
|
// also obsolete.
|
|
|
|
if (data.to === 'scanning') {
|
|
|
|
delete $scope.scanProgress[data.folder];
|
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.LOCAL_INDEX_UPDATED, function (event, arg) {
|
2014-12-07 21:21:12 +01:00
|
|
|
refreshFolderStats();
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.DEVICE_DISCONNECTED, function (event, arg) {
|
2015-08-23 21:56:10 +02:00
|
|
|
$scope.connections[arg.data.id].connected = false;
|
2014-11-26 13:39:59 +01:00
|
|
|
refreshDeviceStats();
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.DEVICE_CONNECTED, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!$scope.connections[arg.data.id]) {
|
|
|
|
$scope.connections[arg.data.id] = {
|
|
|
|
inbps: 0,
|
|
|
|
outbps: 0,
|
2015-03-10 23:45:43 +01:00
|
|
|
inBytesTotal: 0,
|
|
|
|
outBytesTotal: 0,
|
2015-07-17 22:22:07 +02:00
|
|
|
type: arg.data.type,
|
2015-03-10 23:45:43 +01:00
|
|
|
address: arg.data.addr
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
$scope.completion[arg.data.id] = {
|
|
|
|
_total: 100
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on('ConfigLoaded', function () {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.config.options.urAccepted === 0) {
|
2014-11-26 13:39:59 +01:00
|
|
|
// If usage reporting has been neither accepted nor declined,
|
|
|
|
// we want to ask the user to make a choice. But we don't want
|
|
|
|
// to bug them during initial setup, so we set a cookie with
|
|
|
|
// the time of the first visit. When that cookie is present
|
|
|
|
// and the time is more than four hours ago, we ask the
|
|
|
|
// question.
|
|
|
|
|
|
|
|
var firstVisit = document.cookie.replace(/(?:(?:^|.*;\s*)firstVisit\s*\=\s*([^;]*).*$)|^.*$/, "$1");
|
|
|
|
if (!firstVisit) {
|
|
|
|
document.cookie = "firstVisit=" + Date.now() + ";max-age=" + 30 * 24 * 3600;
|
|
|
|
} else {
|
|
|
|
if (+firstVisit < Date.now() - 4 * 3600 * 1000) {
|
|
|
|
$('#ur').modal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.DEVICE_REJECTED, function (event, arg) {
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.deviceRejections[arg.data.device] = arg;
|
|
|
|
});
|
|
|
|
|
2015-08-23 21:56:10 +02:00
|
|
|
$scope.$on(Events.DEVICE_PAUSED, function (event, arg) {
|
|
|
|
$scope.connections[arg.data.device].paused = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.$on(Events.DEVICE_RESUMED, function (event, arg) {
|
|
|
|
$scope.connections[arg.data.device].paused = false;
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.FOLDER_REJECTED, function (event, arg) {
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.folderRejections[arg.data.folder + "-" + arg.data.device] = arg;
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.CONFIG_SAVED, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
updateLocalConfig(arg.data);
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/config/insync').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = data.configInSync;
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.DOWNLOAD_PROGRESS, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var stats = arg.data;
|
|
|
|
var progress = {};
|
2014-11-28 19:39:33 +01:00
|
|
|
for (var folder in stats) {
|
2014-11-26 13:39:59 +01:00
|
|
|
progress[folder] = {};
|
2014-11-28 19:39:33 +01:00
|
|
|
for (var file in stats[folder]) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var s = stats[folder][file];
|
2015-03-10 23:45:43 +01:00
|
|
|
var reused = 100 * s.reused / s.total;
|
|
|
|
var copiedFromOrigin = 100 * s.copiedFromOrigin / s.total;
|
|
|
|
var copiedFromElsewhere = 100 * s.copiedFromElsewhere / s.total;
|
|
|
|
var pulled = 100 * s.pulled / s.total;
|
|
|
|
var pulling = 100 * s.pulling / s.total;
|
2015-04-28 17:34:55 +02:00
|
|
|
// We try to round up pulling to at least a percent so that it would be at least a bit visible.
|
2014-12-04 00:44:39 +01:00
|
|
|
if (pulling < 1 && pulled + copiedFromElsewhere + copiedFromOrigin + reused <= 99) {
|
2014-11-26 13:39:59 +01:00
|
|
|
pulling = 1;
|
|
|
|
}
|
|
|
|
progress[folder][file] = {
|
2015-03-10 23:45:43 +01:00
|
|
|
reused: reused,
|
|
|
|
copiedFromOrigin: copiedFromOrigin,
|
|
|
|
copiedFromElsewhere: copiedFromElsewhere,
|
|
|
|
pulled: pulled,
|
|
|
|
pulling: pulling,
|
|
|
|
bytesTotal: s.bytesTotal,
|
|
|
|
bytesDone: s.bytesDone,
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2014-11-28 19:39:33 +01:00
|
|
|
for (var folder in $scope.progress) {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!(folder in progress)) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if ($scope.neededFolder === folder) {
|
2014-11-26 13:39:59 +01:00
|
|
|
refreshNeed(folder);
|
|
|
|
}
|
2016-03-11 10:48:46 +01:00
|
|
|
} else if ($scope.neededFolder === folder) {
|
2014-11-28 19:39:33 +01:00
|
|
|
for (file in $scope.progress[folder]) {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!(file in progress[folder])) {
|
|
|
|
refreshNeed(folder);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$scope.progress = progress;
|
|
|
|
console.log("DownloadProgress", $scope.progress);
|
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.FOLDER_SUMMARY, function (event, arg) {
|
2015-03-26 23:26:51 +01:00
|
|
|
var data = arg.data;
|
|
|
|
$scope.model[data.folder] = data.summary;
|
2015-07-20 14:48:03 +02:00
|
|
|
recalcLocalStateTotal();
|
2015-03-26 23:26:51 +01:00
|
|
|
});
|
|
|
|
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.$on(Events.FOLDER_COMPLETION, function (event, arg) {
|
2015-03-26 23:26:51 +01:00
|
|
|
var data = arg.data;
|
|
|
|
if (!$scope.completion[data.device]) {
|
|
|
|
$scope.completion[data.device] = {};
|
|
|
|
}
|
|
|
|
$scope.completion[data.device][data.folder] = data.completion;
|
|
|
|
|
|
|
|
var tot = 0,
|
|
|
|
cnt = 0;
|
|
|
|
for (var cmp in $scope.completion[data.device]) {
|
|
|
|
if (cmp === "_total") {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tot += $scope.completion[data.device][cmp];
|
|
|
|
cnt += 1;
|
|
|
|
}
|
|
|
|
$scope.completion[data.device]._total = tot / cnt;
|
|
|
|
});
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
$scope.$on(Events.FOLDER_ERRORS, function (event, arg) {
|
|
|
|
var data = arg.data;
|
|
|
|
$scope.failed[data.folder] = data.errors;
|
|
|
|
});
|
|
|
|
|
2015-08-27 00:49:06 +02:00
|
|
|
$scope.$on(Events.FOLDER_SCAN_PROGRESS, function (event, arg) {
|
|
|
|
var data = arg.data;
|
|
|
|
$scope.scanProgress[data.folder] = {
|
|
|
|
current: data.current,
|
2015-11-17 21:08:36 +01:00
|
|
|
total: data.total,
|
2016-03-11 10:48:46 +01:00
|
|
|
rate: data.rate
|
2015-08-27 00:49:06 +02:00
|
|
|
};
|
|
|
|
console.log("FolderScanProgress", data);
|
|
|
|
});
|
|
|
|
|
2015-01-05 15:42:27 +01:00
|
|
|
$scope.emitHTTPError = function (data, status, headers, config) {
|
|
|
|
$scope.$emit('HTTPError', {data: data, status: status, headers: headers, config: config});
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
var debouncedFuncs = {};
|
|
|
|
|
|
|
|
function refreshFolder(folder) {
|
|
|
|
var key = "refreshFolder" + folder;
|
|
|
|
if (!debouncedFuncs[key]) {
|
|
|
|
debouncedFuncs[key] = debounce(function () {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/db/status?folder=' + encodeURIComponent(folder)).success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.model[folder] = data;
|
2015-07-20 14:48:03 +02:00
|
|
|
recalcLocalStateTotal();
|
2014-11-26 13:39:59 +01:00
|
|
|
console.log("refreshFolder", folder, data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}, 1000, true);
|
|
|
|
}
|
|
|
|
debouncedFuncs[key]();
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateLocalConfig(config) {
|
|
|
|
var hasConfig = !isEmptyObject($scope.config);
|
|
|
|
|
|
|
|
$scope.config = config;
|
2016-05-04 21:38:12 +02:00
|
|
|
$scope.config.options._listenAddressesStr = $scope.config.options.listenAddresses.join(', ');
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.config.options._globalAnnounceServersStr = $scope.config.options.globalAnnounceServers.join(', ');
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.devices = $scope.config.devices;
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.devices.forEach(function (deviceCfg) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.completion[deviceCfg.deviceID] = {
|
2014-11-26 13:39:59 +01:00
|
|
|
_total: 100
|
|
|
|
};
|
|
|
|
});
|
|
|
|
$scope.devices.sort(deviceCompare);
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders = folderMap($scope.config.folders);
|
2014-11-26 13:39:59 +01:00
|
|
|
Object.keys($scope.folders).forEach(function (folder) {
|
|
|
|
refreshFolder(folder);
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[folder].devices.forEach(function (deviceCfg) {
|
|
|
|
refreshCompletion(deviceCfg.deviceID, folder);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-11-16 21:33:55 +01:00
|
|
|
// If we're not listening on localhost, and there is no
|
|
|
|
// authentication configured, and the magic setting to silence the
|
|
|
|
// warning isn't set, then yell at the user.
|
|
|
|
var guiCfg = $scope.config.gui;
|
2016-03-11 10:48:46 +01:00
|
|
|
$scope.openNoAuth = guiCfg.address.substr(0, 4) !== "127."
|
|
|
|
&& guiCfg.address.substr(0, 6) !== "[::1]:"
|
2015-11-16 21:33:55 +01:00
|
|
|
&& (!guiCfg.user || !guiCfg.password)
|
|
|
|
&& !guiCfg.insecureAdminAccess;
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!hasConfig) {
|
|
|
|
$scope.$emit('ConfigLoaded');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function refreshSystem() {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/status').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.myID = data.myID;
|
|
|
|
$scope.system = data;
|
2015-07-17 22:22:07 +02:00
|
|
|
|
2016-05-04 21:38:12 +02:00
|
|
|
var listenersFailed = [];
|
|
|
|
for (var address in data.connectionServiceStatus) {
|
|
|
|
if (data.connectionServiceStatus[address].error) {
|
|
|
|
listenersFailed.push(address + ": " + data.connectionServiceStatus[address].error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$scope.listenersFailed = listenersFailed;
|
|
|
|
$scope.listenersTotal = Object.keys(data.connectionServiceStatus).length;
|
|
|
|
|
2015-09-20 15:30:25 +02:00
|
|
|
$scope.discoveryTotal = data.discoveryMethods;
|
|
|
|
var discoveryFailed = [];
|
|
|
|
for (var disco in data.discoveryErrors) {
|
|
|
|
if (data.discoveryErrors[disco]) {
|
|
|
|
discoveryFailed.push(disco + ": " + data.discoveryErrors[disco]);
|
2015-07-17 22:22:07 +02:00
|
|
|
}
|
|
|
|
}
|
2015-09-20 15:30:25 +02:00
|
|
|
$scope.discoveryFailed = discoveryFailed;
|
2014-11-26 13:39:59 +01:00
|
|
|
console.log("refreshSystem", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
function recalcLocalStateTotal () {
|
|
|
|
$scope.localStateTotal = {
|
|
|
|
bytes: 0,
|
|
|
|
files: 0
|
|
|
|
};
|
|
|
|
|
|
|
|
for (var f in $scope.model) {
|
|
|
|
$scope.localStateTotal.bytes += $scope.model[f].localBytes;
|
|
|
|
$scope.localStateTotal.files += $scope.model[f].localFiles;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
function refreshCompletion(device, folder) {
|
|
|
|
if (device === $scope.myID) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/db/completion?device=' + device + '&folder=' + encodeURIComponent(folder)).success(function (data) {
|
2015-03-26 23:26:51 +01:00
|
|
|
if (!$scope.completion[device]) {
|
|
|
|
$scope.completion[device] = {};
|
|
|
|
}
|
|
|
|
$scope.completion[device][folder] = data.completion;
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-03-26 23:26:51 +01:00
|
|
|
var tot = 0,
|
|
|
|
cnt = 0;
|
|
|
|
for (var cmp in $scope.completion[device]) {
|
|
|
|
if (cmp === "_total") {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tot += $scope.completion[device][cmp];
|
|
|
|
cnt += 1;
|
|
|
|
}
|
|
|
|
$scope.completion[device]._total = tot / cnt;
|
|
|
|
|
|
|
|
console.log("refreshCompletion", device, folder, $scope.completion[device]);
|
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function refreshConnectionStats() {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/connections').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var now = Date.now(),
|
|
|
|
td = (now - prevDate) / 1000,
|
|
|
|
id;
|
|
|
|
|
|
|
|
prevDate = now;
|
2015-04-09 12:14:15 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
data.total.inbps = Math.max(0, (data.total.inBytesTotal - $scope.connectionsTotal.inBytesTotal) / td);
|
|
|
|
data.total.outbps = Math.max(0, (data.total.outBytesTotal - $scope.connectionsTotal.outBytesTotal) / td);
|
|
|
|
} catch (e) {
|
|
|
|
data.total.inbps = 0;
|
|
|
|
data.total.outbps = 0;
|
|
|
|
}
|
|
|
|
$scope.connectionsTotal = data.total;
|
|
|
|
|
2015-04-07 14:57:26 +02:00
|
|
|
data = data.connections;
|
2014-11-26 13:39:59 +01:00
|
|
|
for (id in data) {
|
|
|
|
if (!data.hasOwnProperty(id)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
try {
|
2015-03-10 23:45:43 +01:00
|
|
|
data[id].inbps = Math.max(0, (data[id].inBytesTotal - $scope.connections[id].inBytesTotal) / td);
|
|
|
|
data[id].outbps = Math.max(0, (data[id].outBytesTotal - $scope.connections[id].outBytesTotal) / td);
|
2014-11-26 13:39:59 +01:00
|
|
|
} catch (e) {
|
|
|
|
data[id].inbps = 0;
|
|
|
|
data[id].outbps = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$scope.connections = data;
|
|
|
|
console.log("refreshConnections", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function refreshErrors() {
|
2015-04-07 13:46:39 +02:00
|
|
|
$http.get(urlbase + '/system/error').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.errors = data.errors;
|
|
|
|
console.log("refreshErrors", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function refreshConfig() {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/config').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
updateLocalConfig(data);
|
|
|
|
console.log("refreshConfig", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/config/insync').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = data.configInSync;
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function refreshNeed(folder) {
|
2015-04-25 23:53:44 +02:00
|
|
|
var url = urlbase + "/db/need?folder=" + encodeURIComponent(folder);
|
|
|
|
url += "&page=" + $scope.neededCurrentPage;
|
|
|
|
url += "&perpage=" + $scope.neededPageSize;
|
|
|
|
$http.get(url).success(function (data) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if ($scope.neededFolder === folder) {
|
2014-11-26 13:39:59 +01:00
|
|
|
console.log("refreshNeed", folder, data);
|
2015-04-25 23:53:44 +02:00
|
|
|
parseNeeded(data);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
2015-04-25 23:53:44 +02:00
|
|
|
function needAction(file) {
|
|
|
|
var fDelete = 4096;
|
|
|
|
var fDirectory = 16384;
|
|
|
|
|
|
|
|
if ((file.flags & (fDelete + fDirectory)) === fDelete + fDirectory) {
|
|
|
|
return 'rmdir';
|
|
|
|
} else if ((file.flags & fDelete) === fDelete) {
|
|
|
|
return 'rm';
|
|
|
|
} else if ((file.flags & fDirectory) === fDirectory) {
|
|
|
|
return 'touch';
|
|
|
|
} else {
|
|
|
|
return 'sync';
|
|
|
|
}
|
2015-06-14 16:17:48 +02:00
|
|
|
}
|
2015-04-25 23:53:44 +02:00
|
|
|
|
|
|
|
function parseNeeded(data) {
|
|
|
|
var merged = [];
|
|
|
|
data.progress.forEach(function (item) {
|
|
|
|
item.type = "progress";
|
|
|
|
item.action = needAction(item);
|
|
|
|
merged.push(item);
|
|
|
|
});
|
|
|
|
data.queued.forEach(function (item) {
|
|
|
|
item.type = "queued";
|
|
|
|
item.action = needAction(item);
|
|
|
|
merged.push(item);
|
|
|
|
});
|
|
|
|
data.rest.forEach(function (item) {
|
|
|
|
item.type = "rest";
|
|
|
|
item.action = needAction(item);
|
|
|
|
merged.push(item);
|
|
|
|
});
|
|
|
|
$scope.needed = merged;
|
|
|
|
$scope.neededTotal = data.total;
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.neededPageChanged = function (page) {
|
|
|
|
$scope.neededCurrentPage = page;
|
|
|
|
refreshNeed($scope.neededFolder);
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.neededChangePageSize = function (perpage) {
|
|
|
|
$scope.neededPageSize = perpage;
|
|
|
|
refreshNeed($scope.neededFolder);
|
2015-06-14 16:17:48 +02:00
|
|
|
};
|
2015-04-25 23:53:44 +02:00
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
$scope.failedPageChanged = function (page) {
|
|
|
|
$scope.failedCurrentPage = page;
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.failedChangePageSize = function (perpage) {
|
|
|
|
$scope.failedPageSize = perpage;
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
var refreshDeviceStats = debounce(function () {
|
|
|
|
$http.get(urlbase + "/stats/device").success(function (data) {
|
2014-12-07 21:21:12 +01:00
|
|
|
$scope.deviceStats = data;
|
|
|
|
for (var device in $scope.deviceStats) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.deviceStats[device].lastSeen = new Date($scope.deviceStats[device].lastSeen);
|
|
|
|
$scope.deviceStats[device].lastSeenDays = (new Date() - $scope.deviceStats[device].lastSeen) / 1000 / 86400;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
console.log("refreshDeviceStats", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2015-03-26 23:26:51 +01:00
|
|
|
}, 2500);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2014-12-07 21:21:12 +01:00
|
|
|
var refreshFolderStats = debounce(function () {
|
|
|
|
$http.get(urlbase + "/stats/folder").success(function (data) {
|
|
|
|
$scope.folderStats = data;
|
|
|
|
for (var folder in $scope.folderStats) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.folderStats[folder].lastFile) {
|
|
|
|
$scope.folderStats[folder].lastFile.at = new Date($scope.folderStats[folder].lastFile.at);
|
2014-12-07 21:21:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
console.log("refreshfolderStats", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2015-03-26 23:26:51 +01:00
|
|
|
}, 2500);
|
2014-12-07 21:21:12 +01:00
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.refresh = function () {
|
|
|
|
refreshSystem();
|
|
|
|
refreshConnectionStats();
|
|
|
|
refreshErrors();
|
|
|
|
};
|
|
|
|
|
2014-11-29 09:18:52 +01:00
|
|
|
$scope.folderStatus = function (folderCfg) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if (typeof $scope.model[folderCfg.id] === 'undefined') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'unknown';
|
|
|
|
}
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
// after restart syncthing process state may be empty
|
|
|
|
if (!$scope.model[folderCfg.id].state) {
|
|
|
|
return 'unknown';
|
|
|
|
}
|
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
if (folderCfg.devices.length <= 1) {
|
2014-11-29 09:18:52 +01:00
|
|
|
return 'unshared';
|
|
|
|
}
|
|
|
|
|
2015-04-12 22:12:01 +02:00
|
|
|
if ($scope.model[folderCfg.id].invalid) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'stopped';
|
|
|
|
}
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
var state = '' + $scope.model[folderCfg.id].state;
|
|
|
|
if (state === 'error') {
|
2015-04-12 22:12:01 +02:00
|
|
|
return 'stopped'; // legacy, the state is called "stopped" in the GUI
|
|
|
|
}
|
2015-06-26 14:22:52 +02:00
|
|
|
if (state === 'idle' && $scope.model[folderCfg.id].needFiles > 0) {
|
|
|
|
return 'outofsync';
|
2015-06-10 15:48:16 +02:00
|
|
|
}
|
2015-04-12 22:12:01 +02:00
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
return state;
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
2014-11-29 09:18:52 +01:00
|
|
|
$scope.folderClass = function (folderCfg) {
|
2015-06-26 14:22:52 +02:00
|
|
|
var status = $scope.folderStatus(folderCfg);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2016-03-11 10:48:46 +01:00
|
|
|
if (status === 'idle') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'success';
|
|
|
|
}
|
2016-03-11 10:48:46 +01:00
|
|
|
if (status === 'syncing' || status === 'scanning') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'primary';
|
|
|
|
}
|
2015-06-26 14:22:52 +02:00
|
|
|
if (status === 'unknown') {
|
|
|
|
return 'info';
|
|
|
|
}
|
|
|
|
if (status === 'unshared') {
|
|
|
|
return 'warning';
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-06-26 14:22:52 +02:00
|
|
|
if (status === 'stopped' || status === 'outofsync' || status === 'error') {
|
2015-04-12 22:12:01 +02:00
|
|
|
return 'danger';
|
|
|
|
}
|
2015-06-26 14:22:52 +02:00
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'info';
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.syncPercentage = function (folder) {
|
|
|
|
if (typeof $scope.model[folder] === 'undefined') {
|
|
|
|
return 100;
|
|
|
|
}
|
|
|
|
if ($scope.model[folder].globalBytes === 0) {
|
|
|
|
return 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
var pct = 100 * $scope.model[folder].inSyncBytes / $scope.model[folder].globalBytes;
|
|
|
|
return Math.floor(pct);
|
|
|
|
};
|
|
|
|
|
2015-08-27 00:49:06 +02:00
|
|
|
$scope.scanPercentage = function (folder) {
|
|
|
|
if (!$scope.scanProgress[folder]) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
var pct = 100 * $scope.scanProgress[folder].current / $scope.scanProgress[folder].total;
|
|
|
|
return Math.floor(pct);
|
2016-03-11 10:48:46 +01:00
|
|
|
};
|
2015-08-27 00:49:06 +02:00
|
|
|
|
2015-11-17 21:08:36 +01:00
|
|
|
$scope.scanRate = function (folder) {
|
|
|
|
if (!$scope.scanProgress[folder]) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return $scope.scanProgress[folder].rate;
|
2016-03-11 10:48:46 +01:00
|
|
|
};
|
2015-11-17 21:08:36 +01:00
|
|
|
|
2015-11-18 10:57:11 +01:00
|
|
|
$scope.scanRemaining = function (folder) {
|
|
|
|
// Formats the remaining scan time as a string. Includes days and
|
|
|
|
// hours only when relevant, resulting in time stamps like:
|
|
|
|
// 00m 40s
|
|
|
|
// 32m 40s
|
|
|
|
// 2h 32m
|
|
|
|
// 4d 2h
|
|
|
|
|
|
|
|
if (!$scope.scanProgress[folder]) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
// Calculate remaining bytes and seconds based on our current
|
|
|
|
// rate.
|
2016-03-11 10:48:46 +01:00
|
|
|
|
2015-11-18 10:57:11 +01:00
|
|
|
var remainingBytes = $scope.scanProgress[folder].total - $scope.scanProgress[folder].current;
|
|
|
|
var seconds = remainingBytes / $scope.scanProgress[folder].rate;
|
|
|
|
// Round up to closest ten seconds to avoid flapping too much to
|
|
|
|
// and fro.
|
2016-03-11 10:48:46 +01:00
|
|
|
|
2015-11-18 10:57:11 +01:00
|
|
|
seconds = Math.ceil(seconds / 10) * 10;
|
|
|
|
|
|
|
|
// Separate out the number of days.
|
|
|
|
var days = 0;
|
2016-03-11 10:48:46 +01:00
|
|
|
var res = [];
|
2015-11-18 10:57:11 +01:00
|
|
|
if (seconds >= 86400) {
|
|
|
|
days = Math.floor(seconds / 86400);
|
|
|
|
res.push('' + days + 'd')
|
|
|
|
seconds = seconds % 86400;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Separate out the number of hours.
|
|
|
|
var hours = 0;
|
|
|
|
if (seconds > 3600) {
|
|
|
|
hours = Math.floor(seconds / 3600);
|
|
|
|
res.push('' + hours + 'h')
|
|
|
|
seconds = seconds % 3600;
|
|
|
|
}
|
|
|
|
|
|
|
|
var d = new Date(1970, 0, 1).setSeconds(seconds);
|
|
|
|
|
2016-03-11 10:48:46 +01:00
|
|
|
if (days === 0) {
|
2015-11-18 10:57:11 +01:00
|
|
|
// Format minutes only if we're within a day of completion.
|
|
|
|
var f = $filter('date')(d, "m'm'");
|
|
|
|
res.push(f);
|
|
|
|
}
|
|
|
|
|
2016-03-11 10:48:46 +01:00
|
|
|
if (days === 0 && hours === 0) {
|
2015-11-18 10:57:11 +01:00
|
|
|
// Format seconds only when we're within an hour of completion.
|
|
|
|
var f = $filter('date')(d, "ss's'");
|
|
|
|
res.push(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
return res.join(' ');
|
2016-03-11 10:48:46 +01:00
|
|
|
};
|
2015-11-18 10:57:11 +01:00
|
|
|
|
2014-11-29 09:42:16 +01:00
|
|
|
$scope.deviceStatus = function (deviceCfg) {
|
|
|
|
if ($scope.deviceFolders(deviceCfg).length === 0) {
|
|
|
|
return 'unused';
|
|
|
|
}
|
|
|
|
|
2015-08-23 21:56:10 +02:00
|
|
|
if (typeof $scope.connections[deviceCfg.deviceID] === 'undefined') {
|
|
|
|
return 'unknown';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($scope.connections[deviceCfg.deviceID].paused) {
|
|
|
|
return 'paused';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($scope.connections[deviceCfg.deviceID].connected) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.completion[deviceCfg.deviceID] && $scope.completion[deviceCfg.deviceID]._total === 100) {
|
2014-11-29 09:42:16 +01:00
|
|
|
return 'insync';
|
|
|
|
} else {
|
|
|
|
return 'syncing';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disconnected
|
|
|
|
return 'disconnected';
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.deviceClass = function (deviceCfg) {
|
2014-11-29 09:42:16 +01:00
|
|
|
if ($scope.deviceFolders(deviceCfg).length === 0) {
|
|
|
|
// Unused
|
|
|
|
return 'warning';
|
|
|
|
}
|
|
|
|
|
2015-08-23 21:56:10 +02:00
|
|
|
if (typeof $scope.connections[deviceCfg.deviceID] === 'undefined') {
|
|
|
|
return 'info';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($scope.connections[deviceCfg.deviceID].paused) {
|
|
|
|
return 'default';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($scope.connections[deviceCfg.deviceID].connected) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.completion[deviceCfg.deviceID] && $scope.completion[deviceCfg.deviceID]._total === 100) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'success';
|
|
|
|
} else {
|
|
|
|
return 'primary';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-29 09:42:16 +01:00
|
|
|
// Disconnected
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'info';
|
|
|
|
};
|
|
|
|
|
2016-06-01 21:06:36 +02:00
|
|
|
$scope.syncthingStatus = function () {
|
|
|
|
var syncCount = 0;
|
|
|
|
var notifyCount = 0;
|
|
|
|
var pauseCount = 0;
|
|
|
|
|
|
|
|
// loop through all folders
|
|
|
|
var folderListCache = $scope.folderList();
|
|
|
|
for (var i = 0; i < folderListCache.length; i++) {
|
|
|
|
var status = $scope.folderStatus(folderListCache[i]);
|
|
|
|
switch (status) {
|
|
|
|
case 'syncing':
|
|
|
|
syncCount++;
|
|
|
|
break;
|
|
|
|
case 'stopped':
|
|
|
|
case 'unknown':
|
|
|
|
case 'outofsync':
|
|
|
|
case 'error':
|
|
|
|
notifyCount++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// loop through all devices
|
|
|
|
var deviceCount = $scope.devices.length;
|
|
|
|
for (var i = 0; i < $scope.devices.length; i++) {
|
|
|
|
var status = $scope.deviceStatus({
|
|
|
|
deviceID:$scope.devices[i].deviceID
|
|
|
|
});
|
|
|
|
switch (status) {
|
|
|
|
case 'unknown':
|
|
|
|
notifyCount++;
|
|
|
|
break;
|
|
|
|
case 'paused':
|
|
|
|
pauseCount++;
|
|
|
|
break;
|
|
|
|
case 'unused':
|
|
|
|
deviceCount--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// enumerate notifications
|
|
|
|
if ($scope.openNoAuth || !$scope.configInSync || Object.keys($scope.deviceRejections).length > 0 || Object.keys($scope.folderRejections).length > 0 || $scope.errorList().length > 0 || !online) {
|
|
|
|
notifyCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// at least one folder is syncing
|
|
|
|
if (syncCount > 0) {
|
|
|
|
return 'sync';
|
|
|
|
}
|
|
|
|
|
|
|
|
// a device is unknown or a folder is stopped/unknown/outofsync/error or some other notification is open or gui offline
|
|
|
|
if (notifyCount > 0) {
|
|
|
|
return 'notify';
|
|
|
|
}
|
|
|
|
|
|
|
|
// all used devices are paused except (this) one
|
|
|
|
if (pauseCount === deviceCount-1) {
|
|
|
|
return 'pause';
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'default';
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.deviceAddr = function (deviceCfg) {
|
2015-03-10 23:45:43 +01:00
|
|
|
var conn = $scope.connections[deviceCfg.deviceID];
|
2015-08-23 21:56:10 +02:00
|
|
|
if (conn && conn.connected) {
|
2015-03-10 23:45:43 +01:00
|
|
|
return conn.address;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
return '?';
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.deviceCompletion = function (deviceCfg) {
|
2015-03-10 23:45:43 +01:00
|
|
|
var conn = $scope.connections[deviceCfg.deviceID];
|
2014-11-26 13:39:59 +01:00
|
|
|
if (conn) {
|
2015-03-10 23:45:43 +01:00
|
|
|
return conn.completion + '%';
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
return '';
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.findDevice = function (deviceID) {
|
|
|
|
var matches = $scope.devices.filter(function (n) {
|
2016-03-11 10:48:46 +01:00
|
|
|
return n.deviceID === deviceID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
2016-03-11 10:48:46 +01:00
|
|
|
if (matches.length !== 1) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
return matches[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.deviceName = function (deviceCfg) {
|
2016-04-05 08:36:53 +02:00
|
|
|
if (typeof deviceCfg === 'undefined' || typeof deviceCfg.deviceID === 'undefined') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return "";
|
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
if (deviceCfg.name) {
|
|
|
|
return deviceCfg.name;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
return deviceCfg.deviceID.substr(0, 6);
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.thisDeviceName = function () {
|
|
|
|
var device = $scope.thisDevice();
|
|
|
|
if (typeof device === 'undefined') {
|
|
|
|
return "(unknown device)";
|
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
if (device.name) {
|
|
|
|
return device.name;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
return device.deviceID.substr(0, 6);
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
2015-08-23 21:56:10 +02:00
|
|
|
$scope.pauseDevice = function (device) {
|
|
|
|
$http.post(urlbase + "/system/pause?device=" + device);
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.resumeDevice = function (device) {
|
|
|
|
$http.post(urlbase + "/system/resume?device=" + device);
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.editSettings = function () {
|
|
|
|
// Make a working copy
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.tmpOptions = angular.copy($scope.config.options);
|
|
|
|
$scope.tmpOptions.urEnabled = ($scope.tmpOptions.urAccepted > 0);
|
|
|
|
$scope.tmpOptions.deviceName = $scope.thisDevice().name;
|
|
|
|
$scope.tmpOptions.autoUpgradeEnabled = ($scope.tmpOptions.autoUpgradeIntervalH > 0);
|
|
|
|
$scope.tmpGUI = angular.copy($scope.config.gui);
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#settings').modal();
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.saveConfig = function () {
|
|
|
|
var cfg = JSON.stringify($scope.config);
|
|
|
|
var opts = {
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
}
|
|
|
|
};
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/config', cfg, opts).success(function () {
|
|
|
|
$http.get(urlbase + '/system/config/insync').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = data.configInSync;
|
|
|
|
});
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.saveSettings = function () {
|
|
|
|
// Make sure something changed
|
2015-03-10 23:45:43 +01:00
|
|
|
var changed = !angular.equals($scope.config.options, $scope.tmpOptions) || !angular.equals($scope.config.gui, $scope.tmpGUI);
|
2016-01-10 16:37:31 +01:00
|
|
|
var themeChanged = $scope.config.gui.theme !== $scope.tmpGUI.theme;
|
2014-11-26 13:39:59 +01:00
|
|
|
if (changed) {
|
|
|
|
// Check if usage reporting has been enabled or disabled
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.tmpOptions.urEnabled && $scope.tmpOptions.urAccepted <= 0) {
|
|
|
|
$scope.tmpOptions.urAccepted = 1000;
|
|
|
|
} else if (!$scope.tmpOptions.urEnabled && $scope.tmpOptions.urAccepted > 0) {
|
|
|
|
$scope.tmpOptions.urAccepted = -1;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if auto-upgrade has been enabled or disabled
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.tmpOptions.autoUpgradeEnabled) {
|
|
|
|
$scope.tmpOptions.autoUpgradeIntervalH = $scope.tmpOptions.autoUpgradeIntervalH || 12;
|
2014-11-26 13:39:59 +01:00
|
|
|
} else {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.tmpOptions.autoUpgradeIntervalH = 0;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if protocol will need to be changed on restart
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.config.gui.useTLS !== $scope.tmpGUI.useTLS) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.protocolChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply new settings locally
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.thisDevice().name = $scope.tmpOptions.deviceName;
|
|
|
|
$scope.config.options = angular.copy($scope.tmpOptions);
|
|
|
|
$scope.config.gui = angular.copy($scope.tmpGUI);
|
2014-11-18 23:57:21 +01:00
|
|
|
|
2016-05-04 21:38:12 +02:00
|
|
|
['listenAddresses', 'globalAnnounceServers'].forEach(function (key) {
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.config.options[key] = $scope.config.options["_" + key + "Str"].split(/[ ,]+/).map(function (x) {
|
2014-11-18 23:57:21 +01:00
|
|
|
return x.trim();
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
}
|
|
|
|
|
|
|
|
$('#settings').modal("hide");
|
2016-01-10 16:37:31 +01:00
|
|
|
|
|
|
|
if (themeChanged) {
|
|
|
|
document.location.reload(true);
|
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.saveAdvanced = function () {
|
2015-07-09 22:58:58 +02:00
|
|
|
$scope.config = $scope.advancedConfig;
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.saveConfig();
|
|
|
|
$('#advanced').modal("hide");
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.restart = function () {
|
|
|
|
restarting = true;
|
|
|
|
$('#restarting').modal();
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/restart');
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = true;
|
|
|
|
|
|
|
|
// Switch webpage protocol if needed
|
|
|
|
if ($scope.protocolChanged) {
|
|
|
|
var protocol = 'http';
|
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.config.gui.useTLS) {
|
2014-11-26 13:39:59 +01:00
|
|
|
protocol = 'https';
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(function () {
|
|
|
|
window.location.protocol = protocol;
|
|
|
|
}, 2500);
|
|
|
|
|
|
|
|
$scope.protocolChanged = false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.upgrade = function () {
|
|
|
|
restarting = true;
|
2015-04-22 14:41:08 +02:00
|
|
|
$('#majorUpgrade').modal('hide');
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#upgrading').modal();
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/upgrade').success(function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#restarting').modal();
|
|
|
|
$('#upgrading').modal('hide');
|
|
|
|
}).error(function () {
|
|
|
|
$('#upgrading').modal('hide');
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-04-22 14:41:08 +02:00
|
|
|
$scope.upgradeMajor = function () {
|
|
|
|
$('#majorUpgrade').modal();
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.shutdown = function () {
|
|
|
|
restarting = true;
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/shutdown').success(function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#shutdown').modal();
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.editDevice = function (deviceCfg) {
|
|
|
|
$scope.currentDevice = $.extend({}, deviceCfg);
|
|
|
|
$scope.editingExisting = true;
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.currentDevice._addressesStr = deviceCfg.addresses.join(', ');
|
2015-07-20 14:48:03 +02:00
|
|
|
$scope.currentDevice.selectedFolders = {};
|
|
|
|
$scope.deviceFolders($scope.currentDevice).forEach(function (folder) {
|
|
|
|
$scope.currentDevice.selectedFolders[folder] = true;
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.deviceEditor.$setPristine();
|
|
|
|
$('#editDevice').modal();
|
|
|
|
};
|
|
|
|
|
2015-12-29 22:22:00 +01:00
|
|
|
$scope.idDevice = function (deviceCfg) {
|
|
|
|
$scope.currentDevice = deviceCfg;
|
|
|
|
$('#editDevice').modal('hide');
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#idqr').modal('show');
|
|
|
|
};
|
|
|
|
|
2016-03-25 21:29:07 +01:00
|
|
|
$scope.addDevice = function (deviceID, name) {
|
2015-12-18 21:33:11 +01:00
|
|
|
return $http.get(urlbase + '/system/discovery')
|
2014-11-26 13:39:59 +01:00
|
|
|
.success(function (registry) {
|
|
|
|
$scope.discovery = registry;
|
|
|
|
})
|
|
|
|
.then(function () {
|
|
|
|
$scope.currentDevice = {
|
2016-03-25 21:29:07 +01:00
|
|
|
name: name,
|
2015-12-18 21:33:11 +01:00
|
|
|
deviceID: deviceID,
|
2015-07-03 14:07:38 +02:00
|
|
|
_addressesStr: 'dynamic',
|
2015-03-10 23:45:43 +01:00
|
|
|
compression: 'metadata',
|
|
|
|
introducer: false,
|
2014-12-05 01:22:16 +01:00
|
|
|
selectedFolders: {}
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
$scope.editingExisting = false;
|
|
|
|
$scope.deviceEditor.$setPristine();
|
|
|
|
$('#editDevice').modal();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.deleteDevice = function () {
|
|
|
|
$('#editDevice').modal('hide');
|
|
|
|
if (!$scope.editingExisting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.devices = $scope.devices.filter(function (n) {
|
2015-03-10 23:45:43 +01:00
|
|
|
return n.deviceID !== $scope.currentDevice.deviceID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.devices = $scope.devices;
|
2014-12-28 00:12:12 +01:00
|
|
|
// In case we later added the device manually, remove the ignoral
|
|
|
|
// record.
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.ignoredDevices = $scope.config.ignoredDevices.filter(function (id) {
|
|
|
|
return id !== $scope.currentDevice.deviceID;
|
2014-12-28 00:12:12 +01:00
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
|
|
|
|
for (var id in $scope.folders) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
|
|
|
|
return n.deviceID !== $scope.currentDevice.deviceID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.saveDevice = function () {
|
|
|
|
$('#editDevice').modal('hide');
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.saveDeviceConfig($scope.currentDevice);
|
2015-12-18 21:33:11 +01:00
|
|
|
$scope.dismissDeviceRejection($scope.currentDevice.deviceID);
|
2014-12-28 00:12:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.saveDeviceConfig = function (deviceCfg) {
|
2015-07-03 14:07:38 +02:00
|
|
|
deviceCfg.addresses = deviceCfg._addressesStr.split(',').map(function (x) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return x.trim();
|
|
|
|
});
|
|
|
|
|
2016-03-11 10:48:46 +01:00
|
|
|
var done = false;
|
|
|
|
for (var i = 0; i < $scope.devices.length && !done; i++) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.devices[i].deviceID === deviceCfg.deviceID) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.devices[i] = deviceCfg;
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!done) {
|
|
|
|
$scope.devices.push(deviceCfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.devices.sort(deviceCompare);
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.devices = $scope.devices;
|
2014-12-28 00:12:12 +01:00
|
|
|
// In case we are adding the device manually, remove the ignoral
|
|
|
|
// record.
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.ignoredDevices = $scope.config.ignoredDevices.filter(function (id) {
|
|
|
|
return id !== deviceCfg.deviceID;
|
2014-12-28 00:12:12 +01:00
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
for (var id in deviceCfg.selectedFolders) {
|
|
|
|
if (deviceCfg.selectedFolders[id]) {
|
|
|
|
var found = false;
|
|
|
|
for (i = 0; i < $scope.folders[id].devices.length; i++) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if ($scope.folders[id].devices[i].deviceID === deviceCfg.deviceID) {
|
2015-07-20 14:48:03 +02:00
|
|
|
found = true;
|
|
|
|
break;
|
2014-12-05 01:22:16 +01:00
|
|
|
}
|
2015-07-20 14:48:03 +02:00
|
|
|
}
|
2014-12-05 01:22:16 +01:00
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
if (!found) {
|
|
|
|
$scope.folders[id].devices.push({
|
|
|
|
deviceID: deviceCfg.deviceID
|
2014-12-05 01:22:16 +01:00
|
|
|
});
|
|
|
|
}
|
2015-07-20 14:48:03 +02:00
|
|
|
} else {
|
|
|
|
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
|
2016-03-11 10:48:46 +01:00
|
|
|
return n.deviceID !== deviceCfg.deviceID;
|
2015-07-20 14:48:03 +02:00
|
|
|
});
|
2014-12-05 01:22:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
};
|
|
|
|
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.dismissDeviceRejection = function (device) {
|
|
|
|
delete $scope.deviceRejections[device];
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.ignoreRejectedDevice = function (device) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.ignoredDevices.push(device);
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
$scope.dismissDeviceRejection(device);
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.otherDevices = function () {
|
|
|
|
return $scope.devices.filter(function (n) {
|
2015-03-10 23:45:43 +01:00
|
|
|
return n.deviceID !== $scope.myID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.thisDevice = function () {
|
2016-03-11 10:48:46 +01:00
|
|
|
for (var i = 0; i < $scope.devices.length; i++) {
|
|
|
|
var n = $scope.devices[i];
|
2015-03-10 23:45:43 +01:00
|
|
|
if (n.deviceID === $scope.myID) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.allDevices = function () {
|
|
|
|
var devices = $scope.otherDevices();
|
|
|
|
devices.push($scope.thisDevice());
|
|
|
|
return devices;
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.errorList = function () {
|
2015-10-12 03:12:57 +02:00
|
|
|
if (!$scope.errors) {
|
|
|
|
return [];
|
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
return $scope.errors.filter(function (e) {
|
2015-10-12 07:18:40 +02:00
|
|
|
return e.when > $scope.seenError;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.clearErrors = function () {
|
2015-10-12 07:18:40 +02:00
|
|
|
$scope.seenError = $scope.errors[$scope.errors.length - 1].when;
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/error/clear');
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.friendlyDevices = function (str) {
|
|
|
|
for (var i = 0; i < $scope.devices.length; i++) {
|
|
|
|
var cfg = $scope.devices[i];
|
2015-03-10 23:45:43 +01:00
|
|
|
str = str.replace(cfg.deviceID, $scope.deviceName(cfg));
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
return str;
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.folderList = function () {
|
|
|
|
return folderList($scope.folders);
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.directoryList = [];
|
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.$watch('currentFolder.path', function (newvalue) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if (newvalue && newvalue.trim().charAt(0) === '~') {
|
2015-06-14 16:17:48 +02:00
|
|
|
$scope.currentFolder.path = $scope.system.tilde + newvalue.trim().substring(1);
|
2015-04-10 09:34:24 +02:00
|
|
|
}
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/browse', {
|
2014-11-26 13:39:59 +01:00
|
|
|
params: { current: newvalue }
|
|
|
|
}).success(function (data) {
|
|
|
|
$scope.directoryList = data;
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.editFolder = function (folderCfg) {
|
|
|
|
$scope.currentFolder = angular.copy(folderCfg);
|
2016-03-11 10:48:46 +01:00
|
|
|
if ($scope.currentFolder.path.slice(-1) === $scope.system.pathSeparator) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
|
2014-12-15 00:12:12 +01:00
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.selectedDevices = {};
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.devices.forEach(function (n) {
|
|
|
|
$scope.currentFolder.selectedDevices[n.deviceID] = true;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
2015-06-12 13:04:00 +02:00
|
|
|
if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "trashcan") {
|
|
|
|
$scope.currentFolder.trashcanFileVersioning = true;
|
|
|
|
$scope.currentFolder.fileVersioningSelector = "trashcan";
|
|
|
|
$scope.currentFolder.trashcanClean = +$scope.currentFolder.versioning.params.cleanoutDays;
|
|
|
|
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "simple") {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.simpleFileVersioning = true;
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.fileVersioningSelector = "simple";
|
|
|
|
$scope.currentFolder.simpleKeep = +$scope.currentFolder.versioning.params.keep;
|
|
|
|
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "staggered") {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.staggeredFileVersioning = true;
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.fileVersioningSelector = "staggered";
|
|
|
|
$scope.currentFolder.staggeredMaxAge = Math.floor(+$scope.currentFolder.versioning.params.maxAge / 86400);
|
|
|
|
$scope.currentFolder.staggeredCleanInterval = +$scope.currentFolder.versioning.params.cleanInterval;
|
|
|
|
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.versioning.params.versionsPath;
|
2015-03-19 11:31:21 +01:00
|
|
|
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "external") {
|
|
|
|
$scope.currentFolder.externalFileVersioning = true;
|
|
|
|
$scope.currentFolder.fileVersioningSelector = "external";
|
|
|
|
$scope.currentFolder.externalCommand = $scope.currentFolder.versioning.params.command;
|
2014-11-26 13:39:59 +01:00
|
|
|
} else {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.fileVersioningSelector = "none";
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-06-12 13:04:00 +02:00
|
|
|
$scope.currentFolder.trashcanClean = $scope.currentFolder.trashcanClean || 0; // weeds out nulls and undefineds
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.simpleKeep = $scope.currentFolder.simpleKeep || 5;
|
|
|
|
$scope.currentFolder.staggeredCleanInterval = $scope.currentFolder.staggeredCleanInterval || 3600;
|
|
|
|
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.staggeredVersionsPath || "";
|
|
|
|
|
|
|
|
// staggeredMaxAge can validly be zero, which we should not replace
|
|
|
|
// with the default value of 365. So only set the default if it's
|
|
|
|
// actually undefined.
|
|
|
|
if (typeof $scope.currentFolder.staggeredMaxAge === 'undefined') {
|
|
|
|
$scope.currentFolder.staggeredMaxAge = 365;
|
|
|
|
}
|
2015-03-19 11:31:21 +01:00
|
|
|
$scope.currentFolder.externalCommand = $scope.currentFolder.externalCommand || "";
|
2015-03-21 15:33:31 +01:00
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.editingExisting = true;
|
|
|
|
$scope.folderEditor.$setPristine();
|
|
|
|
$('#editFolder').modal();
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.addFolder = function () {
|
|
|
|
$scope.currentFolder = {
|
2016-03-11 10:48:46 +01:00
|
|
|
selectedDevices: {},
|
2016-05-04 12:47:33 +02:00
|
|
|
type: "readwrite",
|
2016-03-11 10:48:46 +01:00
|
|
|
rescanIntervalS: 60,
|
|
|
|
minDiskFreePct: 1,
|
|
|
|
maxConflicts: 10,
|
|
|
|
order: "random",
|
|
|
|
fileVersioningSelector: "none",
|
|
|
|
trashcanClean: 0,
|
|
|
|
simpleKeep: 5,
|
|
|
|
staggeredMaxAge: 365,
|
|
|
|
staggeredCleanInterval: 3600,
|
|
|
|
staggeredVersionsPath: "",
|
|
|
|
externalCommand: "",
|
|
|
|
autoNormalize: true
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
$scope.editingExisting = false;
|
|
|
|
$scope.folderEditor.$setPristine();
|
2016-05-26 09:25:34 +02:00
|
|
|
$http.get(urlbase + '/svc/random/string?length=10').success(function (data) {
|
|
|
|
$scope.currentFolder.id = data.random.substr(0, 5) + '-' + data.random.substr(5, 5);
|
|
|
|
$('#editFolder').modal();
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
2016-03-11 10:48:46 +01:00
|
|
|
$scope.addFolderAndShare = function (folder, folderLabel, device) {
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.dismissFolderRejection(folder, device);
|
|
|
|
$scope.currentFolder = {
|
2015-04-18 12:23:21 +02:00
|
|
|
id: folder,
|
2016-03-11 10:48:46 +01:00
|
|
|
label: folderLabel,
|
2015-06-14 16:17:48 +02:00
|
|
|
selectedDevices: {},
|
|
|
|
rescanIntervalS: 60,
|
2015-08-08 22:01:16 +02:00
|
|
|
minDiskFreePct: 1,
|
2016-01-03 21:09:02 +01:00
|
|
|
maxConflicts: 10,
|
2015-08-14 09:37:04 +02:00
|
|
|
order: "random",
|
2015-06-14 16:17:48 +02:00
|
|
|
fileVersioningSelector: "none",
|
|
|
|
trashcanClean: 0,
|
|
|
|
simpleKeep: 5,
|
|
|
|
staggeredMaxAge: 365,
|
|
|
|
staggeredCleanInterval: 3600,
|
|
|
|
staggeredVersionsPath: "",
|
|
|
|
externalCommand: "",
|
2016-03-11 10:48:46 +01:00
|
|
|
autoNormalize: true,
|
|
|
|
viewFlags: {
|
|
|
|
importFromOtherDevice: true
|
|
|
|
}
|
2014-12-28 00:12:12 +01:00
|
|
|
};
|
|
|
|
$scope.currentFolder.selectedDevices[device] = true;
|
|
|
|
|
|
|
|
$scope.editingExisting = false;
|
|
|
|
$scope.folderEditor.$setPristine();
|
|
|
|
$('#editFolder').modal();
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.shareFolderWithDevice = function (folder, device) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[folder].devices.push({
|
|
|
|
deviceID: device
|
2014-12-28 00:12:12 +01:00
|
|
|
});
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.folders = folderList($scope.folders);
|
2014-12-28 00:12:12 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
$scope.dismissFolderRejection(folder, device);
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveFolder = function () {
|
|
|
|
$('#editFolder').modal('hide');
|
2016-03-11 10:48:46 +01:00
|
|
|
var folderCfg = $scope.currentFolder;
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.devices = [];
|
2014-11-26 13:39:59 +01:00
|
|
|
folderCfg.selectedDevices[$scope.myID] = true;
|
|
|
|
for (var deviceID in folderCfg.selectedDevices) {
|
|
|
|
if (folderCfg.selectedDevices[deviceID] === true) {
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.devices.push({
|
|
|
|
deviceID: deviceID
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete folderCfg.selectedDevices;
|
|
|
|
|
2015-06-12 13:04:00 +02:00
|
|
|
if (folderCfg.fileVersioningSelector === "trashcan") {
|
|
|
|
folderCfg.versioning = {
|
|
|
|
'Type': 'trashcan',
|
|
|
|
'Params': {
|
|
|
|
'cleanoutDays': '' + folderCfg.trashcanClean
|
|
|
|
}
|
|
|
|
};
|
|
|
|
delete folderCfg.trashcanFileVersioning;
|
|
|
|
delete folderCfg.trashcanClean;
|
|
|
|
} else if (folderCfg.fileVersioningSelector === "simple") {
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.versioning = {
|
2014-11-26 13:39:59 +01:00
|
|
|
'Type': 'simple',
|
|
|
|
'Params': {
|
|
|
|
'keep': '' + folderCfg.simpleKeep
|
|
|
|
}
|
|
|
|
};
|
|
|
|
delete folderCfg.simpleFileVersioning;
|
|
|
|
delete folderCfg.simpleKeep;
|
2015-03-10 23:45:43 +01:00
|
|
|
} else if (folderCfg.fileVersioningSelector === "staggered") {
|
|
|
|
folderCfg.versioning = {
|
|
|
|
'type': 'staggered',
|
|
|
|
'params': {
|
2014-11-26 13:39:59 +01:00
|
|
|
'maxAge': '' + (folderCfg.staggeredMaxAge * 86400),
|
|
|
|
'cleanInterval': '' + folderCfg.staggeredCleanInterval,
|
|
|
|
'versionsPath': '' + folderCfg.staggeredVersionsPath
|
|
|
|
}
|
|
|
|
};
|
|
|
|
delete folderCfg.staggeredFileVersioning;
|
|
|
|
delete folderCfg.staggeredMaxAge;
|
|
|
|
delete folderCfg.staggeredCleanInterval;
|
|
|
|
delete folderCfg.staggeredVersionsPath;
|
|
|
|
|
2015-03-19 11:31:21 +01:00
|
|
|
} else if (folderCfg.fileVersioningSelector === "external") {
|
|
|
|
folderCfg.versioning = {
|
|
|
|
'Type': 'external',
|
|
|
|
'Params': {
|
|
|
|
'command': '' + folderCfg.externalCommand
|
|
|
|
}
|
|
|
|
};
|
|
|
|
delete folderCfg.externalFileVersioning;
|
|
|
|
delete folderCfg.externalCommand;
|
2014-11-26 13:39:59 +01:00
|
|
|
} else {
|
2015-03-10 23:45:43 +01:00
|
|
|
delete folderCfg.versioning;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[folderCfg.id] = folderCfg;
|
|
|
|
$scope.config.folders = folderList($scope.folders);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
};
|
2014-12-28 00:12:12 +01:00
|
|
|
|
|
|
|
$scope.dismissFolderRejection = function (folder, device) {
|
|
|
|
delete $scope.folderRejections[folder + "-" + device];
|
|
|
|
};
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2014-12-01 20:50:27 +01:00
|
|
|
$scope.sharesFolder = function (folderCfg) {
|
|
|
|
var names = [];
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.devices.forEach(function (device) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if (device.deviceID !== $scope.myID) {
|
2015-03-10 23:45:43 +01:00
|
|
|
names.push($scope.deviceName($scope.findDevice(device.deviceID)));
|
2014-12-01 20:50:27 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
names.sort();
|
|
|
|
return names.join(", ");
|
2015-06-14 16:17:48 +02:00
|
|
|
};
|
2014-12-01 20:50:27 +01:00
|
|
|
|
2014-11-29 09:42:16 +01:00
|
|
|
$scope.deviceFolders = function (deviceCfg) {
|
|
|
|
var folders = [];
|
|
|
|
for (var folderID in $scope.folders) {
|
2015-06-14 16:17:48 +02:00
|
|
|
var devices = $scope.folders[folderID].devices;
|
2014-11-29 09:42:16 +01:00
|
|
|
for (var i = 0; i < devices.length; i++) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if (devices[i].deviceID === deviceCfg.deviceID) {
|
|
|
|
var label = $scope.folders[folderID].label;
|
|
|
|
folders.push(label.length > 0 ? label : folderID);
|
2014-12-04 21:11:30 +01:00
|
|
|
break;
|
2014-11-29 09:42:16 +01:00
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-04-03 20:00:13 +02:00
|
|
|
}
|
2014-12-04 21:11:30 +01:00
|
|
|
|
2014-11-29 09:42:16 +01:00
|
|
|
folders.sort();
|
|
|
|
return folders;
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
$scope.deleteFolder = function (id) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#editFolder').modal('hide');
|
|
|
|
if (!$scope.editingExisting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
delete $scope.folders[id];
|
|
|
|
delete $scope.model[id];
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.folders = folderList($scope.folders);
|
2015-07-20 14:48:03 +02:00
|
|
|
recalcLocalStateTotal();
|
2014-11-26 13:39:59 +01:00
|
|
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.editIgnores = function () {
|
|
|
|
if (!$scope.editingExisting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$('#editIgnoresButton').attr('disabled', 'disabled');
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id))
|
2014-11-26 13:39:59 +01:00
|
|
|
.success(function (data) {
|
|
|
|
data.ignore = data.ignore || [];
|
|
|
|
|
2015-07-15 13:20:27 +02:00
|
|
|
$('#editFolder').modal('hide')
|
|
|
|
.one('hidden.bs.modal', function() {
|
|
|
|
var textArea = $('#editIgnores textarea');
|
|
|
|
|
|
|
|
textArea.val(data.ignore.join('\n'));
|
|
|
|
|
|
|
|
$('#editIgnores').modal()
|
|
|
|
.one('hidden.bs.modal', function () {
|
|
|
|
$('#editFolder').modal();
|
|
|
|
})
|
|
|
|
.one('shown.bs.modal', function () {
|
|
|
|
textArea.focus();
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
})
|
|
|
|
.then(function () {
|
|
|
|
$('#editIgnoresButton').removeAttr('disabled');
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.saveIgnores = function () {
|
|
|
|
if (!$scope.editingExisting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id), {
|
2014-11-26 13:39:59 +01:00
|
|
|
ignore: $('#editIgnores textarea').val().split('\n')
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.setAPIKey = function (cfg) {
|
2016-05-26 09:25:34 +02:00
|
|
|
$http.get(urlbase + '/svc/random/string?length=32').success(function (data) {
|
|
|
|
cfg.apiKey = data.random;
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.showURPreview = function () {
|
2015-11-30 14:27:05 +01:00
|
|
|
$('#settings').modal('hide')
|
|
|
|
.one('hidden.bs.modal', function() {
|
|
|
|
$('#urPreview').modal()
|
|
|
|
.one('hidden.bs.modal', function () {
|
|
|
|
$('#settings').modal();
|
|
|
|
});
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.acceptUR = function () {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.options.urAccepted = 1000; // Larger than the largest existing report version
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
$('#ur').modal('hide');
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.declineUR = function () {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.options.urAccepted = -1;
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
$('#ur').modal('hide');
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.showNeed = function (folder) {
|
|
|
|
$scope.neededFolder = folder;
|
|
|
|
refreshNeed(folder);
|
2014-11-28 19:39:33 +01:00
|
|
|
$('#needed').modal().on('hidden.bs.modal', function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.neededFolder = undefined;
|
|
|
|
$scope.needed = undefined;
|
2015-04-25 23:53:44 +02:00
|
|
|
$scope.neededTotal = 0;
|
|
|
|
$scope.neededCurrentPage = 1;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
$scope.showFailed = function (folder) {
|
2016-03-11 10:48:46 +01:00
|
|
|
$scope.failedCurrent = $scope.failed[folder];
|
2015-06-26 14:22:52 +02:00
|
|
|
$('#failed').modal().on('hidden.bs.modal', function () {
|
|
|
|
$scope.failedCurrent = undefined;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.hasFailedFiles = function (folder) {
|
|
|
|
if (!$scope.failed[folder]) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-03-11 10:48:46 +01:00
|
|
|
if ($scope.failed[folder].length === 0) {
|
2015-06-26 14:22:52 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-03-11 10:48:46 +01:00
|
|
|
return true;
|
2015-06-26 14:22:52 +02:00
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.override = function (folder) {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + "/db/override?folder=" + encodeURIComponent(folder));
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
$scope.about = function () {
|
|
|
|
$('#about').modal('show');
|
|
|
|
};
|
|
|
|
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.advanced = function () {
|
2015-07-09 22:58:58 +02:00
|
|
|
$scope.advancedConfig = angular.copy($scope.config);
|
2015-07-03 14:07:38 +02:00
|
|
|
$('#advanced').modal('show');
|
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.showReportPreview = function () {
|
|
|
|
$scope.reportPreview = true;
|
|
|
|
};
|
|
|
|
|
2015-02-11 19:52:59 +01:00
|
|
|
$scope.rescanAllFolders = function () {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + "/db/scan");
|
2015-02-11 19:52:59 +01:00
|
|
|
};
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.rescanFolder = function (folder) {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + "/db/scan?folder=" + encodeURIComponent(folder));
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 20:23:06 +01:00
|
|
|
$scope.bumpFile = function (folder, file) {
|
2015-04-25 23:53:44 +02:00
|
|
|
var url = urlbase + "/db/prio?folder=" + encodeURIComponent(folder) + "&file=" + encodeURIComponent(file);
|
|
|
|
// In order to get the right view of data in the response.
|
|
|
|
url += "&page=" + $scope.neededCurrentPage;
|
|
|
|
url += "&perpage=" + $scope.neededPageSize;
|
|
|
|
$http.post(url).success(function (data) {
|
2016-03-11 10:48:46 +01:00
|
|
|
if ($scope.neededFolder === folder) {
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 20:23:06 +01:00
|
|
|
console.log("bumpFile", folder, data);
|
2015-04-25 23:53:44 +02:00
|
|
|
parseNeeded(data);
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 20:23:06 +01:00
|
|
|
}
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 20:23:06 +01:00
|
|
|
};
|
|
|
|
|
2015-04-09 11:32:54 +02:00
|
|
|
$scope.versionString = function () {
|
|
|
|
if (!$scope.version.version) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
var os = {
|
|
|
|
'darwin': 'Mac OS X',
|
|
|
|
'dragonfly': 'DragonFly BSD',
|
|
|
|
'freebsd': 'FreeBSD',
|
|
|
|
'openbsd': 'OpenBSD',
|
|
|
|
'netbsd': 'NetBSD',
|
|
|
|
'linux': 'Linux',
|
|
|
|
'windows': 'Windows',
|
2016-03-11 10:48:46 +01:00
|
|
|
'solaris': 'Solaris'
|
2015-04-10 12:45:47 +02:00
|
|
|
}[$scope.version.os] || $scope.version.os;
|
2015-04-09 11:32:54 +02:00
|
|
|
|
|
|
|
var arch ={
|
|
|
|
'386': '32 bit',
|
|
|
|
'amd64': '64 bit',
|
2015-04-10 12:45:47 +02:00
|
|
|
'arm': 'ARM',
|
2016-01-01 21:19:15 +01:00
|
|
|
'arm64': 'AArch64',
|
|
|
|
'ppc64': 'PowerPC',
|
2016-03-11 10:48:46 +01:00
|
|
|
'ppc64le': 'PowerPC (LE)'
|
2015-04-10 12:45:47 +02:00
|
|
|
}[$scope.version.arch] || $scope.version.arch;
|
2015-04-09 11:32:54 +02:00
|
|
|
|
|
|
|
return $scope.version.version + ', ' + os + ' (' + arch + ')';
|
|
|
|
};
|
|
|
|
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.inputTypeFor = function (key, value) {
|
|
|
|
if (key.substr(0, 1) === '_') {
|
|
|
|
return 'skip';
|
|
|
|
}
|
2016-01-27 15:41:39 +01:00
|
|
|
if (value === null) {
|
|
|
|
return 'null';
|
|
|
|
}
|
2015-07-03 14:07:38 +02:00
|
|
|
if (typeof value === 'number') {
|
|
|
|
return 'number';
|
|
|
|
}
|
|
|
|
if (typeof value === 'boolean') {
|
|
|
|
return 'checkbox';
|
|
|
|
}
|
|
|
|
if (typeof value === 'object') {
|
|
|
|
return 'skip';
|
|
|
|
}
|
|
|
|
return 'text';
|
|
|
|
};
|
|
|
|
|
2016-03-27 08:40:50 +02:00
|
|
|
$scope.themeName = function (theme) {
|
|
|
|
return theme.replace('-', ' ').replace(/(?:^|\s)\S/g, function (a) {
|
|
|
|
return a.toUpperCase();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-11-28 19:39:33 +01:00
|
|
|
// pseudo main. called on all definitions assigned
|
|
|
|
initController();
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|