mirror of
https://github.com/duplicati/duplicati.git
synced 2025-11-28 19:40:25 +08:00
This adds the common "Movies" and "Public" folders to the list of special folders for MacOS (and Linux, if applicable)
816 lines
28 KiB
JavaScript
816 lines
28 KiB
JavaScript
backupApp.service('AppUtils', function($rootScope, $timeout, $cookies, AppService, DialogService, gettextCatalog) {
|
|
|
|
var apputils = this;
|
|
|
|
this.exampleOptionString = '--dblock-size=100MB';
|
|
|
|
function setMomentLocale() {
|
|
try {
|
|
var browser_lang = navigator.languages ?
|
|
navigator.languages[0] :
|
|
(navigator.language || navigator.userLanguage);
|
|
moment.locale($cookies.get('ui-locale') ? $cookies.get('ui-locale') : browser_lang);
|
|
} catch (e) {
|
|
}
|
|
}
|
|
setMomentLocale();
|
|
$rootScope.$on('ui_language_changed', setMomentLocale);
|
|
|
|
this.formatSizeString = function(val) {
|
|
var formatSizes = ['TB', 'GB', 'MB', 'KB'];
|
|
val = parseInt(val || 0);
|
|
var max = formatSizes.length;
|
|
for(var i = 0; i < formatSizes.length; i++) {
|
|
var m = Math.pow(1024, max - i);
|
|
if (val > m) {
|
|
return (val / m).toFixed(2) + ' ' + formatSizes[i];
|
|
}
|
|
}
|
|
|
|
return val + ' bytes';
|
|
};
|
|
|
|
this.watch = function(scope, m) {
|
|
scope.$on('apputillookupschanged', function() {
|
|
if (m) m();
|
|
|
|
$timeout(function() {
|
|
scope.$digest();
|
|
});
|
|
});
|
|
|
|
if (m) $timeout(m);
|
|
};
|
|
|
|
this.getEntryTypeFromIconCls = function(cls)
|
|
{
|
|
// Entry type is used in as the ALT entry,
|
|
// to guide screen reading software for visually
|
|
// impaired users
|
|
|
|
var res = gettextCatalog.getString('Folder');
|
|
|
|
if (cls == 'x-tree-icon-mydocuments')
|
|
res = gettextCatalog.getString('My Documents');
|
|
else if (cls == 'x-tree-icon-mymusic')
|
|
res = gettextCatalog.getString('My Music');
|
|
else if (cls == 'x-tree-icon-mypictures')
|
|
res = gettextCatalog.getString('My Pictures');
|
|
else if (cls == 'x-tree-icon-desktop')
|
|
res = gettextCatalog.getString('Desktop');
|
|
else if (cls == 'x-tree-icon-home')
|
|
res = gettextCatalog.getString('Home');
|
|
else if (cls == 'x-tree-icon-mymovies')
|
|
res = gettextCatalog.getString('My Movies');
|
|
else if (cls == 'x-tree-icon-mydownloads')
|
|
res = gettextCatalog.getString('My Downloads');
|
|
else if (cls == 'x-tree-icon-mypublic')
|
|
res = gettextCatalog.getString('Public');
|
|
else if (cls == 'x-tree-icon-hypervmachine')
|
|
res = gettextCatalog.getString('Hyper-V Machine');
|
|
else if (cls == 'x-tree-icon-hyperv')
|
|
res = gettextCatalog.getString('Hyper-V Machines');
|
|
else if (cls == 'x-tree-icon-broken')
|
|
res = gettextCatalog.getString('Broken access');
|
|
else if (cls == 'x-tree-icon-locked')
|
|
res = gettextCatalog.getString('Access denied');
|
|
else if (cls == 'x-tree-icon-symlink')
|
|
res = gettextCatalog.getString('Symbolic link');
|
|
else if (cls == 'x-tree-icon-leaf')
|
|
res = gettextCatalog.getString('File');
|
|
|
|
return res;
|
|
};
|
|
|
|
function reloadTexts() {
|
|
apputils.fileSizeMultipliers = [
|
|
{name: gettextCatalog.getString('byte'), value: 'b'},
|
|
{name: gettextCatalog.getString('KByte'), value: 'KB'},
|
|
{name: gettextCatalog.getString('MByte'), value: 'MB'},
|
|
{name: gettextCatalog.getString('GByte'), value: 'GB'},
|
|
{name: gettextCatalog.getString('TByte'), value: 'TB'}
|
|
];
|
|
|
|
apputils.timerangeMultipliers = [
|
|
{name: gettextCatalog.getString('Minutes'), value: 'm'},
|
|
{name: gettextCatalog.getString('Hours'), value: 'h'},
|
|
{name: gettextCatalog.getString('Days'), value: 'D'},
|
|
{name: gettextCatalog.getString('Weeks'), value: 'W'},
|
|
{name: gettextCatalog.getString('Months'), value: 'M'},
|
|
{name: gettextCatalog.getString('Years'), value: 'Y'}
|
|
];
|
|
|
|
apputils.shorttimerangeMultipliers = [
|
|
{name: gettextCatalog.getString('Seconds'), value: 's'},
|
|
{name: gettextCatalog.getString('Minutes'), value: 'm'},
|
|
{name: gettextCatalog.getString('Hours'), value: 'h'}
|
|
];
|
|
|
|
apputils.daysOfWeek = [
|
|
{name: gettextCatalog.getString('Mon'), value: 'mon'},
|
|
{name: gettextCatalog.getString('Tue'), value: 'tue'},
|
|
{name: gettextCatalog.getString('Wed'), value: 'wed'},
|
|
{name: gettextCatalog.getString('Thu'), value: 'thu'},
|
|
{name: gettextCatalog.getString('Fri'), value: 'fri'},
|
|
{name: gettextCatalog.getString('Sat'), value: 'sat'},
|
|
{name: gettextCatalog.getString('Sun'), value: 'sun'}
|
|
];
|
|
|
|
apputils.speedMultipliers = [
|
|
{name: gettextCatalog.getString('byte/s'), value: 'b'},
|
|
{name: gettextCatalog.getString('KByte/s'), value: 'KB'},
|
|
{name: gettextCatalog.getString('MByte/s'), value: 'MB'},
|
|
{name: gettextCatalog.getString('GByte/s'), value: 'GB'},
|
|
{name: gettextCatalog.getString('TByte/s'), value: 'TB'}
|
|
];
|
|
|
|
|
|
apputils.exampleOptionString = gettextCatalog.getString('Enter one option per line in command-line format, e.g. {0}');
|
|
|
|
apputils.filterClasses = [{
|
|
name: gettextCatalog.getString('Exclude directories whose names contain'),
|
|
key: '-dir*',
|
|
prefix: '-*',
|
|
suffix: '*!',
|
|
rx: '\\-\\*([^\\!]+)\\*\\!'
|
|
}, {
|
|
name: gettextCatalog.getString('Exclude files whose names contain'),
|
|
key: '-file*',
|
|
prefix: '-[.*',
|
|
suffix: '[^\\!]*]', // Escape dirsep inside regexp. Required on Windows, no effect on other platforms.
|
|
rx: '\\-\\[\\.\\*[\\^\\!\\]\\*\\]'
|
|
}, {
|
|
name: gettextCatalog.getString('Exclude folder'),
|
|
key: '-folder',
|
|
prefix: '-',
|
|
suffix: '!',
|
|
stripSep: true,
|
|
rx: '\\-(.*)\\!'
|
|
}, {
|
|
name: gettextCatalog.getString('Exclude file'),
|
|
key: '-path',
|
|
prefix: '-',
|
|
stripSep: true,
|
|
exclude: ['*', '?', '{'],
|
|
rx: '\\-([^\\[\\{\\*\\?]+)'
|
|
}, {
|
|
name: gettextCatalog.getString('Exclude file extension'),
|
|
key: '-ext',
|
|
rx: '\\-\\*\.(.*)',
|
|
prefix: '-*.'
|
|
}, {
|
|
name: gettextCatalog.getString('Exclude regular expression'),
|
|
key: '-[]',
|
|
prefix: '-[',
|
|
suffix: ']'
|
|
}, {
|
|
name: gettextCatalog.getString('Include regular expression'),
|
|
key: '+[]',
|
|
prefix: '+[',
|
|
suffix: ']'
|
|
}, {
|
|
name: gettextCatalog.getString('Exclude filter group'),
|
|
key: '-{}',
|
|
prefix: '-{',
|
|
suffix: '}'
|
|
}, {
|
|
//// Since all the current filter groups are intended for exclusion, there isn't a reason to show the 'Include group' section in the UI yet.
|
|
// name: gettextCatalog.getString('Include filter group'),
|
|
// key: '+{}',
|
|
// prefix: '+{',
|
|
// suffix: '}'
|
|
//}, {
|
|
name: gettextCatalog.getString('Include expression'),
|
|
key: '+',
|
|
prefix: '+'
|
|
}, {
|
|
name: gettextCatalog.getString('Exclude expression'),
|
|
key: '-',
|
|
prefix: '-'
|
|
}];
|
|
|
|
apputils.filterGroups = [{
|
|
name: gettextCatalog.getString('Default excludes'),
|
|
value: 'DefaultExcludes'
|
|
}, {
|
|
//// As the DefaultIncludes are currently empty, we don't need to include them in the UI yet.
|
|
// name: gettextCatalog.getString('Default includes'),
|
|
// value: 'DefaultIncludes'
|
|
//}, {
|
|
name: gettextCatalog.getString('System Files'),
|
|
value: 'SystemFiles'
|
|
}, {
|
|
name: gettextCatalog.getString('Operating System'),
|
|
value: 'OperatingSystem'
|
|
}, {
|
|
name: gettextCatalog.getString('Cache Files'),
|
|
value: 'CacheFiles'
|
|
}, {
|
|
name: gettextCatalog.getString('Temporary Files'),
|
|
value: 'TemporaryFiles'
|
|
}, {
|
|
name: gettextCatalog.getString('Applications'),
|
|
value: 'Applications'
|
|
}];
|
|
|
|
apputils.filterTypeMap = {};
|
|
for (var i = apputils.filterClasses.length - 1; i >= 0; i--)
|
|
apputils.filterTypeMap[apputils.filterClasses[i].key] = apputils.filterClasses[i];
|
|
|
|
$rootScope.$broadcast('apputillookupschanged');
|
|
};
|
|
|
|
apputils.filterGroupMap = null;
|
|
apputils.loadFilterGroups = function (reload) {
|
|
if (reload || apputils.filterGroupMap === null) {
|
|
AppService.get('/systeminfo/filtergroups').then(function (data) {
|
|
apputils.filterGroupMap = angular.copy(data.data.FilterGroups);
|
|
|
|
$rootScope.$broadcast('apputillookupschanged');
|
|
}, apputils.connectionError);
|
|
}
|
|
}
|
|
|
|
reloadTexts();
|
|
$rootScope.$on('gettextLanguageChanged', reloadTexts);
|
|
|
|
this.parseBoolString = function(txt, def) {
|
|
txt = (txt || '').toLowerCase();
|
|
if (txt == '0' || txt == 'false' || txt == 'off' || txt == 'no' || txt == 'f')
|
|
return false;
|
|
else if (txt == '1' || txt == 'true' || txt == 'on' || txt == 'yes' || txt == 't')
|
|
return true;
|
|
else
|
|
return def === undefined ? false : def;
|
|
};
|
|
|
|
|
|
this.splitSizeString = function (val) {
|
|
var m = (/(\d*)(\w*)/mg).exec(val);
|
|
var mul = null;
|
|
if (!m)
|
|
return [parseInt(val), null];
|
|
else
|
|
return [parseInt(m[1]), m[2]];
|
|
};
|
|
|
|
this.parseSizeString = function (val) {
|
|
if (val == null) {
|
|
return null;
|
|
}
|
|
var split = this.splitSizeString(val);
|
|
var formatSizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
var idx = formatSizes.indexOf((split[1]||'').toUpperCase());
|
|
if (idx == -1) {
|
|
idx = 0;
|
|
}
|
|
return split[0] * Math.pow(1024, idx);
|
|
};
|
|
|
|
|
|
this.toDisplayDateAndTime = function(dt) {
|
|
return moment(dt).format('lll');
|
|
};
|
|
|
|
this.parseDate = function(dt) {
|
|
if (typeof(dt) == typeof('')) {
|
|
var msec = Date.parse(dt);
|
|
if (isNaN(msec)) {
|
|
if (dt.length == 16 && dt[8] == 'T' && dt[15] == 'Z') {
|
|
dt = dt.substr(0, 4) + '-' + dt.substr(4, 2) + '-' + dt.substr(6, 2) + 'T' +
|
|
dt.substr(9, 2) + ':' + dt.substr(11, 2) + ':' + dt.substr(13, 2) + 'Z';
|
|
}
|
|
return new Date(dt);
|
|
} else {
|
|
return new Date(msec);
|
|
}
|
|
}
|
|
else
|
|
return new Date(dt);
|
|
};
|
|
|
|
this.parseOptionStrings = function (val, dict, validateCallback) {
|
|
// Parse options and return a dict with dict['--key']=value options
|
|
// Include and exclude filters are returned as arrays in dict['include'], dict['exclude'] if present
|
|
dict = dict || {};
|
|
|
|
var lines = null;
|
|
|
|
if (val != null && Array.isArray(val))
|
|
lines = val;
|
|
else
|
|
lines = this.replace_all(val || '', '\r', '\n').split('\n');
|
|
|
|
for(var i in lines) {
|
|
var line = lines[i].trim();
|
|
if (line != '' && line[0] != '#') {
|
|
if (line.indexOf('--') == 0) {
|
|
line = line.substr(2);
|
|
}
|
|
|
|
var eqpos = line.indexOf('=');
|
|
var key = line;
|
|
var value = true;
|
|
if (eqpos > 0) {
|
|
key = line.substr(0, eqpos).trim();
|
|
value = line.substr(eqpos + 1).trim();
|
|
}
|
|
|
|
if (validateCallback)
|
|
if (!validateCallback(dict, key, value))
|
|
return null;
|
|
// Special handing for --include and --exclude, which are the only options that can appear multiple times (compare FilterCollector.DoExtractOptions)
|
|
// In all other cases, the last value overrides the earlier values
|
|
var lower = key.toLowerCase();
|
|
if (lower == 'include' || lower == 'exclude') {
|
|
if (!Array.isArray(dict[lower])) {
|
|
dict[lower] = [value];
|
|
} else {
|
|
dict[lower].push(value);
|
|
}
|
|
} else {
|
|
dict['--' + key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dict;
|
|
};
|
|
|
|
this.parse_extra_options = function(str, dict) {
|
|
return this.parseOptionStrings(str, dict, function(d, k, v) {
|
|
if (d['--' + k] !== undefined) {
|
|
DialogService.dialog(gettextCatalog.getString('Error'), gettextCatalog.getString('Duplicate option {{opt}}', { opt: k }));
|
|
return false;
|
|
} else if (k == 'include' || k == 'exclude') {
|
|
// Cannot specify filters in extra options
|
|
// Not sure that this is really called.
|
|
DialogService.dialog(gettextCatalog.getString('Error'), gettextCatalog.getString('Cannot specify filter include or excludes in extra options'));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}) != null;
|
|
};
|
|
|
|
this.serializeAdvancedOptions = function(opts) {
|
|
return this.serializeAdvancedOptionsToArray(opts).join('\n');
|
|
};
|
|
|
|
this.serializeAdvancedOptionsToArray = function(opts) {
|
|
var res = [];
|
|
for(var n in opts)
|
|
if (n.indexOf('--') == 0)
|
|
res.push(n + '=' + opts[n]);
|
|
|
|
return res;
|
|
};
|
|
|
|
this.mergeAdvancedOptions = function(advStr, target, source) {
|
|
var adv = {}
|
|
if (!this.parse_extra_options(advStr, adv))
|
|
return false;
|
|
|
|
angular.extend(target, adv);
|
|
|
|
// Remove advanced options, no longer in the list
|
|
for(var n in source)
|
|
if (n.indexOf('--') == 0)
|
|
if (target[n] === undefined)
|
|
target[n] = null;
|
|
|
|
};
|
|
|
|
this.notifyInputError = function(msg) {
|
|
DialogService.dialog('Error', msg);
|
|
return false;
|
|
};
|
|
|
|
this.connectionError = function(txt, msg) {
|
|
if (typeof(txt) == typeof('')) {
|
|
if (msg == null)
|
|
return function (msg) {
|
|
var msgText = AppService.responseErrorMessage(msg);
|
|
DialogService.dialog(gettextCatalog.getString('Error'), txt + msgText);
|
|
};
|
|
} else {
|
|
msg = txt;
|
|
txt = '';
|
|
}
|
|
|
|
var msgText = AppService.responseErrorMessage(msg);
|
|
DialogService.dialog(gettextCatalog.getString('Error'), txt + msgText);
|
|
};
|
|
|
|
this.generatePassphrase = function() {
|
|
var specials = '!@#$%^&*()_+{}:"<>?[];\',./';
|
|
var lowercase = 'abcdefghijklmnopqrstuvwxyz';
|
|
var uppercase = lowercase.toUpperCase();
|
|
var numbers = '0123456789';
|
|
var all = specials + lowercase + uppercase + numbers;
|
|
|
|
function choose(str, n) {
|
|
var res = '';
|
|
for (var i = 0; i < n; i++) {
|
|
res += str.charAt(Math.floor(Math.random() * str.length));
|
|
}
|
|
|
|
return res;
|
|
};
|
|
|
|
var pwd = (
|
|
choose(specials, 2) +
|
|
choose(lowercase, 2) +
|
|
choose(uppercase, 2) +
|
|
choose(numbers, 2) +
|
|
choose(all, (Math.random()*5) + 5)
|
|
).split('');
|
|
|
|
for(var i = 0; i < pwd.length; i++) {
|
|
var pos = parseInt(Math.random() * pwd.length);
|
|
var t = pwd[i]
|
|
pwd[i] = pwd[pos];
|
|
pwd[pos] = t;
|
|
}
|
|
|
|
return pwd.join('');
|
|
}
|
|
|
|
this.nl2br = function(str, is_xhtml) {
|
|
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
|
|
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
|
|
};
|
|
|
|
this.preg_quote = function( str ) {
|
|
// http://kevin.vanzonneveld.net
|
|
// + original by: booeyOH
|
|
// + improved by: Ates Goral (http://magnetiq.com)
|
|
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + bugfixed by: Onno Marsman
|
|
// * example 1: preg_quote("$40");
|
|
// * returns 1: '\$40'
|
|
// * example 2: preg_quote("*RRRING* Hello?");
|
|
// * returns 2: '\*RRRING\* Hello\?'
|
|
// * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
|
|
// * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
|
|
|
|
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
|
|
}
|
|
|
|
this.replace_all_insensitive = function(str, pattern, replacement) {
|
|
return str.replace( new RegExp( "(" + this.preg_quote(pattern) + ")" , 'gi' ), replacement );
|
|
};
|
|
|
|
this.replace_all = function(str, pattern, replacement) {
|
|
return str.replace( new RegExp( "(" + this.preg_quote(pattern) + ")" , 'g' ), replacement );
|
|
};
|
|
|
|
this.globToRegexp = function (str) {
|
|
// Escape special chars, except ? and *
|
|
str = (str + '').replace(/([\\\.\+\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
|
|
// Replace ? and * with .? and .*
|
|
return str.replace(/(\?|\*)/g, ".$1");
|
|
};
|
|
|
|
this.format = function() {
|
|
if (arguments == null || arguments.length < 1)
|
|
return null;
|
|
|
|
if (arguments.length == 1)
|
|
return arguments[0];
|
|
|
|
var msg = arguments[0];
|
|
|
|
for(var i = 1; i < arguments.length; i++)
|
|
msg = this.replace_all(msg, '{' + (i-1) + '}', arguments[i]);
|
|
|
|
return msg;
|
|
};
|
|
|
|
this.encodeDictAsUrl = function(obj) {
|
|
if (obj == null)
|
|
return '';
|
|
|
|
var str = [];
|
|
for(var p in obj) {
|
|
var x = encodeURIComponent(p);
|
|
if (obj[p] != null)
|
|
x += "=" + encodeURIComponent(obj[p]);
|
|
str.push(x);
|
|
}
|
|
|
|
if (str.length == 0)
|
|
return '';
|
|
|
|
return '?' + str.join("&");
|
|
};
|
|
|
|
var URL_REGEXP_FIELDS = ['source_uri', 'backend-type', '--auth-username', '--auth-password', 'server-name', 'server-port', 'server-path', 'querystring'];
|
|
var URL_REGEXP = /([^:]+)\:\/\/(?:(?:([^\:]+)(?:\:?:([^@]*))?\@))?(?:([^\/\?\:]*)(?:\:(\d+))?)(?:\/([^\?]*))?(?:\?(.+))?/;
|
|
// Same as URL_REGEXP, but also accepts :\\ as a separator between drive letter (server for legacy reasons) and path
|
|
var FILE_REGEXP = /(file)\:\/\/(?:(?:([^\:]+)(?:\:?:([^@]*))?\@))?(?:([^\/\?\:]*)(?:\:(\d+))?)(?:(?:\/|\:\\)([^\?]*))?(?:\?(.+))?/;
|
|
var QUERY_REGEXP = /(?:^|&)([^&=]*)=?([^&]*)/g;
|
|
|
|
this.decode_uri = function(uri, backendlist) {
|
|
|
|
var i = 0;
|
|
var res = {};
|
|
|
|
// File URLs contain backslashes on Win which breaks the other regexp
|
|
// This is not standard, but for compatibility it is allowed for now
|
|
var fileMatch = FILE_REGEXP.exec(uri);
|
|
if (fileMatch) {
|
|
for (i = 0; i < URL_REGEXP_FIELDS.length; ++i) {
|
|
res[URL_REGEXP_FIELDS[i]] = fileMatch[i] || "";
|
|
}
|
|
} else {
|
|
var m = URL_REGEXP.exec(uri);
|
|
|
|
// Invalid URI
|
|
if (!m)
|
|
return res;
|
|
|
|
for (i = 0; i < URL_REGEXP_FIELDS.length; ++i) {
|
|
res[URL_REGEXP_FIELDS[i]] = m[i] || "";
|
|
}
|
|
}
|
|
|
|
res.querystring.replace(QUERY_REGEXP, function(str, key, val) {
|
|
if (key)
|
|
res['--' + key] = decodeURIComponent((val || '').replace(/\+/g, '%20'));
|
|
});
|
|
|
|
var backends = {};
|
|
for(i in backendlist)
|
|
backends[backendlist[i].Key] = true;
|
|
|
|
var scheme = res['backend-type'];
|
|
|
|
if (scheme && scheme[scheme.length - 1] == 's' && !backends[scheme] && backends[scheme.substr(0, scheme.length-1)]) {
|
|
res['backend-type'] = scheme.substr(0, scheme.length-1);
|
|
res['--use-ssl'] = true;
|
|
}
|
|
|
|
return res;
|
|
};
|
|
|
|
this.find_scheme = function(uri, backendlist) {
|
|
if (!uri || uri.length == 0)
|
|
return null;
|
|
|
|
uri = uri.trim().toLowerCase();
|
|
var ix = uri.indexOf('://');
|
|
if (ix <= 0) {
|
|
for(var i in backendlist)
|
|
if (backendlist[i].Key == 'file')
|
|
return 'file';
|
|
|
|
return backendlist[0];
|
|
}
|
|
|
|
return (EDIT_URI.decode_uri(uri)['backend-type'] || '').toLowerCase()
|
|
};
|
|
|
|
this.contains_value = function(dict, value) {
|
|
for(var k in dict)
|
|
if (dict[k] == value)
|
|
return true;
|
|
|
|
return false;
|
|
};
|
|
|
|
this.contains_key = function(dict, key, keyname) {
|
|
for(var i in dict)
|
|
if (keyname == null ? i == key : dict[i][keyname] == key)
|
|
return true;
|
|
|
|
return false;
|
|
};
|
|
|
|
|
|
this.removeEmptyEntries = function(x) {
|
|
for (var i = x.length - 1; i >= 0; i--) {
|
|
if (x[i] == null || x[i] == '')
|
|
x.splice(i, 1);
|
|
};
|
|
|
|
return x;
|
|
};
|
|
|
|
this.splitFilterIntoTypeAndBody = function(src, dirsep) {
|
|
if (src == null)
|
|
return null;
|
|
|
|
if (dirsep == null) {
|
|
throw new Error('No dirsep provided!');
|
|
}
|
|
|
|
function matches(txt, n) {
|
|
var pre = apputils.replace_all(n.prefix || '', '!', dirsep);
|
|
var suf = apputils.replace_all(n.suffix || '', '!', dirsep);
|
|
|
|
if (txt.indexOf(pre) != 0 || txt.lastIndexOf(suf) != txt.length - suf.length || txt.lastIndexOf(suf) < 0)
|
|
return null;
|
|
|
|
var type = n.key;
|
|
var body = txt.substr(pre.length);
|
|
body = body.substr(0, body.length - suf.length);
|
|
|
|
if (body.length >= 2 && body[1] == '[') {
|
|
body = body.substr(1, body.length - 2);
|
|
}
|
|
|
|
if (n.exclude != null) {
|
|
for (var i = n.exclude.length - 1; i >= 0; i--)
|
|
if (body.indexOf(apputils.replace_all(n.exclude[i] || '', '!', dirsep)) >= 0)
|
|
return null;
|
|
}
|
|
|
|
return [type, body];
|
|
}
|
|
|
|
var m = [];
|
|
for (var i in this.filterClasses) {
|
|
var n = matches(src, this.filterClasses[i]);
|
|
if (n != null)
|
|
m.push(n);
|
|
}
|
|
if (m.length == 0) {
|
|
return null;
|
|
}
|
|
// Select match with shortest body, meaning longest prefix and suffix
|
|
var shortestIdx = 0;
|
|
var shortestLen = src.length;
|
|
for (i = 0; i < m.length; ++i) {
|
|
if (m[i][1].length < shortestLen) {
|
|
shortestIdx = i;
|
|
shortestLen = m[i][1].length;
|
|
}
|
|
}
|
|
return m[shortestIdx];
|
|
};
|
|
|
|
this.buildFilter = function(type, body, dirsep) {
|
|
if (type == null || this.filterTypeMap[type] == null)
|
|
return body;
|
|
|
|
body = body || '';
|
|
|
|
var f = this.filterTypeMap[type];
|
|
var pre = this.replace_all(f.prefix || '', '!', dirsep);
|
|
var suf = this.replace_all(f.suffix || '', '!', dirsep);
|
|
|
|
if (pre.length >= 2 && pre[1] == '[') {
|
|
//Regexp encode body ....
|
|
}
|
|
if (f.stripSep == true && body[body.length - 1] == dirsep) {
|
|
// Remove trailing dirsep
|
|
body = body.slice(0, -1);
|
|
}
|
|
return pre + body + suf;
|
|
};
|
|
|
|
this.filterToRegexpStr = function (filter) {
|
|
var firstChar = filter.substr(0, 1);
|
|
var lastChar = filter.substr(filter.length - 1, 1);
|
|
var isFilterGroup = firstChar == '{' && lastChar == '}';
|
|
if (isFilterGroup && this.filterGroupMap !== null) {
|
|
// Replace filter groups with filter strings
|
|
var filterGroups = filter.substr(1,filter.length - 2).split(',');
|
|
var filterStrings = [];
|
|
for (var i = 0; i < filterGroups.length; ++i) {
|
|
filterStrings = filterStrings.concat(this.filterGroupMap[filterGroups[i].trim()] || []);
|
|
}
|
|
|
|
filter = filterStrings.map(function (s) {
|
|
return '(?:' + apputils.filterToRegexpStr(s) + ')'
|
|
}).join('|');
|
|
|
|
} else {
|
|
var rx = firstChar == '[' && lastChar == ']';
|
|
if (rx)
|
|
filter = filter.substr(1, filter.length - 2);
|
|
else {
|
|
filter = this.globToRegexp(filter);
|
|
}
|
|
}
|
|
|
|
return filter;
|
|
};
|
|
|
|
this.filterListToRegexps = function(filters, caseSensitive) {
|
|
var res = [];
|
|
|
|
for(var i = 0; i < filters.length; i++) {
|
|
var f = filters[i];
|
|
|
|
if (f == null || f.length == 0)
|
|
continue;
|
|
|
|
var flag = f.substr(0, 1);
|
|
var filter = f.substr(1);
|
|
|
|
try {
|
|
var regexp = this.filterToRegexpStr(filter);
|
|
res.push([flag == '+', new RegExp(regexp, caseSensitive ? 'g' : 'gi')]);
|
|
} catch (e) {
|
|
}
|
|
}
|
|
|
|
return res;
|
|
};
|
|
|
|
this.evalFilter = function(path, filters, include) {
|
|
for (var i = 0; i < filters.length; i++) {
|
|
var m = path.match(filters[i][1]);
|
|
// Regex such as '.*' might match empty string at the end which is unwanted
|
|
// Check that the first match covers the full string
|
|
if (m && m.length >= 1 && m[0].length == path.length)
|
|
return filters[i][0];
|
|
}
|
|
|
|
return include === undefined ? true : include;
|
|
};
|
|
|
|
this.buildOptionList = function(sysinfo, encmodule, compmodule, backmodule) {
|
|
if (sysinfo == null || sysinfo.Options == null)
|
|
return null;
|
|
|
|
var items = angular.copy(sysinfo.Options);
|
|
for(var n in items)
|
|
items[n].Category = gettextCatalog.getString('Core options');
|
|
|
|
function copyToList(lst, key) {
|
|
if (key != null && typeof(key) != typeof(''))
|
|
key = null;
|
|
|
|
for(var n in lst)
|
|
{
|
|
if (key == null || key.toLowerCase() == lst[n].Key.toLowerCase())
|
|
{
|
|
var m = angular.copy(lst[n].Options);
|
|
for(var i in m)
|
|
m[i].Category = lst[n].DisplayName;
|
|
items.push.apply(items, m);
|
|
}
|
|
}
|
|
}
|
|
|
|
copyToList(sysinfo.GenericModules);
|
|
|
|
if (encmodule !== false)
|
|
copyToList(sysinfo.EncryptionModules, encmodule);
|
|
if (compmodule !== false)
|
|
copyToList(sysinfo.CompressionModules, compmodule);
|
|
if (backmodule !== false)
|
|
copyToList(sysinfo.BackendModules, backmodule);
|
|
|
|
return items;
|
|
};
|
|
|
|
this.extractServerModuleOptions = function(advOptionsList, servermodulelist, servermodulesettings, optionlistname) {
|
|
if (optionlistname == null)
|
|
optionlistname = 'SupportedCommands';
|
|
|
|
for(var mix in servermodulelist) {
|
|
var mod = servermodulelist[mix];
|
|
for(var oix in mod[optionlistname]) {
|
|
var opt = mod[optionlistname][oix];
|
|
var prefixstr = '--' + opt.Name + '=';
|
|
for (var i = advOptionsList.length - 1; i >= 0; i--) {
|
|
if (advOptionsList[i].indexOf(prefixstr) == 0) {
|
|
servermodulesettings[opt.Name] = advOptionsList[i].substr(prefixstr.length);
|
|
advOptionsList.splice(i, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
this.formatDuration = function(duration) {
|
|
// duration is a timespan string in format (dd.)hh:mm:ss.sss
|
|
// the part (dd.) is as indicated optional
|
|
|
|
if (duration == null) {
|
|
return;
|
|
}
|
|
|
|
var timespanArray = duration.split(':');
|
|
|
|
if (timespanArray.length < 3) {
|
|
return;
|
|
}
|
|
|
|
if (timespanArray[0].indexOf('.') > 0) {
|
|
timespanArray[0] = timespanArray[0].replace('.', " day(s) and ");
|
|
}
|
|
|
|
// round second according to ms
|
|
timespanArray[2] = Math.round(parseFloat(timespanArray[2])).toString();
|
|
// zero-padding
|
|
if (timespanArray[2].length == 1) timespanArray[2] = '0' + timespanArray[2];
|
|
|
|
return timespanArray.join(':');
|
|
};
|
|
|
|
});
|