Initial core the new overhauled ui in angular.html with fixes for ui in the old index.html

This commit is contained in:
hamza zia 2013-01-15 13:24:09 +05:00
parent e7c9e4d0cc
commit 571eaf8257
19 changed files with 15494 additions and 297 deletions

View File

@ -12,13 +12,11 @@ Then download the webui, you can either do that by downloading this repository a
Status
===========
Usable but some bugs here and there. A lot more to features to come so stay tuned.
Usable but some bugs here and there. A complete overhaul is underway in the angular.html but its completely broken currently and once it is completed then it would replace the old index.html
TODO
-----------
* More advance options for new download
* More specialized info for special download types like bit torrent and metalink
* Adding URIs to existing downloads
* Implement all features of the old version into the new architecture in angular.html
Dependencies

208
angular.html Normal file
View File

@ -0,0 +1,208 @@
<!doctype>
<html>
<head>
<link rel="icon" href="favicon.ico" />
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>aria2 web client</title>
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/font-awesome.css">
<link rel="stylesheet" href="css/style.css">
<!-- external javascript dependencies -->
<script src="js/libs/jquery-1.8.3.js"></script>
<script src="js/libs/underscore.js"></script>
<script src="js/libs/bootstrap.js"></script>
<script src="js/libs/angular.js"></script>
<script src="js/libs/jquery.flot.js"></script>
<script src="js/libs/jquery.flot.resize.js"></script>
<!-- webui core -->
<script src="js/app.js"></script>
<script src="js/services/deps.js"></script>
<script src="js/services/base64.js"></script>
<script src="js/services/utils.js"></script>
<script src="js/services/rpc/jsoncall.js"></script>
<script src="js/services/rpc/sockcall.js"></script>
<script src="js/services/rpc/syscall.js"></script>
<script src="js/services/rpc/rpc.js"></script>
<script src="js/ctrls/nav.js"></script>
<script src="js/ctrls/modal.js"></script>
<script src="js/ctrls/download.js"></script>
<script src="js/init.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" ng-controller="NavCtrl">
<div class="navbar-inner">
<div class="container">
<!-- .btn-navbar is used as the toggle for collapsed navbar content -->
<a class="btn btn-navbar"
data-toggle="collapse"
data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand">{{ name }}</a>
<div class="nav-collapse">
<ul class="nav">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
Add <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<a href="#"><i class="icon-download"></i> Add Download URL</a>
</li>
<li>
<a href="#"><i class="icon-file"></i> Add Metalink</a>
</li>
<li>
<a href="#"><i class="icon-file"></i> Add Torrent</a>
</li>
</ul>
</li>
<li class="dropdown" id="stop_downloads">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
Manage <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="#"><i class="icon-pause"></i> Pause All</a></li>
<li><a href="#"><i class="icon-play"></i> Resume Paused</a></li>
<li><a href="#"><i class="icon-remove"></i> Purge Completed</a></li>
<li><a href="#"><i class="icon-fire"></i> Remove All</a></li>
</ul>
</li>
</ul>
<ul class="nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Settings <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="#">
<i class="icon-wrench"></i> Connection Settings</a>
</li>
<li><a href="#">
<i class="icon-wrench"></i> Global Settings</a></li>
</ul>
</li>
</ul>
<ul class="nav pull-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
About <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="#">
<i class="icon-list-alt"></i> Global Statistics</a></li>
<li><a href="#">
<i class="icon-info-sign"></i> About</a></li>
</ul>
</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<div role="main" class="container" ng-controller="DownloadCtrl">
<div id="active_downloads">
<table class="active-download" ng-repeat="download in getDownloads()">
<tbody>
<tr>
<td data-toggle="collapse" data-target=".download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right">
<li class="label label-success">Time left: <span class="download-eta">{{eta}}</span></li>
<li class="label label-success">Download Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label label-success">Progress: <span class="download-down_speed">{{percentage}}%</span></li>
</ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: 90%;"></div>
</div>
</td>
<td class="download-controls">
<div class="btn-group">
<button class="btn btn-mini download_pause"><i class="icon-pause"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a><i class="icon-cog"></i> Settings</a></li>
<li><a><i class="icon-list-alt"></i> Peers</a></li>
<li><a data-toggle="collapse" data-target=".download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#"><i class="icon-remove"></i> Remove</a></li>
</ul>
</div>
</td>
</tr>
<tr data-toggle="collapse" data-target=".download-detail .collapse" class="download-detail">
<td colspan="2">
<div class="collapse more_info">
<canvas class="progress chunk-canvas" width="1400" style="width: 100%;"></canvas>
<ul class="stats">
<li class="label">Status: <span class="download-status">{{status}}</span></li>
<li class="label">GID: <span class="download-gid">{{gid}}</span></li>
<li class="label">Dir: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li>
<li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="download-eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="download-connections">{{connections}}</span></li>
</ul>
<h4 class="hidden-phone">Download Files</h4>
<ul class="download-files hidden-phone">
<li class="label">{{path}} ({{size}})</li>
</ul>
<div>
<div class="download-graph"></div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div id="waiting_downloads"></div>
<div id="stopped_downloads"></div>
</div>
</body>
</html>

87
css/style.css Normal file
View File

@ -0,0 +1,87 @@
.download-name {
font-size: 12px;
}
.active-download {
width: 100%;
margin-bottom: 10px;
background-color: rgb(245, 245, 245);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px 4px 4px 4px;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
}
.waiting-download {
width: 100%;
margin-bottom: 10px;
background-color: rgb(245, 245, 245);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px 4px 4px 4px;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
}
.stopped-download {
width: 100%;
margin-bottom: 10px;
background-color: rgb(245, 245, 245);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px 4px 4px 4px;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
}
.stats {
margin-left: auto;
margin-right: auto;
text-align: center;
}
.stats li {
padding: 5px;
}
.download-controls {
width: 60px;
}
.download-controls .btn-group {
height: 70px;
width: 100%;
float: right;
}
.download-controls .btn-group .btn:nth-child(1) {
width: 70%;
}
.download-controls .btn-group .btn:nth-child(2) {
width: 30%;
}
.download-controls .btn-group .btn {
height: 100%;
}
.download-controls .btn-group .btn i {
height: 100%;
font-size: 28px;
color: gray;
}
.download-overview {
padding: 10px;
cursor: pointer;
}
.download-detail {
cursor: pointer;
}
.download-detail .stats li {
width: 150px;
margin-bottom: 5px;
}
.download-detail h4 {
margin-left: 10px;
}
.download-files {
width: 90%;
margin-left: auto;
margin-right: auto;
}
.download-files li {
padding: 5px;
margin: 5px;
}
@media all and (min-width: 980px) {
body {
padding-top: 60px;
}
}

View File

@ -12,13 +12,14 @@
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/font-awesome.css">
<link rel="stylesheet" href="css/style.css">
<!-- external javascript dependencies -->
<script src="js/libs/jquery-1.8.3.js"></script>
<script src="js/libs/jquery.flot.js"></script>
<script src="js/libs/jquery.flot.resize.js"></script>
<script src="js/libs/lodash.underscore.min.js"></script>
<script src="js/libs/underscore.js"></script>
<script src="js/libs/bootstrap.js"></script>
<script src="js/libs/mustache.js"></script>
<script src="js/libs/piecon.js"></script>
@ -66,229 +67,197 @@
overflow: hidden;
word-wrap: break-word;
}
.active-download {
width: 100%;
margin-bottom: 10px;
background-color: rgb(245, 245, 245);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px 4px 4px 4px;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
}
.stats {
margin-left: auto;
margin-right: auto;
text-align: center;
}
.stats li {
width: 145px;
padding: 5px;
}
.download-controls {
width: 60px;
}
.download-controls .btn-group {
height: 70px;
width: 100%;
float: right;
}
.download-controls .btn-group .btn:nth-child(1) {
width: 70%;
}
.download-controls .btn-group .btn:nth-child(2) {
width: 30%;
}
.download-controls .btn-group .btn {
height: 100%;
}
.download-controls .btn-group .btn i {
height: 100%;
font-size: 28px;
color: gray;
}
.download-overview {
padding: 10px;
cursor: pointer;
}
.download-detail {
cursor: pointer;
}
.download-detail .stats li {
margin-bottom: 5px;
}
.download-detail h4 {
margin-left: 10px;
}
.download-files {
width: 90%;
margin-left: auto;
margin-right: auto;
}
.download-files li {
padding: 5px;
margin-bottom: 5px;
}
@media all and (min-width: 980px) {
body {
padding-top: 60px;
}
}
</style>
<!-- }}} -->
<!--{{{ active downloads template -->
<script type="text/mustache" id="download_active_template">
<div class="row download_item download_active_item" data-gid="{{gid}}" data-settingsName={{sett_name}}>
<div class="span2 download_name">
<b style="font-size: 12px;" class="tmp_name">{{name}}</b>
</div>
<div class="span6">
<div class="pull-right">
<span class="label label-success">Remaining: <span class="tmp_remaining">{{remaining}}</span></span> |
<span class="label label-success">Progress: <span class="tmp_percentage">{{percentage}}</span>%</span> |
<span class="label label-success">Speed: <span class="tmp_down_speed">{{down_speed}}</span></span> |
<span class="label label-success">Time left: <span class="tmp_eta">{{eta}}</span></span>
</div>
</div>
<div class="span3">
<div class="button_set">
<button class="btn btn-mini download_pause"><i class="icon-pause"></i>Pause</button>
<button class="btn btn-mini btn-info" data-toggle="collapse" data-target="[data-gid={{gid}}] .more_info" onclick="$(this).button('toggle');"><i class="icon-info-sign"></i> More Info</button>
<button class="btn btn-mini btn-danger download_remove"><i class="icon-remove"></i> Remove</button>
</div>
</div>
<br>
<div class="span11 progress full-progress progress-striped active">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
<div class="span11 more_info collapse">
<canvas class="span10 progress chunk-canvas" width="1400"></canvas>
<b class="span2 label active_settings">Status: <span class="tmp_status">{{status}}</span></b>
<b class="span2 label active_settings">GID: <span class="tmp_gid">{{gid}}</span></b>
<b class="span2 label active_settings">Dir: <span class="tmp_dir">{{dir}}</span></b>
<b class="span2 label active_settings">Size: <span class="tmp_size">{{size}}</span></b>
<b class="span2 label active_settings">Downloaded: <span class="tmp_downloaded">{{downloaded}}</span></b>
<b class="span2 label active_settings">Num of Pieces: <span class="tmp_numPieces">{{numPieces}}</span></b>
<b class="span2 label active_settings">Piece Length: <span class="tmp_pieceLength">{{pieceLength}}</span></b>
<b class="span2 label active_settings">ETA: <span class="tmp_eta">{{eta}}</span></b>
<b class="span2 label active_settings">Down Speed: <span class="tmp_down_speed">{{down_speed}}</span></b>
<b class="span2 label active_settings">Upload Speed: <span class="tmp_upload_speed">{{upload_speed}}</span></b>
<b class="span2 label active_settings">Upload Length: <span class="tmp_uploadLength">{{uploadLength}}</span></b>
<b class="span2 label active_settings">Connections: <span class="tmp_connections">{{connections}}</span></b>
<div class="span12">
<h3>Files</h3>
<ul>
{{#files}}
<li><b>{{path}} <span class="label">{{size}}</span></b></li>
{{/files}}
</ul>
</div>
<div class="pull-right" style="margin-right: 5px;">
<button class="btn btn-mini download_settings"><i class="icon-cog"></i> Download Settings for {{sett_name}}</button>{{#bittorrent}}<button class="btn btn-mini torrent_info"><i class="icon-list-alt"></i> Peers</button>{{/bittorrent}}
</div>
<div class="span11">
<div class="active_graph"></div>
</div>
</div>
</div>
<table class="active-download" data-gid="{{gid}}" data-settingsName={{sett_name}}>
<tbody>
<tr>
<td data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right">
<li class="label label-success">Time left: <span class="download-eta">{{eta}}</span></li>
<li class="label label-success">Download Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label label-success">Progress: <span class="download-down_speed">{{percentage}}%</span></li>
</ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
</td>
<td class="download-controls">
<div class="btn-group">
<button class="btn btn-mini download_pause"><i class="icon-pause"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" class="download_settings"><i class="icon-cog"></i> Settings</a></li>
{{#bittorrent}}
<li><a href="#" class="torrent_info"><i class="icon-list-alt"></i> Peers</a></li>
{{/bittorrent}}
<li><a href="#" data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#" class="download_remove"><i class="icon-remove"></i> Remove</a></li>
</ul>
</div>
</td>
</tr>
<tr data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-detail">
<td colspan="2">
<div class="collapse more_info">
<canvas class="progress chunk-canvas" width="1400" style="width: 100%;"></canvas>
<ul class="stats">
<li class="label">Status: <span class="download-status">{{status}}</span></li>
<li class="label">GID: <span class="download-gid">{{gid}}</span></li>
<li class="label">Dir: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li>
<li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="download-eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="download-connections">{{connections}}</span></li>
</ul>
<h4 class="hidden-phone">Download Files</h4>
<ul class="download-files hidden-phone">
{{#files}}
<li class="label">{{path}} ({{size}})</li>
{{/files}}
</ul>
<div>
<div class="active_graph"></div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</script>
<!-- active downloads template end }}}-->
<!--{{{ waiting downloads template-->
<script type="text/mustache" id="download_waiting_template">
<div class="row download_item download_waiting_item" data-gid="{{gid}}" data-settingsName={{sett_name}}>
<div class="span2 download_name">
<b style="font-size: 12px;" class="tmp_name">{{name}}</b>
</div>
<div class="span6">
<span class="badge badge-warning">Status: <span class="tmp_status">{{status}}</span></span>
<div class="pull-right">
<span class="label label-warning">Size: <span class="tmp_size">{{size}}</span></span> |
<span class="label label-warning">Remaining: <span class="tmp_remaining">{{remaining}}</span></span> |
<span class="label label-warning">Progress: <span class="tmp_percentage">{{percentage}}</span>%</span>
</div>
</div>
<div class="span3">
<div class="button_set">
<button class="btn btn-mini download_play"><i class="icon-play"></i> Resume</button>
<button class="btn btn-mini btn-info" data-toggle="collapse" data-target="[data-gid={{gid}}] .more_info" onclick="$(this).button('toggle');"><i class="icon-info-sign"></i> More Info</button>
<button class="btn btn-mini btn-danger download_remove"><i class="icon-fire"></i> Purge</button>
</div>
</div>
<br>
<div class="span11 full-progress progress progress-striped">
<div class="bar" style="width: {{percentage}}%; background-color:#FBB450"></div>
</div>
<div class="span12 more_info collapse">
<b class="span2 label active_settings">Status: <span class="tmp_status">{{status}}</span></b>
<b class="span2 label active_settings">GID: <span class="tmp_gid">{{gid}}</span></b>
<b class="span2 label active_settings">Dir: <span class="tmp_dir">{{dir}}</span></b>
<b class="span2 label active_settings">Size: <span class="tmp_size">{{size}}</span></b>
<b class="span2 label active_settings">Downloaded: <span class="tmp_downloaded">{{downloaded}}</span></b>
<b class="span2 label active_settings">Num of Pieces: <span class="tmp_numPieces">{{numPieces}}</span></b>
<b class="span2 label active_settings">Piece Length: <span class="tmp_pieceLength">{{pieceLength}}</span></b>
<b class="span2 label active_settings">Upload Length: <span class="tmp_uploadLength">{{uploadLength}}</span></b>
<div class="pull-right" style="margin-right: 5px;">
<button class="btn btn-mini download_settings"><i class="icon-cog"></i> Download Settings for {{sett_name}}</button>
</div>
</div>
</div>
<table class="waiting-download" data-gid="{{gid}}" data-settingsName={{sett_name}}>
<tbody>
<tr>
<td data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right">
<li class="label label-success">Size: <span class="download-size">{{size}}</span></li>
<li class="label label-success">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label label-success">Path: <span class="download-dir">{{dir}}</span></li>
</ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
</td>
<td class="download-controls">
<div class="btn-group">
<button class="btn btn-mini download_play"><i class="icon-play"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" class="download_settings"><i class="icon-cog"></i> Settings</a></li>
<li><a href="#" data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#" class="download_remove"><i class="icon-remove"></i> Remove</a></li>
</ul>
</div>
</td>
</tr>
<tr data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-detail">
<td colspan="2">
<div class="collapse more_info">
<ul class="stats">
<li class="label">Status: <span class="download-status">{{status}}</span></li>
<li class="label">GID: <span class="download-gid">{{gid}}</span></li>
<li class="label">Dir: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li>
<li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="download-eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="download-connections">{{connections}}</span></li>
</ul>
<h4 class="hidden-phone">Download Files</h4>
<ul class="download-files hidden-phone">
{{#files}}
<li class="label">{{path}} ({{size}})</li>
{{/files}}
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</script>
<!-- waiting downloads template end }}}-->
<!--{{{ stopped downloads template-->
<script type="text/mustache" id="download_stopped_template">
<div class="row download_item download_stopped_item" data-gid="{{gid}}">
<div class="span2 download_name">
<b style="font-size: 12px;" class="tmp_name">{{name}}</b>
</div>
<div class="span6">
{{#booleans.is_error}}
<span class="badge badge-important">Status: <span class="tmp_status">{{status}}</span></span>
<div class="pull-right">
<span class="label label-important">Size: <span class="tmp_size">{{size}}</span></span> |
<span class="label label-important">Downloaded: <span class="tmp_downloaded">{{downloaded}}</span></span> |
<span class="label label-important">Progress: <span class="tmp_percentage">{{percentage}}</span>%</span>
</div>
{{/booleans.is_error}}
{{^booleans.is_error}}
<span class="badge badge-info">Status: <span class="tmp_status">{{status}}</span></span>
<div class="pull-right">
<span class="label label-info">Size: <span class="tmp_size">{{size}}</span></span> |
<span class="label label-info">Downloaded: <span class="tmp_downloaded">{{downloaded}}</span></span> |
<span class="label label-info">Progress: <span class="tmp_percentage">{{percentage}}</span>%</span>
</div>
{{/booleans.is_error}}
</div>
<div class="span3">
<div class="button_set">
<button class="btn btn-mini download_restart"><i class="icon-repeat"></i> Restart</button>
<button class="btn btn-mini btn-info" data-toggle="collapse" data-target="[data-gid={{gid}}] .more_info" onclick="$(this).button('toggle');"><i class="icon-info-sign"></i> More Info</button>
<button class="btn btn-mini btn-danger download_remove"><i class="icon-fire"></i> Purge</button>
</div>
</div>
<br>
<div class="span11 full-progress progress progress-striped">
{{#booleans.is_error}}
<div class="bar" style="width: {{percentage}}%; background-color:rgb(185, 74, 72)"></div>
{{/booleans.is_error}}
{{^booleans.is_error}}
<div class="bar" style="width: {{percentage}}%; background-color:#3A87AD"></div>
{{/booleans.is_error}}
</div>
<div class="span12 more_info collapse">
<b class="span2 label active_settings">Status: <span class="tmp_status">{{status}}</span></b>
<b class="span2 label active_settings">GID: <span class="tmp_gid">{{gid}}</span></b>
<b class="span2 label active_settings">Dir: <span class="tmp_dir">{{dir}}</span></b>
<b class="span2 label active_settings">Size: <span class="tmp_size">{{size}}</span></b>
<b class="span2 label active_settings">Downloaded: <span class="tmp_downloaded">{{downloaded}}</span></b>
<b class="span2 label active_settings">Num of Pieces: <span class="tmp_numPieces">{{numPieces}}</span></b>
<b class="span2 label active_settings">Piece Length: <span class="tmp_pieceLength">{{pieceLength}}</span></b>
<b class="span2 label active_settings">Upload Length: <span class="tmp_uploadLength">{{uploadLength}}</span></b>
</div>
</div>
<table class="stopped-download" data-gid="{{gid}}">
<tbody>
<tr>
<td data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right">
<li class="label label-success">Path: <span class="download-dir">{{dir}}</span></li>
</ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
</td>
<td class="download-controls">
<div class="btn-group">
<button class="btn btn-mini download_restart"><i class="icon-repeat"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#" class="download_remove"><i class="icon-remove"></i> Remove</a></li>
</ul>
</div>
</td>
</tr>
<tr data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-detail">
<td colspan="2">
<div class="collapse more_info">
<ul class="stats">
<li class="label">Status: <span class="download-status">{{status}}</span></li>
<li class="label">GID: <span class="download-gid">{{gid}}</span></li>
<li class="label">Dir: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li>
<li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="download-eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="download-connections">{{connections}}</span></li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</script>
<!-- stopped downloads template end }}}-->
@ -388,7 +357,7 @@
</script>
<!-- }}} -->
<!--{{{ download settings template start -->
<!--{{{ download settings template -->
<script type="text/mustache" id="download_settings_template">
<div class="modal-header">
@ -456,7 +425,7 @@
<button id="save_download_settings" class="btn btn-success">Save</button>
</div>
</script>
<!-- download settings template end }}}-->
<!-- download settings template }}}-->
</head>
<body>
@ -535,66 +504,6 @@
<div role="main" class="container">
<!-- Download items here -->
<table class="active-download">
<tbody>
<tr>
<td data-toggle="collapse" data-target=".download-detail .collapse" class="download-overview">
<b style="font-size: 12px;" class="tmp_name">Sample</b>
<ul class="stats pull-right">
<li class="label label-success">Time left: <span class="tmp_eta">{{eta}}</span></li>
</ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: 100%;"></div>
</div>
</td>
<td class="download-controls">
<div class="btn-group">
<button class="btn btn-mini"><i class="icon-pause"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#">Remove</a></li>
</ul>
</div>
</td>
</tr>
<tr data-toggle="collapse" data-target=".download-detail .collapse" class="download-detail">
<td colspan="2">
<div class="collapse">
<canvas class="progress chunk-canvas" width="1400" class="width: 100%;"></canvas>
<ul class="stats">
<li class="label">Status: <span class="tmp_status">{{status}}</span></li>
<li class="label">GID: <span class="tmp_gid">{{gid}}</span></li>
<li class="label">Dir: <span class="tmp_dir">{{dir}}</span></li>
<li class="label">Size: <span class="tmp_size">{{size}}</span></li>
<li class="label">Downloaded: <span class="tmp_downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="tmp_numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="tmp_pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="tmp_eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="tmp_down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="tmp_upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="tmp_uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="tmp_connections">{{connections}}</span></li>
</ul>
<h4>Download Files</h4>
<ul class="download-files">
<li class="label">sample file (200 MB)</li>
<li class="label">sample file (200 MB)</li>
<li class="label">sample file (200 MB)</li>
<li class="label">sample file (200 MB)</li>
</ul>
<div style="background: black; width: 100%;">
<div class="active_graph">h</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div id="active_downloads"></div>

3
js/app.js Normal file
View File

@ -0,0 +1,3 @@
var app = angular.module('app', []);

40
js/ctrls/download.js Normal file
View File

@ -0,0 +1,40 @@
app.controller('DownloadCtrl', [ '$scope', '$rpc',
function(scope, rpc) {
rpc.configure({
host: 'localhost',
port: 6800
});
scope.active = scope.waiting = scope.stopped = [];
rpc.subscribe('tellActive', [], function(data) {
scope.$apply(function() {
scope.active = data[0];
});
});
rpc.subscribe('tellWaiting', [0, 100], function(data) {
scope.$apply(function() {
scope.waiting = data[0];
});
});
rpc.subscribe('tellStopped', [0, 100], function(data) {
scope.$apply(function() {
scope.stopped = data[0];
});
});
scope.getDownloads = function() {
var rets = scope.active
.concat(scope.waiting).concat(scope.stopped);
return rets;
}
scope.normalize = function(d) {
return "hello";
}
}]);

3
js/ctrls/modal.js Normal file
View File

@ -0,0 +1,3 @@
app.controller('ModalCtrl', ['$scope', function(scope) {
}]);

4
js/ctrls/nav.js Normal file
View File

@ -0,0 +1,4 @@
app.controller('NavCtrl', ['$scope', '$name', function(scope, name) {
scope.name = name;
}]);

3
js/init.js Normal file
View File

@ -0,0 +1,3 @@
$(function() {
angular.bootstrap(document, ['app'])
});

14531
js/libs/angular.js vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@ -398,10 +398,24 @@ function getTemplateCtx(data) {
}
function updateDownloadTemplates(elem, ctx) {
elem = $(elem);
// update spans
for(var i in ctx) {
elem.find('.tmp_' + i).text(ctx[i]);
elem.find('.download-' + i).text(ctx[i]);
}
// update files
var html = '';
for (var i = 0; i < ctx.files.length; i++) {
html += '<li class="label">' + ctx.files[i].path + ' (' + ctx.files[i].size + ')</li>';
}
elem.find('.download-files').html(html);
console.log(html);
// update progress bar
elem.find('.full-progress .bar').css('width', ctx.percentage + '%');
// update the chunks bar
var chunks = ctx.chunks;
if (!chunks || !chunks.length) {
return;
@ -457,7 +471,7 @@ function refreshDownloadTemplates(top_elem, data) {
deleteDownloadTemplates($top_elem, data);
for(var i = 0; i < data.length; i++) {
var ctx = getTemplateCtx(data[i]);
var elem = $top_elem.find('div.download_item[data-gid=' + ctx.gid + ']');
var elem = $top_elem.find('[data-gid=' + ctx.gid + ']');
if(elem.length) {
updateDownloadTemplates(elem, ctx);
} else {
@ -635,9 +649,9 @@ function updateActiveDownloads(data) {
refreshDownloadTemplates('active', data);
updateGraphData(data);
empty_download_set('#active_downloads');
$('.download_active_item .download_settings').unbind('click').click(function() {
var gid = $(this).parents('.download_active_item').attr('data-gid');
var settings_name = $(this).parents('.download_active_item').attr('data-settingsName');
$('.active-download .download_settings').unbind('click').click(function() {
var gid = $(this).parents('.active-download').attr('data-gid');
var settings_name = $(this).parents('.active-download').attr('data-settingsName');
var gen = function(name) {
return { name: name, values: [] };
};
@ -694,8 +708,8 @@ function updateActiveDownloads(data) {
});
});
});
$('.download_active_item .download_pause').unbind('click').click(function() {
var gid = $(this).parents('.download_active_item').attr('data-gid');
$('.active-download .download_pause').unbind('click').click(function() {
var gid = $(this).parents('.active-download').attr('data-gid');
aria_syscall({
func: 'forcePause',
params: [gid],
@ -708,9 +722,9 @@ function updateActiveDownloads(data) {
}
});
});
$('.download_active_item .torrent_info').unbind('click').click(function() {
var info_name = $(this).parents('.download_active_item').attr('data-settingsName');
var gid = $(this).parents('.download_active_item').attr('data-gid');
$('.active-download .torrent_info').unbind('click').click(function() {
var info_name = $(this).parents('.active-download').attr('data-settingsName');
var gid = $(this).parents('.active-download').attr('data-gid');
aria_syscall({
func: 'getPeers',
params: [gid],
@ -740,8 +754,8 @@ function updateActiveDownloads(data) {
}
});
});
$('.download_active_item .download_remove').unbind('click').click(function() {
var gid = $(this).parents('.download_active_item').attr('data-gid');
$('.active-download .download_remove').unbind('click').click(function() {
var gid = $(this).parents('.active-download').attr('data-gid');
aria_syscall({
func: 'remove',
params: [gid],
@ -757,9 +771,9 @@ function updateActiveDownloads(data) {
}
function updateWaitingDownloads(data) {
refreshDownloadTemplates('waiting', data);
$('.download_waiting_item .download_settings').unbind('click').click(function() {
var gid = $(this).parents('.download_waiting_item').attr('data-gid');
var settings_name = $(this).parents('.download_waiting_item').attr('data-settingsName');
$('.waiting-download .download_settings').unbind('click').click(function() {
var gid = $(this).parents('.waiting-download').attr('data-gid');
var settings_name = $(this).parents('.waiting-download').attr('data-settingsName');
var gen = function(name) {
return { name: name, values: [] };
};
@ -830,8 +844,8 @@ function updateWaitingDownloads(data) {
});
});
});
$('.download_waiting_item .download_play').unbind('click').click(function() {
var gid = $(this).parents('.download_waiting_item').attr('data-gid');
$('.waiting-download .download_play').unbind('click').click(function() {
var gid = $(this).parents('.waiting-download').attr('data-gid');
aria_syscall({
func: 'unpause',
params: [gid],
@ -844,8 +858,8 @@ function updateWaitingDownloads(data) {
}
});
});
$('.download_waiting_item .download_remove').unbind('click').click(function() {
var gid = $(this).parents('.download_waiting_item').attr('data-gid');
$('.waiting-download .download_remove').unbind('click').click(function() {
var gid = $(this).parents('.waiting-download').attr('data-gid');
aria_syscall({
func: 'remove',
params: [gid],
@ -862,8 +876,8 @@ function updateWaitingDownloads(data) {
function updateStoppedDownloads(data) {
refreshDownloadTemplates('stopped', data);
$('.download_stopped_item .download_remove').unbind('click').click(function() {
var gid = $(this).parents('.download_stopped_item').attr('data-gid');
$('.stopped-download .download_remove').unbind('click').click(function() {
var gid = $(this).parents('.stopped-download').attr('data-gid');
aria_syscall({
func: 'removeDownloadResult',
params: [gid],
@ -876,8 +890,8 @@ function updateStoppedDownloads(data) {
}
});
});
$('.download_stopped_item .download_restart').unbind('click').click(function() {
var gid = $(this).parents('.download_stopped_item').attr('data-gid');
$('.stopped-download .download_restart').unbind('click').click(function() {
var gid = $(this).parents('.stopped-download').attr('data-gid');
var files;
var uris = [];
for(var i = 0; i < d_files.stopped.length; i++) {

56
js/services/base64.js Normal file
View File

@ -0,0 +1,56 @@
app.factory('$base64', [function() {
var obj = {};
var a64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
a256 = {
indexOf: function (c) {
return c.charCodeAt(0);
},
charAt: String.fromCharCode
};
function code(s, discard, alpha, beta, w1, w2) {
s = String(s);
var b = 0, x = '', i, c, bs = 1, sb = 1, length = s.length, tmp;
for (i = 0; i < length || (!discard && sb > 1); i += 1) {
b *= w1;
bs *= w1;
if (i < length) {
c = alpha.indexOf(s.charAt(i));
if (c <= -1 || c >= w1) {
throw new RangeError();
}
sb *= w1;
b += c;
}
while (bs >= w2) {
bs /= w2;
if (sb > 1) {
tmp = b;
b %= bs;
x += beta.charAt((tmp - b) / bs);
sb /= w2;
}
}
}
return x;
}
obj.btoa = function (s) {
s = code(s, false, a256, a64, 256, 64);
return s + '===='.slice((s.length % 4) || 4);
};
obj.atob = function (s) {
var i;
s = String(s).split('=');
for (i = s.length - 1; i >= 0; i -= 1) {
if (s[i].length % 4 === 1) {
throw new RangeError();
}
s[i] = code(s[i], true, a64, a256, 64, 256);
}
return s.join('');
};
return obj;
}]);

6
js/services/deps.js Normal file
View File

@ -0,0 +1,6 @@
app.constant('$name', 'webui-aria2');
app.constant('$globalTimeout', 1000);
app.value('$', $);
app.value('$_', _);
app.value('$json', JSON);

View File

@ -0,0 +1,73 @@
app.factory('$jsoncall', ['$', '$json', '$base64', function($, JSON, base64) {
return {
init: function(conf) {
this.avgTimeout = 2000;
this.serverConf = conf;
},
encode: function(obj) {
return base64.btoa( JSON.stringify(obj) );
},
ariaRequest: function(url, funcName, params, success, error) {
var startTime = new Date();
var conn = this;
$.ajax({
url: url,
timeout: this.avgTimeout,
data: {
jsonrpc: 2.0,
id: 'webui',
method: funcName,
params: params && params.length ? this.encode(params) : undefined
},
success: function(data) {
conn.avgTimeout = 3 * (new Date() - startTime);
return success(data)
},
error: error,
dataType: 'jsonp',
jsonp: 'jsoncallback'
});
},
invoke: function(opts) {
var rpc = this;
var scheme = rpc.serverConf.encryption ? 'https' : 'http';
rpc.ariaRequest(
scheme + '://' + rpc.serverConf.host + ':' + rpc.serverConf.port + '/jsonrpc',
opts.name,
opts.params,
opts.success,
function() {
// check if authentication details are given, if yes then use a hack to support
// http authentication otherwise emit error
if (!rpc.serverConf.auth) {
console.log("no user name and still error!!!");
return opts.error();
}
var authUrl = scheme + '://' +
rpc.serverConf.auth.user + ":" +
rpc.serverConf.auth.pass + "@" +
rpc.serverConf.host + ':' +
rpc.serverConf.port + '/jsonrpc';
// hack is to basically inject an image with same uri as the aria2 rpc url,
// most browsers will then cache the authentication details and we dont have
// to give them next time we make a request
var img = $('<img/>').attr("src", authUrl);
$('body').append(img);
img.remove();
// timeout to let the image load and then make a request,
setTimeout(function() {
rpc.ariaRequest(
authUrl,
opts.params,
opts.success,
opts.error
);
}, rpc.avgTimeout);
}
);
}
};
}]);

111
js/services/rpc/rpc.js Normal file
View File

@ -0,0 +1,111 @@
app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) {
var subscriptions = [],
timeout = null,
forceNextUpdate = false;
syscall.init({
host: 'localhost',
port: 6800
});
// update is implemented such that
// only one syscall at max is ongoing
// (i.e. serially) so should be private
// to maintain that invariant
var update = function() {
if (!subscriptions.length) return;
subscriptions = _.filter(subscriptions, function(e) { return !!e });
var params = _.map(subscriptions, function(s) {
return {
methodName: s.name,
params: s.params && s.params.length ? s.params : undefined
};
});
syscall.invoke({
name: 'system.multicall',
params: [params],
success: function(data) {
_.each(data.result, function(d, i) {
var handle = subscriptions[i];
if (handle) {
handle.cb(d);
if (handle.once) {
subscriptions[i] = null;
}
}
});
if (forceNextUpdate) {
forceNextUpdate = false;
return update();
}
timeout = setTimeout(update, time);
},
error: function() {
// TODO: implement a retry error handler
// which would also look for other suitable
// connection configurations for the connection
}
});
}
return {
// change the connection configuration,
// for all options read rpc/syscall.js
configure: function(conf) {
syscall.init(conf);
},
// syscall is done only once
once: function(name, params, cb) {
subscriptions.push({
once: true,
name: 'aria2.' + name,
params: params,
cb: cb
});
this.forceUpdate();
},
// callback is called each time with updated syscall data
// after the global timeout
subscribe: function(name, params, cb) {
var handle = {
once: false,
name: 'aria2.' + name,
params: params,
cb: cb
};
subscriptions.push(handle);
this.forceUpdate();
return handle;
},
// remove the subscribed callback by passing
// the returned handle bysubscribe
unsubscribe: function(handle) {
var ind = subscriptions.indexOf(handle);
subscriptions[ind] = null;
},
// force the global syscall update
forceUpdate: function() {
if (timeout) {
clearTimeout(timeout);
timeout = null;
update();
}
else if (subscriptions.length) {
update();
}
else {
// a batch call is already in progress,
// wait till it returns and force the next one
forceNextUpdate = true;
}
}
};
}]);

View File

@ -0,0 +1,88 @@
app.factory('$sockcall', ['$_', '$json', '$name', '$utils', function(_, JSON, name, utils) {
return {
// true when sockrpc is ready to be used,
// false when either initializing
// or no support for web sockets
initialized: false,
// ongoing connection handles containing connection id and callbacks
handles: [],
// websocket connection socket used for all connections
sock: null,
// connection configuration
conf: null,
// socket connection scheme, default to unencrypted connection
scheme: 'ws',
// called when a connection error occurs
onerror: function() {
_.each(this.handles, function(h) { h.error() });
this.handles = [];
},
// when connection opens
onopen: function() {
this.initialized = true;
},
// when message is recieved
onmessage: function(message) {
var data = JSON.parse(message.data);
// reverse loop because we are deleting elements
// while looping over the old items
for (var i = this.handles.length - 1; i >= 0; i--) {
if (this.handles[i].id === data.id) {
this.handles[i].success(data);
this.handles.splice(i, 1);
return;
}
}
},
// call to use the rpc
invoke: function(opts) {
var data = {
jsonrpc: 2.0,
id: name + '_' + utils.randStr(),
method: opts.func,
params: opts.params && opts.params.length ? opts.params : undefined
};
if (data.params && !data.params.length) data.params = undefined;
this.handles.push({
success: opts.success || angular.noop,
error: opts.error || angular.noop,
id: data.id
});
this.sock.send( JSON.stringify(data) );
},
// should be called initially to start using the sock rpc
init: function(conf) {
if (typeof WebSocket == "undefined") {
return;
}
this.conf = conf || this.conf;
this.initialized = false;
this.scheme = this.conf.encryption ? 'wss' : 'ws';
if (this.sock) {
this.onopen = this.sock.onmessage = this.sock.onerror = this.sock.onclose = null;
this.onerror();
}
this.sock = new WebSocket(this.scheme + '://' + conf.host + ':' + conf.port + '/jsonrpc');
this.sock.onopen = this.onopen;
this.sock.onclose = this.sock.onerror = this.onerror;
this.sock.onmessage = this.onmessage;
},
};
}]);

View File

@ -0,0 +1,43 @@
app.factory('$syscall', ['$log', '$jsoncall', '$sockcall', function(log, jsonRPC, sockRPC) {
return {
// called to initialize the rpc interface, call everytime configuration changes
// conf has the following structure:
// {
// host (string): host for the aria2 server
// port (number): port number for the aria2 server
// encryption (boolean, optional): true if encryption is enabled in the aria2 server
// auth (optional): {
// user (string): username for http authentication if enabled
// pass (string): password for the http authentication if enabled
// }
init: function(conf) {
conf = conf || {
host: 'localhost',
port: 6800
};
conf.encryption = conf.encryption || false;
jsonRPC.init(conf);
sockRPC.init(conf);
},
// call this to start an rpc call, opts has the following structure:
// {
// name (string): name of the actual aria2 syscall
// success (function): callback called with (parsed) data if rpc is successfull
// error (function): callback called when an error occurs
// params (array, optional): the params for some syscall (like multicall) and is optional for others
// }
invoke: function(opts) {
opts.success = opts.success || angular.noop;
opts.error = opts.error || angular.noop;
if (sockRPC.initialized)
return sockRPC.invoke(opts);
else
return jsonRPC.invoke(opts);
}
};
}]);

20
js/services/utils.js Normal file
View File

@ -0,0 +1,20 @@
app.factory('$utils', function() {
return {
randStr: function() {
var str = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
str[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
return str.join("");
},
mixin: function(fromObj, toObj) {
for (var key in toObj) {
if (toObj.hasOwnProperty(key) && fromObj[key]) {
toObj[key] = fromObj[key];
}
}
return toObj;
}
};
});