2016-06-01 17:31:48 +02:00
|
|
|
(function () {
|
|
|
|
'use strict';
|
|
|
|
|
2018-08-12 14:26:26 +02:00
|
|
|
angular.module('ariaNg').directive('ngSetting', ['$timeout', '$q', 'ariaNgConstants', 'ariaNgLocalizationService', 'aria2SettingService', function ($timeout, $q, ariaNgConstants, ariaNgLocalizationService, aria2SettingService) {
|
2016-06-01 17:31:48 +02:00
|
|
|
return {
|
|
|
|
restrict: 'E',
|
2016-06-11 12:52:40 +02:00
|
|
|
templateUrl: 'views/setting.html',
|
2016-06-01 17:31:48 +02:00
|
|
|
require: '?ngModel',
|
|
|
|
replace: true,
|
|
|
|
scope: {
|
|
|
|
option: '=',
|
|
|
|
ngModel: '=',
|
2016-07-03 11:08:06 +02:00
|
|
|
defaultValue: '=?',
|
2016-06-01 17:31:48 +02:00
|
|
|
onChangeValue: '&'
|
|
|
|
},
|
|
|
|
link: function (scope, element, attrs, ngModel) {
|
|
|
|
var pendingSaveRequest = null;
|
|
|
|
var options = {
|
2016-06-25 18:26:12 +02:00
|
|
|
lazySaveTimeout: ariaNgConstants.lazySaveTimeout,
|
|
|
|
errorTooltipDelay: ariaNgConstants.errorTooltipDelay
|
2016-06-01 17:31:48 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
angular.extend(options, attrs);
|
|
|
|
|
2018-05-20 16:56:12 +02:00
|
|
|
var loadHistory = function () {
|
|
|
|
if (!scope.option || !scope.option.showHistory) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scope.history = aria2SettingService.getSettingHistory(scope.option.key);
|
|
|
|
};
|
|
|
|
|
2016-06-25 18:26:12 +02:00
|
|
|
var destroyTooltip = function () {
|
|
|
|
angular.element(element).tooltip('destroy');
|
|
|
|
};
|
2016-06-25 15:48:06 +02:00
|
|
|
|
2016-06-25 18:26:12 +02:00
|
|
|
var showTooltip = function (cause, type, causeParams) {
|
|
|
|
if (!cause) {
|
|
|
|
return;
|
|
|
|
}
|
2016-06-25 15:48:06 +02:00
|
|
|
|
2016-06-25 18:26:12 +02:00
|
|
|
$timeout(function () {
|
2016-11-06 08:20:44 +01:00
|
|
|
var currentValue = scope.optionStatus.getValue();
|
|
|
|
|
|
|
|
if (currentValue !== 'failed' && currentValue !== 'error') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-25 15:48:06 +02:00
|
|
|
angular.element(element).tooltip({
|
2018-08-12 14:26:26 +02:00
|
|
|
title: ariaNgLocalizationService.getLocalizedText(cause, causeParams),
|
2016-06-25 15:48:06 +02:00
|
|
|
trigger: 'focus',
|
2016-06-25 18:26:12 +02:00
|
|
|
placement: 'auto top',
|
2016-06-25 15:48:06 +02:00
|
|
|
container: element,
|
|
|
|
template:
|
|
|
|
'<div class="tooltip' + (type ? ' tooltip-' + type : '') + '" role="tooltip">' +
|
|
|
|
'<div class="tooltip-arrow"></div>' +
|
|
|
|
'<div class="tooltip-inner"></div>' +
|
|
|
|
'</div>'
|
|
|
|
}).tooltip('show');
|
2016-06-25 18:26:12 +02:00
|
|
|
}, options.errorTooltipDelay);
|
|
|
|
};
|
|
|
|
|
2016-12-04 16:21:06 +01:00
|
|
|
var getHumanReadableSize = function (size) {
|
|
|
|
if (!size || parseInt(size).toString() != size) {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
var sizeUnits = ['', 'K', 'M', 'G'];
|
|
|
|
var unitIndex = 0;
|
|
|
|
|
|
|
|
for (var i = 0; i < sizeUnits.length; i++) {
|
2018-05-20 17:09:59 +02:00
|
|
|
if ((size < 1024) || (size % 1024 !== 0)) {
|
2016-12-04 16:21:06 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = size / 1024;
|
|
|
|
unitIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return size + sizeUnits[unitIndex];
|
|
|
|
};
|
|
|
|
|
|
|
|
var getHumanReadableValue = function (value) {
|
|
|
|
if (scope.option.suffix === 'Bytes') {
|
|
|
|
return getHumanReadableSize(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
};
|
|
|
|
|
2016-06-25 18:26:12 +02:00
|
|
|
scope.optionStatus = (function () {
|
|
|
|
var value = 'ready';
|
2016-06-25 15:48:06 +02:00
|
|
|
|
2016-06-02 18:16:27 +02:00
|
|
|
return {
|
|
|
|
getValue: function () {
|
|
|
|
return value;
|
|
|
|
},
|
|
|
|
setReady: function () {
|
2016-06-25 15:48:06 +02:00
|
|
|
destroyTooltip();
|
2016-06-02 18:16:27 +02:00
|
|
|
value = 'ready';
|
|
|
|
},
|
|
|
|
setPending: function () {
|
2016-06-25 15:48:06 +02:00
|
|
|
destroyTooltip();
|
2016-06-02 18:16:27 +02:00
|
|
|
value = 'pending';
|
|
|
|
},
|
|
|
|
setSaving: function () {
|
2016-06-25 15:48:06 +02:00
|
|
|
destroyTooltip();
|
2016-06-02 18:16:27 +02:00
|
|
|
value = 'pending';
|
|
|
|
},
|
|
|
|
setSuccess: function () {
|
2016-06-25 15:48:06 +02:00
|
|
|
destroyTooltip();
|
2016-06-02 18:16:27 +02:00
|
|
|
value = 'success';
|
|
|
|
},
|
2016-06-25 15:48:06 +02:00
|
|
|
setFailed: function (cause) {
|
|
|
|
destroyTooltip();
|
2016-06-02 18:16:27 +02:00
|
|
|
value = 'failed';
|
2016-06-25 15:48:06 +02:00
|
|
|
showTooltip(cause, 'warning');
|
2016-06-02 18:16:27 +02:00
|
|
|
},
|
2016-06-25 18:26:12 +02:00
|
|
|
setError: function (cause, causeParams) {
|
2016-06-25 15:48:06 +02:00
|
|
|
destroyTooltip();
|
2016-06-02 18:16:27 +02:00
|
|
|
value = 'error';
|
2016-06-25 18:26:12 +02:00
|
|
|
showTooltip(cause, 'error', causeParams);
|
2016-06-02 18:16:27 +02:00
|
|
|
},
|
|
|
|
getStatusFeedbackStyle: function () {
|
2016-08-01 19:26:10 +02:00
|
|
|
if (value === 'success') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'has-success';
|
2016-08-01 19:26:10 +02:00
|
|
|
} else if (value === 'failed') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'has-warning';
|
2016-08-01 19:26:10 +02:00
|
|
|
} else if (value === 'error') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'has-error';
|
|
|
|
} else {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getStatusIcon: function () {
|
2016-08-01 19:26:10 +02:00
|
|
|
if (value === 'pending') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'fa-hourglass-start';
|
2016-08-01 19:26:10 +02:00
|
|
|
} else if (value === 'saving') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'fa-spin fa-pulse fa-spinner';
|
2016-08-01 19:26:10 +02:00
|
|
|
} else if (value === 'success') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'fa-check';
|
2016-08-01 19:26:10 +02:00
|
|
|
} else if (value === 'failed') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'fa-exclamation';
|
2016-08-01 19:26:10 +02:00
|
|
|
} else if (value === 'error') {
|
2016-06-02 18:16:27 +02:00
|
|
|
return 'fa-times';
|
|
|
|
} else {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
},
|
|
|
|
isShowStatusIcon: function () {
|
2016-08-01 19:26:10 +02:00
|
|
|
return this.getStatusIcon() !== '';
|
2016-06-02 18:16:27 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
2016-06-01 17:31:48 +02:00
|
|
|
scope.getTotalCount = function () {
|
|
|
|
if (!scope.optionValue && !angular.isString(scope.optionValue)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return scope.optionValue.split(scope.option.split).length;
|
|
|
|
};
|
|
|
|
|
|
|
|
scope.changeValue = function (optionValue, lazySave) {
|
2016-06-25 18:26:12 +02:00
|
|
|
if (pendingSaveRequest) {
|
|
|
|
$timeout.cancel(pendingSaveRequest);
|
|
|
|
}
|
|
|
|
|
2016-06-01 17:31:48 +02:00
|
|
|
scope.optionValue = optionValue;
|
2016-06-02 18:16:27 +02:00
|
|
|
scope.optionStatus.setReady();
|
2016-06-01 17:31:48 +02:00
|
|
|
|
|
|
|
if (!scope.option || !scope.option.key || scope.option.readonly) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-01 19:26:10 +02:00
|
|
|
if (scope.option.required && optionValue === '') {
|
2016-06-25 18:26:12 +02:00
|
|
|
scope.optionStatus.setError('Option value cannot be empty!');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-25 05:57:07 +01:00
|
|
|
if (optionValue !== '' && scope.option.type === 'integer' && !/^-?\d+$/.test(optionValue)) {
|
2016-06-25 18:26:12 +02:00
|
|
|
scope.optionStatus.setError('Input number is invalid!');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-25 05:57:07 +01:00
|
|
|
if (optionValue !== '' && scope.option.type === 'float' && !/^-?(\d*\.)?\d+$/.test(optionValue)) {
|
2016-06-25 18:26:12 +02:00
|
|
|
scope.optionStatus.setError('Input number is invalid!');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-25 05:57:07 +01:00
|
|
|
if (optionValue !== '' && (scope.option.type === 'integer' || scope.option.type === 'float') && (angular.isDefined(scope.option.min) || angular.isDefined(scope.option.max))) {
|
2016-06-25 18:26:12 +02:00
|
|
|
var number = optionValue;
|
|
|
|
|
2016-08-01 19:26:10 +02:00
|
|
|
if (scope.option.type === 'integer') {
|
2016-06-25 18:26:12 +02:00
|
|
|
number = parseInt(optionValue);
|
2016-08-01 19:26:10 +02:00
|
|
|
} else if (scope.option.type === 'float') {
|
2016-06-25 18:26:12 +02:00
|
|
|
number = parseFloat(optionValue);
|
|
|
|
}
|
|
|
|
|
2016-06-26 08:21:20 +02:00
|
|
|
if (angular.isDefined(scope.option.min) && number < scope.option.min) {
|
2016-06-25 18:26:12 +02:00
|
|
|
scope.optionStatus.setError('Input number is below min value!', { value: scope.option.min });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-26 08:21:20 +02:00
|
|
|
if (angular.isDefined(scope.option.max) && number > scope.option.max) {
|
2016-06-25 18:26:12 +02:00
|
|
|
scope.optionStatus.setError('Input number is above max value!', { value: scope.option.max });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-25 05:57:07 +01:00
|
|
|
if (optionValue !== '' && angular.isDefined(scope.option.pattern) && !(new RegExp(scope.option.pattern).test(optionValue))) {
|
2016-06-25 18:26:12 +02:00
|
|
|
scope.optionStatus.setError('Input value is invalid!');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-01 17:31:48 +02:00
|
|
|
var data = {
|
|
|
|
key: scope.option.key,
|
2016-06-02 18:16:27 +02:00
|
|
|
value: optionValue,
|
|
|
|
optionStatus: scope.optionStatus
|
2016-06-01 17:31:48 +02:00
|
|
|
};
|
|
|
|
|
2016-06-02 18:16:27 +02:00
|
|
|
var invokeChange = function () {
|
|
|
|
scope.optionStatus.setSaving();
|
|
|
|
scope.onChangeValue(data);
|
|
|
|
};
|
|
|
|
|
2016-06-01 17:31:48 +02:00
|
|
|
if (scope.onChangeValue) {
|
|
|
|
if (lazySave) {
|
2016-06-02 18:16:27 +02:00
|
|
|
scope.optionStatus.setPending();
|
|
|
|
|
2016-06-01 17:31:48 +02:00
|
|
|
pendingSaveRequest = $timeout(function () {
|
2016-06-02 18:16:27 +02:00
|
|
|
invokeChange();
|
2016-06-01 17:31:48 +02:00
|
|
|
}, options.lazySaveTimeout);
|
|
|
|
} else {
|
2016-06-02 18:16:27 +02:00
|
|
|
invokeChange();
|
2016-06-01 17:31:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-05-20 16:56:12 +02:00
|
|
|
scope.filterHistory = function (userInput) {
|
|
|
|
var result = [];
|
|
|
|
|
|
|
|
if (scope.history && userInput) {
|
|
|
|
for (var i = 0; i < scope.history.length; i++) {
|
|
|
|
if (scope.history[i].indexOf(userInput) === 0) {
|
|
|
|
result.push(scope.history[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $q.resolve(result);
|
|
|
|
};
|
|
|
|
|
2016-06-20 18:22:18 +02:00
|
|
|
if (ngModel) {
|
2016-06-19 17:10:26 +02:00
|
|
|
scope.$watch(function () {
|
|
|
|
return ngModel.$viewValue;
|
|
|
|
}, function (value) {
|
2016-12-04 16:21:06 +01:00
|
|
|
scope.optionValue = getHumanReadableValue(value);
|
2016-06-19 17:10:26 +02:00
|
|
|
});
|
|
|
|
}
|
2016-06-05 09:52:55 +02:00
|
|
|
|
2016-06-11 12:52:40 +02:00
|
|
|
scope.$watch('option', function () {
|
2018-05-20 16:56:12 +02:00
|
|
|
loadHistory();
|
2016-06-11 12:52:40 +02:00
|
|
|
element.find('[data-toggle="popover"]').popover();
|
2016-06-05 09:52:55 +02:00
|
|
|
});
|
2016-07-03 11:08:06 +02:00
|
|
|
|
|
|
|
scope.$watch('defaultValue', function (value) {
|
|
|
|
var displayValue = value;
|
|
|
|
|
|
|
|
if (scope.option && scope.option.options) {
|
|
|
|
for (var i = 0; i < scope.option.options.length; i++) {
|
|
|
|
var optionItem = scope.option.options[i];
|
|
|
|
|
|
|
|
if (optionItem.value === value) {
|
|
|
|
displayValue = optionItem.name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-04 16:21:06 +01:00
|
|
|
scope.placeholder = getHumanReadableValue(displayValue);
|
2016-07-03 11:08:06 +02:00
|
|
|
});
|
2018-05-20 16:56:12 +02:00
|
|
|
|
|
|
|
loadHistory();
|
2016-06-01 17:31:48 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}]);
|
2016-08-01 16:49:16 +02:00
|
|
|
}());
|