setting.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. (function () {
  2. 'use strict';
  3. angular.module('ariaNg').directive('ngSetting', ['$timeout', '$q', 'ariaNgConstants', 'ariaNgLocalizationService', 'ariaNgKeyboardService', 'aria2SettingService', function ($timeout, $q, ariaNgConstants, ariaNgLocalizationService, ariaNgKeyboardService, aria2SettingService) {
  4. return {
  5. restrict: 'E',
  6. templateUrl: 'views/setting.html',
  7. require: '?ngModel',
  8. replace: true,
  9. scope: {
  10. option: '=',
  11. ngModel: '=',
  12. defaultValue: '=?',
  13. fixedValue: '=?',
  14. supportForceDeleteEmpty: '=?',
  15. onChangeValue: '&'
  16. },
  17. link: function (scope, element, attrs, ngModel) {
  18. var pendingSaveRequest = null;
  19. var options = {
  20. showPlaceholderCount: false,
  21. lazySaveTimeout: ariaNgConstants.lazySaveTimeout,
  22. errorTooltipPlacement: 'top',
  23. errorTooltipDelay: ariaNgConstants.errorTooltipDelay
  24. };
  25. angular.extend(options, attrs);
  26. var loadHistory = function () {
  27. if (!scope.option || !scope.option.showHistory) {
  28. return;
  29. }
  30. scope.history = aria2SettingService.getSettingHistory(scope.option.key);
  31. };
  32. var destroyTooltip = function () {
  33. angular.element(element).tooltip('destroy');
  34. };
  35. var showTooltip = function (cause, type, causeParams) {
  36. if (!cause) {
  37. return;
  38. }
  39. $timeout(function () {
  40. var currentValue = scope.optionStatus.getValue();
  41. if (currentValue !== 'failed' && currentValue !== 'error') {
  42. return;
  43. }
  44. angular.element(element).tooltip({
  45. animation: false,
  46. title: ariaNgLocalizationService.getLocalizedText(cause, causeParams),
  47. trigger: 'focus',
  48. placement: 'auto ' + options.errorTooltipPlacement,
  49. container: element,
  50. template:
  51. '<div class="tooltip' + (type ? ' tooltip-' + type : '') + '" role="tooltip">' +
  52. '<div class="tooltip-arrow"></div>' +
  53. '<div class="tooltip-inner"></div>' +
  54. '</div>'
  55. }).tooltip('show');
  56. }, options.errorTooltipDelay);
  57. };
  58. var getHumanReadableSize = function (size) {
  59. if (!size || parseInt(size).toString() != size) {
  60. return size;
  61. }
  62. var sizeUnits = ['', 'K', 'M', 'G'];
  63. var unitIndex = 0;
  64. for (var i = 0; i < sizeUnits.length; i++) {
  65. if ((size < 1024) || (size % 1024 !== 0)) {
  66. break;
  67. }
  68. size = size / 1024;
  69. unitIndex++;
  70. }
  71. return size + sizeUnits[unitIndex];
  72. };
  73. var getTotalCount = function (itemsText, separator, trim) {
  74. if (!itemsText || !separator || !angular.isString(itemsText)) {
  75. return 0;
  76. }
  77. var items = itemsText.split(separator);
  78. var totalCount = items.length;
  79. if (trim) {
  80. for (var i = 0; i < items.length; i++) {
  81. if (!items[i] || items[i] === '' || items[i].trim() === '') {
  82. totalCount--;
  83. }
  84. }
  85. }
  86. return totalCount;
  87. };
  88. var getHumanReadableValue = function (value) {
  89. if (scope.option.suffix === 'Bytes') {
  90. return getHumanReadableSize(value);
  91. }
  92. return value;
  93. };
  94. scope.optionStatus = (function () {
  95. var value = 'ready';
  96. return {
  97. getValue: function () {
  98. return value;
  99. },
  100. setReady: function () {
  101. destroyTooltip();
  102. value = 'ready';
  103. },
  104. setPending: function () {
  105. destroyTooltip();
  106. value = 'pending';
  107. },
  108. setSaving: function () {
  109. destroyTooltip();
  110. value = 'pending';
  111. },
  112. setSuccess: function () {
  113. destroyTooltip();
  114. value = 'success';
  115. },
  116. setFailed: function (cause) {
  117. destroyTooltip();
  118. value = 'failed';
  119. showTooltip(cause, 'warning');
  120. },
  121. setError: function (cause, causeParams) {
  122. destroyTooltip();
  123. value = 'error';
  124. showTooltip(cause, 'error', causeParams);
  125. },
  126. getStatusFeedbackStyle: function () {
  127. if (value === 'success') {
  128. return 'has-success';
  129. } else if (value === 'failed') {
  130. return 'has-warning';
  131. } else if (value === 'error') {
  132. return 'has-error';
  133. } else {
  134. return '';
  135. }
  136. },
  137. getStatusIcon: function () {
  138. if (value === 'pending') {
  139. return 'fa-hourglass-start';
  140. } else if (value === 'saving') {
  141. return 'fa-spin fa-pulse fa-spinner';
  142. } else if (value === 'success') {
  143. return 'fa-check';
  144. } else if (value === 'failed') {
  145. return 'fa-exclamation';
  146. } else if (value === 'error') {
  147. return 'fa-times';
  148. } else {
  149. return '';
  150. }
  151. },
  152. isShowStatusIcon: function () {
  153. return this.getStatusIcon() !== '';
  154. }
  155. };
  156. })();
  157. scope.getTotalCount = function () {
  158. var fixedValueCount = getTotalCount(scope.fixedValue, scope.option.separator, scope.option.trimCount);
  159. var inputValueCount = getTotalCount(scope.optionValue, scope.option.separator, scope.option.trimCount);
  160. if (!scope.optionValue && scope.showPlaceholderCount && scope.placeholderItemCount) {
  161. inputValueCount = scope.placeholderItemCount;
  162. }
  163. return fixedValueCount + inputValueCount;
  164. };
  165. scope.changeValue = function (optionValue, lazySave) {
  166. if (pendingSaveRequest) {
  167. $timeout.cancel(pendingSaveRequest);
  168. }
  169. scope.optionValue = optionValue;
  170. scope.optionStatus.setReady();
  171. if (!scope.option || !scope.option.key || scope.option.readonly) {
  172. return;
  173. }
  174. if (scope.option.required && optionValue === '') {
  175. scope.optionStatus.setError('Option value cannot be empty!');
  176. return;
  177. }
  178. if (optionValue !== '' && scope.option.type === 'integer' && !/^-?\d+$/.test(optionValue)) {
  179. scope.optionStatus.setError('Input number is invalid!');
  180. return;
  181. }
  182. if (optionValue !== '' && scope.option.type === 'float' && !/^-?(\d*\.)?\d+$/.test(optionValue)) {
  183. scope.optionStatus.setError('Input number is invalid!');
  184. return;
  185. }
  186. if (optionValue !== '' && (scope.option.type === 'integer' || scope.option.type === 'float') && (angular.isDefined(scope.option.min) || angular.isDefined(scope.option.max))) {
  187. var number = optionValue;
  188. if (scope.option.type === 'integer') {
  189. number = parseInt(optionValue);
  190. } else if (scope.option.type === 'float') {
  191. number = parseFloat(optionValue);
  192. }
  193. if (angular.isDefined(scope.option.min) && number < scope.option.min) {
  194. scope.optionStatus.setError('Input number is below min value!', { value: scope.option.min });
  195. return;
  196. }
  197. if (angular.isDefined(scope.option.max) && number > scope.option.max) {
  198. scope.optionStatus.setError('Input number is above max value!', { value: scope.option.max });
  199. return;
  200. }
  201. }
  202. if (optionValue !== '' && angular.isDefined(scope.option.pattern) && !(new RegExp(scope.option.pattern).test(optionValue))) {
  203. scope.optionStatus.setError('Input value is invalid!');
  204. return;
  205. }
  206. var data = {
  207. key: scope.option.key,
  208. value: optionValue,
  209. optionStatus: scope.optionStatus
  210. };
  211. var invokeChange = function () {
  212. scope.optionStatus.setSaving();
  213. scope.onChangeValue(data);
  214. };
  215. if (scope.onChangeValue) {
  216. if (lazySave) {
  217. scope.optionStatus.setPending();
  218. pendingSaveRequest = $timeout(function () {
  219. invokeChange();
  220. }, options.lazySaveTimeout);
  221. } else {
  222. invokeChange();
  223. }
  224. }
  225. };
  226. scope.inputKeyUp = function (event, lazySave) {
  227. if (options.deleteKeyAlwaysChangeValue === true || options.deleteKeyAlwaysChangeValue === 'true') {
  228. if (ariaNgKeyboardService.isBackspacePressed(event) || ariaNgKeyboardService.isDeletePressed(event)) {
  229. if (scope.optionValue && scope.optionValue !== '') {
  230. return; // onChange event has been triggered
  231. }
  232. scope.changeValue('', lazySave);
  233. }
  234. }
  235. };
  236. scope.filterHistory = function (userInput) {
  237. var result = [];
  238. if (scope.history && userInput) {
  239. for (var i = 0; i < scope.history.length; i++) {
  240. if (scope.history[i].indexOf(userInput) === 0) {
  241. result.push(scope.history[i]);
  242. }
  243. }
  244. }
  245. return $q.resolve(result);
  246. };
  247. if (ngModel) {
  248. scope.$watch(function () {
  249. return ngModel.$viewValue;
  250. }, function (value) {
  251. scope.optionValue = getHumanReadableValue(value);
  252. });
  253. }
  254. scope.$watch('option', function () {
  255. loadHistory();
  256. element.find('[data-toggle="popover"]').popover();
  257. });
  258. scope.$watch('defaultValue', function (value) {
  259. var displayValue = value;
  260. if (scope.option && scope.option.options) {
  261. for (var i = 0; i < scope.option.options.length; i++) {
  262. var optionItem = scope.option.options[i];
  263. if (optionItem.value === value) {
  264. displayValue = optionItem.name;
  265. break;
  266. }
  267. }
  268. }
  269. scope.placeholder = getHumanReadableValue(displayValue);
  270. if (scope.option) {
  271. scope.placeholderItemCount = getTotalCount(scope.placeholder, scope.option.separator, scope.option.trimCount);
  272. } else {
  273. scope.placeholderItemCount = 0;
  274. }
  275. });
  276. scope.showPlaceholderCount = options.showPlaceholderCount === true || options.showPlaceholderCount === 'true';
  277. loadHistory();
  278. }
  279. };
  280. }]);
  281. }());