Flatten GUI tree somewhat
The very deep tree structure didn't really aggree with me, sorry. This makes the core module rather large, but on the other hand that just highlights that it is rather large.
This commit is contained in:
7
gui/syncthing/core/aboutModalDirective.js
Normal file
7
gui/syncthing/core/aboutModalDirective.js
Normal file
@@ -0,0 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('aboutModal', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'syncthing/core/aboutModalView.html'
|
||||
};
|
||||
});
|
||||
90
gui/syncthing/core/aboutModalView.html
Normal file
90
gui/syncthing/core/aboutModalView.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<modal id="about" large="yes" close="yes" status="info" title="{{'About' | translate}}">
|
||||
<h1 class="text-center"><img alt="Syncthing" title="Syncthing" src="assets/img/logo-horizontal.svg" style="vertical-align: -16px" height="100" width="366"/><br/><small>{{versionString()}}</small></h1>
|
||||
<hr/>
|
||||
|
||||
<p translate>Copyright © 2015 the following Contributors:</p>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<ul class="list-unstyled three-columns" id="contributor-list">
|
||||
<li class="auto-generated">Aaron Bieber</li>
|
||||
<li class="auto-generated">Alexander Graf</li>
|
||||
<li class="auto-generated">Andrew Dunham</li>
|
||||
<li class="auto-generated">Antony Male</li>
|
||||
<li class="auto-generated">Arthur Axel fREW Schmidt</li>
|
||||
<li class="auto-generated">Audrius Butkevicius</li>
|
||||
<li class="auto-generated">Bart De Vries</li>
|
||||
<li class="auto-generated">Ben Curthoys</li>
|
||||
<li class="auto-generated">Ben Schulz</li>
|
||||
<li class="auto-generated">Ben Sidhom</li>
|
||||
<li class="auto-generated">Brandon Philips</li>
|
||||
<li class="auto-generated">Brendan Long</li>
|
||||
<li class="auto-generated">Brian R. Becker</li>
|
||||
<li class="auto-generated">Caleb Callaway</li>
|
||||
<li class="auto-generated">Carsten Hagemann</li>
|
||||
<li class="auto-generated">Cathryne Linenweaver</li>
|
||||
<li class="auto-generated">Chris Howie</li>
|
||||
<li class="auto-generated">Chris Joel</li>
|
||||
<li class="auto-generated">Colin Kennedy</li>
|
||||
<li class="auto-generated">Daniel Bergmann</li>
|
||||
<li class="auto-generated">Daniel Martí</li>
|
||||
<li class="auto-generated">Denis A.</li>
|
||||
<li class="auto-generated">Dennis Wilson</li>
|
||||
<li class="auto-generated">Dominik Heidler</li>
|
||||
<li class="auto-generated">Elias Jarlebring</li>
|
||||
<li class="auto-generated">Emil Hessman</li>
|
||||
<li class="auto-generated">Erik Meitner</li>
|
||||
<li class="auto-generated">Federico Castagnini</li>
|
||||
<li class="auto-generated">Felix Ableitner</li>
|
||||
<li class="auto-generated">Felix Unterpaintner</li>
|
||||
<li class="auto-generated">Francois-Xavier Gsell</li>
|
||||
<li class="auto-generated">Frank Isemann</li>
|
||||
<li class="auto-generated">Gilli Sigurdsson</li>
|
||||
<li class="auto-generated">Jacek Szafarkiewicz</li>
|
||||
<li class="auto-generated">Jakob Borg</li>
|
||||
<li class="auto-generated">James Patterson</li>
|
||||
<li class="auto-generated">Jaroslav Malec</li>
|
||||
<li class="auto-generated">Jens Diemer</li>
|
||||
<li class="auto-generated">Jochen Voss</li>
|
||||
<li class="auto-generated">Johan Vromans</li>
|
||||
<li class="auto-generated">Karol Różycki</li>
|
||||
<li class="auto-generated">Ken'ichi Kamada</li>
|
||||
<li class="auto-generated">Lode Hoste</li>
|
||||
<li class="auto-generated">Lord Landon Agahnim</li>
|
||||
<li class="auto-generated">Marc Laporte</li>
|
||||
<li class="auto-generated">Marc Pujol</li>
|
||||
<li class="auto-generated">Marcin Dziadus</li>
|
||||
<li class="auto-generated">Michael Jephcote</li>
|
||||
<li class="auto-generated">Michael Tilli</li>
|
||||
<li class="auto-generated">Pascal Jungblut</li>
|
||||
<li class="auto-generated">Peter Hoeg</li>
|
||||
<li class="auto-generated">Philippe Schommers</li>
|
||||
<li class="auto-generated">Phill Luby</li>
|
||||
<li class="auto-generated">Piotr Bejda</li>
|
||||
<li class="auto-generated">Ryan Sullivan</li>
|
||||
<li class="auto-generated">Sergey Mishin</li>
|
||||
<li class="auto-generated">Stefan Tatschner</li>
|
||||
<li class="auto-generated">Tim Abell</li>
|
||||
<li class="auto-generated">Tobias Nygren</li>
|
||||
<li class="auto-generated">Tomas Cerveny</li>
|
||||
<li class="auto-generated">Tully Robinson</li>
|
||||
<li class="auto-generated">Veeti Paananen</li>
|
||||
<li class="auto-generated">Vil Brekin</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
<p translate>Syncthing includes the following software or portions thereof:</p>
|
||||
<ul class="list-unstyled two-columns">
|
||||
<li><a href="https://golang.org">The Go Programming Language</a>, Copyright © 2012 The Go Authors.</li>
|
||||
<li><a href="https://github.com/bkaradzic/go-lz4">bkaradzic/go-lz4</a>, Copyright © 2011-2012 Branimir Karadzic, 2013 Damian Gryski.</li>
|
||||
<li><a href="https://github.com/kardianos/osext">kardianos/osext</a>, Copyright © 2012 Daniel Theophanes.</li>
|
||||
<li><a href="https://github.com/golang/snappy">golang/snappy</a>, Copyright © 2011 The Snappy-Go Authors.</li>
|
||||
<li><a href="https://github.com/juju/ratelimit">juju/ratelimit</a>, Copyright © 2015 Canonical Ltd.</li>
|
||||
<li><a href="https://github.com/thejerf/suture">thejerf/suture</a>, Copyright © 2014-2015 Barracuda Networks, Inc.</li>
|
||||
<li><a href="https://github.com/syndtr/goleveldb">syndtr/goleveldb</a>, Copyright © 2012, Suryandaru Triandana</li>
|
||||
<li><a href="https://github.com/vitrun/qart">vitrun/qart</a>, Copyright © The Go Authors.</li>
|
||||
<li><a href="https://angularjs.org/">AngularJS</a>, Copyright © 2010-2015 Google, Inc.</li>
|
||||
<li><a href="http://getbootstrap.com/">Bootstrap</a>, Copyright © 2011-2015 Twitter, Inc.</li>
|
||||
</ul>
|
||||
</modal>
|
||||
9
gui/syncthing/core/alwaysNumberFilter.js
Normal file
9
gui/syncthing/core/alwaysNumberFilter.js
Normal file
@@ -0,0 +1,9 @@
|
||||
angular.module('syncthing.core')
|
||||
.filter('alwaysNumber', function () {
|
||||
return function (input) {
|
||||
if (input === undefined) {
|
||||
return 0;
|
||||
}
|
||||
return input;
|
||||
};
|
||||
});
|
||||
12
gui/syncthing/core/basenameFilter.js
Normal file
12
gui/syncthing/core/basenameFilter.js
Normal file
@@ -0,0 +1,12 @@
|
||||
angular.module('syncthing.core')
|
||||
.filter('basename', function () {
|
||||
return function (input) {
|
||||
if (input === undefined)
|
||||
return "";
|
||||
var parts = input.split(/[\/\\]/);
|
||||
if (!parts || parts.length < 1) {
|
||||
return input;
|
||||
}
|
||||
return parts[parts.length - 1];
|
||||
};
|
||||
});
|
||||
21
gui/syncthing/core/binaryFilter.js
Normal file
21
gui/syncthing/core/binaryFilter.js
Normal file
@@ -0,0 +1,21 @@
|
||||
angular.module('syncthing.core')
|
||||
.filter('binary', function () {
|
||||
return function (input) {
|
||||
if (input === undefined) {
|
||||
return '0 ';
|
||||
}
|
||||
if (input > 1024 * 1024 * 1024) {
|
||||
input /= 1024 * 1024 * 1024;
|
||||
return input.toFixed(decimals(input, 2)) + ' Gi';
|
||||
}
|
||||
if (input > 1024 * 1024) {
|
||||
input /= 1024 * 1024;
|
||||
return input.toFixed(decimals(input, 2)) + ' Mi';
|
||||
}
|
||||
if (input > 1024) {
|
||||
input /= 1024;
|
||||
return input.toFixed(decimals(input, 2)) + ' Ki';
|
||||
}
|
||||
return Math.round(input) + ' ';
|
||||
};
|
||||
});
|
||||
35
gui/syncthing/core/durationFilter.js
Normal file
35
gui/syncthing/core/durationFilter.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/** convert amount of seconds to string format "d h m s" without zero values
|
||||
* precision must be one of 'd', 'h', 'm', 's'(default)
|
||||
* Example:
|
||||
* {{121020003|duration}} --> 1400d 16h 40m 3s
|
||||
* {{121020003|duration:"m"}} --> 1400d 16h 40m
|
||||
* {{121020003|duration:"h"}} --> 1400d 16h
|
||||
* {{1|duration:"h"}} --> <1h
|
||||
**/
|
||||
angular.module('syncthing.core')
|
||||
.filter('duration', function () {
|
||||
'use strict';
|
||||
|
||||
var SECONDS_IN = {"d": 86400, "h": 3600, "m": 60, "s": 1};
|
||||
return function (input, precision) {
|
||||
var result = "";
|
||||
if (!precision) {
|
||||
precision = "s";
|
||||
}
|
||||
input = parseInt(input, 10);
|
||||
for (var k in SECONDS_IN) {
|
||||
var t = (input/SECONDS_IN[k] | 0); // Math.floor
|
||||
|
||||
if (t > 0) {
|
||||
result += " " + t + k;
|
||||
}
|
||||
|
||||
if (precision == k) {
|
||||
return result ? result : "<1" + k;
|
||||
} else {
|
||||
input %= SECONDS_IN[k];
|
||||
}
|
||||
}
|
||||
return "[Error: incorrect usage, precision must be one of " + Object.keys(SECONDS_IN) + "]";
|
||||
};
|
||||
});
|
||||
86
gui/syncthing/core/eventService.js
Normal file
86
gui/syncthing/core/eventService.js
Normal file
@@ -0,0 +1,86 @@
|
||||
var debugEvents = !true;
|
||||
|
||||
angular.module('syncthing.core')
|
||||
.service('Events', ['$http', '$rootScope', '$timeout', function ($http, $rootScope, $timeout) {
|
||||
'use strict';
|
||||
|
||||
var lastID = 0;
|
||||
var self = this;
|
||||
|
||||
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.
|
||||
// This basically means that the connection has been reset, and the call
|
||||
// was not actually successful.
|
||||
if (!data) {
|
||||
errorFn(data);
|
||||
return;
|
||||
}
|
||||
$rootScope.$broadcast(self.ONLINE);
|
||||
|
||||
if (lastID > 0) { // not emit events from first response
|
||||
data.forEach(function (event) {
|
||||
if (debugEvents) {
|
||||
console.log("event", event.id, event.type, event.data);
|
||||
}
|
||||
$rootScope.$broadcast(event.type, event);
|
||||
});
|
||||
}
|
||||
|
||||
var lastEvent = data.pop();
|
||||
if (lastEvent) {
|
||||
lastID = lastEvent.id;
|
||||
}
|
||||
|
||||
$timeout(function () {
|
||||
$http.get(urlbase + '/events?since=' + lastID)
|
||||
.success(successFn)
|
||||
.error(errorFn);
|
||||
}, 500, false);
|
||||
}
|
||||
|
||||
function errorFn (dummy) {
|
||||
$rootScope.$broadcast(self.OFFLINE);
|
||||
|
||||
$timeout(function () {
|
||||
$http.get(urlbase + '/events?limit=1')
|
||||
.success(successFn)
|
||||
.error(errorFn);
|
||||
}, 1000, false);
|
||||
}
|
||||
|
||||
angular.extend(self, {
|
||||
// emitted by this
|
||||
|
||||
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
|
||||
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
|
||||
PING: 'Ping', // Generated automatically every 60 seconds
|
||||
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
|
||||
|
||||
start: function() {
|
||||
$http.get(urlbase + '/events?limit=1')
|
||||
.success(successFn)
|
||||
.error(errorFn);
|
||||
}
|
||||
});
|
||||
}]);
|
||||
27
gui/syncthing/core/filterStabilize.js
Normal file
27
gui/syncthing/core/filterStabilize.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* m59peacemaker's filterStabilize
|
||||
*
|
||||
* See https://github.com/m59peacemaker/angular-pmkr-components/tree/master/src/filterStabilize
|
||||
* Released under the MIT license
|
||||
*/
|
||||
angular.module('syncthing.core')
|
||||
.factory('pmkr.filterStabilize', [
|
||||
'pmkr.memoize',
|
||||
function(memoize) {
|
||||
function service(fn) {
|
||||
function filter() {
|
||||
var args = [].slice.call(arguments);
|
||||
// always pass a copy of the args so that the original input can't be modified
|
||||
args = angular.copy(args);
|
||||
// return the `fn` return value or input reference (makes `fn` return optional)
|
||||
var filtered = fn.apply(this, args) || args[0];
|
||||
return filtered;
|
||||
}
|
||||
|
||||
var memoized = memoize(filter);
|
||||
return memoized;
|
||||
|
||||
}
|
||||
return service;
|
||||
}
|
||||
]);
|
||||
25
gui/syncthing/core/groupFilter.js
Normal file
25
gui/syncthing/core/groupFilter.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Groups input in chunks of the specified size
|
||||
*
|
||||
* E.g. [1, 2, 3, 4, 5] with groupSize = 3 => [[1, 2, 3], [4, 5]]
|
||||
* Uses pmkr.memoize to avoid infdig, see 'Johnny Hauser's "Filter Stablize" Solution'
|
||||
* here: http://sobrepere.com/blog/2014/10/14/creating-groupby-filter-angularjs/
|
||||
*/
|
||||
angular.module('syncthing.core')
|
||||
.filter('group', [
|
||||
'pmkr.filterStabilize',
|
||||
function (stabilize) {
|
||||
return stabilize(function(items, groupSize) {
|
||||
var groups = [];
|
||||
var inner;
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (i % groupSize === 0) {
|
||||
inner = [];
|
||||
groups.push(inner);
|
||||
}
|
||||
inner.push(items[i]);
|
||||
}
|
||||
return groups;
|
||||
});
|
||||
}
|
||||
]);
|
||||
7
gui/syncthing/core/httpErrorDialogDirective.js
Normal file
7
gui/syncthing/core/httpErrorDialogDirective.js
Normal file
@@ -0,0 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('httpErrorDialog', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'syncthing/core//httpErrorDialogView.html'
|
||||
};
|
||||
});
|
||||
5
gui/syncthing/core/httpErrorDialogView.html
Normal file
5
gui/syncthing/core/httpErrorDialogView.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<modal id="httpError" status="danger" icon="exclamation-sign" title="{{'Connection Error' | translate}}">
|
||||
<p translate>
|
||||
Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.
|
||||
</p>
|
||||
</modal>
|
||||
65
gui/syncthing/core/identiconDirective.js
Normal file
65
gui/syncthing/core/identiconDirective.js
Normal file
@@ -0,0 +1,65 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('identicon', ['$window', function ($window) {
|
||||
var svgNS = 'http://www.w3.org/2000/svg';
|
||||
|
||||
function Identicon(value, size) {
|
||||
var svg = document.createElementNS(svgNS, 'svg');
|
||||
var shouldFillRectAt = function (row, col) {
|
||||
return !($window.parseInt(value.charCodeAt(row + col * size), 10) % 2);
|
||||
};
|
||||
var shouldMirrorRectAt = function (row, col) {
|
||||
return !(size % 2 && col === middleCol)
|
||||
};
|
||||
var mirrorColFor = function (col) {
|
||||
return size - col - 1;
|
||||
};
|
||||
var fillRectAt = function (row, col) {
|
||||
var rect = document.createElementNS(svgNS, 'rect');
|
||||
|
||||
rect.setAttribute('x', (col * rectSize) + '%');
|
||||
rect.setAttribute('y', (row * rectSize) + '%');
|
||||
rect.setAttribute('width', rectSize + '%');
|
||||
rect.setAttribute('height', rectSize + '%');
|
||||
|
||||
svg.appendChild(rect);
|
||||
};
|
||||
var rect;
|
||||
var row;
|
||||
var col;
|
||||
var middleCol;
|
||||
var rectSize;
|
||||
|
||||
svg.setAttribute('class', 'identicon');
|
||||
size = size || 5;
|
||||
rectSize = 100 / size;
|
||||
middleCol = Math.ceil(size / 2) - 1;
|
||||
|
||||
if (value) {
|
||||
value = value.toString().replace(/[\W_]/i, '');
|
||||
|
||||
for (row = 0; row < size; ++row) {
|
||||
for (col = middleCol; col > -1; --col) {
|
||||
if (shouldFillRectAt(row, col)) {
|
||||
fillRectAt(row, col);
|
||||
|
||||
if (shouldMirrorRectAt(row, col)) {
|
||||
fillRectAt(row, mirrorColFor(col));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return svg;
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
value: '='
|
||||
},
|
||||
link: function (scope, element, attributes) {
|
||||
element.append(new Identicon(scope.value));
|
||||
}
|
||||
}
|
||||
}]);
|
||||
48
gui/syncthing/core/languageSelectDirective.js
Normal file
48
gui/syncthing/core/languageSelectDirective.js
Normal file
@@ -0,0 +1,48 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('languageSelect', function (LocaleService) {
|
||||
'use strict';
|
||||
return {
|
||||
restrict: 'EA',
|
||||
template:
|
||||
'<a ng-if="visible" href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="true"><span class="glyphicon glyphicon-globe"></span> {{localesNames[currentLocale] || "English"}} <span class="caret"></span></a>'+
|
||||
'<ul ng-if="visible" class="dropdown-menu">'+
|
||||
'<li ng-repeat="(i,name) in localesNames" ng-class="{active: i==currentLocale}">'+
|
||||
'<a href="#" data-ng-click="changeLanguage(i)">{{name}}</a>'+
|
||||
'</li>'+
|
||||
'</ul>',
|
||||
|
||||
link: function ($scope) {
|
||||
var availableLocales = LocaleService.getAvailableLocales();
|
||||
var localeNames = LocaleService.getLocalesDisplayNames();
|
||||
var availableLocaleNames = {};
|
||||
|
||||
// get only locale names that present in available locales
|
||||
for (var i = 0; i < availableLocales.length; i++) {
|
||||
var a = availableLocales[i];
|
||||
if (localeNames[a]) {
|
||||
availableLocaleNames[a] = localeNames[a];
|
||||
} else {
|
||||
// show code lang if it is not in the dict
|
||||
availableLocaleNames[a] = '[' + a + ']';
|
||||
}
|
||||
}
|
||||
|
||||
$scope.localesNames = availableLocaleNames;
|
||||
$scope.visible = $scope.localesNames && $scope.localesNames['en'];
|
||||
|
||||
// using $watch cause LocaleService.currentLocale will be change after receive async query accepted-languages
|
||||
// in LocaleService.readBrowserLocales
|
||||
var remove_watch = $scope.$watch(LocaleService.getCurrentLocale, function (newValue) {
|
||||
if (newValue) {
|
||||
$scope.currentLocale = newValue;
|
||||
remove_watch();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.changeLanguage = function (locale) {
|
||||
LocaleService.useLocale(locale, true);
|
||||
$scope.currentLocale = locale;
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
12
gui/syncthing/core/lastErrorComponentFilter.js
Normal file
12
gui/syncthing/core/lastErrorComponentFilter.js
Normal file
@@ -0,0 +1,12 @@
|
||||
angular.module('syncthing.core')
|
||||
.filter('lastErrorComponent', function () {
|
||||
return function (input) {
|
||||
if (input === undefined)
|
||||
return "";
|
||||
var parts = input.split(/:\s*/);
|
||||
if (!parts || parts.length < 1) {
|
||||
return input;
|
||||
}
|
||||
return parts[parts.length - 1];
|
||||
};
|
||||
});
|
||||
119
gui/syncthing/core/localeService.js
Normal file
119
gui/syncthing/core/localeService.js
Normal file
@@ -0,0 +1,119 @@
|
||||
angular.module('syncthing.core')
|
||||
.provider('LocaleService', function () {
|
||||
'use strict';
|
||||
|
||||
function detectLocalStorage() {
|
||||
// Feature detect localStorage; https://mathiasbynens.be/notes/localstorage-pattern
|
||||
try {
|
||||
var uid = new Date();
|
||||
var storage = window.localStorage;
|
||||
storage.setItem(uid, uid);
|
||||
var success = storage.getItem(uid) == uid;
|
||||
storage.removeItem(uid);
|
||||
return storage;
|
||||
} catch (exception) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
var _defaultLocale,
|
||||
_availableLocales,
|
||||
_localStorage = detectLocalStorage();
|
||||
|
||||
var _SYNLANG = "SYN_LANG"; // const key for localStorage
|
||||
|
||||
this.setDefaultLocale = function (locale) {
|
||||
_defaultLocale = locale;
|
||||
};
|
||||
|
||||
this.setAvailableLocales = function (locales) {
|
||||
_availableLocales = locales;
|
||||
};
|
||||
|
||||
|
||||
this.$get = ['$http', '$translate', '$location', function ($http, $translate, $location) {
|
||||
|
||||
/**
|
||||
* Requests the server in order to get the browser's requested locale strings.
|
||||
*
|
||||
* @returns promise which on success resolves with a locales array
|
||||
*/
|
||||
function readBrowserLocales() {
|
||||
// @TODO: check if there is nice way to utilize window.navigator.languages or similar api.
|
||||
|
||||
return $http.get(urlbase + "/svc/lang");
|
||||
}
|
||||
|
||||
function autoConfigLocale() {
|
||||
var params = $location.search();
|
||||
var savedLang;
|
||||
if (_localStorage) {
|
||||
savedLang = _localStorage[_SYNLANG];
|
||||
}
|
||||
|
||||
if(params.lang) {
|
||||
useLocale(params.lang, true);
|
||||
} else if (savedLang) {
|
||||
useLocale(savedLang);
|
||||
} else {
|
||||
readBrowserLocales().success(function (langs) {
|
||||
// Find the first language in the list provided by the user's browser
|
||||
// that is a prefix of a language we have available. That is, "en"
|
||||
// sent by the browser will match "en" or "en-US", while "zh-TW" will
|
||||
// match only "zh-TW" and not "zh-CN".
|
||||
|
||||
var i,
|
||||
lang,
|
||||
matching,
|
||||
locale = _defaultLocale;
|
||||
|
||||
for (i = 0; i < langs.length; i++) {
|
||||
lang = langs[i];
|
||||
|
||||
if (lang.length < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matching = _availableLocales.filter(function (possibleLang) {
|
||||
// The langs returned by the /rest/langs call will be in lower
|
||||
// case. We compare to the lowercase version of the language
|
||||
// code we have as well.
|
||||
possibleLang = possibleLang.toLowerCase();
|
||||
if (possibleLang.length > lang.length) {
|
||||
return possibleLang.indexOf(lang) === 0;
|
||||
} else {
|
||||
return lang.indexOf(possibleLang) === 0;
|
||||
}
|
||||
});
|
||||
|
||||
if (matching.length >= 1) {
|
||||
locale = matching[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fallback if nothing matched
|
||||
useLocale(locale);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function useLocale(language, save2Storage) {
|
||||
if (language) {
|
||||
$translate.use(language).then(function () {
|
||||
if (save2Storage && _localStorage)
|
||||
_localStorage[_SYNLANG] = language;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
autoConfigLocale: autoConfigLocale,
|
||||
useLocale: useLocale,
|
||||
getCurrentLocale: function() { return $translate.use() },
|
||||
getAvailableLocales: function() { return _availableLocales },
|
||||
// langPrettyprint comes from an included global
|
||||
getLocalesDisplayNames: function() { return langPrettyprint }
|
||||
}
|
||||
}];
|
||||
|
||||
});
|
||||
7
gui/syncthing/core/majorUpgradeModalDirective.js
Normal file
7
gui/syncthing/core/majorUpgradeModalDirective.js
Normal file
@@ -0,0 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('majorUpgradeModal', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'syncthing/core//majorUpgradeModalView.html'
|
||||
};
|
||||
});
|
||||
26
gui/syncthing/core/majorUpgradeModalView.html
Normal file
26
gui/syncthing/core/majorUpgradeModalView.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<div id="majorUpgrade" class="modal fade" tabindex="-1" data-backdrop="true" data-keyboard="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header alert alert-danger">
|
||||
<h4 class="modal-title">
|
||||
<span ng-if="icon" class="glyphicon glyphicon-chevron-up"></span>
|
||||
<span translate>Major Upgrade</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
<span translate>This is a major version upgrade.</span>
|
||||
<span translate>A new major version may not be compatible with previous versions.</span>
|
||||
<span translate>Please consult the release notes before performing a major upgrade.</span>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/syncthing/syncthing/releases/tag/{{upgradeInfo.latest}}" target="_blank" translate>Release Notes</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="upgrade()"><span class="glyphicon glyphicon-ok"></span> <span translate>Upgrade</span></button>
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> <span translate>Close</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
28
gui/syncthing/core/memoize.js
Normal file
28
gui/syncthing/core/memoize.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* m59peacemaker's memoize
|
||||
*
|
||||
* See https://github.com/m59peacemaker/angular-pmkr-components/tree/master/src/memoize
|
||||
* Released under the MIT license
|
||||
*/
|
||||
angular.module('syncthing.core')
|
||||
.factory('pmkr.memoize', [
|
||||
function() {
|
||||
function service() {
|
||||
return memoizeFactory.apply(this, arguments);
|
||||
}
|
||||
function memoizeFactory(fn) {
|
||||
var cache = {};
|
||||
function memoized() {
|
||||
var args = [].slice.call(arguments);
|
||||
var key = JSON.stringify(args);
|
||||
if (cache.hasOwnProperty(key)) {
|
||||
return cache[key];
|
||||
}
|
||||
cache[key] = fn.apply(this, arguments);
|
||||
return cache[key];
|
||||
}
|
||||
return memoized;
|
||||
}
|
||||
return service;
|
||||
}
|
||||
]);
|
||||
16
gui/syncthing/core/modalDirective.js
Normal file
16
gui/syncthing/core/modalDirective.js
Normal file
@@ -0,0 +1,16 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('modal', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'modal.html',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: '@',
|
||||
status: '@',
|
||||
icon: '@',
|
||||
close: '@',
|
||||
large: '@'
|
||||
}
|
||||
};
|
||||
});
|
||||
1
gui/syncthing/core/module.js
Normal file
1
gui/syncthing/core/module.js
Normal file
@@ -0,0 +1 @@
|
||||
angular.module('syncthing.core', []);
|
||||
6
gui/syncthing/core/naturalFilter.js
Normal file
6
gui/syncthing/core/naturalFilter.js
Normal file
@@ -0,0 +1,6 @@
|
||||
angular.module('syncthing.core')
|
||||
.filter('natural', function () {
|
||||
return function (input, valid) {
|
||||
return input.toFixed(decimals(input, valid));
|
||||
};
|
||||
});
|
||||
7
gui/syncthing/core/networkErrorDialogDirective.js
Normal file
7
gui/syncthing/core/networkErrorDialogDirective.js
Normal file
@@ -0,0 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('networkErrorDialog', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'syncthing/core//networkErrorDialogView.html'
|
||||
};
|
||||
});
|
||||
5
gui/syncthing/core/networkErrorDialogView.html
Normal file
5
gui/syncthing/core/networkErrorDialogView.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<modal id="networkError" status="danger" icon="exclamation-sign" title="{{'Connection Error' | translate}}">
|
||||
<p translate>
|
||||
Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…
|
||||
</p>
|
||||
</modal>
|
||||
9
gui/syncthing/core/popoverDirective.js
Normal file
9
gui/syncthing/core/popoverDirective.js
Normal file
@@ -0,0 +1,9 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('popover', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function (scope, element, attributes) {
|
||||
$(element).popover();
|
||||
}
|
||||
};
|
||||
});
|
||||
7
gui/syncthing/core/restartingDialogDirective.js
Normal file
7
gui/syncthing/core/restartingDialogDirective.js
Normal file
@@ -0,0 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('restartingDialog', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'syncthing/core//restartingDialogView.html'
|
||||
};
|
||||
});
|
||||
3
gui/syncthing/core/restartingDialogView.html
Normal file
3
gui/syncthing/core/restartingDialogView.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<modal id="restarting" icon="refresh" title="{{'Restarting' | translate}}" status="info">
|
||||
<p><span translate>Syncthing is restarting.</span> <span translate>Please wait</span>...</p>
|
||||
</modal>
|
||||
14
gui/syncthing/core/selectOnClickDirective.js
Normal file
14
gui/syncthing/core/selectOnClickDirective.js
Normal file
@@ -0,0 +1,14 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('selectOnClick', function ($window) {
|
||||
return {
|
||||
link: function (scope, element, attrs) {
|
||||
element.on('click', function() {
|
||||
var selection = $window.getSelection();
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(element[0]);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
7
gui/syncthing/core/shutdownDialogDirective.js
Normal file
7
gui/syncthing/core/shutdownDialogDirective.js
Normal file
@@ -0,0 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('shutdownDialog', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'syncthing/core//shutdownDialogView.html'
|
||||
};
|
||||
});
|
||||
3
gui/syncthing/core/shutdownDialogView.html
Normal file
3
gui/syncthing/core/shutdownDialogView.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<modal id="shutdown" icon="off" status="success" title="{{'Shutdown Complete' | translate}}">
|
||||
<p translate>Syncthing has been shut down.</p>
|
||||
</modal>
|
||||
1389
gui/syncthing/core/syncthingController.js
Executable file
1389
gui/syncthing/core/syncthingController.js
Executable file
File diff suppressed because it is too large
Load Diff
21
gui/syncthing/core/uniqueFolderDirective.js
Normal file
21
gui/syncthing/core/uniqueFolderDirective.js
Normal file
@@ -0,0 +1,21 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('uniqueFolder', function () {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function (viewValue) {
|
||||
if (scope.editingExisting) {
|
||||
// we shouldn't validate
|
||||
ctrl.$setValidity('uniqueFolder', true);
|
||||
} else if (scope.folders.hasOwnProperty(viewValue)) {
|
||||
// the folder exists already
|
||||
ctrl.$setValidity('uniqueFolder', false);
|
||||
} else {
|
||||
// the folder is unique
|
||||
ctrl.$setValidity('uniqueFolder', true);
|
||||
}
|
||||
return viewValue;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
7
gui/syncthing/core/upgradingDialogDirective.js
Normal file
7
gui/syncthing/core/upgradingDialogDirective.js
Normal file
@@ -0,0 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('upgradingDialog', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'syncthing/core//upgradingDialogView.html'
|
||||
};
|
||||
});
|
||||
3
gui/syncthing/core/upgradingDialogView.html
Normal file
3
gui/syncthing/core/upgradingDialogView.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<modal id="upgrading" icon="refresh" title="{{'Upgrading' | translate}}" status="info">
|
||||
<p><span translate>Syncthing is upgrading.</span> <span translate>Please wait</span>...</p>
|
||||
</modal>
|
||||
23
gui/syncthing/core/validDeviceidDirective.js
Normal file
23
gui/syncthing/core/validDeviceidDirective.js
Normal file
@@ -0,0 +1,23 @@
|
||||
angular.module('syncthing.core')
|
||||
.directive('validDeviceid', function ($http) {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function (viewValue) {
|
||||
if (scope.editingExisting) {
|
||||
// we shouldn't validate
|
||||
ctrl.$setValidity('validDeviceid', true);
|
||||
} else {
|
||||
$http.get(urlbase + '/svc/deviceid?id=' + viewValue).success(function (resp) {
|
||||
if (resp.error) {
|
||||
ctrl.$setValidity('validDeviceid', false);
|
||||
} else {
|
||||
ctrl.$setValidity('validDeviceid', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
return viewValue;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user