support validate option value before submitting to aria2

master
MaysWind 2016-06-26 00:26:12 +08:00
parent 05bcc7225d
commit 361ef9cb74
5 changed files with 286 additions and 103 deletions

View File

@ -120,6 +120,11 @@
"Changes to the settings take effect after refreshing page.": "设置将在页面刷新后生效.",
"Type is illegal!": "类型错误!",
"Parameter is invalid!": "请求参数无效",
"Option value cannot be empty!": "参数内容不能为空!",
"Input number is invalid!": "输入的数字无效!",
"Input number is below min value!": "输入的数字小于最小值 {{value}} !",
"Input number is above max value!": "输入的数字大于最大值 {{value}} !",
"Input value is invalid!": "输入的内容无效!",
"format": {
"longdate": "YYYY年MM月DD日 HH:mm:ss",
"time.millisecond": "{{value}} 毫秒",

View File

@ -1,22 +1,41 @@
(function () {
'use strict';
angular.module('ariaNg').constant('aria2AllOptions', {
// EXAMPLE:
// 'option name': {
// type: 'string|integer|float|text|boolean|option',
// [suffix: 'Bytes|Milliseconds|Seconds|Minutes|Hours',]
// [defaultValue: '',]
// [readonly: true|false,] //default: false
// [split: '',] //SUPPORT 'text' type
// [showCount: true|false,] //SUPPORT 'text' type, parameter 'split' is required, default: false
// [options: [],] //SUPPORT 'option' type
// [min: 0,] //SUPPORT 'integer', 'float'
// [max: 0,] //SUPPORT 'integer', 'float'
// [pattern: '']
// }
'dir': {
type: 'string'
type: 'string',
required: true
},
'log': {
type: 'string'
type: 'string',
required: true
},
'max-concurrent-downloads': {
type: 'integer',
defaultValue: '5'
defaultValue: '5',
required: true,
min: 1
},
'check-integrity': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'continue': {
type: 'boolean'
type: 'boolean',
required: true
},
'all-proxy': {
type: 'string'
@ -30,33 +49,48 @@
'connect-timeout': {
type: 'integer',
suffix: 'Seconds',
defaultValue: '60'
defaultValue: '60',
required: true,
min: 1,
max: 600
},
'dry-run': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'lowest-speed-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '0'
defaultValue: '0',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'max-connection-per-server': {
type: 'integer',
defaultValue: '1'
defaultValue: '1',
required: true,
min: 1,
max: 16
},
'max-file-not-found': {
type: 'integer',
defaultValue: '0'
defaultValue: '0',
required: true,
min: 0
},
'max-tries': {
type: 'integer',
defaultValue: '5'
defaultValue: '5',
required: true,
min: 0
},
'min-split-size': {
type: 'string',
suffix: 'Bytes',
defaultValue: '20M'
defaultValue: '20M',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'netrc-path': {
type: 'string',
@ -64,7 +98,8 @@
defaultValue: '$(HOME)/.netrc'
},
'no-netrc': {
type: 'boolean'
type: 'boolean',
required: true
},
'no-proxy': {
type: 'text',
@ -74,20 +109,26 @@
'proxy-method': {
type: 'option',
options: ['get', 'tunnel'],
defaultValue: 'get'
defaultValue: 'get',
required: true
},
'remote-time': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'reuse-uri': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'retry-wait': {
type: 'integer',
suffix: 'Seconds',
defaultValue: '0'
defaultValue: '0',
required: true,
min: 0,
max: 600
},
'server-stat-of': {
type: 'string'
@ -100,22 +141,29 @@
},
'split': {
type: 'integer',
defaultValue: '5'
defaultValue: '5',
required: true,
min: 1
},
'stream-piece-selector': {
type: 'option',
options: ['default', 'inorder', 'random', 'geom'],
defaultValue: 'default'
defaultValue: 'default',
required: true
},
'timeout': {
type: 'integer',
suffix: 'Seconds',
defaultValue: '60'
defaultValue: '60',
required: true,
min: 1,
max: 600
},
'uri-selector': {
type: 'option',
options: ['inorder', 'feedback', 'adaptive'],
defaultValue: 'feedback'
defaultValue: 'feedback',
required: true
},
'check-certificate': {
type: 'boolean',
@ -124,15 +172,18 @@
},
'http-accept-gzip': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'http-auth-challenge': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'http-no-cache': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'http-user': {
type: 'string'
@ -163,11 +214,13 @@
},
'enable-http-keep-alive': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'enable-http-pipelining': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'header': {
type: 'string'
@ -177,7 +230,8 @@
},
'use-head': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'user-agent': {
type: 'string',
@ -193,7 +247,8 @@
},
'ftp-pasv': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'ftp-proxy': {
type: 'string'
@ -207,11 +262,13 @@
'ftp-type': {
type: 'option',
options: ['binary', 'ascii'],
defaultValue: 'binary'
defaultValue: 'binary',
required: true
},
'ftp-reuse-connection': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'ssh-host-key-md': {
type: 'string'
@ -227,11 +284,13 @@
},
'bt-enable-hook-after-hash-check': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'bt-enable-lpd': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'bt-exclude-tracker': {
type: 'text',
@ -243,57 +302,73 @@
},
'bt-force-encryption': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'bt-hash-check-seed': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'bt-max-open-files': {
type: 'integer',
defaultValue: '100'
defaultValue: '100',
required: true,
min: 1
},
'bt-max-peers': {
type: 'integer',
defaultValue: '55'
defaultValue: '55',
required: true,
min: 0
},
'bt-metadata-only': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'bt-min-crypto-level': {
type: 'option',
options: ['plain', 'arc4'],
defaultValue: 'plain'
defaultValue: 'plain',
required: true
},
'bt-prioritize-piece': {
type: 'string'
},
'bt-remove-unselected-file': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'bt-require-crypto': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'bt-request-peer-speed-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '50K'
defaultValue: '50K',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'bt-save-metadata': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'bt-seed-unverified': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'bt-stop-timeout': {
type: 'integer',
suffix: 'Seconds',
defaultValue: '0'
defaultValue: '0',
required: true,
min: 0
},
'bt-tracker': {
type: 'text',
@ -303,17 +378,25 @@
'bt-tracker-connect-timeout': {
type: 'integer',
suffix: 'Seconds',
defaultValue: '60'
defaultValue: '60',
required: true,
min: 1,
max: 600
},
'bt-tracker-interval': {
type: 'integer',
suffix: 'Seconds',
defaultValue: '0'
defaultValue: '0',
required: true,
min: 0
},
'bt-tracker-timeout': {
type: 'integer',
suffix: 'Seconds',
defaultValue: '60'
defaultValue: '60',
required: true,
min: 1,
max: 600
},
'dht-file-path': {
type: 'string',
@ -326,7 +409,7 @@
defaultValue: '$HOME/.aria2/dht6.dat'
},
'dht-listen-port': {
type: 'integer',
type: 'string',
readonly: true,
defaultValue: '6881-6999'
},
@ -347,12 +430,14 @@
},
'enable-peer-exchange': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'follow-torrent': {
type: 'option',
options: ['true', 'false', 'mem'],
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'listen-port': {
type: 'integer',
@ -362,12 +447,16 @@
'max-overall-upload-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '0'
defaultValue: '0',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'max-upload-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '0'
defaultValue: '0',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'peer-id-prefix': {
type: 'string',
@ -376,16 +465,21 @@
},
'seed-ratio': {
type: 'float',
defaultValue: '1.0'
defaultValue: '1.0',
required: true,
min: 0
},
'seed-time': {
type: 'integer',
suffix: 'Minutes'
suffix: 'Minutes',
required: true,
min: 0
},
'follow-metalink': {
type: 'option',
options: ['true', 'false', 'mem'],
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'metalink-base-uri': {
type: 'string'
@ -405,11 +499,13 @@
'metalink-preferred-protocol': {
type: 'option',
options: ['http', 'https', 'ftp', 'none'],
defaultValue: 'none'
defaultValue: 'none',
required: true
},
'metalink-enable-unique-protocol': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'enable-rpc': {
type: 'boolean',
@ -418,7 +514,8 @@
},
'pause-metadata': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'rpc-allow-origin-all': {
type: 'boolean',
@ -443,7 +540,8 @@
},
'rpc-save-upload-metadata': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'rpc-secure': {
type: 'boolean',
@ -451,23 +549,28 @@
},
'allow-overwrite': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'allow-piece-length-change': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'always-resume': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'async-dns': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'auto-file-renaming': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'auto-save-interval': {
type: 'integer',
@ -477,7 +580,8 @@
},
'conditional-get': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'conf-path': {
type: 'string',
@ -514,7 +618,8 @@
'download-result': {
type: 'option',
options: ['default', 'full', 'hide'],
defaultValue: 'default'
defaultValue: 'default',
required: true
},
'dscp': {
type: 'string',
@ -531,7 +636,8 @@
},
'enable-mmap': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'event-poll': {
type: 'option',
@ -541,15 +647,18 @@
'file-allocation': {
type: 'option',
options: ['none', 'prealloc', 'trunc', 'falloc'],
defaultValue: 'prealloc'
defaultValue: 'prealloc',
required: true
},
'force-save': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'hash-check-only': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'human-readable': {
type: 'boolean',
@ -558,16 +667,22 @@
},
'max-download-result': {
type: 'integer',
defaultValue: '1000'
defaultValue: '1000',
required: true,
min: 0
},
'max-mmap-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '9223372036854775807'
defaultValue: '9223372036854775807',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'max-resume-failure-tries': {
type: 'integer',
defaultValue: '0'
defaultValue: '0',
required: true,
min: 0
},
'min-tls-version': {
type: 'option',
@ -578,15 +693,19 @@
'log-level': {
type: 'option',
options: ['debug', 'info', 'notice', 'warn', 'error'],
defaultValue: 'debug'
defaultValue: 'debug',
required: true
},
'optimize-concurrent-downloads': {
type: 'string',
defaultValue: 'false'
},
'piece-length': {
type: 'integer',
defaultValue: '1M'
type: 'string',
suffix: 'Bytes',
defaultValue: '1M',
required: true,
pattern: '^(0|[1-9]\\d*M?)$'
},
'show-console-readout': {
type: 'boolean',
@ -602,12 +721,16 @@
'max-overall-download-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '0'
defaultValue: '0',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'max-download-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '0'
defaultValue: '0',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'no-conf': {
type: 'boolean',
@ -616,11 +739,14 @@
'no-file-allocation-limit': {
type: 'string',
suffix: 'Bytes',
defaultValue: '5M'
defaultValue: '5M',
required: true,
pattern: '^(0|[1-9]\\d*(K|M)?)$'
},
'parameterized-uri': {
type: 'boolean',
defaultValue: 'false'
defaultValue: 'false',
required: true
},
'quiet': {
type: 'boolean',
@ -629,10 +755,12 @@
},
'realtime-chunk-checksum': {
type: 'boolean',
defaultValue: 'true'
defaultValue: 'true',
required: true
},
'remove-control-file': {
type: 'boolean'
type: 'boolean',
required: true
},
'save-session': {
type: 'string'

View File

@ -7,7 +7,8 @@
optionStorageKey: 'Options',
globalStatStorageCapacity: 120,
taskStatStorageCapacity: 300,
lazySaveTimeout: 500
lazySaveTimeout: 500,
errorTooltipDelay: 200
}).constant('ariaNgDefaultOptions', {
language: 'en',
rpcHost: '',

View File

@ -124,6 +124,11 @@
'Changes to the settings take effect after refreshing page.': 'Changes to the settings take effect after refreshing page.',
'Type is illegal!': 'Type is illegal!',
'Parameter is invalid!': 'Parameter is invalid!',
'Option value cannot be empty!': 'Option value cannot be empty!',
'Input number is invalid!': 'Input number is invalid!',
'Input number is below min value!': 'Input number is below min value {{value}}!',
'Input number is above max value!': 'Input number is above max value {{value}}!',
'Input value is invalid!': 'Input value is invalid!',
'format': {
'longdate': 'MM/DD/YYYY HH:mm:ss',
'time.millisecond': '{{value}} Millisecond',

View File

@ -1,7 +1,7 @@
(function () {
'use strict';
angular.module('ariaNg').directive('ngSetting', ['$timeout', 'ariaNgConstants', function ($timeout, ariaNgConstants) {
angular.module('ariaNg').directive('ngSetting', ['$timeout', '$translate', 'ariaNgConstants', function ($timeout, $translate, ariaNgConstants) {
return {
restrict: 'E',
templateUrl: 'views/setting.html',
@ -16,26 +16,26 @@
link: function (scope, element, attrs, ngModel) {
var pendingSaveRequest = null;
var options = {
lazySaveTimeout: ariaNgConstants.lazySaveTimeout
lazySaveTimeout: ariaNgConstants.lazySaveTimeout,
errorTooltipDelay: ariaNgConstants.errorTooltipDelay
};
angular.extend(options, attrs);
scope.optionStatus = (function () {
var value = 'ready';
var destroyTooltip = function () {
angular.element(element).tooltip('destroy');
};
var destroyTooltip = function () {
angular.element(element).tooltip('destroy');
};
var showTooltip = function (cause, type) {
if (!cause) {
return;
}
var showTooltip = function (cause, type, causeParams) {
if (!cause) {
return;
}
$timeout(function () {
angular.element(element).tooltip({
title: cause,
title: $translate.instant(cause, causeParams),
trigger: 'focus',
placement: 'auto top',
container: element,
template:
'<div class="tooltip' + (type ? ' tooltip-' + type : '') + '" role="tooltip">' +
@ -43,7 +43,11 @@
'<div class="tooltip-inner"></div>' +
'</div>'
}).tooltip('show');
};
}, options.errorTooltipDelay);
};
scope.optionStatus = (function () {
var value = 'ready';
return {
getValue: function () {
@ -70,10 +74,10 @@
value = 'failed';
showTooltip(cause, 'warning');
},
setError: function (cause) {
setError: function (cause, causeParams) {
destroyTooltip();
value = 'error';
showTooltip(cause, 'error');
showTooltip(cause, 'error', causeParams);
},
getStatusFeedbackStyle: function () {
if (value == 'success') {
@ -116,6 +120,10 @@
};
scope.changeValue = function (optionValue, lazySave) {
if (pendingSaveRequest) {
$timeout.cancel(pendingSaveRequest);
}
scope.optionValue = optionValue;
scope.optionStatus.setReady();
@ -123,16 +131,52 @@
return;
}
if (scope.option.required && optionValue == '') {
scope.optionStatus.setError('Option value cannot be empty!');
return;
}
if (scope.option.type == 'integer' && !/^-?\d+$/.test(optionValue)) {
scope.optionStatus.setError('Input number is invalid!');
return;
}
if (scope.option.type == 'float' && !/^-?(\d*\.)?\d+$/.test(optionValue)) {
scope.optionStatus.setError('Input number is invalid!');
return;
}
if ((scope.option.type == 'integer' || scope.option.type == 'float') && (!angular.isUndefined(scope.option.min) || !angular.isUndefined(scope.option.max))) {
var number = optionValue;
if (scope.option.type == 'integer') {
number = parseInt(optionValue);
} else if (scope.option.type == 'float') {
number = parseFloat(optionValue);
}
if (!angular.isUndefined(scope.option.min) && number < scope.option.min) {
scope.optionStatus.setError('Input number is below min value!', { value: scope.option.min });
return;
}
if (!angular.isUndefined(scope.option.max) && number > scope.option.max) {
scope.optionStatus.setError('Input number is above max value!', { value: scope.option.max });
return;
}
}
if (!angular.isUndefined(scope.option.pattern) && !(new RegExp(scope.option.pattern).test(optionValue))) {
scope.optionStatus.setError('Input value is invalid!');
return;
}
var data = {
key: scope.option.key,
value: optionValue,
optionStatus: scope.optionStatus
};
if (pendingSaveRequest) {
$timeout.cancel(pendingSaveRequest);
}
var invokeChange = function () {
scope.optionStatus.setSaving();
scope.onChangeValue(data);