new uri modal in angular, with overall refactoring and fixes in the

codebase
This commit is contained in:
hamza zia 2013-01-28 19:18:21 +05:00
parent 5c02174117
commit 489f12ea1e
11 changed files with 1365 additions and 40 deletions

View File

@ -16,16 +16,19 @@
<link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/download.css"> <link rel="stylesheet" href="css/download.css">
<link rel="stylesheet" href="css/modals.css">
<!-- external javascript dependencies --> <!-- external javascript dependencies -->
<script src="js/libs/jquery-1.8.3.js"></script> <script src="js/libs/jquery-1.8.3.js"></script>
<script src="js/libs/underscore.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/angular.js"></script>
<script src="js/libs/bootstrap.js"></script>
<script src="js/libs/angularui-bootstrap.js"></script>
<script src="js/libs/jquery.flot.js"></script> <script src="js/libs/jquery.flot.js"></script>
<!-- We do manual resize currently <!-- We do manual resize currently
<script src="js/libs/jquery.flot.resize.js"></script> <script src="js/libs/jquery.flot.resize.js"></script>
@ -66,8 +69,7 @@
<div class="container"> <div class="container">
<!-- using a span for collapse rather than a to fix the angular majic on anchors --> <!-- using a span for collapse rather than a to fix the angular majic on anchors -->
<span class="btn btn-navbar" <span class="btn btn-navbar"
data-toggle="collapse" ng-click="collapsed = !collapsed">
data-target=".nav-collapse">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
@ -76,27 +78,29 @@
<a class="brand">{{ name }}</a> <a class="brand">{{ name }}</a>
<div class="nav-collapse"> <div class="nav-collapse" collapse="collapsed">
<ul class="nav"> <ul class="nav">
<li class="dropdown"> <li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> <a class="dropdown-toggle" href="#">
Add <b class="caret"></b> Add <b class="caret"></b>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li> <li>
<a href="#"><i class="icon-download"></i> Add Download URL</a> <a href="#" data-toggle="modal" data-target=".modal-adduris">
<i class="icon-download"></i> By URIs
</a>
</li> </li>
<li> <li>
<a href="#"><i class="icon-file"></i> Add Metalink</a> <a href="#"><i class="icon-file"></i> By Metalinks</a>
</li> </li>
<li> <li>
<a href="#"><i class="icon-file"></i> Add Torrent</a> <a href="#"><i class="icon-file"></i> By Torrents</a>
</li> </li>
</ul> </ul>
</li> </li>
<li class="dropdown" id="stop_downloads"> <li class="dropdown" id="stop_downloads">
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> <a class="dropdown-toggle" href="#">
Manage <b class="caret"></b> Manage <b class="caret"></b>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@ -111,7 +115,7 @@
<ul class="nav"> <ul class="nav">
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> <a href="#" class="dropdown-toggle" >
Settings <b class="caret"></b> Settings <b class="caret"></b>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@ -126,7 +130,7 @@
<ul class="nav pull-right"> <ul class="nav pull-right">
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> <a href="#" class="dropdown-toggle" >
About <b class="caret"></b> About <b class="caret"></b>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@ -150,7 +154,7 @@
<table ng-repeat="download in getDownloads()" class="download" data-gid="{{download.gid}}"> <table ng-repeat="download in getDownloads()" class="download" data-gid="{{download.gid}}">
<tbody> <tbody>
<tr> <tr>
<td data-toggle="collapse" data-target="[data-gid={{download.gid}}] .download-detail .collapse" class="download-overview"> <td ng-click="download.collapsed = !download.collapsed" class="download-overview">
<b class="download-name">{{getName(download)}}</b> <b class="download-name">{{getName(download)}}</b>
</td> </td>
<td class="download-controls" rowspan="2"> <td class="download-controls" rowspan="2">
@ -164,7 +168,7 @@
<button class="btn download_settings hidden-phone"><i class="icon-cog"></i></button> <button class="btn download_settings hidden-phone"><i class="icon-cog"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown"> <button class="btn dropdown-toggle">
<i class="caret"></i> <i class="caret"></i>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@ -173,7 +177,7 @@
<li ng-show="download.bittorrent"><a href="#" class="torrent_info"><i class="icon-list-alt"></i> Peers</a></li> <li ng-show="download.bittorrent"><a href="#" class="torrent_info"><i class="icon-list-alt"></i> Peers</a></li>
<li><a href="#" data-toggle="collapse" data-target="[data-gid={{download.gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li> <li><a href="#" ng-click="download.collapsed = !download.collapsed"><i class="icon-info-sign"></i> More Info</a></li>
<li class="visible-phone"><a href="#" ng-click="remove(download)"><i class="icon-remove"></i> Remove</a></li> <li class="visible-phone"><a href="#" ng-click="remove(download)"><i class="icon-remove"></i> Remove</a></li>
</ul> </ul>
@ -181,7 +185,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td data-toggle="collapse" data-target="[data-gid={{download.gid}}] .download-detail .collapse" class="download-overview"> <td ng-click="download.collapsed = !download.collapsed" class="download-overview">
<ul class="stats pull-left"> <ul class="stats pull-left">
<li ng-show="hasStatus(download, 'active')" class="label label-success hidden-phone hidden-tablet">Status: <span class="download-status">{{download.status}}</span></li> <li ng-show="hasStatus(download, 'active')" class="label label-success hidden-phone hidden-tablet">Status: <span class="download-status">{{download.status}}</span></li>
@ -216,7 +220,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="download-progress" colspan="2" data-toggle="collapse" data-target="[data-gid={{download.gid}}] .download-detail .collapse"> <td class="download-progress" colspan="2" ng-click="download.collapsed = !download.collapsed">
<div ng-show="hasStatus(download, 'active')" class="progress progress-striped" style="width: 100%; margin: 0; padding: 0;"> <div ng-show="hasStatus(download, 'active')" class="progress progress-striped" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{getProgress(download)}}%;"></div> <div class="bar" style="width: {{getProgress(download)}}%;"></div>
@ -240,10 +244,10 @@
</td> </td>
</tr> </tr>
<tr data-toggle="collapse" data-target="[data-gid={{download.gid}}] .download-detail .collapse" class="download-detail"> <tr ng-click="download.collapsed = !download.collapsed" class="download-detail">
<td colspan="2"> <td colspan="2">
<div class="collapse more_info"> <div class="more_info" collapse="download.collapsed">
<canvas bitfield="download.bitfield" pieces="download.numPieces" class="progress chunk-canvas" width="1400" style="width: 100%; margin: 5px;" chunkbar></canvas> <canvas bitfield="download.bitfield" draw="!download.collapsed" pieces="download.numPieces" class="progress chunk-canvas" width="1400" style="width: 100%; margin: 5px;" chunkbar></canvas>
<ul class="stats"> <ul class="stats">
<li class="label">Status: <span class="download-status">{{download.status}}</span></li> <li class="label">Status: <span class="download-status">{{download.status}}</span></li>
<li class="label">GID: <span class="download-gid">{{download.gid}}</span></li> <li class="label">GID: <span class="download-gid">{{download.gid}}</span></li>
@ -267,7 +271,7 @@
</ul> </ul>
<div ng-show="hasStatus(download, 'active')"> <div ng-show="hasStatus(download, 'active')">
<div class="download-graph" dspeed="download.downloadSpeed" uspeed="download.uploadSpeed" dgraph></div> <div class="download-graph" dspeed="download.downloadSpeed" uspeed="download.uploadSpeed" dgraph draw="!download.collapsed"></div>
</div> </div>
</div> </div>
</td> </td>
@ -281,6 +285,49 @@
<!-- {{{ modals --> <!-- {{{ modals -->
<div ng-controller="ModalCtrl"> <div ng-controller="ModalCtrl">
<!--{{{ add new Download template -->
<div class="modal hide modal-adduris">
<div class="modal-header">
<button class="close" data-dismiss="modal">x</button>
<h3>Add Downloads By URIs</h3>
</div>
<form class="modal-body">
<fieldset>
<legend>Download URIs</legend>
<textarea rows="6" ng-model="uris"></textarea>
<p class="help-block">
{{ uris }} <br>
You can add multiple downloads (files)
at the same time by putting uris for each
file on a separate line.
<br>
You can also add multiple uris (mirrors) for the
*same* file. To do this separate the uris
by a space.
<br>
Aria2 will use multiple uris (or mirrors) to boost
the download speed for that file (download).
<br>
E.g. To add 2 files (downloads) f1.jpg
and f2.mp4 with 2 uris (mirrors) each,
add uris as following:
<br>
<!-- pre tags print tabs, so do not indent them! -->
<pre>
http://ex1.com/f1.jpg http://ex2.com/f1.jpg
http://ex1.com/f2.mp4 http://ex2.com/f2.mp4
</pre>
A URI can be HTTP(S)/FTP/BitTorrent Magnet URI.
</p>
</fieldset>
</form>
<div class="modal-footer">
<button class="btn" ng-click="addUris()">Add Downloads</button>
</div>
</div>
<!-- add new Download template end }}}-->
</div> </div>
<!-- }}} --> <!-- }}} -->

3
css/modals.css Normal file
View File

@ -0,0 +1,3 @@
.modal-adduris textarea {
width: 100%;
}

View File

@ -83,9 +83,14 @@ function(scope, rpc, utils) {
'status', 'gid', 'bitfield', 'numPieces', 'connections', 'status', 'gid', 'bitfield', 'numPieces', 'connections',
'bittorrent' 'bittorrent'
], function(e) { ], function(e) {
if (ctx[e] != d[e])
ctx[e] = d[e]; ctx[e] = d[e];
}); });
// collapse the download details initially
if (ctx.collapsed === undefined)
ctx.collapsed = true;
return ctx; return ctx;
}; };

View File

@ -1,3 +1,27 @@
app.controller('ModalCtrl', ['$scope', function(scope) { app.controller('ModalCtrl', ['$_', '$scope', '$rpc', function(_, scope, rpc) {
scope.uris = '';
scope.addUris = function() {
console.log(scope.uris);
var cnt = 0;
var cb = function() {
cnt--;
if (!cnt) {
// close modal
console.log('closing modal');
}
};
_.chain(scope.uris.trim().split(/\n\r?/g))
.map(function(d) { return d.trim().split(/\s+/g) })
.filter(function(d) { return d.length })
.each(function(uris) {
cnt++;
// passing true to batch all the addUri calls
rpc.once('addUri', [uris], cb, true);
})
// now dispatch all addUri syscalls
rpc.forceUpdate();
scope.uris = '';
};
}]); }]);

View File

@ -1,4 +1,6 @@
app.controller('NavCtrl', ['$scope', '$name', function(scope, name) { app.controller('NavCtrl', ['$scope', '$name', function(scope, name) {
scope.name = name; scope.name = name;
// initially collapsed in mobile resolution
scope.collapsed = true;
}]); }]);

View File

@ -17,10 +17,14 @@ var draw = function(canvas, chunks, fillStyle) {
x += dx; x += dx;
}); });
}; };
// put chunkbar and bitfield attributes in a canvas element
// to use the directive, draw is optional and canvas is
// only drawn when it is true if given
app.directive('chunkbar', ['$utils', function(utils) { app.directive('chunkbar', ['$utils', function(utils) {
return function(scope, elem, attrs) { return function(scope, elem, attrs) {
var bitfield = "", pieces = 0; var bitfield = "", pieces = 0, canDraw = true;
var update = function() { var update = function() {
if (canDraw)
draw(elem[0], utils.getChunksFromHex(bitfield, pieces), attrs.fillStyle); draw(elem[0], utils.getChunksFromHex(bitfield, pieces), attrs.fillStyle);
}; };
scope.$watch(attrs.bitfield, function(bf) { scope.$watch(attrs.bitfield, function(bf) {
@ -32,6 +36,12 @@ app.directive('chunkbar', ['$utils', function(utils) {
update(); update();
}); });
if (attrs.draw) {
scope.$watch(attrs.draw, function(val) {
canDraw = val;
});
}
update(); update();
}; };
}]); }]);

View File

@ -1,7 +1,10 @@
// graph takes dspeed and uspeed, it queries them every second and draws
app.directive('dgraph', ['$', '$filter', function($, filter) { // the last 20 secs, it also takes draw as an optional attribute and only
// draws the graph when it is true, if not given then graph is always drawn
app.directive('dgraph', ['$', '$filter', '$parse', function($, filter, parse) {
return function(scope, elem, attrs) { return function(scope, elem, attrs) {
var canDraw = true;
var graphSize = 20 var graphSize = 20
, dspeed = 0, uspeed = 0 , dspeed = 0, uspeed = 0
@ -60,9 +63,9 @@ app.directive('dgraph', ['$', '$filter', function($, filter) {
if (uconf.data.length > graphSize) uconf.data.shift(); if (uconf.data.length > graphSize) uconf.data.shift();
// if any parents is collapsable, then confirm if it isnt // if any parents is collapsable, then confirm if it isnt
var collapsable = elem.parents('.collapse'); if (canDraw)
if (!collapsable.length || collapsable.hasClass('in'))
draw(); draw();
}; };
scope.$watch(attrs.dspeed, function(val) { scope.$watch(attrs.dspeed, function(val) {
@ -73,6 +76,12 @@ app.directive('dgraph', ['$', '$filter', function($, filter) {
uspeed = val; uspeed = val;
}); });
if (attrs.draw) {
scope.$watch(attrs.draw, function(val) {
canDraw = val;
});
}
var interval = setInterval(update, 1000); var interval = setInterval(update, 1000);
angular.element(window).bind('resize', draw); angular.element(window).bind('resize', draw);

View File

@ -1,5 +1,10 @@
var app = angular.module('app', []); var app = angular.module('app', []);
$(function() { $(function() {
angular.bootstrap(document, ['app']) angular.bootstrap(document, [
// external deps
'ui.bootstrap.collapse', 'ui.bootstrap.dropdownToggle',
'app'
])
}); });

1210
js/libs/angularui-bootstrap.js vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@ -426,8 +426,11 @@ function getTemplateCtx(data) {
function updateDownloadTemplates(elem, ctx) { function updateDownloadTemplates(elem, ctx) {
elem = $(elem); elem = $(elem);
var oldctx = elem.data('ctx') || {};
// update spans // update spans
for(var i in ctx) { for(var i in ctx) {
if (oldctx[i] != ctx[i])
elem.find('.download-' + i).text(ctx[i]); elem.find('.download-' + i).text(ctx[i]);
} }
@ -439,6 +442,7 @@ function updateDownloadTemplates(elem, ctx) {
elem.find('.download-files').html(html); elem.find('.download-files').html(html);
// update progress bar // update progress bar
if (oldctx.percentage != ctx.percentage)
elem.find('.progress .bar').css('width', ctx.percentage + '%'); elem.find('.progress .bar').css('width', ctx.percentage + '%');
// update the chunks bar // update the chunks bar
@ -462,6 +466,7 @@ function updateDownloadTemplates(elem, ctx) {
ctx.fillRect(x, 0, dx, height); ctx.fillRect(x, 0, dx, height);
x += dx; x += dx;
}); });
elem.data('ctx', ctx);
} }
function deleteDownloadTemplates($top_elem, data) { function deleteDownloadTemplates($top_elem, data) {
if(!data) { if(!data) {

View File

@ -66,8 +66,10 @@ app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) {
else else
configurations = [conf]; configurations = [conf];
}, },
// syscall is done only once // syscall is done only once, delay is optional
once: function(name, params, cb) { // and pass true to only dispatch it in the global timeout
// which can be used to batch up once calls
once: function(name, params, cb, delay) {
cb = cb || angular.noop; cb = cb || angular.noop;
params = params || []; params = params || [];
@ -78,12 +80,16 @@ app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) {
cb: cb cb: cb
}); });
if (!delay) {
this.forceUpdate(); this.forceUpdate();
}
}, },
// callback is called each time with updated syscall data // callback is called each time with updated syscall data
// after the global timeout // after the global timeout, delay is optional and pass it
subscribe: function(name, params, cb) { // true to dispatch the first syscall also on global timeout
// which can be used to batch the subscribe calls
subscribe: function(name, params, cb, delay) {
cb = cb || angular.noop; cb = cb || angular.noop;
params = params || []; params = params || [];
@ -95,8 +101,7 @@ app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) {
}; };
subscriptions.push(handle); subscriptions.push(handle);
this.forceUpdate(); if (!delay) this.forceUpdate();
return handle; return handle;
}, },