root.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. (function () {
  2. 'use strict';
  3. angular.module('ariaNg').run(['$window', '$rootScope', '$location', '$document', 'ariaNgCommonService', 'ariaNgLocalizationService', 'ariaNgLogService', 'ariaNgSettingService', 'aria2TaskService', 'ariaNgNativeElectronService', function ($window, $rootScope, $location, $document, ariaNgCommonService, ariaNgLocalizationService, ariaNgLogService, ariaNgSettingService, aria2TaskService, ariaNgNativeElectronService) {
  4. var autoRefreshAfterPageLoad = false;
  5. var isUrlMatchUrl2 = function (url, url2) {
  6. if (url === url2) {
  7. return true;
  8. }
  9. var index = url2.indexOf(url);
  10. if (index !== 0) {
  11. return false;
  12. }
  13. var lastPart = url2.substring(url.length);
  14. if (lastPart.indexOf('/') === 0) {
  15. return true;
  16. }
  17. return false;
  18. };
  19. var setLightTheme = function () {
  20. $rootScope.currentTheme = 'light';
  21. angular.element('body').removeClass('theme-dark');
  22. };
  23. var setDarkTheme = function () {
  24. $rootScope.currentTheme = 'dark';
  25. angular.element('body').addClass('theme-dark');
  26. };
  27. var setThemeBySystemSettings = function () {
  28. if (!ariaNgSettingService.isBrowserSupportDarkMode()) {
  29. setLightTheme();
  30. return;
  31. }
  32. var matchPreferColorScheme = $window.matchMedia('(prefers-color-scheme: dark)');
  33. ariaNgLogService.info('[root.setThemeBySystemSettings] system uses ' + (matchPreferColorScheme.matches ? 'dark' : 'light') + ' theme');
  34. if (matchPreferColorScheme.matches) {
  35. setDarkTheme();
  36. } else {
  37. setLightTheme();
  38. }
  39. };
  40. var initTheme = function () {
  41. if (ariaNgSettingService.getTheme() === 'system') {
  42. setThemeBySystemSettings();
  43. } else if (ariaNgSettingService.getTheme() === 'dark') {
  44. setDarkTheme();
  45. } else {
  46. setLightTheme();
  47. }
  48. };
  49. var initCheck = function () {
  50. var browserFeatures = ariaNgSettingService.getBrowserFeatures();
  51. if (!browserFeatures.localStroage) {
  52. ariaNgLogService.warn('[root.initCheck] LocalStorage is not supported!');
  53. }
  54. if (!browserFeatures.cookies) {
  55. ariaNgLogService.warn('[root.initCheck] Cookies is not supported!');
  56. }
  57. if (!ariaNgSettingService.isBrowserSupportStorage()) {
  58. angular.element('body').prepend('<div class="disable-overlay"></div>');
  59. angular.element('.main-sidebar').addClass('blur');
  60. angular.element('.navbar').addClass('blur');
  61. angular.element('.content-body').addClass('blur');
  62. ariaNgLocalizationService.notifyInPage('', 'You cannot use AriaNg because this browser does not meet the minimum requirements for data storage.', {
  63. type: 'error',
  64. delay: false
  65. });
  66. throw new Error('You cannot use AriaNg because this browser does not meet the minimum requirements for data storage.');
  67. }
  68. };
  69. var initNavbar = function () {
  70. angular.element('section.sidebar > ul > li[data-href-match] > a').click(function () {
  71. angular.element('section.sidebar > ul li').removeClass('active');
  72. angular.element(this).parent().addClass('active');
  73. });
  74. angular.element('section.sidebar > ul > li.treeview > ul.treeview-menu > li[data-href-match] > a').click(function () {
  75. angular.element('section.sidebar > ul li').removeClass('active');
  76. angular.element(this).parent().addClass('active').parent().parent().addClass('active');
  77. });
  78. };
  79. var setNavbarSelected = function (location) {
  80. angular.element('section.sidebar > ul li').removeClass('active');
  81. angular.element('section.sidebar > ul > li[data-href-match]').each(function (index, element) {
  82. var match = angular.element(element).attr('data-href-match');
  83. if (isUrlMatchUrl2(match, location)) {
  84. angular.element(element).addClass('active');
  85. }
  86. });
  87. angular.element('section.sidebar > ul > li.treeview > ul.treeview-menu > li[data-href-match]').each(function (index, element) {
  88. var match = angular.element(element).attr('data-href-match');
  89. if (isUrlMatchUrl2(match, location)) {
  90. angular.element(element).addClass('active').parent().parent().addClass('active');
  91. }
  92. });
  93. };
  94. var initContentWrapper = function () {
  95. //copy from AdminLTE app.js
  96. var defaultNavbarWithAppTitleHeight = 74; // defined in "min-height" of ".custom-app-title .main-header .navbar" in app-title.css
  97. var defaultNavbarHeight = 50; // defined in "min-height" of ".main-header .navbar" in AdminLTE.css
  98. var defaultFooterHeight = 1 + 15 + 15 + 17; // defined in "border-top" of ".main-footer" in AdminLTE.css, "padding" of ".main-footer" in AdminLTE.css and "line-height" of ".skin-aria-ng .main-footer > .navbar > .navbar-toolbar > .nav > li > a" in default.css;
  99. var windowHeight = $(window).height();
  100. var headerHeight = $('.main-header').outerHeight() || (ariaNgNativeElectronService.useCustomAppTitle() ? defaultNavbarWithAppTitleHeight : defaultNavbarHeight);
  101. var footerHeight = $('.main-footer').outerHeight() || defaultFooterHeight;
  102. var neg = headerHeight + footerHeight;
  103. $('.content-wrapper').css('min-height', windowHeight - footerHeight);
  104. $('.content-body').css('height', windowHeight - neg);
  105. };
  106. var initFileDragSupport = function () {
  107. var getDropFile = function (e) {
  108. if (!e || !e.dataTransfer) {
  109. return null;
  110. }
  111. if (e.dataTransfer.items && e.dataTransfer.items[0] && e.dataTransfer.items[0].kind === 'file') {
  112. return e.dataTransfer.items[0].getAsFile();
  113. } else if (e.dataTransfer.files && e.dataTransfer.files[0]) {
  114. return e.dataTransfer.files[0];
  115. } else {
  116. return null;
  117. }
  118. };
  119. var getDropText = function (e) {
  120. if (!e || !e.dataTransfer) {
  121. return null;
  122. }
  123. return e.dataTransfer.getData('text');
  124. };
  125. var dropzone = angular.element('#dropzone');
  126. var dropzoneFileZone = angular.element('#dropzone-filezone');
  127. angular.element($window).on('dragenter', function (e) {
  128. dropzone.show();
  129. e.preventDefault();
  130. });
  131. dropzoneFileZone.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
  132. e.preventDefault();
  133. e.stopPropagation();
  134. }).on('dragleave dragend drop', function() {
  135. dropzone.hide();
  136. }).on('drop', function(e) {
  137. var file = getDropFile(e.originalEvent);
  138. if (file) {
  139. ariaNgNativeElectronService.sendNewDropFileMessageToMainProcess({
  140. filePath: file.path,
  141. location: $location.url()
  142. });
  143. return;
  144. }
  145. var text = getDropText(e.originalEvent);
  146. if (text) {
  147. ariaNgNativeElectronService.sendNewDropTextMessageToMainProcess({
  148. text: text,
  149. location: $location.url()
  150. });
  151. }
  152. });
  153. };
  154. var showSidebar = function () {
  155. angular.element('body').removeClass('sidebar-collapse').addClass('sidebar-open');
  156. };
  157. var hideSidebar = function () {
  158. angular.element('body').addClass('sidebar-collapse').removeClass('sidebar-open');
  159. };
  160. var isSidebarShowInSmallScreen = function () {
  161. return angular.element('body').hasClass('sidebar-open');
  162. };
  163. var toggleMaximizeButton = function () {
  164. angular.element('#native-title-maximize-icon').addClass('fa-window-maximize').removeClass('fa-window-restore');
  165. angular.element('#native-title-maximize-btn').attr('title', ariaNgLocalizationService.getLocalizedText('Maximize'));
  166. };
  167. var toggleRestoreButton = function () {
  168. angular.element('#native-title-maximize-icon').addClass('fa-window-restore').removeClass('fa-window-maximize');
  169. angular.element('#native-title-maximize-btn').attr('title', ariaNgLocalizationService.getLocalizedText('Restore Down'));
  170. };
  171. $rootScope.currentTheme = 'light';
  172. $rootScope.searchContext = {
  173. text: ''
  174. };
  175. $rootScope.taskContext = {
  176. rpcStatus: 'Connecting',
  177. list: [],
  178. selected: {},
  179. enableSelectAll: false,
  180. getSelectedTaskIds: function () {
  181. var result = [];
  182. if (!this.list || !this.selected || this.list.length < 1) {
  183. return result;
  184. }
  185. for (var i = 0; i < this.list.length; i++) {
  186. var task = this.list[i];
  187. if (this.selected[task.gid]) {
  188. result.push(task.gid);
  189. }
  190. }
  191. return result;
  192. },
  193. getSelectedTasks: function () {
  194. var result = [];
  195. if (!this.list || !this.selected || this.list.length < 1) {
  196. return result;
  197. }
  198. for (var i = 0; i < this.list.length; i++) {
  199. var task = this.list[i];
  200. if (this.selected[task.gid]) {
  201. result.push(task);
  202. }
  203. }
  204. return result;
  205. },
  206. isAllSelected: function () {
  207. var isAllSelected = true;
  208. for (var i = 0; i < this.list.length; i++) {
  209. var task = this.list[i];
  210. if (!$rootScope.filterTask(task)) {
  211. continue;
  212. }
  213. if (!this.selected[task.gid]) {
  214. isAllSelected = false;
  215. break;
  216. }
  217. }
  218. return isAllSelected;
  219. },
  220. hasRetryableTask: function () {
  221. for (var i = 0; i < this.list.length; i++) {
  222. var task = this.list[i];
  223. if (!$rootScope.filterTask(task)) {
  224. continue;
  225. }
  226. if ($rootScope.isTaskRetryable(task)) {
  227. return true;
  228. }
  229. }
  230. return false;
  231. },
  232. hasCompletedTask: function () {
  233. for (var i = 0; i < this.list.length; i++) {
  234. var task = this.list[i];
  235. if (!$rootScope.filterTask(task)) {
  236. continue;
  237. }
  238. if (task.status === 'complete') {
  239. return true;
  240. }
  241. }
  242. return false;
  243. },
  244. selectAll: function () {
  245. if (!this.list || !this.selected || this.list.length < 1) {
  246. return;
  247. }
  248. if (!this.enableSelectAll) {
  249. return;
  250. }
  251. var isAllSelected = this.isAllSelected();
  252. for (var i = 0; i < this.list.length; i++) {
  253. var task = this.list[i];
  254. if (!$rootScope.filterTask(task)) {
  255. continue;
  256. }
  257. this.selected[task.gid] = !isAllSelected;
  258. }
  259. },
  260. selectAllFailed: function () {
  261. if (!this.list || !this.selected || this.list.length < 1) {
  262. return;
  263. }
  264. if (!this.enableSelectAll) {
  265. return;
  266. }
  267. var isAllFailedSelected = true;
  268. for (var i = 0; i < this.list.length; i++) {
  269. var task = this.list[i];
  270. if (!$rootScope.filterTask(task)) {
  271. continue;
  272. }
  273. if (!$rootScope.isTaskRetryable(task)) {
  274. continue;
  275. }
  276. if (!this.selected[task.gid]) {
  277. isAllFailedSelected = false;
  278. }
  279. }
  280. for (var i = 0; i < this.list.length; i++) {
  281. var task = this.list[i];
  282. if (!$rootScope.filterTask(task)) {
  283. continue;
  284. }
  285. if (!$rootScope.isTaskRetryable(task)) {
  286. this.selected[task.gid] = false;
  287. continue;
  288. }
  289. this.selected[task.gid] = !isAllFailedSelected;
  290. }
  291. },
  292. selectAllCompleted: function () {
  293. if (!this.list || !this.selected || this.list.length < 1) {
  294. return;
  295. }
  296. if (!this.enableSelectAll) {
  297. return;
  298. }
  299. var isAllFailedSelected = true;
  300. for (var i = 0; i < this.list.length; i++) {
  301. var task = this.list[i];
  302. if (!$rootScope.filterTask(task)) {
  303. continue;
  304. }
  305. if (task.status !== 'complete') {
  306. continue;
  307. }
  308. if (!this.selected[task.gid]) {
  309. isAllFailedSelected = false;
  310. }
  311. }
  312. for (var i = 0; i < this.list.length; i++) {
  313. var task = this.list[i];
  314. if (!$rootScope.filterTask(task)) {
  315. continue;
  316. }
  317. if (task.status !== 'complete') {
  318. this.selected[task.gid] = false;
  319. continue;
  320. }
  321. this.selected[task.gid] = !isAllFailedSelected;
  322. }
  323. }
  324. };
  325. $rootScope.filterTask = function (task) {
  326. if (!task || !angular.isString(task.taskName)) {
  327. return false;
  328. }
  329. if (!$rootScope.searchContext || !$rootScope.searchContext.text) {
  330. return true;
  331. }
  332. return (task.taskName.toLowerCase().indexOf($rootScope.searchContext.text.toLowerCase()) >= 0);
  333. };
  334. $rootScope.isTaskRetryable = function (task) {
  335. return task && task.status === 'error' && task.errorDescription && !task.bittorrent;
  336. };
  337. $rootScope.keydownActions = {};
  338. $rootScope.swipeActions = {
  339. leftSwipe: function () {
  340. if (!ariaNgSettingService.getSwipeGesture()) {
  341. return;
  342. }
  343. if (isSidebarShowInSmallScreen()) {
  344. hideSidebar();
  345. return;
  346. }
  347. if (!this.extendLeftSwipe ||
  348. (angular.isFunction(this.extendLeftSwipe) && !this.extendLeftSwipe())) {
  349. hideSidebar();
  350. }
  351. },
  352. rightSwipe: function () {
  353. if (!ariaNgSettingService.getSwipeGesture()) {
  354. return;
  355. }
  356. if (!this.extendRightSwipe ||
  357. (angular.isFunction(this.extendRightSwipe) && !this.extendRightSwipe())) {
  358. showSidebar();
  359. }
  360. }
  361. };
  362. $rootScope.refreshPage = function () {
  363. $window.location.reload();
  364. };
  365. $rootScope.setAutoRefreshAfterPageLoad = function () {
  366. autoRefreshAfterPageLoad = true;
  367. };
  368. $rootScope.setTheme = function (theme) {
  369. if (theme === 'system') {
  370. setThemeBySystemSettings();
  371. } else if (theme === 'dark') {
  372. setDarkTheme();
  373. } else {
  374. setLightTheme();
  375. }
  376. };
  377. $rootScope.useCustomAppTitle = ariaNgNativeElectronService.useCustomAppTitle();
  378. ariaNgNativeElectronService.getWindowMaximizedAsync(function (maximized) {
  379. if (maximized) {
  380. toggleRestoreButton();
  381. } else {
  382. toggleMaximizeButton();
  383. }
  384. });
  385. $window.addEventListener('keydown', function (event) {
  386. if (!ariaNgSettingService.getKeyboardShortcuts()) {
  387. return;
  388. }
  389. var keyCode = event.keyCode || event.which || event.charCode;
  390. if ((event.code === 'KeyA' || keyCode === 65) && (event.ctrlKey || event.metaKey)) { // Select All Tasks
  391. if (angular.isFunction($rootScope.keydownActions.selectAll)) {
  392. $rootScope.keydownActions.selectAll();
  393. }
  394. } else if (event.code === 'Delete' || keyCode === 46) { // Remove Selected Task
  395. if (angular.isFunction($rootScope.keydownActions.delete)) {
  396. $rootScope.keydownActions.delete();
  397. }
  398. }
  399. }, true);
  400. ariaNgNativeElectronService.onMainWindowMaximize(function () {
  401. toggleRestoreButton();
  402. });
  403. ariaNgNativeElectronService.onMainWindowUnmaximize(function () {
  404. toggleMaximizeButton();
  405. });
  406. ariaNgNativeElectronService.onMainProcessNavigateTo(function (event, routeUrl) {
  407. $location.path(routeUrl);
  408. });
  409. ariaNgNativeElectronService.onMainProcessShowError(function (event, message) {
  410. ariaNgLocalizationService.showError(message);
  411. });
  412. ariaNgSettingService.setDebugMode(ariaNgNativeElectronService.isDevMode());
  413. ariaNgSettingService.onApplicationCacheUpdated(function () {
  414. ariaNgLocalizationService.notifyInPage('', 'Application cache has been updated, please reload the page for the changes to take effect.', {
  415. delay: false,
  416. type: 'info',
  417. templateUrl: 'views/notification-reloadable.html'
  418. });
  419. });
  420. ariaNgSettingService.onFirstAccess(function () {
  421. ariaNgLocalizationService.notifyInPage('', 'Tap to configure and get started with AriaNg.', {
  422. delay: false,
  423. onClose: function () {
  424. $location.path('/settings/ariang');
  425. }
  426. });
  427. });
  428. aria2TaskService.onFirstSuccess(function (event) {
  429. ariaNgLocalizationService.notifyInPage('', 'is connected', {
  430. type: 'success',
  431. contentPrefix: event.rpcName + ' '
  432. });
  433. });
  434. aria2TaskService.onConnectionSuccess(function () {
  435. if ($rootScope.taskContext.rpcStatus !== 'Connected') {
  436. $rootScope.taskContext.rpcStatus = 'Connected';
  437. }
  438. });
  439. aria2TaskService.onConnectionFailed(function () {
  440. if ($rootScope.taskContext.rpcStatus !== 'Disconnected') {
  441. $rootScope.taskContext.rpcStatus = 'Disconnected';
  442. }
  443. });
  444. aria2TaskService.onTaskCompleted(function (event) {
  445. ariaNgLocalizationService.notifyTaskComplete(event.task);
  446. });
  447. aria2TaskService.onBtTaskCompleted(function (event) {
  448. ariaNgLocalizationService.notifyBtTaskComplete(event.task);
  449. });
  450. aria2TaskService.onTaskErrorOccur(function (event) {
  451. ariaNgLocalizationService.notifyTaskError(event.task);
  452. });
  453. $rootScope.$on('$locationChangeStart', function (event) {
  454. ariaNgCommonService.closeAllDialogs();
  455. $rootScope.loadPromise = null;
  456. delete $rootScope.keydownActions.selectAll;
  457. delete $rootScope.keydownActions.delete;
  458. delete $rootScope.swipeActions.extendLeftSwipe;
  459. delete $rootScope.swipeActions.extendRightSwipe;
  460. if (angular.isArray($rootScope.taskContext.list) && $rootScope.taskContext.list.length > 0) {
  461. $rootScope.taskContext.list.length = 0;
  462. }
  463. if (angular.isObject($rootScope.taskContext.selected)) {
  464. $rootScope.taskContext.selected = {};
  465. }
  466. $rootScope.taskContext.enableSelectAll = false;
  467. });
  468. $rootScope.$on('$routeChangeStart', function (event, next, current) {
  469. var location = $location.path();
  470. setNavbarSelected(location);
  471. $document.unbind('keypress');
  472. });
  473. $rootScope.$on('$viewContentLoaded', function () {
  474. ariaNgNativeElectronService.sendViewLoadedMessageToMainProcess($location.path());
  475. });
  476. $rootScope.$on('$translateChangeSuccess', function(event, current, previous) {
  477. ariaNgNativeElectronService.setMainWindowLanguage();
  478. });
  479. if (ariaNgSettingService.isBrowserSupportDarkMode()) {
  480. var matchPreferColorScheme = $window.matchMedia('(prefers-color-scheme: dark)');
  481. matchPreferColorScheme.addEventListener('change', function (e) {
  482. ariaNgLogService.info('[root] system switches to ' + (e.matches ? 'dark' : 'light') + ' theme');
  483. if (ariaNgSettingService.getTheme() === 'system') {
  484. if (e.matches) {
  485. setDarkTheme();
  486. } else {
  487. setLightTheme();
  488. }
  489. }
  490. });
  491. }
  492. $rootScope.$on('$locationChangeSuccess', function (event, newUrl) {
  493. if (autoRefreshAfterPageLoad) {
  494. $window.location.reload();
  495. }
  496. });
  497. initTheme();
  498. initCheck();
  499. initNavbar();
  500. initContentWrapper();
  501. initFileDragSupport();
  502. }]);
  503. }());