support tree view for bittorrent files
This commit is contained in:
parent
26d9eafbe7
commit
4305b80c21
|
@ -372,6 +372,7 @@
|
||||||
<script src="scripts/directives/pieceBar.js"></script>
|
<script src="scripts/directives/pieceBar.js"></script>
|
||||||
<script src="scripts/directives/pieceMap.js"></script>
|
<script src="scripts/directives/pieceMap.js"></script>
|
||||||
<script src="scripts/directives/chart.js"></script>
|
<script src="scripts/directives/chart.js"></script>
|
||||||
|
<script src="scripts/directives/indeterminate.js"></script>
|
||||||
<script src="scripts/directives/placeholder.js"></script>
|
<script src="scripts/directives/placeholder.js"></script>
|
||||||
<script src="scripts/directives/setting.js"></script>
|
<script src="scripts/directives/setting.js"></script>
|
||||||
<script src="scripts/directives/settingDialog.js"></script>
|
<script src="scripts/directives/settingDialog.js"></script>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
defaultLanguage: 'en',
|
defaultLanguage: 'en',
|
||||||
defaultHost: 'localhost',
|
defaultHost: 'localhost',
|
||||||
defaultSecureProtocol: 'https',
|
defaultSecureProtocol: 'https',
|
||||||
|
defaultPathSeparator: '/',
|
||||||
globalStatStorageCapacity: 120,
|
globalStatStorageCapacity: 120,
|
||||||
taskStatStorageCapacity: 300,
|
taskStatStorageCapacity: 300,
|
||||||
lazySaveTimeout: 500,
|
lazySaveTimeout: 500,
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var includeLocalPeer = true;
|
var includeLocalPeer = true;
|
||||||
|
var addVirtualFileNode = true;
|
||||||
|
|
||||||
if (!$scope.task) {
|
if (!$scope.task) {
|
||||||
return aria2TaskService.getTaskStatus($routeParams.gid, function (response) {
|
return aria2TaskService.getTaskStatus($routeParams.gid, function (response) {
|
||||||
|
@ -82,7 +83,7 @@
|
||||||
}
|
}
|
||||||
}, silent, includeLocalPeer);
|
}, silent, includeLocalPeer);
|
||||||
}
|
}
|
||||||
}, silent);
|
}, silent, addVirtualFileNode);
|
||||||
} else {
|
} else {
|
||||||
return aria2TaskService.getTaskStatusAndBtPeers($routeParams.gid, function (response) {
|
return aria2TaskService.getTaskStatusAndBtPeers($routeParams.gid, function (response) {
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
@ -91,7 +92,7 @@
|
||||||
|
|
||||||
processTask(response.task);
|
processTask(response.task);
|
||||||
processPeers(response.peers);
|
processPeers(response.peers);
|
||||||
}, silent, requireBtPeers($scope.task), includeLocalPeer);
|
}, silent, requireBtPeers($scope.task), includeLocalPeer, addVirtualFileNode);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@
|
||||||
for (var i = 0; i < $scope.task.files.length; i++) {
|
for (var i = 0; i < $scope.task.files.length; i++) {
|
||||||
var file = $scope.task.files[i];
|
var file = $scope.task.files[i];
|
||||||
|
|
||||||
if (file && file.selected) {
|
if (file && file.selected && !file.isDir) {
|
||||||
selectedFileIndex.push(file.index);
|
selectedFileIndex.push(file.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,10 +123,78 @@
|
||||||
}, silent);
|
}, silent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var setSelectedNode = function (node, value) {
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.files && node.files.length) {
|
||||||
|
for (var i = 0; i < node.files.length; i++) {
|
||||||
|
var fileNode = node.files[i];
|
||||||
|
fileNode.selected = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.subDirs && node.subDirs.length) {
|
||||||
|
for (var i = 0; i < node.subDirs.length; i++) {
|
||||||
|
var dirNode = node.subDirs[i];
|
||||||
|
setSelectedNode(dirNode, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.selected = value;
|
||||||
|
node.partialSelected = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateDirNodeSelectedStatus = function (node) {
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedSubNodesCount = 0;
|
||||||
|
var partitalSelectedSubNodesCount = 0;
|
||||||
|
|
||||||
|
if (node.files && node.files.length) {
|
||||||
|
for (var i = 0; i < node.files.length; i++) {
|
||||||
|
var fileNode = node.files[i];
|
||||||
|
selectedSubNodesCount += (fileNode.selected ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.subDirs && node.subDirs.length) {
|
||||||
|
for (var i = 0; i < node.subDirs.length; i++) {
|
||||||
|
var dirNode = node.subDirs[i];
|
||||||
|
updateDirNodeSelectedStatus(dirNode);
|
||||||
|
selectedSubNodesCount += (dirNode.selected ? 1 : 0);
|
||||||
|
partitalSelectedSubNodesCount += (dirNode.partialSelected ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.selected = (selectedSubNodesCount > 0 && selectedSubNodesCount === (node.subDirs.length + node.files.length));
|
||||||
|
node.partialSelected = ((selectedSubNodesCount > 0 && selectedSubNodesCount < (node.subDirs.length + node.files.length)) || partitalSelectedSubNodesCount > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
var updateAllDirNodesSelectedStatus = function () {
|
||||||
|
if (!$scope.task || !$scope.task.multiDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < $scope.task.files.length; i++) {
|
||||||
|
var node = $scope.task.files[i];
|
||||||
|
|
||||||
|
if (!node.isDir) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDirNodeSelectedStatus(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.context = {
|
$scope.context = {
|
||||||
currentTab: 'overview',
|
currentTab: 'overview',
|
||||||
isEnableSpeedChart: ariaNgSettingService.getDownloadTaskRefreshInterval() > 0,
|
isEnableSpeedChart: ariaNgSettingService.getDownloadTaskRefreshInterval() > 0,
|
||||||
showChooseFilesToolbar: false,
|
showChooseFilesToolbar: false,
|
||||||
|
collapsedDirs: {},
|
||||||
btPeers: [],
|
btPeers: [],
|
||||||
healthPercent: 0,
|
healthPercent: 0,
|
||||||
collapseTrackers: true,
|
collapseTrackers: true,
|
||||||
|
@ -165,6 +234,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.changeFileListDisplayOrder = function (type, autoSetReverse) {
|
$scope.changeFileListDisplayOrder = function (type, autoSetReverse) {
|
||||||
|
if ($scope.task && $scope.task.multiDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var oldType = ariaNgCommonService.parseOrderType(ariaNgSettingService.getFileListDisplayOrder());
|
var oldType = ariaNgCommonService.parseOrderType(ariaNgSettingService.getFileListDisplayOrder());
|
||||||
var newType = ariaNgCommonService.parseOrderType(type);
|
var newType = ariaNgCommonService.parseOrderType(type);
|
||||||
|
|
||||||
|
@ -183,6 +256,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getFileListOrderType = function () {
|
$scope.getFileListOrderType = function () {
|
||||||
|
if ($scope.task && $scope.task.multiDir) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return ariaNgSettingService.getFileListDisplayOrder();
|
return ariaNgSettingService.getFileListDisplayOrder();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -191,14 +268,16 @@
|
||||||
$scope.context.showChooseFilesToolbar = true;
|
$scope.context.showChooseFilesToolbar = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getSelectedFileCount = function () {
|
$scope.isAnyFileSelected = function () {
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
for (var i = 0; i < $scope.task.files.length; i++) {
|
for (var i = 0; i < $scope.task.files.length; i++) {
|
||||||
count += $scope.task.files[i].selected ? 1 : 0;
|
var file = $scope.task.files[i];
|
||||||
|
|
||||||
|
if (!file.isDir && file.selected) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.selectFiles = function (type) {
|
$scope.selectFiles = function (type) {
|
||||||
|
@ -207,14 +286,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < $scope.task.files.length; i++) {
|
for (var i = 0; i < $scope.task.files.length; i++) {
|
||||||
|
var file = $scope.task.files[i];
|
||||||
|
|
||||||
|
if (file.isDir) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'all') {
|
if (type === 'all') {
|
||||||
$scope.task.files[i].selected = true;
|
file.selected = true;
|
||||||
} else if (type === 'none') {
|
} else if (type === 'none') {
|
||||||
$scope.task.files[i].selected = false;
|
file.selected = false;
|
||||||
} else if (type === 'reverse') {
|
} else if (type === 'reverse') {
|
||||||
$scope.task.files[i].selected = !$scope.task.files[i].selected;
|
file.selected = !file.selected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAllDirNodesSelectedStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.chooseSpecifiedFiles = function (type) {
|
$scope.chooseSpecifiedFiles = function (type) {
|
||||||
|
@ -222,12 +309,19 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var files = $scope.task.files;
|
||||||
var extensions = ariaNgFileTypes[type];
|
var extensions = ariaNgFileTypes[type];
|
||||||
var fileIndexes = [];
|
var fileIndexes = [];
|
||||||
var isAllSelected = true;
|
var isAllSelected = true;
|
||||||
|
|
||||||
for (var i = 0; i < $scope.task.files.length; i++) {
|
for (var i = 0; i < files.length; i++) {
|
||||||
var extension = ariaNgCommonService.getFileExtension($scope.task.files[i].fileName);
|
var file = files[i];
|
||||||
|
|
||||||
|
if (file.isDir) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var extension = ariaNgCommonService.getFileExtension(file.fileName);
|
||||||
|
|
||||||
if (extension) {
|
if (extension) {
|
||||||
extension = extension.toLowerCase();
|
extension = extension.toLowerCase();
|
||||||
|
@ -236,7 +330,7 @@
|
||||||
if (extensions.indexOf(extension) >= 0) {
|
if (extensions.indexOf(extension) >= 0) {
|
||||||
fileIndexes.push(i);
|
fileIndexes.push(i);
|
||||||
|
|
||||||
if (!$scope.task.files[i].selected) {
|
if (!file.selected) {
|
||||||
isAllSelected = false;
|
isAllSelected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,8 +338,14 @@
|
||||||
|
|
||||||
for (var i = 0; i < fileIndexes.length; i++) {
|
for (var i = 0; i < fileIndexes.length; i++) {
|
||||||
var index = fileIndexes[i];
|
var index = fileIndexes[i];
|
||||||
$scope.task.files[index].selected = !isAllSelected;
|
var file = files[index];
|
||||||
|
|
||||||
|
if (file && !file.isDir) {
|
||||||
|
file.selected = !isAllSelected;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAllDirNodesSelectedStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.saveChoosedFiles = function () {
|
$scope.saveChoosedFiles = function () {
|
||||||
|
@ -263,12 +363,43 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.setSelectedFile = function () {
|
$scope.setSelectedFile = function (updateNodeSelectedStatus) {
|
||||||
|
if (updateNodeSelectedStatus) {
|
||||||
|
updateAllDirNodesSelectedStatus();
|
||||||
|
}
|
||||||
|
|
||||||
if (!$scope.context.showChooseFilesToolbar) {
|
if (!$scope.context.showChooseFilesToolbar) {
|
||||||
setSelectFiles(true);
|
setSelectFiles(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.collapseDir = function (dirNode, newValue) {
|
||||||
|
var nodePath = dirNode.nodePath;
|
||||||
|
|
||||||
|
if (angular.isUndefined(newValue)) {
|
||||||
|
newValue = !$scope.context.collapsedDirs[nodePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newValue) {
|
||||||
|
for (var i = 0; i < dirNode.subDirs.length; i++) {
|
||||||
|
$scope.collapseDir(dirNode.subDirs[i], newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodePath) {
|
||||||
|
$scope.context.collapsedDirs[nodePath] = newValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setSelectedNode = function (dirNode) {
|
||||||
|
setSelectedNode(dirNode, dirNode.selected);
|
||||||
|
updateAllDirNodesSelectedStatus();
|
||||||
|
|
||||||
|
if (!$scope.context.showChooseFilesToolbar) {
|
||||||
|
$scope.setSelectedFile(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
$scope.changePeerListDisplayOrder = function (type, autoSetReverse) {
|
$scope.changePeerListDisplayOrder = function (type, autoSetReverse) {
|
||||||
var oldType = ariaNgCommonService.parseOrderType(ariaNgSettingService.getPeerListDisplayOrder());
|
var oldType = ariaNgCommonService.parseOrderType(ariaNgSettingService.getPeerListDisplayOrder());
|
||||||
var newType = ariaNgCommonService.parseOrderType(type);
|
var newType = ariaNgCommonService.parseOrderType(type);
|
||||||
|
|
17
src/scripts/directives/indeterminate.js
Normal file
17
src/scripts/directives/indeterminate.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('ariaNg').directive('ngIndeterminate', function () {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
scope: {
|
||||||
|
indeterminate: '=ngIndeterminate'
|
||||||
|
},
|
||||||
|
link: function (scope, element) {
|
||||||
|
scope.$watch('indeterminate', function () {
|
||||||
|
element[0].indeterminate = (scope.indeterminate === 'true' || scope.indeterminate === true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}());
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
angular.module('ariaNg').filter('fileOrderBy', ['$filter', 'ariaNgCommonService', function ($filter, ariaNgCommonService) {
|
angular.module('ariaNg').filter('fileOrderBy', ['$filter', 'ariaNgCommonService', function ($filter, ariaNgCommonService) {
|
||||||
return function (array, type) {
|
return function (array, type) {
|
||||||
if (!angular.isArray(array)) {
|
if (!angular.isArray(array) || !type) {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('ariaNg').factory('aria2TaskService', ['$q', 'bittorrentPeeridService', 'aria2Errors', 'aria2RpcService', 'ariaNgCommonService', 'ariaNgLocalizationService', 'ariaNgLogService', 'ariaNgSettingService', function ($q, bittorrentPeeridService, aria2Errors, aria2RpcService, ariaNgCommonService, ariaNgLocalizationService, ariaNgLogService, ariaNgSettingService) {
|
angular.module('ariaNg').factory('aria2TaskService', ['$q', 'bittorrentPeeridService', 'ariaNgConstants', 'aria2Errors', 'aria2RpcService', 'ariaNgCommonService', 'ariaNgLocalizationService', 'ariaNgLogService', 'ariaNgSettingService', function ($q, bittorrentPeeridService, ariaNgConstants, aria2Errors, aria2RpcService, ariaNgCommonService, ariaNgLocalizationService, ariaNgLogService, ariaNgSettingService) {
|
||||||
var getFileName = function (file) {
|
var getFileName = function (file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
ariaNgLogService.warn('[aria2TaskService.getFileName] file is null');
|
ariaNgLogService.warn('[aria2TaskService.getFileName] file is null');
|
||||||
|
@ -54,6 +54,150 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var getRelativePath = function (task, file) {
|
||||||
|
var downloadPath = task.dir;
|
||||||
|
var relativePath = file.path;
|
||||||
|
|
||||||
|
if (downloadPath) {
|
||||||
|
downloadPath = downloadPath.replace(/\\/g, ariaNgConstants.defaultPathSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relativePath) {
|
||||||
|
relativePath = relativePath.replace(/\\/g, ariaNgConstants.defaultPathSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
var trimStartPathSeparator = function () {
|
||||||
|
if (relativePath.length > 1 && relativePath.charAt(0) === ariaNgConstants.defaultPathSeparator) {
|
||||||
|
relativePath = relativePath.substr(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var trimEndPathSeparator = function () {
|
||||||
|
if (relativePath.length > 1 && relativePath.charAt(relativePath.length - 1) === ariaNgConstants.defaultPathSeparator) {
|
||||||
|
relativePath = relativePath.substr(0, relativePath.length - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (downloadPath && relativePath.indexOf(downloadPath) === 0) {
|
||||||
|
relativePath = relativePath.substr(downloadPath.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
trimStartPathSeparator();
|
||||||
|
|
||||||
|
if (task.bittorrent && task.bittorrent.mode === 'multi' && task.bittorrent.info && task.bittorrent.info.name) {
|
||||||
|
var bittorrentName = task.bittorrent.info.name;
|
||||||
|
|
||||||
|
if (relativePath.indexOf(bittorrentName) === 0) {
|
||||||
|
relativePath = relativePath.substr(bittorrentName.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trimStartPathSeparator();
|
||||||
|
|
||||||
|
if (file.fileName && ((relativePath.lastIndexOf(file.fileName) + file.fileName.length) === relativePath.length)) {
|
||||||
|
relativePath = relativePath.substr(0, relativePath.length - file.fileName.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
trimEndPathSeparator();
|
||||||
|
|
||||||
|
return relativePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getDirectoryNode = function (path, allDirectories, allDirectoryMap) {
|
||||||
|
var node = allDirectoryMap[path];
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentNode = null;
|
||||||
|
var nodeName = path;
|
||||||
|
|
||||||
|
if (path.length) {
|
||||||
|
var parentPath = '';
|
||||||
|
var lastSeparatorIndex = path.lastIndexOf(ariaNgConstants.defaultPathSeparator);
|
||||||
|
|
||||||
|
if (lastSeparatorIndex > 0) {
|
||||||
|
parentPath = path.substring(0, lastSeparatorIndex);
|
||||||
|
nodeName = path.substring(lastSeparatorIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
parentNode = getDirectoryNode(parentPath, allDirectories, allDirectoryMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = {
|
||||||
|
isDir: true,
|
||||||
|
nodePath: path,
|
||||||
|
nodeName: nodeName,
|
||||||
|
relativePath: (parentNode && parentNode.nodePath) || '',
|
||||||
|
level: (parentNode && parentNode.level + 1) || 0,
|
||||||
|
length: 0,
|
||||||
|
selected: true,
|
||||||
|
partialSelected: false,
|
||||||
|
files: [],
|
||||||
|
subDirs: []
|
||||||
|
};
|
||||||
|
|
||||||
|
allDirectories.push(node);
|
||||||
|
allDirectoryMap[path] = node;
|
||||||
|
|
||||||
|
if (parentNode) {
|
||||||
|
parentNode.subDirs.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
|
||||||
|
var pushFileToDirectoryNode = function (file, allDirectories, allDirectoryMap) {
|
||||||
|
if (!file || !allDirectories || !allDirectoryMap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodePath = file.relativePath || '';
|
||||||
|
var directoryNode = getDirectoryNode(nodePath, allDirectories, allDirectoryMap);
|
||||||
|
|
||||||
|
directoryNode.files.push(file);
|
||||||
|
|
||||||
|
return directoryNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
var fillAllNodes = function (node, allDirectoryMap, allNodes) {
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allSubNodesLength = 0;
|
||||||
|
var selectedSubNodesCount = 0;
|
||||||
|
var partitalSelectedSubNodesCount = 0;
|
||||||
|
|
||||||
|
if (node.subDirs && node.subDirs.length) {
|
||||||
|
for (var i = 0; i < node.subDirs.length; i++) {
|
||||||
|
var dirNode = node.subDirs[i];
|
||||||
|
allNodes.push(dirNode);
|
||||||
|
|
||||||
|
fillAllNodes(dirNode, allDirectoryMap, allNodes);
|
||||||
|
|
||||||
|
allSubNodesLength += dirNode.length;
|
||||||
|
selectedSubNodesCount += (dirNode.selected ? 1 : 0);
|
||||||
|
partitalSelectedSubNodesCount += (dirNode.partialSelected ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.files && node.files.length) {
|
||||||
|
for (var i = 0; i < node.files.length; i++) {
|
||||||
|
var fileNode = node.files[i];
|
||||||
|
allNodes.push(fileNode);
|
||||||
|
|
||||||
|
allSubNodesLength += fileNode.length;
|
||||||
|
selectedSubNodesCount += (fileNode.selected ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.length = allSubNodesLength;
|
||||||
|
node.selected = (selectedSubNodesCount > 0 && selectedSubNodesCount === (node.subDirs.length + node.files.length));
|
||||||
|
node.partialSelected = ((selectedSubNodesCount > 0 && selectedSubNodesCount < (node.subDirs.length + node.files.length)) || partitalSelectedSubNodesCount > 0);
|
||||||
|
};
|
||||||
|
|
||||||
var getTaskErrorDescription = function (task) {
|
var getTaskErrorDescription = function (task) {
|
||||||
if (!task.errorCode) {
|
if (!task.errorCode) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -121,12 +265,14 @@
|
||||||
return combinedPieces;
|
return combinedPieces;
|
||||||
};
|
};
|
||||||
|
|
||||||
var processDownloadTask = function (task) {
|
var processDownloadTask = function (task, addVirtualFileNode) {
|
||||||
if (!task) {
|
if (!task) {
|
||||||
ariaNgLogService.warn('[aria2TaskService.processDownloadTask] task is null');
|
ariaNgLogService.warn('[aria2TaskService.processDownloadTask] task is null');
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addVirtualFileNode = addVirtualFileNode && task.bittorrent && task.bittorrent.mode === 'multi';
|
||||||
|
|
||||||
var pieceStatus = getPieceStatus(task.bitfield, task.numPieces);
|
var pieceStatus = getPieceStatus(task.bitfield, task.numPieces);
|
||||||
|
|
||||||
task.totalLength = parseInt(task.totalLength);
|
task.totalLength = parseInt(task.totalLength);
|
||||||
|
@ -156,6 +302,8 @@
|
||||||
|
|
||||||
if (task.files) {
|
if (task.files) {
|
||||||
var selectedFileCount = 0;
|
var selectedFileCount = 0;
|
||||||
|
var allDirectories = [];
|
||||||
|
var allDirectoryMap = {};
|
||||||
|
|
||||||
for (var i = 0; i < task.files.length; i++) {
|
for (var i = 0; i < task.files.length; i++) {
|
||||||
var file = task.files[i];
|
var file = task.files[i];
|
||||||
|
@ -166,9 +314,24 @@
|
||||||
file.completedLength = parseInt(file.completedLength);
|
file.completedLength = parseInt(file.completedLength);
|
||||||
file.completePercent = (file.length > 0 ? file.completedLength / file.length * 100 : 0);
|
file.completePercent = (file.length > 0 ? file.completedLength / file.length * 100 : 0);
|
||||||
|
|
||||||
|
if (addVirtualFileNode) {
|
||||||
|
file.relativePath = getRelativePath(task, file);
|
||||||
|
var dirNode = pushFileToDirectoryNode(file, allDirectories, allDirectoryMap);
|
||||||
|
file.level = dirNode.level + 1;
|
||||||
|
}
|
||||||
|
|
||||||
selectedFileCount += file.selected ? 1 : 0;
|
selectedFileCount += file.selected ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (addVirtualFileNode && allDirectories.length > 1) {
|
||||||
|
var allNodes = [];
|
||||||
|
var rootNode = allDirectoryMap[''];
|
||||||
|
fillAllNodes(rootNode, allDirectoryMap, allNodes);
|
||||||
|
|
||||||
|
task.files = allNodes;
|
||||||
|
task.multiDir = true;
|
||||||
|
}
|
||||||
|
|
||||||
task.selectedFileCount = selectedFileCount;
|
task.selectedFileCount = selectedFileCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +467,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getTaskStatus: function (gid, callback, silent) {
|
getTaskStatus: function (gid, callback, silent, addVirtualFileNode) {
|
||||||
return aria2RpcService.tellStatus({
|
return aria2RpcService.tellStatus({
|
||||||
gid: gid,
|
gid: gid,
|
||||||
silent: !!silent,
|
silent: !!silent,
|
||||||
|
@ -315,7 +478,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
processDownloadTask(response.data);
|
processDownloadTask(response.data, addVirtualFileNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(response);
|
callback(response);
|
||||||
|
@ -371,7 +534,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getTaskStatusAndBtPeers: function (gid, callback, silent, requirePeers, includeLocalPeer) {
|
getTaskStatusAndBtPeers: function (gid, callback, silent, requirePeers, includeLocalPeer, addVirtualFileNode) {
|
||||||
var methods = [
|
var methods = [
|
||||||
aria2RpcService.tellStatus({ gid: gid }, true)
|
aria2RpcService.tellStatus({ gid: gid }, true)
|
||||||
];
|
];
|
||||||
|
@ -393,7 +556,7 @@
|
||||||
|
|
||||||
if (response.success && response.data.length > 0) {
|
if (response.success && response.data.length > 0) {
|
||||||
response.task = response.data[0][0];
|
response.task = response.data[0][0];
|
||||||
processDownloadTask(response.task);
|
processDownloadTask(response.task, addVirtualFileNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.success && response.task.bittorrent && response.data.length > 1) {
|
if (response.success && response.task.bittorrent && response.data.length > 1) {
|
||||||
|
@ -685,14 +848,14 @@
|
||||||
callback: createTaskEventCallback(this.getTaskStatus, callback, 'error')
|
callback: createTaskEventCallback(this.getTaskStatus, callback, 'error')
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
processDownloadTasks: function (tasks) {
|
processDownloadTasks: function (tasks, addVirtualFileNode) {
|
||||||
if (!angular.isArray(tasks)) {
|
if (!angular.isArray(tasks)) {
|
||||||
ariaNgLogService.warn('[aria2TaskService.processDownloadTasks] tasks is not array', tasks);
|
ariaNgLogService.warn('[aria2TaskService.processDownloadTasks] tasks is not array', tasks);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < tasks.length; i++) {
|
for (var i = 0; i < tasks.length; i++) {
|
||||||
processDownloadTask(tasks[i]);
|
processDownloadTask(tasks[i], addVirtualFileNode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPieceStatus: function (bitField, pieceCount) {
|
getPieceStatus: function (bitField, pieceCount) {
|
||||||
|
|
|
@ -110,6 +110,14 @@
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-inline {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-expand + .checkbox {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/* angular-input-dropdown */
|
/* angular-input-dropdown */
|
||||||
input-dropdown[input-class-name="form-control"] > .input-dropdown {
|
input-dropdown[input-class-name="form-control"] > .input-dropdown {
|
||||||
width: 100%
|
width: 100%
|
||||||
|
|
|
@ -454,7 +454,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* awesome-bootstrap-checkbox extend */
|
/* awesome-bootstrap-checkbox extend */
|
||||||
.skin-aria-ng .checkbox-primary input[type="checkbox"]:checked + label::before, .checkbox-primary input[type="radio"]:checked + label::before {
|
.skin-aria-ng .checkbox-primary input[type="checkbox"]:checked + label::before,
|
||||||
|
.skin-aria-ng .checkbox-primary input[type="radio"]:checked + label::before,
|
||||||
|
.skin-aria-ng .checkbox-primary input[type="checkbox"]:indeterminate + label::before,
|
||||||
|
.skin-aria-ng .checkbox-primary input[type="radio"]:indeterminate + label::before {
|
||||||
background-color: #208fe5;
|
background-color: #208fe5;
|
||||||
border-color: #208fe5;
|
border-color: #208fe5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,17 +182,17 @@
|
||||||
<div class="task-table-title">
|
<div class="task-table-title">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<a ng-click="changeFileListDisplayOrder('name:asc', true)" translate>File Name</a>
|
<a ng-click="changeFileListDisplayOrder('name:asc', true)" ng-class="{true: 'default-cursor'}[task.multiDir]" translate>File Name</a>
|
||||||
<i class="fa" ng-class="{'fa-sort-asc fa-order-asc': isSetFileListDisplayOrder('name:asc'), 'fa-sort-desc fa-order-desc': isSetFileListDisplayOrder('name:desc')}"></i>
|
<i ng-if="!task.multiDir" class="fa" ng-class="{'fa-sort-asc fa-order-asc': isSetFileListDisplayOrder('name:asc'), 'fa-sort-desc fa-order-desc': isSetFileListDisplayOrder('name:desc')}"></i>
|
||||||
<a ng-click="showChooseFilesToolbar()" ng-if="task && task.files && task.files.length > 1 && (task.status === 'waiting' || task.status === 'paused')" translate>(Choose Files)</a>
|
<a ng-click="showChooseFilesToolbar()" ng-if="task && task.files && task.files.length > 1 && (task.status === 'waiting' || task.status === 'paused')" translate>(Choose Files)</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
<a ng-click="changeFileListDisplayOrder('percent:desc', true)" translate>Progress</a>
|
<a ng-click="changeFileListDisplayOrder('percent:desc', true)" ng-class="{true: 'default-cursor'}[task.multiDir]" translate>Progress</a>
|
||||||
<i class="fa" ng-class="{'fa-sort-asc fa-order-asc': isSetFileListDisplayOrder('percent:asc'), 'fa-sort-desc fa-order-desc': isSetFileListDisplayOrder('percent:desc')}"></i>
|
<i ng-if="!task.multiDir" class="fa" ng-class="{'fa-sort-asc fa-order-asc': isSetFileListDisplayOrder('percent:asc'), 'fa-sort-desc fa-order-desc': isSetFileListDisplayOrder('percent:desc')}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
<a ng-click="changeFileListDisplayOrder('size:asc', true)" translate>File Size</a>
|
<a ng-click="changeFileListDisplayOrder('size:asc', true)" ng-class="{true: 'default-cursor'}[task.multiDir]" translate>File Size</a>
|
||||||
<i class="fa" ng-class="{'fa-sort-asc fa-order-asc': isSetFileListDisplayOrder('size:asc'), 'fa-sort-desc fa-order-desc': isSetFileListDisplayOrder('size:desc')}"></i>
|
<i ng-if="!task.multiDir" class="fa" ng-class="{'fa-sort-asc fa-order-asc': isSetFileListDisplayOrder('size:asc'), 'fa-sort-desc fa-order-desc': isSetFileListDisplayOrder('size:desc')}"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -226,21 +226,32 @@
|
||||||
<i class="fa fa-file-archive-o"></i>
|
<i class="fa fa-file-archive-o"></i>
|
||||||
<span translate>Archives</span>
|
<span translate>Archives</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-xs btn-success" ng-click="saveChoosedFiles()" ng-disabled="getSelectedFileCount() < 1" translate>Confirm</button>
|
<button class="btn btn-xs btn-success" ng-click="saveChoosedFiles()" ng-disabled="!isAnyFileSelected()" translate>Confirm</button>
|
||||||
<button class="btn btn-xs btn-default" ng-click="cancelChooseFiles()" translate>Cancel</button>
|
<button class="btn btn-xs btn-default" ng-click="cancelChooseFiles()" translate>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="task-table-body">
|
<div class="task-table-body">
|
||||||
<div class="row" ng-repeat="file in task.files | fileOrderBy: getFileListOrderType()" data-file-index="{{file.index}}">
|
<div class="row" ng-repeat="file in task.files | fileOrderBy: getFileListOrderType()"
|
||||||
<div class="col-sm-8">
|
ng-if="!context.collapsedDirs[file.relativePath]" data-file-index="{{file.index}}">
|
||||||
|
<div class="col-sm-10" ng-if="file.isDir" style="{{'padding-left: ' + (file.level * 16) + 'px'}}">
|
||||||
|
<i class="icon-expand pointer-cursor fa" ng-click="collapseDir(file)"
|
||||||
|
ng-class="{true: 'fa-plus', false: 'fa-minus'}[!!context.collapsedDirs[file.nodePath]]"
|
||||||
|
title="{{(context.collapsedDirs[file.nodePath] ? 'Expand' : 'Collapse') | translate}}"></i>
|
||||||
|
<div class="checkbox checkbox-primary checkbox-inline">
|
||||||
|
<input id="{{'node_' + file.nodePath}}" type="checkbox" ng-disabled="!task || !task.files || task.files.length <= 1 || (task.status !== 'waiting' && task.status !== 'paused')"
|
||||||
|
ng-model="file.selected" ng-indeterminate="file.partialSelected" ng-change="setSelectedNode(file)"/>
|
||||||
|
<label for="{{'node_' + file.nodePath}}" class="allow-word-break" ng-bind="file.nodeName" title="{{file.nodeName}}"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8" ng-if="!file.isDir" style="{{'padding-left: ' + (11 + 3 + 4 + file.level * 16) + 'px'}}">
|
||||||
<div class="checkbox checkbox-primary">
|
<div class="checkbox checkbox-primary">
|
||||||
<input id="{{'file_' + file.index}}" type="checkbox" ng-disabled="!task || !task.files || task.files.length < 2 || (task.status !== 'waiting' && task.status !== 'paused')"
|
<input id="{{'file_' + file.index}}" type="checkbox" ng-disabled="!task || !task.files || task.files.length <= 1 || (task.status !== 'waiting' && task.status !== 'paused')"
|
||||||
ng-model="file.selected" ng-change="setSelectedFile()"/>
|
ng-model="file.selected" ng-change="setSelectedFile(true)"/>
|
||||||
<label for="{{'file_' + file.index}}" class="allow-word-break" ng-bind="file.fileName" title="{{file.fileName}}"></label>
|
<label for="{{'file_' + file.index}}" class="allow-word-break" ng-bind="file.fileName" title="{{file.fileName}}"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2" ng-if="!file.isDir">
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-primary" role="progressbar"
|
<div class="progress-bar progress-bar-primary" role="progressbar"
|
||||||
aria-valuenow="{{file.completePercent}}" aria-valuemin="1"
|
aria-valuenow="{{file.completePercent}}" aria-valuemin="1"
|
||||||
|
|
Reference in a new issue