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: []
}
$.each(children, function(path, data) {
$.each(children, function (path, data) {
var parts = path.split('/');
var name = parts.splice(-1)[0];
@ -214,26 +214,26 @@ function unitPrefixed(input, binary) {
if (input > factor * factor * factor * factor * 1000) {
// Don't show any decimals for more than 4 digits
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)
if (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) {
input /= factor * factor * factor;
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) {
input /= factor * factor;
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) {
input /= factor;
@ -242,9 +242,9 @@ function unitPrefixed(input, binary) {
prefix = ' K';
}
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() + ' ';
};

View File

@ -1,13 +1,13 @@
<modal id="about" status="info" icon="far fa-heart" heading="{{'About' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<h1 class="text-center">
<img alt="Syncthing" src="assets/img/logo-horizontal.svg" style="vertical-align: -16px" height="100" width="366"/>
<br/>
<img alt="Syncthing" src="assets/img/logo-horizontal.svg" style="vertical-align: -16px" height="100" width="366" />
<br />
<small>{{versionString()}}</small>
<br/>
<br />
<small><i>"{{version.codename}}"</i></small>
</h1>
<hr/>
<hr />
<p translate>Copyright &copy; 2014-2017 the following Contributors:</p>
<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, 佛跳墙
</div>
</div>
<hr/>
<hr />
<p translate>Syncthing includes the following software or portions thereof:</p>
<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="panel panel-default">
<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>

View File

@ -10,7 +10,7 @@ angular.module('syncthing.core')
.filter('duration', function () {
'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) {
var result = "";
if (!precision) {
@ -18,7 +18,7 @@ angular.module('syncthing.core')
}
input = parseInt(input, 10);
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) {
result += " " + t + k;

View File

@ -7,7 +7,7 @@ angular.module('syncthing.core')
var lastID = 0;
var self = this;
function successFn (data) {
function successFn(data) {
// When Syncthing restarts while the long polling connection is in
// progress the browser on some platforms returns a 200 (since the
// headers has been flushed with the return code 200), with no data.
@ -38,7 +38,7 @@ angular.module('syncthing.core')
.error(errorFn);
}
function errorFn (dummy) {
function errorFn(dummy) {
$rootScope.$broadcast(self.OFFLINE);
$timeout(function () {
@ -51,35 +51,35 @@ angular.module('syncthing.core')
angular.extend(self, {
// emitted by this
ONLINE: 'UIOnline',
ONLINE: 'UIOnline',
OFFLINE: 'UIOffline',
// emitted by syncthing process
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_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_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_RESUMED: 'DeviceResumed', // Emitted when a device has been resumed
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_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
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
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
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_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_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_RESUMED: 'DeviceResumed', // Emitted when a device has been resumed
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_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
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
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
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
STATE_CHANGED: 'StateChanged', // Emitted when a folder changes state
FOLDER_ERRORS: 'FolderErrors', // Emitted when a folder has errors preventing a full sync
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
STATE_CHANGED: 'StateChanged', // Emitted when a folder changes state
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_PAUSED: 'FolderPaused', // Emitted when a folder is paused
FOLDER_RESUMED: 'FolderResumed', // Emitted when a folder is resumed
FOLDER_PAUSED: 'FolderPaused', // Emitted when a folder is paused
FOLDER_RESUMED: 'FolderResumed', // Emitted when a folder is resumed
start: function() {
start: function () {
$http.get(urlbase + '/events?limit=1')
.success(successFn)
.error(errorFn);

View File

@ -4,12 +4,12 @@ angular.module('syncthing.core')
return {
restrict: 'EA',
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>'+
'<ul ng-if="visible" class="dropdown-menu">'+
'<li ng-repeat="name in localesNamesInvKeys" ng-class="{active: localesNamesInv[name]==currentLocale}">'+
'<a href="#" data-ng-click="changeLanguage(localesNamesInv[name])">{{name}}</a>'+
'</li>'+
'</ul>',
'<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">' +
'<li ng-repeat="name in localesNamesInvKeys" ng-class="{active: localesNamesInv[name]==currentLocale}">' +
'<a href="#" data-ng-click="changeLanguage(localesNamesInv[name])">{{name}}</a>' +
'</li>' +
'</ul>',
link: function ($scope) {
var availableLocales = LocaleService.getAvailableLocales();
@ -27,12 +27,12 @@ angular.module('syncthing.core')
}
}
$scope.localesNames = availableLocaleNames;
var invert = function (obj) {
var new_obj = {};
for (var prop in obj) {
if(obj.hasOwnProperty(prop)) {
if (obj.hasOwnProperty(prop)) {
new_obj[obj[prop]] = prop;
}
}
@ -40,7 +40,7 @@ angular.module('syncthing.core')
};
$scope.localesNamesInv = invert($scope.localesNames);
$scope.localesNamesInvKeys = Object.keys($scope.localesNamesInv).sort();
$scope.visible = $scope.localesNames && $scope.localesNames['en'];
// 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];
}
if(params.lang) {
if (params.lang) {
useLocale(params.lang, true);
} else if (savedLang) {
useLocale(savedLang);
@ -99,8 +99,8 @@ angular.module('syncthing.core')
function useLocale(language, save2Storage) {
if (language) {
$translate.use(language).then(function () {
if (save2Storage && _localStorage)
$translate.use(language).then(function () {
if (save2Storage && _localStorage)
_localStorage[_SYNLANG] = language;
});
}
@ -109,10 +109,10 @@ angular.module('syncthing.core')
return {
autoConfigLocale: autoConfigLocale,
useLocale: useLocale,
getCurrentLocale: function() { return $translate.use() },
getAvailableLocales: function() { return _availableLocales },
getCurrentLocale: function () { return $translate.use() },
getAvailableLocales: function () { return _availableLocales },
// 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">
<p translate>Automatic upgrade now offers the choice between stable releases and release candidates.</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>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>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>You can read more about the two release channels at the link below.</span>
</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>
@ -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><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>
<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>
</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) {
return false;
}
return xdirArr.map(function(e, i) {
return xdirArr.map(function (e, i) {
return xdirArr[i] === ydirArr[i];
}).every(function(e) { return e });
}).every(function (e) { return e });
}
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
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%)
return input.toLocaleString(undefined, {maximumSignificantDigits: 2}) + '%';
return input.toLocaleString(undefined, { maximumSignificantDigits: 2 }) + '%';
};
});

View File

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

View File

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

View File

@ -1,6 +1,6 @@
angular.module('syncthing.core')
.config(function($locationProvider) {
$locationProvider.html5Mode({enabled: true, requireBase: false}).hashPrefix('!');
.config(function ($locationProvider) {
$locationProvider.html5Mode({ enabled: true, requireBase: false }).hashPrefix('!');
})
.controller('SyncthingController', function ($scope, $http, $location, LocaleService, Events, $filter, $q, $compile, $timeout, $rootScope, $translate) {
'use strict';
@ -65,7 +65,7 @@ angular.module('syncthing.core')
rescanIntervalS: 3600,
fsWatcherDelayS: 10,
fsWatcherEnabled: true,
minDiskFree: {value: 1, unit: "%"},
minDiskFree: { value: 1, unit: "%" },
maxConflicts: 10,
fsync: true,
order: "random",
@ -131,7 +131,7 @@ angular.module('syncthing.core')
}
$scope.version = data;
$scope.version.isDevelopmentVersion = data.version.indexOf('-')>0;
$scope.version.isDevelopmentVersion = data.version.indexOf('-') > 0;
}).error($scope.emitHTTPError);
$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
// to update last scan time.
if(data.from === 'scanning' && data.to === 'idle') {
if (data.from === 'scanning' && data.to === 'idle') {
refreshFolderStats();
}
}
@ -342,7 +342,7 @@ angular.module('syncthing.core')
});
$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 = {};
@ -447,7 +447,7 @@ angular.module('syncthing.core')
}).error($scope.emitHTTPError);
}
function recalcLocalStateTotal () {
function recalcLocalStateTotal() {
$scope.localStateTotal = {
bytes: 0,
directories: 0,
@ -455,9 +455,9 @@ angular.module('syncthing.core')
};
for (var f in $scope.model) {
$scope.localStateTotal.bytes += $scope.model[f].localBytes;
$scope.localStateTotal.files += $scope.model[f].localFiles;
$scope.localStateTotal.directories += $scope.model[f].localDirectories;
$scope.localStateTotal.bytes += $scope.model[f].localBytes;
$scope.localStateTotal.files += $scope.model[f].localFiles;
$scope.localStateTotal.directories += $scope.model[f].localDirectories;
}
}
@ -962,7 +962,7 @@ angular.module('syncthing.core')
var pendingFolders = 0;
for (var i = 0; i < $scope.devices.length; i++) {
var status = $scope.deviceStatus({
deviceID:$scope.devices[i].deviceID
deviceID: $scope.devices[i].deviceID
});
switch (status) {
case 'unknown':
@ -996,7 +996,7 @@ angular.module('syncthing.core')
}
// all used devices are paused except (this) one
if (pauseCount === deviceCount-1) {
if (pauseCount === deviceCount - 1) {
return 'pause';
}
@ -1085,11 +1085,11 @@ angular.module('syncthing.core')
$scope.logging = {
facilities: {},
refreshFacilities: function() {
refreshFacilities: function () {
$http.get(urlbase + '/system/debug').success(function (data) {
var facilities = {};
data.enabled = data.enabled || [];
$.each(data.facilities, function(key, value) {
$.each(data.facilities, function (key, value) {
facilities[key] = {
description: value,
enabled: data.enabled.indexOf(key) > -1
@ -1098,12 +1098,12 @@ angular.module('syncthing.core')
$scope.logging.facilities = facilities;
}).error($scope.emitHTTPError);
},
show: function() {
show: function () {
$scope.logging.refreshFacilities();
$scope.logging.timer = $timeout($scope.logging.fetch);
var textArea = $('#logViewerText');
textArea.on("scroll", $scope.logging.onScroll);
$('#logViewer').modal().one('shown.bs.modal', function() {
$('#logViewer').modal().one('shown.bs.modal', function () {
// Scroll to bottom.
textArea.scrollTop(textArea[0].scrollHeight);
}).one('hidden.bs.modal', function () {
@ -1113,17 +1113,17 @@ angular.module('syncthing.core')
$scope.logging.entries = [];
});
},
onFacilityChange: function(facility) {
onFacilityChange: function (facility) {
var enabled = $scope.logging.facilities[facility].enabled;
// 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;
})
$http.post(urlbase + '/system/debug?' + (enabled ? 'enable=':'disable=') + facility)
$http.post(urlbase + '/system/debug?' + (enabled ? 'enable=' : 'disable=') + facility)
.success($scope.logging.refreshFacilities)
.error($scope.emitHTTPError);
},
onScroll: function() {
onScroll: function () {
var textArea = $('#logViewerText');
var scrollTop = textArea.prop('scrollTop');
var scrollHeight = textArea.prop('scrollHeight');
@ -1134,14 +1134,14 @@ angular.module('syncthing.core')
timer: null,
entries: [],
paused: false,
content: function() {
content: function () {
var content = "";
$.each($scope.logging.entries, function (idx, entry) {
content += entry.when.split('.')[0].replace('T', ' ') + ' ' + entry.message + "\n";
});
return content;
},
fetch: function() {
fetch: function () {
var textArea = $('#logViewerText');
if ($scope.logging.paused) {
if (!$scope.logging.timer) return;
@ -1151,7 +1151,7 @@ angular.module('syncthing.core')
var last = null;
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) {
@ -1161,7 +1161,7 @@ angular.module('syncthing.core')
if (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.
$timeout(function() {
$timeout(function () {
textArea.scrollTop(textArea[0].scrollHeight);
});
}
@ -1191,7 +1191,7 @@ angular.module('syncthing.core')
settingsModal.off('hide.bs.modal');
}).on('hide.bs.modal', function (e) {
if ($scope.settingsModified()) {
$("#discard-changes-confirmation").modal().one('hidden.bs.modal', function() {
$("#discard-changes-confirmation").modal().one('hidden.bs.modal', function () {
if (!$scope.settingsModified()) {
settingsModal.modal('hide');
}
@ -1220,7 +1220,7 @@ angular.module('syncthing.core')
}).error($scope.emitHTTPError);
};
$scope.urVersions = function() {
$scope.urVersions = function () {
var result = [];
if ($scope.system) {
for (var i = $scope.system.urVersionMax; i >= 2; i--) {
@ -1357,7 +1357,7 @@ angular.module('syncthing.core')
$scope.currentDevice = $.extend({}, deviceCfg);
$scope.editingExisting = true;
$scope.willBeReintroducedBy = undefined;
if (deviceCfg.introducedBy) {
if (deviceCfg.introducedBy) {
var introducerDevice = $scope.findDevice(deviceCfg.introducedBy);
if (introducerDevice && introducerDevice.introducer) {
$scope.willBeReintroducedBy = $scope.deviceName(introducerDevice);
@ -1619,7 +1619,7 @@ angular.module('syncthing.core')
};
$scope.loadFormIntoScope = function (form) {
console.log('loadFormIntoScope',form.$name);
console.log('loadFormIntoScope', form.$name);
switch (form.$name) {
case 'deviceEditor':
$scope.deviceEditor = form;
@ -1707,16 +1707,16 @@ angular.module('syncthing.core')
$scope.editFolderModal();
};
$scope.selectAllDevices = function() {
$scope.selectAllDevices = function () {
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.deSelectAllDevices = function() {
$scope.deSelectAllDevices = function () {
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;
}
};
@ -1908,7 +1908,7 @@ angular.module('syncthing.core')
errors: null,
filters: {},
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 (action == 'unset') {
delete $scope.restoreVersions.selections[key];
@ -1916,7 +1916,7 @@ angular.module('syncthing.core')
}
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);
})
@ -1931,8 +1931,8 @@ angular.module('syncthing.core')
}
});
},
filterVersions: function(versions) {
var filteredVersions = [];
filterVersions: function (versions) {
var filteredVersions = [];
$.each(versions, function (idx, version) {
if (moment(version.versionTime).isBetween($scope.restoreVersions.filters['start'], $scope.restoreVersions.filters['end'], null, '[]')) {
filteredVersions.push(version);
@ -1940,9 +1940,9 @@ angular.module('syncthing.core')
});
return filteredVersions;
},
selectionCount: function() {
selectionCount: function () {
var count = 0;
$.each($scope.restoreVersions.selections, function(key, value) {
$.each($scope.restoreVersions.selections, function (key, value) {
if (value) {
count++;
}
@ -1950,12 +1950,12 @@ angular.module('syncthing.core')
return count;
},
restore: function() {
restore: function () {
$scope.restoreVersions.tree.clear();
$scope.restoreVersions.tree = null;
$scope.restoreVersions.versions = null;
var selections = {};
$.each($scope.restoreVersions.selections, function(key, value) {
$.each($scope.restoreVersions.selections, function (key, value) {
if (value) {
selections[key] = value;
}
@ -1970,7 +1970,7 @@ angular.module('syncthing.core')
}
});
},
show: function(folder) {
show: function (folder) {
$scope.restoreVersions.folder = folder;
var closed = false;
@ -1978,14 +1978,14 @@ angular.module('syncthing.core')
$('#restoreVersions').modal().one('hidden.bs.modal', function () {
closed = true;
resetRestoreVersions();
}).one('shown.bs.modal', function() {
}).one('shown.bs.modal', function () {
modalShown.resolve();
});
var dataReceived = $http.get(urlbase + '/folder/versions?folder=' + encodeURIComponent($scope.restoreVersions.folder))
.success(function (data) {
$.each(data, function(key, values) {
$.each(values, function(idx, value) {
$.each(data, function (key, values) {
$.each(values, function (idx, value) {
value.modTime = new Date(value.modTime);
value.versionTime = new Date(value.versionTime);
});
@ -1994,8 +1994,8 @@ angular.module('syncthing.core')
$scope.restoreVersions.versions = data;
});
$q.all([dataReceived, modalShown.promise]).then(function() {
$timeout(function(){
$q.all([dataReceived, modalShown.promise]).then(function () {
$timeout(function () {
if (closed) {
resetRestoreVersions();
return;
@ -2020,7 +2020,7 @@ angular.module('syncthing.core')
},
debugLevel: 2,
source: buildTree($scope.restoreVersions.versions),
renderColumns: function(event, data) {
renderColumns: function (event, data) {
var node = data.node,
$tdList = $(node.tr).find(">td"),
template;
@ -2039,7 +2039,7 @@ angular.module('syncthing.core')
);
// Force angular to redraw.
$timeout(function() {
$timeout(function () {
$scope.$apply();
});
}
@ -2050,8 +2050,8 @@ angular.module('syncthing.core')
date;
// Find version window.
$.each($scope.restoreVersions.versions, function(key) {
$.each($scope.restoreVersions.versions[key], function(idx, version) {
$.each($scope.restoreVersions.versions, function (key) {
$.each($scope.restoreVersions.versions[key], function (idx, version) {
date = moment(version.versionTime);
if (date.isBefore(minDate)) {
minDate = date;
@ -2066,17 +2066,17 @@ angular.module('syncthing.core')
$scope.restoreVersions.filters['end'] = maxDate;
var ranges = {
'All time': [minDate, maxDate],
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')],
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
'All time': [minDate, maxDate],
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')],
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
};
// 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, '[]')) {
delete ranges[key];
}
@ -2097,12 +2097,12 @@ angular.module('syncthing.core')
locale: {
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['end'] = picker.endDate;
// Events for this UI element are not managed by angular.
// Force angular to wake up.
$timeout(function() {
$timeout(function () {
$scope.$apply();
});
});
@ -2113,7 +2113,7 @@ angular.module('syncthing.core')
}
resetRestoreVersions();
$scope.$watchCollection('restoreVersions.filters', function() {
$scope.$watchCollection('restoreVersions.filters', function () {
if (!$scope.restoreVersions.tree) return;
$scope.restoreVersions.tree.filterNodes(function (node) {
@ -2163,7 +2163,7 @@ angular.module('syncthing.core')
$scope.showRemoteNeed = function (device) {
resetRemoteNeed();
$scope.remoteNeedDevice = device;
$scope.deviceFolders(device).forEach(function(folder) {
$scope.deviceFolders(device).forEach(function (folder) {
var comp = $scope.completion[device.deviceID][folder];
if (comp !== undefined && comp.needItems + comp.needDeletes === 0) {
return;
@ -2233,11 +2233,11 @@ angular.module('syncthing.core')
if ($scope.reportDataPreviewDiff && version > 2) {
$q.all([
$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) {
var newReport = responses[0].data;
var oldReport = responses[1].data;
angular.forEach(oldReport, function(_, key) {
angular.forEach(oldReport, function (_, key) {
delete newReport[key];
});
$scope.reportDataPreview = newReport;
@ -2257,7 +2257,7 @@ angular.module('syncthing.core')
$http.post(urlbase + "/db/scan?folder=" + encodeURIComponent(folder));
};
$scope.setAllFoldersPause = function(pause) {
$scope.setAllFoldersPause = function (pause) {
var folderListCache = $scope.folderList();
for (var i = 0; i < folderListCache.length; i++) {
@ -2268,7 +2268,7 @@ angular.module('syncthing.core')
$scope.saveConfig();
};
$scope.isAtleastOneFolderPausedStateSetTo = function(pause) {
$scope.isAtleastOneFolderPausedStateSetTo = function (pause) {
var folderListCache = $scope.folderList();
for (var i = 0; i < folderListCache.length; i++) {
@ -2280,10 +2280,10 @@ angular.module('syncthing.core')
return false;
};
$scope.activateAllFsWatchers = function() {
$scope.activateAllFsWatchers = function () {
var folders = $scope.folderList();
$.each(folders, function(i) {
$.each(folders, function (i) {
if (folders[i].fsWatcherEnabled) {
return;
}
@ -2331,7 +2331,7 @@ angular.module('syncthing.core')
'solaris': 'Solaris'
}[$scope.version.os] || $scope.version.os;
var arch ={
var arch = {
'386': '32 bit',
'amd64': '64 bit',
'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">
<link href="assets/css/dev.css" rel="stylesheet"/>
<link href="assets/css/dev.css" rel="stylesheet" />
<div class="row">
<div class="col-xs-4"><b>DEV</b></div>
<div id="log" class="col-xs-8">
@ -10,4 +10,4 @@
</span>
</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)">
<label translate for="deviceID">Device ID</label>
<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">
<option ng-repeat="id in discovery" value="{{id}}"/>
<option ng-repeat="id in discovery" value="{{id}}" />
</datalist>
<p class="help-block" ng-if="discovery">
<span translate>You can also select one of these nearby devices:</span>
@ -33,7 +33,7 @@
</div>
<div class="form-group">
<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 updated to the name the device advertises if left empty.</p>
</div>
@ -94,7 +94,7 @@
<div class="form-group">
<label translate for="addresses">Addresses</label>
<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 class="col-md-6">
@ -116,7 +116,7 @@
<div class="row">
<span class="col-md-8" translate>Incoming Rate Limit (KiB/s)</span>
<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>
<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">
<span class="col-md-8" translate>Outgoing Rate Limit (KiB/s)</span>
<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>
<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>
<modal id="globalChanges" status="default" icon="fas fa-info-circle" heading="{{'Recent Changes' | translate}}" large="yes" closeable="yes">
<div class="modal-body">
<div class="table-responsive">
<table class="table-condensed table-striped table" style="table-layout: auto;">
<thead>
<tr>
<th translate>Device</th>
<th translate>Action</th>
<th translate>Type</th>
<th translate>Folder</th>
<th translate>Path</th>
<th translate>Time</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="changeEvent in globalChangeEvents">
<td ng-if="changeEvent.data.modifiedBy">{{friendlyNameFromShort(changeEvent.data.modifiedBy)}}</td>
<td ng-if="!changeEvent.data.modifiedBy"><span translate>Unknown</span></td>
<td>{{changeEvent.data.action}}</td>
<td>{{changeEvent.data.type}}</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.time | date:"yyyy-MM-dd HH:mm:ss"}}</td>
</tr>
</tbody>
</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 class="modal-body">
<div class="table-responsive">
<table class="table-condensed table-striped table" style="table-layout: auto;">
<thead>
<tr>
<th translate>Device</th>
<th translate>Action</th>
<th translate>Type</th>
<th translate>Folder</th>
<th translate>Path</th>
<th translate>Time</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="changeEvent in globalChangeEvents">
<td ng-if="changeEvent.data.modifiedBy">{{friendlyNameFromShort(changeEvent.data.modifiedBy)}}</td>
<td ng-if="!changeEvent.data.modifiedBy"><span translate>Unknown</span></td>
<td>{{changeEvent.data.action}}</td>
<td>{{changeEvent.data.type}}</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.time | date:"yyyy-MM-dd HH:mm:ss"}}</td>
</tr>
</tbody>
</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>
</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">
<div class="modal-body">
<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 class="modal-footer">
<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 class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty}">
<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">
<span translate ng-if="folderEditor.folderLabel.$valid || folderEditor.folderLabel.$pristine">Optional descriptive label for the folder. Can be different on each device.</span>
</p>
</div>
<div class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
<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">
<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>
@ -29,7 +29,7 @@
</div>
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty}">
<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">
<option ng-repeat="directory in directoryList" value="{{ directory }}" />
</datalist>
@ -55,7 +55,7 @@
<div class="col-md-4" ng-repeat="device in otherDevices()">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentFolder.selectedDevices[device.deviceID]"/> {{deviceName(device)}}
<input type="checkbox" ng-model="currentFolder.selectedDevices[device.deviceID]" /> {{deviceName(device)}}
</label>
</div>
</div>
@ -77,7 +77,7 @@
<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>
<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>
<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}">
<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>
<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">
<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>
@ -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 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>
<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">
<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>
@ -109,7 +109,7 @@
</div>
<div class="form-group" ng-if="currentFolder.fileVersioningSelector == 'staggered'">
<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>
</div>
<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">
<p translate>Enter ignore patterns, one per line.</p>
<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>
<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>(?i)</code></dt> <dd><span translate>Prefix indicating that the pattern should be matched without case sensitivity</span></dd>
<dt><code>!</code></dt> <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>
<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>(?i)</code></dt>
<dd><span translate>Prefix indicating that the pattern should be matched without case sensitivity</span></dd>
<dt><code>!</code></dt>
<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>
<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">Creating ignore patterns, overwriting an existing file at {%path%}.</span></div>
</div>
@ -152,7 +158,7 @@
<div class="row">
<span class="col-md-8" translate>Full Rescan Interval (s)</span>
<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>
<p class="help-block" ng-if="!folderEditor.rescanIntervalS.$valid && folderEditor.rescanIntervalS.$dirty" translate>
@ -188,10 +194,10 @@
</div>
<div class="row">
<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="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 class="col-md-3">
<select class="form-control" ng-model="currentFolder.minDiskFree.unit">
@ -208,8 +214,8 @@
</p>
</div>
<div class="col-md-6 form-group">
<label translate>Permissions</label><br/>
<input type="checkbox" ng-model="currentFolder.ignorePerms"/> <span translate>Ignore</span>
<label translate>Permissions</label><br />
<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 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>

View File

@ -12,7 +12,7 @@
<tbody>
</tbody>
</table>
<hr/>
<hr />
<div class="row form-inline">
<div class="col-md-6">
<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">
<label for="guiInput{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
<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]" />
</div>
</div>
@ -37,7 +37,7 @@
<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>
<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]" />
</div>
</div>
@ -47,12 +47,12 @@
</div>
<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">
<span translate>Folder</span> "{{folder.id}}"
<span translate>Folder</span> "{{folder.id}}"
</h4>
<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>
</div>
<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">
<label for="folder{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
<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]" />
</div>
</div>
@ -71,9 +71,9 @@
</div>
<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">
<span translate>Device</span> "{{deviceName(device)}}"
<span translate>Device</span> "{{deviceName(device)}}"
</h4>
</div>
<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">
<label for="device{{$index}}Input{{$index}}" class="col-sm-4 control-label">{{key | uncamel}}</label>
<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]" />
</div>
</div>

View File

@ -28,22 +28,22 @@
<div id="settings-general" class="tab-pane in active">
<div class="form-group">
<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 class="row">
<div class="col-md-6">
<div class="form-horizontal">
<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/>
<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>
<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-3"><select class="col-sm-3 form-control" ng-model="tmpOptions.minHomeDiskFree.unit">
<option value="%">%</option>
<option value="kB">kB</option>
<option value="MB">MB</option>
<option value="GB">GB</option>
<option value="TB">TB</option>
</select></div>
<option value="%">%</option>
<option value="kB">kB</option>
<option value="MB">MB</option>
<option value="GB">GB</option>
<option value="TB">TB</option>
</select></div>
<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-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">
<label translate>API Key</label>
<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">
<button type="button" class="btn btn-default btn-secondary" ng-click="setAPIKey(tmpGUI)">
<span class="fas fa-redo"></span>&nbsp;<span translate>Generate</span>
@ -99,7 +99,7 @@
</div>
<div class="form-group">
<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">
<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%}.
@ -109,28 +109,28 @@
</div>
<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>
<p class="text-warning" ng-show="system.guiAddressOverridden">
<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 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>
</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>
<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 class="row">
<div class="col-md-6">
<div class="form-group">
<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 class="col-md-6">
<div class="form-group">
<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>
@ -139,7 +139,7 @@
<div class="form-group">
<div class="checkbox">
<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>
</div>
</div>
@ -148,7 +148,7 @@
<div class="form-group">
<div class="checkbox">
<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>
</div>
</div>
@ -176,13 +176,13 @@
<div id="settings-connections" class="tab-pane">
<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>
<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 class="row">
<div class="col-md-6">
<div class="form-group" ng-class="{'has-error': settingsEditor.MaxRecvKbps.$invalid && settingsEditor.MaxRecvKbps.$dirty}">
<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">
<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>
@ -191,7 +191,7 @@
<div class="col-md-6">
<div class="form-group" ng-class="{'has-error': settingsEditor.MaxSendKbps.$invalid && settingsEditor.MaxSendKbps.$dirty}">
<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">
<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>
@ -203,7 +203,7 @@
<div class="form-group">
<div class="checkbox">
<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>
</div>
</div>
@ -212,7 +212,7 @@
<div class="form-group">
<div class="checkbox">
<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>
</div>
</div>
@ -223,7 +223,7 @@
<div class="form-group">
<div class="checkbox">
<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>
</div>
</div>
@ -232,7 +232,7 @@
<div class="form-group">
<div class="checkbox">
<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>
</div>
</div>
@ -242,7 +242,7 @@
<div class="col-md-6">
<div class="form-group">
<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 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">
<div class="modal-body">
<div class="progress">
<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>
@ -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>
<hr/>
<hr />
<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><a href="https://data.syncthing.net/" target="_blank">https://data.syncthing.net/</a></p>
<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 ng-repeat="n in urVersions()" value="{{n}}">{{'Version' | translate}} {{n}}</option>
</select>
<div class="checkbox" ng-if="$parent.$parent.reportDataPreviewVersion > 2">
<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>
</label>
</div>