gui: Reformat HTML & JS for consistent white space

(white space only change)
This commit is contained in:
Jakob Borg 2018-12-25 14:26:46 +01:00
parent 41469c5393
commit 0ac6ea6f1e
27 changed files with 252 additions and 246 deletions

View File

@ -156,7 +156,7 @@ function buildTree(children) {
children: [] children: []
} }
$.each(children, function(path, data) { $.each(children, function (path, data) {
var parts = path.split('/'); var parts = path.split('/');
var name = parts.splice(-1)[0]; var name = parts.splice(-1)[0];
@ -214,26 +214,26 @@ function unitPrefixed(input, binary) {
if (input > factor * factor * factor * factor * 1000) { if (input > factor * factor * factor * factor * 1000) {
// Don't show any decimals for more than 4 digits // Don't show any decimals for more than 4 digits
input /= factor * factor * factor * factor; input /= factor * factor * factor * factor;
return input.toLocaleString(undefined, {maximumFractionDigits: 0}) + ' T' + i; return input.toLocaleString(undefined, { maximumFractionDigits: 0 }) + ' T' + i;
} }
// Show 3 significant digits (e.g. 123T or 2.54T) // Show 3 significant digits (e.g. 123T or 2.54T)
if (input > factor * factor * factor * factor) { if (input > factor * factor * factor * factor) {
input /= factor * factor * factor * factor; input /= factor * factor * factor * factor;
return input.toLocaleString(undefined, {maximumSignificantDigits: 3}) + ' T' + i; return input.toLocaleString(undefined, { maximumSignificantDigits: 3 }) + ' T' + i;
} }
if (input > factor * factor * factor) { if (input > factor * factor * factor) {
input /= factor * factor * factor; input /= factor * factor * factor;
if (binary && input >= 1000) { if (binary && input >= 1000) {
return input.toLocaleString(undefined, {maximumFractionDigits: 0}) + ' G' + i; return input.toLocaleString(undefined, { maximumFractionDigits: 0 }) + ' G' + i;
} }
return input.toLocaleString(undefined, {maximumSignificantDigits: 3}) + ' G' + i; return input.toLocaleString(undefined, { maximumSignificantDigits: 3 }) + ' G' + i;
} }
if (input > factor * factor) { if (input > factor * factor) {
input /= factor * factor; input /= factor * factor;
if (binary && input >= 1000) { if (binary && input >= 1000) {
return input.toLocaleString(undefined, {maximumFractionDigits: 0}) + ' M' + i; return input.toLocaleString(undefined, { maximumFractionDigits: 0 }) + ' M' + i;
} }
return input.toLocaleString(undefined, {maximumSignificantDigits: 3}) + ' M' + i; return input.toLocaleString(undefined, { maximumSignificantDigits: 3 }) + ' M' + i;
} }
if (input > factor) { if (input > factor) {
input /= factor; input /= factor;
@ -242,9 +242,9 @@ function unitPrefixed(input, binary) {
prefix = ' K'; prefix = ' K';
} }
if (binary && input >= 1000) { if (binary && input >= 1000) {
return input.toLocaleString(undefined, {maximumFractionDigits: 0}) + prefix + i; return input.toLocaleString(undefined, { maximumFractionDigits: 0 }) + prefix + i;
} }
return input.toLocaleString(undefined, {maximumSignificantDigits: 3}) + prefix + i; return input.toLocaleString(undefined, { maximumSignificantDigits: 3 }) + prefix + i;
} }
return Math.round(input).toLocaleString() + ' '; return Math.round(input).toLocaleString() + ' ';
}; };

View File

@ -1,13 +1,13 @@
<modal id="about" status="info" icon="far fa-heart" heading="{{'About' | translate}}" large="yes" closeable="yes"> <modal id="about" status="info" icon="far fa-heart" heading="{{'About' | translate}}" large="yes" closeable="yes">
<div class="modal-body"> <div class="modal-body">
<h1 class="text-center"> <h1 class="text-center">
<img alt="Syncthing" src="assets/img/logo-horizontal.svg" style="vertical-align: -16px" height="100" width="366"/> <img alt="Syncthing" src="assets/img/logo-horizontal.svg" style="vertical-align: -16px" height="100" width="366" />
<br/> <br />
<small>{{versionString()}}</small> <small>{{versionString()}}</small>
<br/> <br />
<small><i>"{{version.codename}}"</i></small> <small><i>"{{version.codename}}"</i></small>
</h1> </h1>
<hr/> <hr />
<p translate>Copyright &copy; 2014-2017 the following Contributors:</p> <p translate>Copyright &copy; 2014-2017 the following Contributors:</p>
<div class="row"> <div class="row">
@ -15,7 +15,7 @@
Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Andrew Dunham, Andrew Rabert, Andrey D, Antoine Lamielle, Aranjedeath, Arthur Axel fREW Schmidt, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, Colin Kennedy, Cromefire_, Dale Visser, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Han Boetes, Harrison Jones, Heiko Zuerker, Iain Barnett, Ian Johnson, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonathan Cross, Jose Manuel Delicado, Jörg Thalheim, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matic Potočnik, Matt Burke, Matteo Ruina, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Tilli, Mike Boone, MikeLund, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Niels Peter Roest, Nils Jakobi, NoLooseEnds, Oyebanji Jacob Mayowa, Pascal Jungblut, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Richard Hartmann, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tomas Cerveny, Tommy Thorn, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, chucic, derekriemer, desbma, janost, jaseg, klemens, marco-m, perewa, rubenbe, wangguoliang, xjtdy888, 佛跳墙 Jakob Borg, Audrius Butkevicius, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Andrew Dunham, Andrew Rabert, Andrey D, Antoine Lamielle, Aranjedeath, Arthur Axel fREW Schmidt, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benno Fünfstück, Benny Ng, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Chris Tonkinson, Colin Kennedy, Cromefire_, Dale Visser, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dmitry Saveliev, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Graham Miln, Han Boetes, Harrison Jones, Heiko Zuerker, Iain Barnett, Ian Johnson, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonathan Cross, Jose Manuel Delicado, Jörg Thalheim, Karol Różycki, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matic Potočnik, Matt Burke, Matteo Ruina, Max Schulze, MaximAL, Maxime Thirouin, Michael Jephcote, Michael Tilli, Mike Boone, MikeLund, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Niels Peter Roest, Nils Jakobi, NoLooseEnds, Oyebanji Jacob Mayowa, Pascal Jungblut, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Richard Hartmann, Robert Carosi, Roman Zaynetdinov, Ross Smith II, Sacheendra Talluri, Scott Klupfel, Sly_tom_cat, Stefan Kuntz, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Nygren, Tobias Tom, Tomas Cerveny, Tommy Thorn, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, chucic, derekriemer, desbma, janost, jaseg, klemens, marco-m, perewa, rubenbe, wangguoliang, xjtdy888, 佛跳墙
</div> </div>
</div> </div>
<hr/> <hr />
<p translate>Syncthing includes the following software or portions thereof:</p> <p translate>Syncthing includes the following software or portions thereof:</p>
<ul class="list-unstyled two-columns" id="copyright-notices"> <ul class="list-unstyled two-columns" id="copyright-notices">

View File

@ -8,7 +8,7 @@
<div class="col-md-offset-2 col-md-8"> <div class="col-md-offset-2 col-md-8">
<div class="panel panel-default"> <div class="panel panel-default">
<div translate class="panel-body"> <div translate class="panel-body">
Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity. Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,7 +10,7 @@ angular.module('syncthing.core')
.filter('duration', function () { .filter('duration', function () {
'use strict'; 'use strict';
var SECONDS_IN = {"d": 86400, "h": 3600, "m": 60, "s": 1}; var SECONDS_IN = { "d": 86400, "h": 3600, "m": 60, "s": 1 };
return function (input, precision) { return function (input, precision) {
var result = ""; var result = "";
if (!precision) { if (!precision) {
@ -18,7 +18,7 @@ angular.module('syncthing.core')
} }
input = parseInt(input, 10); input = parseInt(input, 10);
for (var k in SECONDS_IN) { for (var k in SECONDS_IN) {
var t = (input/SECONDS_IN[k] | 0); // Math.floor var t = (input / SECONDS_IN[k] | 0); // Math.floor
if (t > 0) { if (t > 0) {
result += " " + t + k; result += " " + t + k;

View File

@ -7,7 +7,7 @@ angular.module('syncthing.core')
var lastID = 0; var lastID = 0;
var self = this; var self = this;
function successFn (data) { function successFn(data) {
// When Syncthing restarts while the long polling connection is in // When Syncthing restarts while the long polling connection is in
// progress the browser on some platforms returns a 200 (since the // progress the browser on some platforms returns a 200 (since the
// headers has been flushed with the return code 200), with no data. // headers has been flushed with the return code 200), with no data.
@ -38,7 +38,7 @@ angular.module('syncthing.core')
.error(errorFn); .error(errorFn);
} }
function errorFn (dummy) { function errorFn(dummy) {
$rootScope.$broadcast(self.OFFLINE); $rootScope.$broadcast(self.OFFLINE);
$timeout(function () { $timeout(function () {
@ -51,35 +51,35 @@ angular.module('syncthing.core')
angular.extend(self, { angular.extend(self, {
// emitted by this // emitted by this
ONLINE: 'UIOnline', ONLINE: 'UIOnline',
OFFLINE: 'UIOffline', OFFLINE: 'UIOffline',
// emitted by syncthing process // emitted by syncthing process
CONFIG_SAVED: 'ConfigSaved', // Emitted after the config has been saved by the user or by Syncthing itself CONFIG_SAVED: 'ConfigSaved', // Emitted after the config has been saved by the user or by Syncthing itself
DEVICE_CONNECTED: 'DeviceConnected', // Generated each time a connection to a device has been established DEVICE_CONNECTED: 'DeviceConnected', // Generated each time a connection to a device has been established
DEVICE_DISCONNECTED: 'DeviceDisconnected', // Generated each time a connection to a device has been terminated DEVICE_DISCONNECTED: 'DeviceDisconnected', // Generated each time a connection to a device has been terminated
DEVICE_DISCOVERED: 'DeviceDiscovered', // Emitted when a new device is discovered using local discovery DEVICE_DISCOVERED: 'DeviceDiscovered', // Emitted when a new device is discovered using local discovery
DEVICE_REJECTED: 'DeviceRejected', // Emitted when there is a connection from a device we are not configured to talk to DEVICE_REJECTED: 'DeviceRejected', // Emitted when there is a connection from a device we are not configured to talk to
DEVICE_PAUSED: 'DevicePaused', // Emitted when a device has been paused DEVICE_PAUSED: 'DevicePaused', // Emitted when a device has been paused
DEVICE_RESUMED: 'DeviceResumed', // Emitted when a device has been resumed DEVICE_RESUMED: 'DeviceResumed', // Emitted when a device has been resumed
DOWNLOAD_PROGRESS: 'DownloadProgress', // Emitted during file downloads for each folder for each file DOWNLOAD_PROGRESS: 'DownloadProgress', // Emitted during file downloads for each folder for each file
FOLDER_COMPLETION: 'FolderCompletion', //Emitted when the local or remote contents for a folder changes FOLDER_COMPLETION: 'FolderCompletion', //Emitted when the local or remote contents for a folder changes
FOLDER_REJECTED: 'FolderRejected', // Emitted when a device sends index information for a folder we do not have, or have but do not share with the device in question FOLDER_REJECTED: 'FolderRejected', // Emitted when a device sends index information for a folder we do not have, or have but do not share with the device in question
FOLDER_SUMMARY: 'FolderSummary', // Emitted when folder contents have changed locally FOLDER_SUMMARY: 'FolderSummary', // Emitted when folder contents have changed locally
ITEM_FINISHED: 'ItemFinished', // Generated when Syncthing ends synchronizing a file to a newer version ITEM_FINISHED: 'ItemFinished', // Generated when Syncthing ends synchronizing a file to a newer version
ITEM_STARTED: 'ItemStarted', // Generated when Syncthing begins synchronizing a file to a newer version ITEM_STARTED: 'ItemStarted', // Generated when Syncthing begins synchronizing a file to a newer version
LOCAL_INDEX_UPDATED: 'LocalIndexUpdated', // Generated when the local index information has changed, due to synchronizing one or more items from the cluster or discovering local changes during a scan LOCAL_INDEX_UPDATED: 'LocalIndexUpdated', // Generated when the local index information has changed, due to synchronizing one or more items from the cluster or discovering local changes during a scan
REMOTE_INDEX_UPDATED: 'RemoteIndexUpdated', // Generated each time new index information is received from a device REMOTE_INDEX_UPDATED: 'RemoteIndexUpdated', // Generated each time new index information is received from a device
STARTING: 'Starting', // Emitted exactly once, when Syncthing starts, before parsing configuration etc STARTING: 'Starting', // Emitted exactly once, when Syncthing starts, before parsing configuration etc
STARTUP_COMPLETED: 'StartupCompleted', // Emitted exactly once, when initialization is complete and Syncthing is ready to start exchanging data with other devices STARTUP_COMPLETED: 'StartupCompleted', // Emitted exactly once, when initialization is complete and Syncthing is ready to start exchanging data with other devices
STATE_CHANGED: 'StateChanged', // Emitted when a folder changes state STATE_CHANGED: 'StateChanged', // Emitted when a folder changes state
FOLDER_ERRORS: 'FolderErrors', // Emitted when a folder has errors preventing a full sync FOLDER_ERRORS: 'FolderErrors', // Emitted when a folder has errors preventing a full sync
FOLDER_SCAN_PROGRESS: 'FolderScanProgress', // Emitted every ScanProgressIntervalS seconds, indicating how far into the scan it is at. FOLDER_SCAN_PROGRESS: 'FolderScanProgress', // Emitted every ScanProgressIntervalS seconds, indicating how far into the scan it is at.
FOLDER_PAUSED: 'FolderPaused', // Emitted when a folder is paused FOLDER_PAUSED: 'FolderPaused', // Emitted when a folder is paused
FOLDER_RESUMED: 'FolderResumed', // Emitted when a folder is resumed FOLDER_RESUMED: 'FolderResumed', // Emitted when a folder is resumed
start: function() { start: function () {
$http.get(urlbase + '/events?limit=1') $http.get(urlbase + '/events?limit=1')
.success(successFn) .success(successFn)
.error(errorFn); .error(errorFn);

View File

@ -4,12 +4,12 @@ angular.module('syncthing.core')
return { return {
restrict: 'EA', restrict: 'EA',
template: template:
'<a ng-if="visible" href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="fas fa-globe"></span><span class="hidden-xs">&nbsp;{{localesNames[currentLocale] || "English"}}</span> <span class="caret"></span></a>'+ '<a ng-if="visible" href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="fas fa-globe"></span><span class="hidden-xs">&nbsp;{{localesNames[currentLocale] || "English"}}</span> <span class="caret"></span></a>' +
'<ul ng-if="visible" class="dropdown-menu">'+ '<ul ng-if="visible" class="dropdown-menu">' +
'<li ng-repeat="name in localesNamesInvKeys" ng-class="{active: localesNamesInv[name]==currentLocale}">'+ '<li ng-repeat="name in localesNamesInvKeys" ng-class="{active: localesNamesInv[name]==currentLocale}">' +
'<a href="#" data-ng-click="changeLanguage(localesNamesInv[name])">{{name}}</a>'+ '<a href="#" data-ng-click="changeLanguage(localesNamesInv[name])">{{name}}</a>' +
'</li>'+ '</li>' +
'</ul>', '</ul>',
link: function ($scope) { link: function ($scope) {
var availableLocales = LocaleService.getAvailableLocales(); var availableLocales = LocaleService.getAvailableLocales();
@ -27,12 +27,12 @@ angular.module('syncthing.core')
} }
} }
$scope.localesNames = availableLocaleNames; $scope.localesNames = availableLocaleNames;
var invert = function (obj) { var invert = function (obj) {
var new_obj = {}; var new_obj = {};
for (var prop in obj) { for (var prop in obj) {
if(obj.hasOwnProperty(prop)) { if (obj.hasOwnProperty(prop)) {
new_obj[obj[prop]] = prop; new_obj[obj[prop]] = prop;
} }
} }
@ -40,7 +40,7 @@ angular.module('syncthing.core')
}; };
$scope.localesNamesInv = invert($scope.localesNames); $scope.localesNamesInv = invert($scope.localesNames);
$scope.localesNamesInvKeys = Object.keys($scope.localesNamesInv).sort(); $scope.localesNamesInvKeys = Object.keys($scope.localesNamesInv).sort();
$scope.visible = $scope.localesNames && $scope.localesNames['en']; $scope.visible = $scope.localesNames && $scope.localesNames['en'];
// using $watch cause LocaleService.currentLocale will be change after receive async query accepted-languages // using $watch cause LocaleService.currentLocale will be change after receive async query accepted-languages

View File

@ -51,7 +51,7 @@ angular.module('syncthing.core')
savedLang = _localStorage[_SYNLANG]; savedLang = _localStorage[_SYNLANG];
} }
if(params.lang) { if (params.lang) {
useLocale(params.lang, true); useLocale(params.lang, true);
} else if (savedLang) { } else if (savedLang) {
useLocale(savedLang); useLocale(savedLang);
@ -99,8 +99,8 @@ angular.module('syncthing.core')
function useLocale(language, save2Storage) { function useLocale(language, save2Storage) {
if (language) { if (language) {
$translate.use(language).then(function () { $translate.use(language).then(function () {
if (save2Storage && _localStorage) if (save2Storage && _localStorage)
_localStorage[_SYNLANG] = language; _localStorage[_SYNLANG] = language;
}); });
} }
@ -109,10 +109,10 @@ angular.module('syncthing.core')
return { return {
autoConfigLocale: autoConfigLocale, autoConfigLocale: autoConfigLocale,
useLocale: useLocale, useLocale: useLocale,
getCurrentLocale: function() { return $translate.use() }, getCurrentLocale: function () { return $translate.use() },
getAvailableLocales: function() { return _availableLocales }, getAvailableLocales: function () { return _availableLocales },
// langPrettyprint comes from an included global // langPrettyprint comes from an included global
getLocalesDisplayNames: function() { return langPrettyprint } getLocalesDisplayNames: function () { return langPrettyprint }
} }
}]; }];

View File

@ -11,4 +11,4 @@ angular.module('syncthing.core')
}; };
} }
}; };
}); });

View File

@ -23,9 +23,9 @@
<div class="panel-body"> <div class="panel-body">
<p translate>Automatic upgrade now offers the choice between stable releases and release candidates.</p> <p translate>Automatic upgrade now offers the choice between stable releases and release candidates.</p>
<p> <p>
<span translate>Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.</span> <span translate>Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.</span>
<span translate>Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.</span> <span translate>Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.</span>
<span translate>You can read more about the two release channels at the link below.</span> <span translate>You can read more about the two release channels at the link below.</span>
</p> </p>
<p translate>You can change your choice at any time in the Settings dialog.</p> <p translate>You can change your choice at any time in the Settings dialog.</p>
<p><a href="https://docs.syncthing.net/users/releases.html"><span class="fas fa-info-circle"></span>&nbsp;<span translate>Learn more</span></a></p> <p><a href="https://docs.syncthing.net/users/releases.html"><span class="fas fa-info-circle"></span>&nbsp;<span translate>Learn more</span></a></p>
@ -51,7 +51,7 @@
<p translate>Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.</p> <p translate>Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.</p>
<p><a href="https://docs.syncthing.net/users/syncing.html#scanning"><span class="fas fa-info-circle"></span>&nbsp;<span translate>Learn more</span></a></p> <p><a href="https://docs.syncthing.net/users/syncing.html#scanning"><span class="fas fa-info-circle"></span>&nbsp;<span translate>Learn more</span></a></p>
<p> <p>
<span translate>Do you want to enable watching for changes for all your folders?</span><br/> <span translate>Do you want to enable watching for changes for all your folders?</span><br />
<span translate>Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.</span> <span translate>Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.</span>
</p> </p>
<p translate translate-value-syncthing-inotify="syncthing-inotify">Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.</p> <p translate translate-value-syncthing-inotify="syncthing-inotify">Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.</p>

View File

@ -15,9 +15,9 @@ angular.module('syncthing.core')
if (xdirArr.length > ydirArr.length) { if (xdirArr.length > ydirArr.length) {
return false; return false;
} }
return xdirArr.map(function(e, i) { return xdirArr.map(function (e, i) {
return xdirArr[i] === ydirArr[i]; return xdirArr[i] === ydirArr[i];
}).every(function(e) { return e }); }).every(function (e) { return e });
} }
scope.folderPathErrors.isSub = false; scope.folderPathErrors.isSub = false;
@ -43,4 +43,4 @@ angular.module('syncthing.core')
}); });
} }
}; };
}); });

View File

@ -7,9 +7,9 @@ angular.module('syncthing.core')
} }
// Hard limit at two decimals // Hard limit at two decimals
if (input < 0.1) { if (input < 0.1) {
return input.toLocaleString(undefined, {maximumFractionDigits: 2}) + '%'; return input.toLocaleString(undefined, { maximumFractionDigits: 2 }) + '%';
} }
// "Soft" limit at two significant digits (e.g. 1.2%, not 1.27%) // "Soft" limit at two significant digits (e.g. 1.2%, not 1.27%)
return input.toLocaleString(undefined, {maximumSignificantDigits: 2}) + '%'; return input.toLocaleString(undefined, { maximumSignificantDigits: 2 }) + '%';
}; };
}); });

View File

@ -6,4 +6,4 @@ angular.module('syncthing.core')
$(element).popover(); $(element).popover();
} }
}; };
}); });

View File

@ -2,7 +2,7 @@ angular.module('syncthing.core')
.directive('selectOnClick', function ($window) { .directive('selectOnClick', function ($window) {
return { return {
link: function (scope, element, attrs) { link: function (scope, element, attrs) {
element.on('click', function() { element.on('click', function () {
var selection = $window.getSelection(); var selection = $window.getSelection();
var range = document.createRange(); var range = document.createRange();
range.selectNodeContents(element[0]); range.selectNodeContents(element[0]);
@ -11,4 +11,4 @@ angular.module('syncthing.core')
}); });
} }
}; };
}); });

View File

@ -1,6 +1,6 @@
angular.module('syncthing.core') angular.module('syncthing.core')
.config(function($locationProvider) { .config(function ($locationProvider) {
$locationProvider.html5Mode({enabled: true, requireBase: false}).hashPrefix('!'); $locationProvider.html5Mode({ enabled: true, requireBase: false }).hashPrefix('!');
}) })
.controller('SyncthingController', function ($scope, $http, $location, LocaleService, Events, $filter, $q, $compile, $timeout, $rootScope, $translate) { .controller('SyncthingController', function ($scope, $http, $location, LocaleService, Events, $filter, $q, $compile, $timeout, $rootScope, $translate) {
'use strict'; 'use strict';
@ -65,7 +65,7 @@ angular.module('syncthing.core')
rescanIntervalS: 3600, rescanIntervalS: 3600,
fsWatcherDelayS: 10, fsWatcherDelayS: 10,
fsWatcherEnabled: true, fsWatcherEnabled: true,
minDiskFree: {value: 1, unit: "%"}, minDiskFree: { value: 1, unit: "%" },
maxConflicts: 10, maxConflicts: 10,
fsync: true, fsync: true,
order: "random", order: "random",
@ -131,7 +131,7 @@ angular.module('syncthing.core')
} }
$scope.version = data; $scope.version = data;
$scope.version.isDevelopmentVersion = data.version.indexOf('-')>0; $scope.version.isDevelopmentVersion = data.version.indexOf('-') > 0;
}).error($scope.emitHTTPError); }).error($scope.emitHTTPError);
$http.get(urlbase + '/svc/report').success(function (data) { $http.get(urlbase + '/svc/report').success(function (data) {
@ -205,7 +205,7 @@ angular.module('syncthing.core')
// If a folder finished scanning, then refresh folder stats // If a folder finished scanning, then refresh folder stats
// to update last scan time. // to update last scan time.
if(data.from === 'scanning' && data.to === 'idle') { if (data.from === 'scanning' && data.to === 'idle') {
refreshFolderStats(); refreshFolderStats();
} }
} }
@ -342,7 +342,7 @@ angular.module('syncthing.core')
}); });
$scope.emitHTTPError = function (data, status, headers, config) { $scope.emitHTTPError = function (data, status, headers, config) {
$scope.$emit('HTTPError', {data: data, status: status, headers: headers, config: config}); $scope.$emit('HTTPError', { data: data, status: status, headers: headers, config: config });
}; };
var debouncedFuncs = {}; var debouncedFuncs = {};
@ -447,7 +447,7 @@ angular.module('syncthing.core')
}).error($scope.emitHTTPError); }).error($scope.emitHTTPError);
} }
function recalcLocalStateTotal () { function recalcLocalStateTotal() {
$scope.localStateTotal = { $scope.localStateTotal = {
bytes: 0, bytes: 0,
directories: 0, directories: 0,
@ -455,9 +455,9 @@ angular.module('syncthing.core')
}; };
for (var f in $scope.model) { for (var f in $scope.model) {
$scope.localStateTotal.bytes += $scope.model[f].localBytes; $scope.localStateTotal.bytes += $scope.model[f].localBytes;
$scope.localStateTotal.files += $scope.model[f].localFiles; $scope.localStateTotal.files += $scope.model[f].localFiles;
$scope.localStateTotal.directories += $scope.model[f].localDirectories; $scope.localStateTotal.directories += $scope.model[f].localDirectories;
} }
} }
@ -962,7 +962,7 @@ angular.module('syncthing.core')
var pendingFolders = 0; var pendingFolders = 0;
for (var i = 0; i < $scope.devices.length; i++) { for (var i = 0; i < $scope.devices.length; i++) {
var status = $scope.deviceStatus({ var status = $scope.deviceStatus({
deviceID:$scope.devices[i].deviceID deviceID: $scope.devices[i].deviceID
}); });
switch (status) { switch (status) {
case 'unknown': case 'unknown':
@ -996,7 +996,7 @@ angular.module('syncthing.core')
} }
// all used devices are paused except (this) one // all used devices are paused except (this) one
if (pauseCount === deviceCount-1) { if (pauseCount === deviceCount - 1) {
return 'pause'; return 'pause';
} }
@ -1085,11 +1085,11 @@ angular.module('syncthing.core')
$scope.logging = { $scope.logging = {
facilities: {}, facilities: {},
refreshFacilities: function() { refreshFacilities: function () {
$http.get(urlbase + '/system/debug').success(function (data) { $http.get(urlbase + '/system/debug').success(function (data) {
var facilities = {}; var facilities = {};
data.enabled = data.enabled || []; data.enabled = data.enabled || [];
$.each(data.facilities, function(key, value) { $.each(data.facilities, function (key, value) {
facilities[key] = { facilities[key] = {
description: value, description: value,
enabled: data.enabled.indexOf(key) > -1 enabled: data.enabled.indexOf(key) > -1
@ -1098,12 +1098,12 @@ angular.module('syncthing.core')
$scope.logging.facilities = facilities; $scope.logging.facilities = facilities;
}).error($scope.emitHTTPError); }).error($scope.emitHTTPError);
}, },
show: function() { show: function () {
$scope.logging.refreshFacilities(); $scope.logging.refreshFacilities();
$scope.logging.timer = $timeout($scope.logging.fetch); $scope.logging.timer = $timeout($scope.logging.fetch);
var textArea = $('#logViewerText'); var textArea = $('#logViewerText');
textArea.on("scroll", $scope.logging.onScroll); textArea.on("scroll", $scope.logging.onScroll);
$('#logViewer').modal().one('shown.bs.modal', function() { $('#logViewer').modal().one('shown.bs.modal', function () {
// Scroll to bottom. // Scroll to bottom.
textArea.scrollTop(textArea[0].scrollHeight); textArea.scrollTop(textArea[0].scrollHeight);
}).one('hidden.bs.modal', function () { }).one('hidden.bs.modal', function () {
@ -1113,17 +1113,17 @@ angular.module('syncthing.core')
$scope.logging.entries = []; $scope.logging.entries = [];
}); });
}, },
onFacilityChange: function(facility) { onFacilityChange: function (facility) {
var enabled = $scope.logging.facilities[facility].enabled; var enabled = $scope.logging.facilities[facility].enabled;
// Disable checkboxes while we're in flight. // Disable checkboxes while we're in flight.
$.each($scope.logging.facilities, function(key) { $.each($scope.logging.facilities, function (key) {
$scope.logging.facilities[key].enabled = null; $scope.logging.facilities[key].enabled = null;
}) })
$http.post(urlbase + '/system/debug?' + (enabled ? 'enable=':'disable=') + facility) $http.post(urlbase + '/system/debug?' + (enabled ? 'enable=' : 'disable=') + facility)
.success($scope.logging.refreshFacilities) .success($scope.logging.refreshFacilities)
.error($scope.emitHTTPError); .error($scope.emitHTTPError);
}, },
onScroll: function() { onScroll: function () {
var textArea = $('#logViewerText'); var textArea = $('#logViewerText');
var scrollTop = textArea.prop('scrollTop'); var scrollTop = textArea.prop('scrollTop');
var scrollHeight = textArea.prop('scrollHeight'); var scrollHeight = textArea.prop('scrollHeight');
@ -1134,14 +1134,14 @@ angular.module('syncthing.core')
timer: null, timer: null,
entries: [], entries: [],
paused: false, paused: false,
content: function() { content: function () {
var content = ""; var content = "";
$.each($scope.logging.entries, function (idx, entry) { $.each($scope.logging.entries, function (idx, entry) {
content += entry.when.split('.')[0].replace('T', ' ') + ' ' + entry.message + "\n"; content += entry.when.split('.')[0].replace('T', ' ') + ' ' + entry.message + "\n";
}); });
return content; return content;
}, },
fetch: function() { fetch: function () {
var textArea = $('#logViewerText'); var textArea = $('#logViewerText');
if ($scope.logging.paused) { if ($scope.logging.paused) {
if (!$scope.logging.timer) return; if (!$scope.logging.timer) return;
@ -1151,7 +1151,7 @@ angular.module('syncthing.core')
var last = null; var last = null;
if ($scope.logging.entries.length > 0) { if ($scope.logging.entries.length > 0) {
last = $scope.logging.entries[$scope.logging.entries.length-1].when; last = $scope.logging.entries[$scope.logging.entries.length - 1].when;
} }
$http.get(urlbase + '/system/log' + (last ? '?since=' + encodeURIComponent(last) : '')).success(function (data) { $http.get(urlbase + '/system/log' + (last ? '?since=' + encodeURIComponent(last) : '')).success(function (data) {
@ -1161,7 +1161,7 @@ angular.module('syncthing.core')
if (data.messages) { if (data.messages) {
$scope.logging.entries.push.apply($scope.logging.entries, data.messages); $scope.logging.entries.push.apply($scope.logging.entries, data.messages);
// Wait for the text area to be redrawn, adding new lines, and then scroll to bottom. // Wait for the text area to be redrawn, adding new lines, and then scroll to bottom.
$timeout(function() { $timeout(function () {
textArea.scrollTop(textArea[0].scrollHeight); textArea.scrollTop(textArea[0].scrollHeight);
}); });
} }
@ -1191,7 +1191,7 @@ angular.module('syncthing.core')
settingsModal.off('hide.bs.modal'); settingsModal.off('hide.bs.modal');
}).on('hide.bs.modal', function (e) { }).on('hide.bs.modal', function (e) {
if ($scope.settingsModified()) { if ($scope.settingsModified()) {
$("#discard-changes-confirmation").modal().one('hidden.bs.modal', function() { $("#discard-changes-confirmation").modal().one('hidden.bs.modal', function () {
if (!$scope.settingsModified()) { if (!$scope.settingsModified()) {
settingsModal.modal('hide'); settingsModal.modal('hide');
} }
@ -1220,7 +1220,7 @@ angular.module('syncthing.core')
}).error($scope.emitHTTPError); }).error($scope.emitHTTPError);
}; };
$scope.urVersions = function() { $scope.urVersions = function () {
var result = []; var result = [];
if ($scope.system) { if ($scope.system) {
for (var i = $scope.system.urVersionMax; i >= 2; i--) { for (var i = $scope.system.urVersionMax; i >= 2; i--) {
@ -1357,7 +1357,7 @@ angular.module('syncthing.core')
$scope.currentDevice = $.extend({}, deviceCfg); $scope.currentDevice = $.extend({}, deviceCfg);
$scope.editingExisting = true; $scope.editingExisting = true;
$scope.willBeReintroducedBy = undefined; $scope.willBeReintroducedBy = undefined;
if (deviceCfg.introducedBy) { if (deviceCfg.introducedBy) {
var introducerDevice = $scope.findDevice(deviceCfg.introducedBy); var introducerDevice = $scope.findDevice(deviceCfg.introducedBy);
if (introducerDevice && introducerDevice.introducer) { if (introducerDevice && introducerDevice.introducer) {
$scope.willBeReintroducedBy = $scope.deviceName(introducerDevice); $scope.willBeReintroducedBy = $scope.deviceName(introducerDevice);
@ -1619,7 +1619,7 @@ angular.module('syncthing.core')
}; };
$scope.loadFormIntoScope = function (form) { $scope.loadFormIntoScope = function (form) {
console.log('loadFormIntoScope',form.$name); console.log('loadFormIntoScope', form.$name);
switch (form.$name) { switch (form.$name) {
case 'deviceEditor': case 'deviceEditor':
$scope.deviceEditor = form; $scope.deviceEditor = form;
@ -1707,16 +1707,16 @@ angular.module('syncthing.core')
$scope.editFolderModal(); $scope.editFolderModal();
}; };
$scope.selectAllDevices = function() { $scope.selectAllDevices = function () {
var devices = $scope.otherDevices(); var devices = $scope.otherDevices();
for (var i = 0; i < devices.length; i++){ for (var i = 0; i < devices.length; i++) {
$scope.currentFolder.selectedDevices[devices[i].deviceID] = true; $scope.currentFolder.selectedDevices[devices[i].deviceID] = true;
} }
}; };
$scope.deSelectAllDevices = function() { $scope.deSelectAllDevices = function () {
var devices = $scope.otherDevices(); var devices = $scope.otherDevices();
for (var i = 0; i < devices.length; i++){ for (var i = 0; i < devices.length; i++) {
$scope.currentFolder.selectedDevices[devices[i].deviceID] = false; $scope.currentFolder.selectedDevices[devices[i].deviceID] = false;
} }
}; };
@ -1908,7 +1908,7 @@ angular.module('syncthing.core')
errors: null, errors: null,
filters: {}, filters: {},
massAction: function (name, action) { massAction: function (name, action) {
$.each($scope.restoreVersions.versions, function(key) { $.each($scope.restoreVersions.versions, function (key) {
if (key.startsWith(name + '/') && (!$scope.restoreVersions.filters.text || key.indexOf($scope.restoreVersions.filters.text) > -1)) { if (key.startsWith(name + '/') && (!$scope.restoreVersions.filters.text || key.indexOf($scope.restoreVersions.filters.text) > -1)) {
if (action == 'unset') { if (action == 'unset') {
delete $scope.restoreVersions.selections[key]; delete $scope.restoreVersions.selections[key];
@ -1916,7 +1916,7 @@ angular.module('syncthing.core')
} }
var availableVersions = []; var availableVersions = [];
$.each($scope.restoreVersions.filterVersions($scope.restoreVersions.versions[key]), function(idx, version) { $.each($scope.restoreVersions.filterVersions($scope.restoreVersions.versions[key]), function (idx, version) {
availableVersions.push(version.versionTime); availableVersions.push(version.versionTime);
}) })
@ -1931,8 +1931,8 @@ angular.module('syncthing.core')
} }
}); });
}, },
filterVersions: function(versions) { filterVersions: function (versions) {
var filteredVersions = []; var filteredVersions = [];
$.each(versions, function (idx, version) { $.each(versions, function (idx, version) {
if (moment(version.versionTime).isBetween($scope.restoreVersions.filters['start'], $scope.restoreVersions.filters['end'], null, '[]')) { if (moment(version.versionTime).isBetween($scope.restoreVersions.filters['start'], $scope.restoreVersions.filters['end'], null, '[]')) {
filteredVersions.push(version); filteredVersions.push(version);
@ -1940,9 +1940,9 @@ angular.module('syncthing.core')
}); });
return filteredVersions; return filteredVersions;
}, },
selectionCount: function() { selectionCount: function () {
var count = 0; var count = 0;
$.each($scope.restoreVersions.selections, function(key, value) { $.each($scope.restoreVersions.selections, function (key, value) {
if (value) { if (value) {
count++; count++;
} }
@ -1950,12 +1950,12 @@ angular.module('syncthing.core')
return count; return count;
}, },
restore: function() { restore: function () {
$scope.restoreVersions.tree.clear(); $scope.restoreVersions.tree.clear();
$scope.restoreVersions.tree = null; $scope.restoreVersions.tree = null;
$scope.restoreVersions.versions = null; $scope.restoreVersions.versions = null;
var selections = {}; var selections = {};
$.each($scope.restoreVersions.selections, function(key, value) { $.each($scope.restoreVersions.selections, function (key, value) {
if (value) { if (value) {
selections[key] = value; selections[key] = value;
} }
@ -1970,7 +1970,7 @@ angular.module('syncthing.core')
} }
}); });
}, },
show: function(folder) { show: function (folder) {
$scope.restoreVersions.folder = folder; $scope.restoreVersions.folder = folder;
var closed = false; var closed = false;
@ -1978,14 +1978,14 @@ angular.module('syncthing.core')
$('#restoreVersions').modal().one('hidden.bs.modal', function () { $('#restoreVersions').modal().one('hidden.bs.modal', function () {
closed = true; closed = true;
resetRestoreVersions(); resetRestoreVersions();
}).one('shown.bs.modal', function() { }).one('shown.bs.modal', function () {
modalShown.resolve(); modalShown.resolve();
}); });
var dataReceived = $http.get(urlbase + '/folder/versions?folder=' + encodeURIComponent($scope.restoreVersions.folder)) var dataReceived = $http.get(urlbase + '/folder/versions?folder=' + encodeURIComponent($scope.restoreVersions.folder))
.success(function (data) { .success(function (data) {
$.each(data, function(key, values) { $.each(data, function (key, values) {
$.each(values, function(idx, value) { $.each(values, function (idx, value) {
value.modTime = new Date(value.modTime); value.modTime = new Date(value.modTime);
value.versionTime = new Date(value.versionTime); value.versionTime = new Date(value.versionTime);
}); });
@ -1994,8 +1994,8 @@ angular.module('syncthing.core')
$scope.restoreVersions.versions = data; $scope.restoreVersions.versions = data;
}); });
$q.all([dataReceived, modalShown.promise]).then(function() { $q.all([dataReceived, modalShown.promise]).then(function () {
$timeout(function(){ $timeout(function () {
if (closed) { if (closed) {
resetRestoreVersions(); resetRestoreVersions();
return; return;
@ -2020,7 +2020,7 @@ angular.module('syncthing.core')
}, },
debugLevel: 2, debugLevel: 2,
source: buildTree($scope.restoreVersions.versions), source: buildTree($scope.restoreVersions.versions),
renderColumns: function(event, data) { renderColumns: function (event, data) {
var node = data.node, var node = data.node,
$tdList = $(node.tr).find(">td"), $tdList = $(node.tr).find(">td"),
template; template;
@ -2039,7 +2039,7 @@ angular.module('syncthing.core')
); );
// Force angular to redraw. // Force angular to redraw.
$timeout(function() { $timeout(function () {
$scope.$apply(); $scope.$apply();
}); });
} }
@ -2050,8 +2050,8 @@ angular.module('syncthing.core')
date; date;
// Find version window. // Find version window.
$.each($scope.restoreVersions.versions, function(key) { $.each($scope.restoreVersions.versions, function (key) {
$.each($scope.restoreVersions.versions[key], function(idx, version) { $.each($scope.restoreVersions.versions[key], function (idx, version) {
date = moment(version.versionTime); date = moment(version.versionTime);
if (date.isBefore(minDate)) { if (date.isBefore(minDate)) {
minDate = date; minDate = date;
@ -2066,17 +2066,17 @@ angular.module('syncthing.core')
$scope.restoreVersions.filters['end'] = maxDate; $scope.restoreVersions.filters['end'] = maxDate;
var ranges = { var ranges = {
'All time': [minDate, maxDate], 'All time': [minDate, maxDate],
'Today': [moment(), moment()], 'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'Last 7 Days': [moment().subtract(6, 'days'), moment()], 'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'Last 30 Days': [moment().subtract(29, 'days'), moment()], 'Last 30 Days': [moment().subtract(29, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')], 'This Month': [moment().startOf('month'), moment().endOf('month')],
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
}; };
// Filter out invalid ranges. // Filter out invalid ranges.
$.each(ranges, function(key, range) { $.each(ranges, function (key, range) {
if (!range[0].isBetween(minDate, maxDate, null, '[]') && !range[1].isBetween(minDate, maxDate, null, '[]')) { if (!range[0].isBetween(minDate, maxDate, null, '[]') && !range[1].isBetween(minDate, maxDate, null, '[]')) {
delete ranges[key]; delete ranges[key];
} }
@ -2097,12 +2097,12 @@ angular.module('syncthing.core')
locale: { locale: {
format: 'YYYY/MM/DD HH:mm:ss', format: 'YYYY/MM/DD HH:mm:ss',
} }
}).on('apply.daterangepicker', function(ev, picker) { }).on('apply.daterangepicker', function (ev, picker) {
$scope.restoreVersions.filters['start'] = picker.startDate; $scope.restoreVersions.filters['start'] = picker.startDate;
$scope.restoreVersions.filters['end'] = picker.endDate; $scope.restoreVersions.filters['end'] = picker.endDate;
// Events for this UI element are not managed by angular. // Events for this UI element are not managed by angular.
// Force angular to wake up. // Force angular to wake up.
$timeout(function() { $timeout(function () {
$scope.$apply(); $scope.$apply();
}); });
}); });
@ -2113,7 +2113,7 @@ angular.module('syncthing.core')
} }
resetRestoreVersions(); resetRestoreVersions();
$scope.$watchCollection('restoreVersions.filters', function() { $scope.$watchCollection('restoreVersions.filters', function () {
if (!$scope.restoreVersions.tree) return; if (!$scope.restoreVersions.tree) return;
$scope.restoreVersions.tree.filterNodes(function (node) { $scope.restoreVersions.tree.filterNodes(function (node) {
@ -2163,7 +2163,7 @@ angular.module('syncthing.core')
$scope.showRemoteNeed = function (device) { $scope.showRemoteNeed = function (device) {
resetRemoteNeed(); resetRemoteNeed();
$scope.remoteNeedDevice = device; $scope.remoteNeedDevice = device;
$scope.deviceFolders(device).forEach(function(folder) { $scope.deviceFolders(device).forEach(function (folder) {
var comp = $scope.completion[device.deviceID][folder]; var comp = $scope.completion[device.deviceID][folder];
if (comp !== undefined && comp.needItems + comp.needDeletes === 0) { if (comp !== undefined && comp.needItems + comp.needDeletes === 0) {
return; return;
@ -2233,11 +2233,11 @@ angular.module('syncthing.core')
if ($scope.reportDataPreviewDiff && version > 2) { if ($scope.reportDataPreviewDiff && version > 2) {
$q.all([ $q.all([
$http.get(urlbase + '/svc/report?version=' + version), $http.get(urlbase + '/svc/report?version=' + version),
$http.get(urlbase + '/svc/report?version=' + (version-1)), $http.get(urlbase + '/svc/report?version=' + (version - 1)),
]).then(function (responses) { ]).then(function (responses) {
var newReport = responses[0].data; var newReport = responses[0].data;
var oldReport = responses[1].data; var oldReport = responses[1].data;
angular.forEach(oldReport, function(_, key) { angular.forEach(oldReport, function (_, key) {
delete newReport[key]; delete newReport[key];
}); });
$scope.reportDataPreview = newReport; $scope.reportDataPreview = newReport;
@ -2257,7 +2257,7 @@ angular.module('syncthing.core')
$http.post(urlbase + "/db/scan?folder=" + encodeURIComponent(folder)); $http.post(urlbase + "/db/scan?folder=" + encodeURIComponent(folder));
}; };
$scope.setAllFoldersPause = function(pause) { $scope.setAllFoldersPause = function (pause) {
var folderListCache = $scope.folderList(); var folderListCache = $scope.folderList();
for (var i = 0; i < folderListCache.length; i++) { for (var i = 0; i < folderListCache.length; i++) {
@ -2268,7 +2268,7 @@ angular.module('syncthing.core')
$scope.saveConfig(); $scope.saveConfig();
}; };
$scope.isAtleastOneFolderPausedStateSetTo = function(pause) { $scope.isAtleastOneFolderPausedStateSetTo = function (pause) {
var folderListCache = $scope.folderList(); var folderListCache = $scope.folderList();
for (var i = 0; i < folderListCache.length; i++) { for (var i = 0; i < folderListCache.length; i++) {
@ -2280,10 +2280,10 @@ angular.module('syncthing.core')
return false; return false;
}; };
$scope.activateAllFsWatchers = function() { $scope.activateAllFsWatchers = function () {
var folders = $scope.folderList(); var folders = $scope.folderList();
$.each(folders, function(i) { $.each(folders, function (i) {
if (folders[i].fsWatcherEnabled) { if (folders[i].fsWatcherEnabled) {
return; return;
} }
@ -2331,7 +2331,7 @@ angular.module('syncthing.core')
'solaris': 'Solaris' 'solaris': 'Solaris'
}[$scope.version.os] || $scope.version.os; }[$scope.version.os] || $scope.version.os;
var arch ={ var arch = {
'386': '32 bit', '386': '32 bit',
'amd64': '64 bit', 'amd64': '64 bit',
'arm': 'ARM', 'arm': 'ARM',

View File

@ -8,4 +8,4 @@ angular.module('syncthing.core')
}); });
} }
}; };
}); });

View File

@ -18,4 +18,4 @@ angular.module('syncthing.core')
}); });
} }
}; };
}); });

View File

@ -29,4 +29,4 @@ angular.module('syncthing.core')
}); });
} }
}; };
}); });

View File

@ -1,5 +1,5 @@
<div class="dev-top-bar" id="dev-top-bar" style="display: none"> <div class="dev-top-bar" id="dev-top-bar" style="display: none">
<link href="assets/css/dev.css" rel="stylesheet"/> <link href="assets/css/dev.css" rel="stylesheet" />
<div class="row"> <div class="row">
<div class="col-xs-4"><b>DEV</b></div> <div class="col-xs-4"><b>DEV</b></div>
<div id="log" class="col-xs-8"> <div id="log" class="col-xs-8">
@ -10,4 +10,4 @@
</span> </span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -11,9 +11,9 @@
<div class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)"> <div class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
<label translate for="deviceID">Device ID</label> <label translate for="deviceID">Device ID</label>
<div ng-if="!editingExisting"> <div ng-if="!editingExisting">
<input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true"/> <input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
<datalist id="discovery-list"> <datalist id="discovery-list">
<option ng-repeat="id in discovery" value="{{id}}"/> <option ng-repeat="id in discovery" value="{{id}}" />
</datalist> </datalist>
<p class="help-block" ng-if="discovery"> <p class="help-block" ng-if="discovery">
<span translate>You can also select one of these nearby devices:</span> <span translate>You can also select one of these nearby devices:</span>
@ -33,7 +33,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label translate for="name">Device Name</label> <label translate for="name">Device Name</label>
<input id="name" class="form-control" type="text" ng-model="currentDevice.name"/> <input id="name" class="form-control" type="text" ng-model="currentDevice.name" />
<p translate ng-if="currentDevice.deviceID == myID" class="help-block">Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.</p> <p translate ng-if="currentDevice.deviceID == myID" class="help-block">Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.</p>
<p translate ng-if="currentDevice.deviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p> <p translate ng-if="currentDevice.deviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p>
</div> </div>
@ -94,7 +94,7 @@
<div class="form-group"> <div class="form-group">
<label translate for="addresses">Addresses</label> <label translate for="addresses">Addresses</label>
<input ng-disabled="currentDevice.deviceID == myID" id="addresses" class="form-control" type="text" ng-model="currentDevice._addressesStr"></input> <input ng-disabled="currentDevice.deviceID == myID" id="addresses" class="form-control" type="text" ng-model="currentDevice._addressesStr"></input>
<p translate class="help-block">Enter comma separated ("tcp://ip:port", "tcp://host:port") addresses or "dynamic" to perform automatic discovery of the address.</p> <p translate class="help-block">Enter comma separated ("tcp://ip:port", "tcp://host:port") addresses or "dynamic" to perform automatic discovery of the address.</p>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
@ -116,7 +116,7 @@
<div class="row"> <div class="row">
<span class="col-md-8" translate>Incoming Rate Limit (KiB/s)</span> <span class="col-md-8" translate>Incoming Rate Limit (KiB/s)</span>
<div class="col-md-4"> <div class="col-md-4">
<input name="maxRecvKbps" id="maxRecvKbps" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.maxRecvKbps" min="0"/> <input name="maxRecvKbps" id="maxRecvKbps" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.maxRecvKbps" min="0" />
</div> </div>
</div> </div>
<p class="help-block" ng-if="!deviceEditor.maxRecvKbps.$valid && deviceEditor.maxRecvKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p> <p class="help-block" ng-if="!deviceEditor.maxRecvKbps.$valid && deviceEditor.maxRecvKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>
@ -125,7 +125,7 @@
<div class="row"> <div class="row">
<span class="col-md-8" translate>Outgoing Rate Limit (KiB/s)</span> <span class="col-md-8" translate>Outgoing Rate Limit (KiB/s)</span>
<div class="col-md-4"> <div class="col-md-4">
<input name="maxSendKbps" id="maxSendKbps" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.maxSendKbps" min="0"/> <input name="maxSendKbps" id="maxSendKbps" class="form-control" type="number" pattern="\d+" ng-model="currentDevice.maxSendKbps" min="0" />
</div> </div>
</div> </div>
<p class="help-block" ng-if="!deviceEditor.maxSendKbps.$valid && deviceEditor.maxSendKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p> <p class="help-block" ng-if="!deviceEditor.maxSendKbps.$valid && deviceEditor.maxSendKbps.$dirty" translate>The rate limit must be a non-negative number (0: no limit)</p>

View File

@ -1,35 +1,35 @@
<style> th, td { padding: 6px; } </style> <style> th, td { padding: 6px; } </style>
<modal id="globalChanges" status="default" icon="fas fa-info-circle" heading="{{'Recent Changes' | translate}}" large="yes" closeable="yes"> <modal id="globalChanges" status="default" icon="fas fa-info-circle" heading="{{'Recent Changes' | translate}}" large="yes" closeable="yes">
<div class="modal-body"> <div class="modal-body">
<div class="table-responsive"> <div class="table-responsive">
<table class="table-condensed table-striped table" style="table-layout: auto;"> <table class="table-condensed table-striped table" style="table-layout: auto;">
<thead> <thead>
<tr> <tr>
<th translate>Device</th> <th translate>Device</th>
<th translate>Action</th> <th translate>Action</th>
<th translate>Type</th> <th translate>Type</th>
<th translate>Folder</th> <th translate>Folder</th>
<th translate>Path</th> <th translate>Path</th>
<th translate>Time</th> <th translate>Time</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="changeEvent in globalChangeEvents"> <tr ng-repeat="changeEvent in globalChangeEvents">
<td ng-if="changeEvent.data.modifiedBy">{{friendlyNameFromShort(changeEvent.data.modifiedBy)}}</td> <td ng-if="changeEvent.data.modifiedBy">{{friendlyNameFromShort(changeEvent.data.modifiedBy)}}</td>
<td ng-if="!changeEvent.data.modifiedBy"><span translate>Unknown</span></td> <td ng-if="!changeEvent.data.modifiedBy"><span translate>Unknown</span></td>
<td>{{changeEvent.data.action}}</td> <td>{{changeEvent.data.action}}</td>
<td>{{changeEvent.data.type}}</td> <td>{{changeEvent.data.type}}</td>
<td class="no-overflow-ellipse">{{folderLabel(changeEvent.data.folder)}}</td> <td class="no-overflow-ellipse">{{folderLabel(changeEvent.data.folder)}}</td>
<td class="no-overflow-ellipse">{{changeEvent.data.path}}</td> <td class="no-overflow-ellipse">{{changeEvent.data.path}}</td>
<td class="no-overflow-ellipse">{{changeEvent.time | date:"yyyy-MM-dd HH:mm:ss"}}</td> <td class="no-overflow-ellipse">{{changeEvent.time | date:"yyyy-MM-dd HH:mm:ss"}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>
</div> </div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
<span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
</button>
</div>
</modal> </modal>

View File

@ -1,7 +1,7 @@
<modal id="idqr" status="info" icon="fas fa-qrcode" heading="{{'Device Identification' | translate}} - {{deviceName(currentDevice)}}" large="yes" closeable="yes"> <modal id="idqr" status="info" icon="fas fa-qrcode" heading="{{'Device Identification' | translate}} - {{deviceName(currentDevice)}}" large="yes" closeable="yes">
<div class="modal-body"> <div class="modal-body">
<div class="well well-sm text-monospace text-center" select-on-click>{{currentDevice.deviceID}}</div> <div class="well well-sm text-monospace text-center" select-on-click>{{currentDevice.deviceID}}</div>
<img ng-if="currentDevice.deviceID" class="center-block img-thumbnail" ng-src="qr/?text={{currentDevice.deviceID}}" alt="qr code"/> <img ng-if="currentDevice.deviceID" class="center-block img-thumbnail" ng-src="qr/?text={{currentDevice.deviceID}}" alt="qr code" />
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal"> <button type="button" class="btn btn-default btn-sm" data-dismiss="modal">

View File

@ -12,14 +12,14 @@
<div id="folder-general" class="tab-pane in active"> <div id="folder-general" class="tab-pane in active">
<div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty}"> <div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty}">
<label for="folderLabel"><span translate>Folder Label</span></label> <label for="folderLabel"><span translate>Folder Label</span></label>
<input name="folderLabel" id="folderLabel" class="form-control" type="text" ng-model="currentFolder.label" value="{{currentFolder.label}}"/> <input name="folderLabel" id="folderLabel" class="form-control" type="text" ng-model="currentFolder.label" value="{{currentFolder.label}}" />
<p class="help-block"> <p class="help-block">
<span translate ng-if="folderEditor.folderLabel.$valid || folderEditor.folderLabel.$pristine">Optional descriptive label for the folder. Can be different on each device.</span> <span translate ng-if="folderEditor.folderLabel.$valid || folderEditor.folderLabel.$pristine">Optional descriptive label for the folder. Can be different on each device.</span>
</p> </p>
</div> </div>
<div class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}"> <div class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
<label for="folderID"><span translate>Folder ID</span></label> <label for="folderID"><span translate>Folder ID</span></label>
<input name="folderID" ng-readonly="editingExisting || (!editingExisting && currentFolder.viewFlags.importFromOtherDevice)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required="" aria-required="true" unique-folder value="{{currentFolder.id}}"/> <input name="folderID" ng-readonly="editingExisting || (!editingExisting && currentFolder.viewFlags.importFromOtherDevice)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required="" aria-required="true" unique-folder value="{{currentFolder.id}}" />
<p class="help-block"> <p class="help-block">
<span translate ng-if="folderEditor.folderID.$valid || folderEditor.folderID.$pristine">Required identifier for the folder. Must be the same on all cluster devices.</span> <span translate ng-if="folderEditor.folderID.$valid || folderEditor.folderID.$pristine">Required identifier for the folder. Must be the same on all cluster devices.</span>
<span translate ng-if="folderEditor.folderID.$error.uniqueFolder">The folder ID must be unique.</span> <span translate ng-if="folderEditor.folderID.$error.uniqueFolder">The folder ID must be unique.</span>
@ -29,7 +29,7 @@
</div> </div>
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty}"> <div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty}">
<label translate for="folderPath">Folder Path</label> <label translate for="folderPath">Folder Path</label>
<input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" required="" aria-required="true" path-is-sub-dir/> <input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" required="" aria-required="true" path-is-sub-dir />
<datalist id="directory-list"> <datalist id="directory-list">
<option ng-repeat="directory in directoryList" value="{{ directory }}" /> <option ng-repeat="directory in directoryList" value="{{ directory }}" />
</datalist> </datalist>
@ -55,7 +55,7 @@
<div class="col-md-4" ng-repeat="device in otherDevices()"> <div class="col-md-4" ng-repeat="device in otherDevices()">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" ng-model="currentFolder.selectedDevices[device.deviceID]"/> {{deviceName(device)}} <input type="checkbox" ng-model="currentFolder.selectedDevices[device.deviceID]" /> {{deviceName(device)}}
</label> </label>
</div> </div>
</div> </div>
@ -77,7 +77,7 @@
<p translate class="help-block">Files are moved to .stversions directory when replaced or deleted by Syncthing.</p> <p translate class="help-block">Files are moved to .stversions directory when replaced or deleted by Syncthing.</p>
<label translate for="trashcanClean">Clean out after</label> <label translate for="trashcanClean">Clean out after</label>
<div class="input-group"> <div class="input-group">
<input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder.trashcanClean" required="" aria-required="true" min="0"/> <input name="trashcanClean" id="trashcanClean" class="form-control text-right" type="number" ng-model="currentFolder.trashcanClean" required="" aria-required="true" min="0" />
<div class="input-group-addon" translate>days</div> <div class="input-group-addon" translate>days</div>
</div> </div>
<p class="help-block"> <p class="help-block">
@ -89,7 +89,7 @@
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.simpleKeep.$invalid && folderEditor.simpleKeep.$dirty}"> <div class="form-group" ng-if="currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.simpleKeep.$invalid && folderEditor.simpleKeep.$dirty}">
<p translate class="help-block">Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</p> <p translate class="help-block">Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</p>
<label translate for="simpleKeep">Keep Versions</label> <label translate for="simpleKeep">Keep Versions</label>
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder.simpleKeep" required="" aria-required="true" min="1"/> <input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder.simpleKeep" required="" aria-required="true" min="1" />
<p class="help-block"> <p class="help-block">
<span translate ng-if="folderEditor.simpleKeep.$valid || folderEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span> <span translate ng-if="folderEditor.simpleKeep.$valid || folderEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
<span translate ng-if="folderEditor.simpleKeep.$error.required && folderEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span> <span translate ng-if="folderEditor.simpleKeep.$error.required && folderEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
@ -100,7 +100,7 @@
<p class="help-block"><span translate>Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</span> <span translate>Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.</span></p> <p class="help-block"><span translate>Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.</span> <span translate>Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.</span></p>
<p translate class="help-block">The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.</p> <p translate class="help-block">The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.</p>
<label translate for="staggeredMaxAge">Maximum Age</label> <label translate for="staggeredMaxAge">Maximum Age</label>
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder.staggeredMaxAge" required="" aria-required="true" min="0"/> <input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentFolder.staggeredMaxAge" required="" aria-required="true" min="0" />
<p class="help-block"> <p class="help-block">
<span translate ng-if="folderEditor.staggeredMaxAge.$valid || folderEditor.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span> <span translate ng-if="folderEditor.staggeredMaxAge.$valid || folderEditor.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span>
<span translate ng-if="folderEditor.staggeredMaxAge.$error.required && folderEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span> <span translate ng-if="folderEditor.staggeredMaxAge.$error.required && folderEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
@ -109,7 +109,7 @@
</div> </div>
<div class="form-group" ng-if="currentFolder.fileVersioningSelector == 'staggered'"> <div class="form-group" ng-if="currentFolder.fileVersioningSelector == 'staggered'">
<label translate for="staggeredVersionsPath">Versions Path</label> <label translate for="staggeredVersionsPath">Versions Path</label>
<input name="staggeredVersionsPath" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentFolder.staggeredVersionsPath"/> <input name="staggeredVersionsPath" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentFolder.staggeredVersionsPath" />
<p translate class="help-block">Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).</p> <p translate class="help-block">Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).</p>
</div> </div>
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}"> <div class="form-group" ng-if="currentFolder.fileVersioningSelector=='external'" ng-class="{'has-error': folderEditor.externalCommand.$invalid && folderEditor.externalCommand.$dirty}">
@ -125,17 +125,23 @@
<div id="folder-ignores" class="tab-pane"> <div id="folder-ignores" class="tab-pane">
<p translate>Enter ignore patterns, one per line.</p> <p translate>Enter ignore patterns, one per line.</p>
<textarea class="form-control" rows="5"></textarea> <textarea class="form-control" rows="5"></textarea>
<hr/> <hr />
<p class="small"><span translate>Quick guide to supported patterns</span> (<a href="https://docs.syncthing.net/users/ignoring.html" target="_blank" translate>full documentation</a>):</p> <p class="small"><span translate>Quick guide to supported patterns</span> (<a href="https://docs.syncthing.net/users/ignoring.html" target="_blank" translate>full documentation</a>):</p>
<dl class="dl-horizontal dl-narrow small"> <dl class="dl-horizontal dl-narrow small">
<dt><code>(?d)</code></dt> <dd><b><span translate>Prefix indicating that the file can be deleted if preventing directory removal</span></b></dd> <dt><code>(?d)</code></dt>
<dt><code>(?i)</code></dt> <dd><span translate>Prefix indicating that the pattern should be matched without case sensitivity</span></dd> <dd><b><span translate>Prefix indicating that the file can be deleted if preventing directory removal</span></b></dd>
<dt><code>!</code></dt> <dd><span translate>Inversion of the given condition (i.e. do not exclude)</span></dd> <dt><code>(?i)</code></dt>
<dt><code>*</code></dt> <dd><span translate>Single level wildcard (matches within a directory only)</span></dd> <dd><span translate>Prefix indicating that the pattern should be matched without case sensitivity</span></dd>
<dt><code>**</code></dt> <dd><span translate>Multi level wildcard (matches multiple directory levels)</span></dd> <dt><code>!</code></dt>
<dt><code>//</code></dt> <dd><span translate>Comment, when used at the start of a line</span></dd> <dd><span translate>Inversion of the given condition (i.e. do not exclude)</span></dd>
<dt><code>*</code></dt>
<dd><span translate>Single level wildcard (matches within a directory only)</span></dd>
<dt><code>**</code></dt>
<dd><span translate>Multi level wildcard (matches multiple directory levels)</span></dd>
<dt><code>//</code></dt>
<dd><span translate>Comment, when used at the start of a line</span></dd>
</dl> </dl>
<hr/> <hr />
<div class="pull-left" ng-show="editingExisting"><span translate translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Editing {%path%}.</span></div> <div class="pull-left" ng-show="editingExisting"><span translate translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Editing {%path%}.</span></div>
<div class="pull-left" ng-show="!editingExisting"><span translate translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Creating ignore patterns, overwriting an existing file at {%path%}.</span></div> <div class="pull-left" ng-show="!editingExisting"><span translate translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Creating ignore patterns, overwriting an existing file at {%path%}.</span></div>
</div> </div>
@ -152,7 +158,7 @@
<div class="row"> <div class="row">
<span class="col-md-8" translate>Full Rescan Interval (s)</span> <span class="col-md-8" translate>Full Rescan Interval (s)</span>
<div class="col-md-4"> <div class="col-md-4">
<input name="rescanIntervalS" id="rescanIntervalS" class="form-control" type="number" ng-model="currentFolder.rescanIntervalS" required="" aria-required="true" min="0"/> <input name="rescanIntervalS" id="rescanIntervalS" class="form-control" type="number" ng-model="currentFolder.rescanIntervalS" required="" aria-required="true" min="0" />
</div> </div>
</div> </div>
<p class="help-block" ng-if="!folderEditor.rescanIntervalS.$valid && folderEditor.rescanIntervalS.$dirty" translate> <p class="help-block" ng-if="!folderEditor.rescanIntervalS.$valid && folderEditor.rescanIntervalS.$dirty" translate>
@ -188,10 +194,10 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6 form-horizontal form-group" ng-class="{'has-error': folderEditor.minDiskFree.$invalid && folderEditor.minDiskFree.$dirty}"> <div class="col-md-6 form-horizontal form-group" ng-class="{'has-error': folderEditor.minDiskFree.$invalid && folderEditor.minDiskFree.$dirty}">
<label for="minDiskFree" translate>Minimum Free Disk Space</label><br/> <label for="minDiskFree" translate>Minimum Free Disk Space</label><br />
<div class="row"> <div class="row">
<div class="col-md-9"> <div class="col-md-9">
<input name="minDiskFree" id="minDiskFree" class="form-control" type="number" ng-model="currentFolder.minDiskFree.value" required="" aria-required="true" min="0" step="0.01"/> <input name="minDiskFree" id="minDiskFree" class="form-control" type="number" ng-model="currentFolder.minDiskFree.value" required="" aria-required="true" min="0" step="0.01" />
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<select class="form-control" ng-model="currentFolder.minDiskFree.unit"> <select class="form-control" ng-model="currentFolder.minDiskFree.unit">
@ -208,8 +214,8 @@
</p> </p>
</div> </div>
<div class="col-md-6 form-group"> <div class="col-md-6 form-group">
<label translate>Permissions</label><br/> <label translate>Permissions</label><br />
<input type="checkbox" ng-model="currentFolder.ignorePerms"/> <span translate>Ignore</span> <input type="checkbox" ng-model="currentFolder.ignorePerms" /> <span translate>Ignore</span>
<p translate class="help-block">File permission bits are ignored when looking for changes. Use on FAT file systems.</p> <p translate class="help-block">File permission bits are ignored when looking for changes. Use on FAT file systems.</p>
<p class="col-xs-12 help-block" ng-show="folderEditor.minDiskFree.$invalid"> <p class="col-xs-12 help-block" ng-show="folderEditor.minDiskFree.$invalid">
<span translate>Enter a non-negative number (e.g., "2.35") and select a unit. Percentages are as part of the total disk size.</span> <span translate>Enter a non-negative number (e.g., "2.35") and select a unit. Percentages are as part of the total disk size.</span>

View File

@ -12,7 +12,7 @@
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<hr/> <hr />
<div class="row form-inline"> <div class="row form-inline">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">

View File

@ -18,7 +18,7 @@
<div ng-repeat="(key, value) in advancedConfig.gui" ng-init="type = inputTypeFor(key, value)" ng-if="type != 'skip'" class="form-group"> <div ng-repeat="(key, value) in advancedConfig.gui" ng-init="type = inputTypeFor(key, value)" ng-if="type != 'skip'" class="form-group">
<label for="guiInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label> <label for="guiInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.gui[key]" ng-list/> <input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.gui[key]" ng-list />
<input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.gui[key]" /> <input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.gui[key]" />
</div> </div>
</div> </div>
@ -37,7 +37,7 @@
<div ng-repeat="(key, value) in advancedConfig.options" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group"> <div ng-repeat="(key, value) in advancedConfig.options" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
<label for="optionsInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label> <label for="optionsInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.options[key]" ng-list/> <input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="advancedConfig.options[key]" ng-list />
<input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.options[key]" /> <input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="advancedConfig.options[key]" />
</div> </div>
</div> </div>
@ -47,12 +47,12 @@
</div> </div>
<div class="panel panel-default" ng-repeat="folder in advancedConfig.folders"> <div class="panel panel-default" ng-repeat="folder in advancedConfig.folders">
<div class="panel-heading" role="tab" id="folder{{$index}}Heading" data-toggle="collapse" data-parent="#advancedAccordion" href="#folder{{$index}}Config" aria-expanded="false" aria-controls="folder{{$index}}Config" style="cursor: pointer;"> <div class="panel-heading" role="tab" id="folder{{$index}}Heading" data-toggle="collapse" data-parent="#advancedAccordion" href="#folder{{$index}}Config" aria-expanded="false" aria-controls="folder{{$index}}Config" style="cursor: pointer;">
<h4 ng-if="folder.label.length == 0" class="panel-title" tabindex="0"> <h4 ng-if="folder.label.length == 0" class="panel-title" tabindex="0">
<span translate>Folder</span> "{{folder.id}}" <span translate>Folder</span> "{{folder.id}}"
</h4> </h4>
<h4 ng-if="folder.label.length != 0" class="panel-title" tabindex="0"> <h4 ng-if="folder.label.length != 0" class="panel-title" tabindex="0">
<span translate>Folder</span> "{{folder.label}}" ({{folder.id}}) <span translate>Folder</span> "{{folder.label}}" ({{folder.id}})
</h4> </h4>
</div> </div>
<div id="folder{{$index}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="folder{{$index}}Heading"> <div id="folder{{$index}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="folder{{$index}}Heading">
@ -61,7 +61,7 @@
<div ng-repeat="(key, value) in folder" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group"> <div ng-repeat="(key, value) in folder" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
<label for="folder{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label> <label for="folder{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="folder[key]" ng-list/> <input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="folder[key]" ng-list />
<input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="folder[key]" /> <input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="folder[key]" />
</div> </div>
</div> </div>
@ -71,9 +71,9 @@
</div> </div>
<div class="panel panel-default" ng-repeat="device in advancedConfig.devices"> <div class="panel panel-default" ng-repeat="device in advancedConfig.devices">
<div class="panel-heading" role="tab" id="device{{$index}}Heading" data-toggle="collapse" data-parent="#advancedAccordion" href="#device{{$index}}Config" aria-expanded="false" aria-controls="folder{{$index}}Config" style="cursor: pointer;"> <div class="panel-heading" role="tab" id="device{{$index}}Heading" data-toggle="collapse" data-parent="#advancedAccordion" href="#device{{$index}}Config" aria-expanded="false" aria-controls="folder{{$index}}Config" style="cursor: pointer;">
<h4 class="panel-title" tabindex="0"> <h4 class="panel-title" tabindex="0">
<span translate>Device</span> "{{deviceName(device)}}" <span translate>Device</span> "{{deviceName(device)}}"
</h4> </h4>
</div> </div>
<div id="device{{$index}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="device{{$index}}Heading"> <div id="device{{$index}}Config" class="panel-collapse collapse" role="tabpanel" aria-labelledby="device{{$index}}Heading">
@ -82,7 +82,7 @@
<div ng-repeat="(key, value) in device" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group"> <div ng-repeat="(key, value) in device" ng-if="inputTypeFor(key, value) != 'skip'" class="form-group">
<label for="device{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label> <label for="device{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="device[key]" ng-list/> <input ng-if="inputTypeFor(key, value) == 'list'" id="optionsInput{{$index}}" class="form-control" type="text" ng-model="device[key]" ng-list />
<input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="device[key]" /> <input ng-if="inputTypeFor(key, value) != 'list'" id="optionsInput{{$index}}" class="form-control" type="{{inputTypeFor(key, value)}}" ng-model="device[key]" />
</div> </div>
</div> </div>

View File

@ -28,22 +28,22 @@
<div id="settings-general" class="tab-pane in active"> <div id="settings-general" class="tab-pane in active">
<div class="form-group"> <div class="form-group">
<label translate for="DeviceName">Device Name</label> <label translate for="DeviceName">Device Name</label>
<input id="DeviceName" class="form-control" type="text" ng-model="tmpOptions.deviceName"/> <input id="DeviceName" class="form-control" type="text" ng-model="tmpOptions.deviceName" />
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-horizontal"> <div class="form-horizontal">
<div class="form-group" ng-class="{'has-error': settingsEditor.minHomeDiskFree.$invalid && settingsEditor.minHomeDiskFree.$dirty}"> <div class="form-group" ng-class="{'has-error': settingsEditor.minHomeDiskFree.$invalid && settingsEditor.minHomeDiskFree.$dirty}">
<label class="col-xs-12" for="minHomeDiskFree"><span translate>Minimum Free Disk Space</span></label><br/> <label class="col-xs-12" for="minHomeDiskFree"><span translate>Minimum Free Disk Space</span></label><br />
<div class="col-xs-9"><input name="minHomeDiskFree" id="minHomeDiskFree" class="form-control" type="number" ng-model="tmpOptions.minHomeDiskFree.value" required="" aria-required="true" min="0" step="0.01"/></div> <div class="col-xs-9"><input name="minHomeDiskFree" id="minHomeDiskFree" class="form-control" type="number" ng-model="tmpOptions.minHomeDiskFree.value" required="" aria-required="true" min="0" step="0.01" /></div>
<div class="col-xs-3"><select class="col-sm-3 form-control" ng-model="tmpOptions.minHomeDiskFree.unit"> <div class="col-xs-3"><select class="col-sm-3 form-control" ng-model="tmpOptions.minHomeDiskFree.unit">
<option value="%">%</option> <option value="%">%</option>
<option value="kB">kB</option> <option value="kB">kB</option>
<option value="MB">MB</option> <option value="MB">MB</option>
<option value="GB">GB</option> <option value="GB">GB</option>
<option value="TB">TB</option> <option value="TB">TB</option>
</select></div> </select></div>
<p class="col-xs-12 help-block"> <p class="col-xs-12 help-block">
<span translate ng-show="settingsEditor.minHomeDiskFree.$invalid">Enter a non-negative number (e.g., "2.35") and select a unit. Percentages are as part of the total disk size.</span> <span translate ng-show="settingsEditor.minHomeDiskFree.$invalid">Enter a non-negative number (e.g., "2.35") and select a unit. Percentages are as part of the total disk size.</span>
<span translate ng-hide="settingsEditor.minHomeDiskFree.$invalid">This setting controls the free space required on the home (i.e., index database) disk.</span> <span translate ng-hide="settingsEditor.minHomeDiskFree.$invalid">This setting controls the free space required on the home (i.e., index database) disk.</span>
@ -55,7 +55,7 @@
<div class="form-group"> <div class="form-group">
<label translate>API Key</label> <label translate>API Key</label>
<div class="input-group"> <div class="input-group">
<input type="text" readonly class="text-monospace form-control" value="{{tmpGUI.apiKey || '-'}}"/> <input type="text" readonly class="text-monospace form-control" value="{{tmpGUI.apiKey || '-'}}" />
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" class="btn btn-default btn-secondary" ng-click="setAPIKey(tmpGUI)"> <button type="button" class="btn btn-default btn-secondary" ng-click="setAPIKey(tmpGUI)">
<span class="fas fa-redo"></span>&nbsp;<span translate>Generate</span> <span class="fas fa-redo"></span>&nbsp;<span translate>Generate</span>
@ -99,7 +99,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label translate for="urVersion">Default Folder Path</label> <label translate for="urVersion">Default Folder Path</label>
<input id="DefaultFolderPath" class="form-control" type="text" ng-model="tmpOptions.defaultFolderPath"/> <input id="DefaultFolderPath" class="form-control" type="text" ng-model="tmpOptions.defaultFolderPath" />
<p class="help-block"> <p class="help-block">
<span translate translate-value-tilde="{{system.tilde}}"> <span translate translate-value-tilde="{{system.tilde}}">
Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}. Path where new auto accepted folders will be created, as well as the default suggested path when adding new folders via the UI. Tilde character (~) expands to {%tilde%}.
@ -109,28 +109,28 @@
</div> </div>
<div id="settings-gui" class="tab-pane"> <div id="settings-gui" class="tab-pane">
<div class="form-group" ng-class="{'has-error': settingsEditor.Address.$invalid && settingsEditor.Address.$dirty}"> <div class="form-group" ng-class="{'has-error': settingsEditor.Address.$invalid && settingsEditor.Address.$dirty}">
<label translate for="Address">GUI Listen Address</label>&emsp;<a href="https://docs.syncthing.net/users/guilisten.html" target="_blank"><span class="fas fa-question-circle"></span>&nbsp;<span translate>Help</span></a> <label translate for="Address">GUI Listen Address</label>&emsp;<a href="https://docs.syncthing.net/users/guilisten.html" target="_blank"><span class="fas fa-question-circle"></span>&nbsp;<span translate>Help</span></a>
<p class="text-warning" ng-show="system.guiAddressOverridden"> <p class="text-warning" ng-show="system.guiAddressOverridden">
<span class="fas fa-exclamation-triangle"></span> <span class="fas fa-exclamation-triangle"></span>
<span translate>The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.</span> <span translate>The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.</span>
</p>
<input id="Address" name="Address" class="form-control" type="text" ng-model="tmpGUI.address" ng-pattern="/^(/.*)|(.*:0*((102[4-9])|(10[3-9][0-9])|(1[1-9][0-9][0-9])|([2-9][0-9][0-9][0-9])|([1-6]\d{4})))$/" />
<p class="help-block" ng-show="settingsEditor.Address.$invalid" translate>
Enter a non-privileged port number (1024 - 65535).
</p> </p>
<input id="Address" name="Address" class="form-control" type="text" ng-model="tmpGUI.address" ng-pattern="/^(/.*)|(.*:0*((102[4-9])|(10[3-9][0-9])|(1[1-9][0-9][0-9])|([2-9][0-9][0-9][0-9])|([1-6]\d{4})))$/"/>
<p class="help-block" ng-show="settingsEditor.Address.$invalid" translate>
Enter a non-privileged port number (1024 - 65535).
</p>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label translate for="User">GUI Authentication User</label> <label translate for="User">GUI Authentication User</label>
<input id="User" class="form-control" type="text" ng-model="tmpGUI.user"/> <input id="User" class="form-control" type="text" ng-model="tmpGUI.user" />
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label translate for="Password">GUI Authentication Password</label> <label translate for="Password">GUI Authentication Password</label>
<input id="Password" class="form-control" type="password" ng-model="tmpGUI.password" ng-trim="false"/> <input id="Password" class="form-control" type="password" ng-model="tmpGUI.password" ng-trim="false" />
</div> </div>
</div> </div>
</div> </div>
@ -139,7 +139,7 @@
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input id="UseTLS" type="checkbox" ng-model="tmpGUI.useTLS"/> <span translate>Use HTTPS for GUI</span> <input id="UseTLS" type="checkbox" ng-model="tmpGUI.useTLS" /> <span translate>Use HTTPS for GUI</span>
</label> </label>
</div> </div>
</div> </div>
@ -148,7 +148,7 @@
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input id="StartBrowser" type="checkbox" ng-model="tmpOptions.startBrowser"/> <span translate>Start Browser</span> <input id="StartBrowser" type="checkbox" ng-model="tmpOptions.startBrowser" /> <span translate>Start Browser</span>
</label> </label>
</div> </div>
</div> </div>
@ -176,13 +176,13 @@
<div id="settings-connections" class="tab-pane"> <div id="settings-connections" class="tab-pane">
<div class="form-group"> <div class="form-group">
<label translate for="ListenAddressesStr">Sync Protocol Listen Addresses</label>&emsp;<a href="https://docs.syncthing.net/users/config.html#listen-addresses" target="_blank"><span class="fas fa-question-circle"></span>&nbsp;<span translate>Help</span></a> <label translate for="ListenAddressesStr">Sync Protocol Listen Addresses</label>&emsp;<a href="https://docs.syncthing.net/users/config.html#listen-addresses" target="_blank"><span class="fas fa-question-circle"></span>&nbsp;<span translate>Help</span></a>
<input id="ListenAddressesStr" class="form-control" type="text" ng-model="tmpOptions._listenAddressesStr"/> <input id="ListenAddressesStr" class="form-control" type="text" ng-model="tmpOptions._listenAddressesStr" />
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group" ng-class="{'has-error': settingsEditor.MaxRecvKbps.$invalid && settingsEditor.MaxRecvKbps.$dirty}"> <div class="form-group" ng-class="{'has-error': settingsEditor.MaxRecvKbps.$invalid && settingsEditor.MaxRecvKbps.$dirty}">
<label translate for="MaxRecvKbps">Incoming Rate Limit (KiB/s)</label> <label translate for="MaxRecvKbps">Incoming Rate Limit (KiB/s)</label>
<input id="MaxRecvKbps" name="MaxRecvKbps" class="form-control" type="number" ng-model="tmpOptions.maxRecvKbps" min="0"/> <input id="MaxRecvKbps" name="MaxRecvKbps" class="form-control" type="number" ng-model="tmpOptions.maxRecvKbps" min="0" />
<p class="help-block"> <p class="help-block">
<span translate ng-if="settingsEditor.MaxRecvKbps.$error.min && settingsEditor.MaxRecvKbps.$dirty">The rate limit must be a non-negative number (0: no limit)</span> <span translate ng-if="settingsEditor.MaxRecvKbps.$error.min && settingsEditor.MaxRecvKbps.$dirty">The rate limit must be a non-negative number (0: no limit)</span>
</p> </p>
@ -191,7 +191,7 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group" ng-class="{'has-error': settingsEditor.MaxSendKbps.$invalid && settingsEditor.MaxSendKbps.$dirty}"> <div class="form-group" ng-class="{'has-error': settingsEditor.MaxSendKbps.$invalid && settingsEditor.MaxSendKbps.$dirty}">
<label translate for="MaxSendKbps">Outgoing Rate Limit (KiB/s)</label> <label translate for="MaxSendKbps">Outgoing Rate Limit (KiB/s)</label>
<input id="MaxSendKbps" name="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.maxSendKbps" min="0"/> <input id="MaxSendKbps" name="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.maxSendKbps" min="0" />
<p class="help-block"> <p class="help-block">
<span translate ng-if="settingsEditor.MaxSendKbps.$error.min && settingsEditor.MaxSendKbps.$dirty">The rate limit must be a non-negative number (0: no limit)</span> <span translate ng-if="settingsEditor.MaxSendKbps.$error.min && settingsEditor.MaxSendKbps.$dirty">The rate limit must be a non-negative number (0: no limit)</span>
</p> </p>
@ -203,7 +203,7 @@
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input id="NATEnabled" type="checkbox" ng-model="tmpOptions.natEnabled"/> <span translate>Enable NAT traversal</span> <input id="NATEnabled" type="checkbox" ng-model="tmpOptions.natEnabled" /> <span translate>Enable NAT traversal</span>
</label> </label>
</div> </div>
</div> </div>
@ -212,7 +212,7 @@
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.localAnnounceEnabled"/> <span translate>Local Discovery</span> <input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.localAnnounceEnabled" /> <span translate>Local Discovery</span>
</label> </label>
</div> </div>
</div> </div>
@ -223,7 +223,7 @@
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.globalAnnounceEnabled"/> <span translate>Global Discovery</span> <input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.globalAnnounceEnabled" /> <span translate>Global Discovery</span>
</label> </label>
</div> </div>
</div> </div>
@ -232,7 +232,7 @@
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input id="RelaysEnabled" type="checkbox" ng-model="tmpOptions.relaysEnabled"/> <span translate>Enable Relaying</span> <input id="RelaysEnabled" type="checkbox" ng-model="tmpOptions.relaysEnabled" /> <span translate>Enable Relaying</span>
</label> </label>
</div> </div>
</div> </div>
@ -242,7 +242,7 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label translate for="GlobalAnnServersStr">Global Discovery Servers</label> <label translate for="GlobalAnnServersStr">Global Discovery Servers</label>
<input ng-disabled="!tmpOptions.globalAnnounceEnabled" id="GlobalAnnServersStr" class="form-control" type="text" ng-model="tmpOptions._globalAnnounceServersStr"/> <input ng-disabled="!tmpOptions.globalAnnounceEnabled" id="GlobalAnnServersStr" class="form-control" type="text" ng-model="tmpOptions._globalAnnounceServersStr" />
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">

View File

@ -1,7 +1,7 @@
<modal id="needed" status="info" icon="fas fa-cloud-download-alt" heading="{{'Out of Sync Items' | translate}}" large="yes" closeable="yes"> <modal id="needed" status="info" icon="fas fa-cloud-download-alt" heading="{{'Out of Sync Items' | translate}}" large="yes" closeable="yes">
<div class="modal-body"> <div class="modal-body">
<div class="progress"> <div class="progress">
<div class="progress-bar progress-bar-success" style="width: 20%"><span translate class="show">Reused</span></div> <div class="progress-bar progress-bar-success" style="width: 20%"><span translate class="show">Reused</span></div>
<div class="progress-bar" style="width: 20%"><span translate class="show">Copied from original</span></div> <div class="progress-bar" style="width: 20%"><span translate class="show">Copied from original</span></div>
@ -10,7 +10,7 @@
<div class="progress-bar progress-bar-danger progress-bar-striped active" style="width: 20%"><span translate class="show">Downloading</span></div> <div class="progress-bar progress-bar-danger progress-bar-striped active" style="width: 20%"><span translate class="show">Downloading</span></div>
</div> </div>
<hr/> <hr />
<table class="table table-striped table-condensed"> <table class="table table-striped table-condensed">

View File

@ -6,13 +6,13 @@
<p translate>The aggregated statistics are publicly available at the URL below.</p> <p translate>The aggregated statistics are publicly available at the URL below.</p>
<p><a href="https://data.syncthing.net/" target="_blank">https://data.syncthing.net/</a></p> <p><a href="https://data.syncthing.net/" target="_blank">https://data.syncthing.net/</a></p>
<label translate>Version</label> <label translate>Version</label>
<select id="urPreviewVersion" class="form-control" ng-model="$parent.$parent.reportDataPreviewVersion" ng-change="refreshReportDataPreview()" > <select id="urPreviewVersion" class="form-control" ng-model="$parent.$parent.reportDataPreviewVersion" ng-change="refreshReportDataPreview()">
<option selected value translate>Select a version</option> <option selected value translate>Select a version</option>
<option ng-repeat="n in urVersions()" value="{{n}}">{{'Version' | translate}} {{n}}</option> <option ng-repeat="n in urVersions()" value="{{n}}">{{'Version' | translate}} {{n}}</option>
</select> </select>
<div class="checkbox" ng-if="$parent.$parent.reportDataPreviewVersion > 2"> <div class="checkbox" ng-if="$parent.$parent.reportDataPreviewVersion > 2">
<label> <label>
<input type="checkbox" ng-model="$parent.$parent.$parent.reportDataPreviewDiff" ng-change="refreshReportDataPreview()"/> <input type="checkbox" ng-model="$parent.$parent.$parent.reportDataPreviewDiff" ng-change="refreshReportDataPreview()" />
<span translate>Show diff with previous version</span> <span translate>Show diff with previous version</span>
</label> </label>
</div> </div>