added directives for chunkbar and graphs, with overall fixes in improvement in angular implementation

This commit is contained in:
hamza zia 2013-01-21 19:07:55 +05:00
parent 6481737c61
commit 73dbcb67e0
9 changed files with 181 additions and 47 deletions

View File

@ -32,6 +32,9 @@
<!-- webui core --> <!-- webui core -->
<script src="js/app.js"></script> <script src="js/app.js"></script>
<script src="js/directives/chunkbar.js"></script>
<script src="js/directives/dgraph.js"></script>
<script src="js/services/deps.js"></script> <script src="js/services/deps.js"></script>
<script src="js/services/base64.js"></script> <script src="js/services/base64.js"></script>
<script src="js/services/utils.js"></script> <script src="js/services/utils.js"></script>
@ -136,8 +139,10 @@
</div> </div>
<!-- }}} --> <!-- }}} -->
<!-- {{{ downloads -->
<div role="main" class="container" ng-controller="DownloadCtrl"> <div role="main" class="container" ng-controller="DownloadCtrl">
<!-- {{{ download template -->
<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>
@ -178,8 +183,10 @@
<li ng-show="download.booleans.can_pause" class="label label-success hidden-phone">Downloaded: <span class="download-completedLength">{{download.completedLength}}</span></li> <li ng-show="download.booleans.can_pause" class="label label-success hidden-phone">Downloaded: <span class="download-completedLength">{{download.completedLength}}</span></li>
<li ng-show="download.booleans.can_pause" class="label label-success hidden-phone hidden-tablet">Progress: <span class="download-percentage">{{download.percentage}}</span>%</li> <li ng-show="download.booleans.can_pause" class="label label-success hidden-phone hidden-tablet">Progress: <span class="download-percentage">{{download.percentage}}</span>%</li>
<li ng-show="download.booleans.can_pause" class="label label-success">Speed: <span class="download-downloadSpeed">{{download.downloadSpeed}}</span></li> <li ng-show="download.booleans.can_pause" class="label label-success">Speed: <span class="download-downloadSpeed">{{download.downloadSpeed}}</span></li>
<!-- TODO: figure out how to do multiple ng-show
<li ng-show="download.booleans.bittorrent" class="label label-success hidden-phone">Upload Speed: <span class="download-uploadSpeed">{{download.uploadSpeed}}</span></li>
-->
<li ng-show="download.booleans.bittorrent" class="label">Upload Speed: <span class="download-uploadSpeed">{{download.uploadSpeed}}</span></li>
<li ng-show="download.booleans.can_play" class="label label-info">Status: <span class="download-status">{{download.status}}</span></li> <li ng-show="download.booleans.can_play" class="label label-info">Status: <span class="download-status">{{download.status}}</span></li>
<li ng-show="download.booleans.can_play" class="label label-info">Size: <span class="download-totalLength">{{download.totalLength}}</span></li> <li ng-show="download.booleans.can_play" class="label label-info">Size: <span class="download-totalLength">{{download.totalLength}}</span></li>
@ -208,7 +215,7 @@
<div class="bar" style="width: {{download.percentage}}%;"></div> <div class="bar" style="width: {{download.percentage}}%;"></div>
</div> </div>
<div ng-show="download.booleans.is_play" class="progress progress-info progress-striped" style="width: 100%; margin: 0; padding: 0;"> <div ng-show="download.booleans.can_play" class="progress progress-info progress-striped" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{download.percentage}}%;"></div> <div class="bar" style="width: {{download.percentage}}%;"></div>
</div> </div>
@ -229,7 +236,7 @@
<tr data-toggle="collapse" data-target="[data-gid={{download.gid}}] .download-detail .collapse" class="download-detail"> <tr data-toggle="collapse" data-target="[data-gid={{download.gid}}] .download-detail .collapse" class="download-detail">
<td colspan="2"> <td colspan="2">
<div class="collapse more_info"> <div class="collapse more_info">
<canvas class="progress chunk-canvas" width="1400" style="width: 100%; margin: 5px;"></canvas> <canvas bitfield="download.bitfield" 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>
@ -249,19 +256,27 @@
<h4 class="hidden-phone">Download Files</h4> <h4 class="hidden-phone">Download Files</h4>
<ul class="download-files hidden-phone"> <ul class="download-files hidden-phone">
<li class="label" ng-repeat="file in download.files">{{download.download.path}} ({{download.download.length}})</li> <li class="label" ng-repeat="file in download.files">{{file.path}} ({{file.length}})</li>
</ul> </ul>
<div> <div ng-show="download.booleans.can_pause">
<div class="active_graph"></div> <div class="download-graph" dspeed="download.dspeed" uspeed="download.uspeed" dgraph></div>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<!-- }}} -->
</div> </div>
<!-- }}} -->
<!-- {{{ modals -->
<div ng-controller="ModalCtrl">
</div>
<!-- }}} -->
</body> </body>
</html> </html>

View File

@ -10,7 +10,10 @@
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
} }
.active_graph { .download-graph {
width: 80%;
margin-top: 5px;
margin-bottom: 5px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }

View File

@ -52,11 +52,6 @@
text-align: center; text-align: center;
margin-bottom: 3px; margin-bottom: 3px;
} }
.active_graph {
width: 80%;
margin-top: 10px;
margin-bottom: 10px;
}
.download_item { .download_item {
margin-bottom: 10px; margin-bottom: 10px;
padding-top: 10px; padding-top: 10px;
@ -123,7 +118,7 @@
<li class="label label-success hidden-phone hidden-tablet">Progress: <span class="download-percentage">{{percentage}}</span>%</li> <li class="label label-success hidden-phone hidden-tablet">Progress: <span class="download-percentage">{{percentage}}</span>%</li>
<li class="label label-success">Speed: <span class="download-downloadSpeed">{{downloadSpeed}}</span></li> <li class="label label-success">Speed: <span class="download-downloadSpeed">{{downloadSpeed}}</span></li>
{{#booleans.bittorrent}} {{#booleans.bittorrent}}
<li class="label">Upload Speed: <span class="download-uploadSpeed">{{uploadSpeed}}</span></li> <li class="label label-success hidden-phone">Upload Speed: <span class="download-uploadSpeed">{{uploadSpeed}}</span></li>
{{/booleans.bittorrent}} {{/booleans.bittorrent}}
{{/booleans.can_pause}} {{/booleans.can_pause}}
{{#booleans.can_play}} {{#booleans.can_play}}
@ -209,7 +204,7 @@
</ul> </ul>
<div> <div>
<div class="active_graph"></div> <div class="download-graph"></div>
</div> </div>
</div> </div>
</td> </td>

View File

@ -14,20 +14,20 @@ function(scope, rpc, utils) {
rpc.subscribe('tellActive', [], function(data) { rpc.subscribe('tellActive', [], function(data) {
console.log('got active data'); console.log('got active data');
scope.$apply(function() { scope.$apply(function() {
scope.mapDownloads(data[0], scope.active); utils.mergeMap(data[0], scope.active, scope.getCtx);
}); });
}); });
rpc.subscribe('tellWaiting', [0, 100], function(data) { rpc.subscribe('tellWaiting', [0, 1000], function(data) {
scope.$apply(function() { scope.$apply(function() {
scope.mapDownloads(data[0], scope.waiting); utils.mergeMap(data[0], scope.waiting, scope.getCtx);
}); });
}); });
rpc.subscribe('tellStopped', [0, 100], function(data) { rpc.subscribe('tellStopped', [0, 1000], function(data) {
scope.$apply(function() { scope.$apply(function() {
scope.mapDownloads(data[0], scope.stopped); utils.mergeMap(data[0], scope.stopped, scope.getCtx);
}); });
}); });
@ -37,33 +37,12 @@ function(scope, rpc, utils) {
return downs; return downs;
} }
scope.mapDownloads = function(downs, mdowns) {
if (!mdowns) mdowns = [];
for (i = 0; i < mdowns.length; i++) {
if (i >= downs.length) {
// remove the deleted downloads
mdowns.splice(i, mdowns.length - downs.length);
break;
}
if (!mdowns[i]) mdowns[i] = {};
scope.getCtx(downs[i], mdowns[i]);
}
// insert newly created downloads
while (i < downs.length) {
mdowns.push(scope.getCtx(downs[i++]));
}
return mdowns;
}
scope.getCtx = function(d, ctx) { scope.getCtx = function(d, ctx) {
ctx = ctx || {}; ctx = ctx || {};
ctx.status = d.status; ctx.status = d.status;
ctx.bitfield = d.bitfield;
ctx.gid = d.gid; ctx.gid = d.gid;
ctx.numPieces = d.numPieces; ctx.numPieces = d.numPieces;
ctx.connections = d.connections; ctx.connections = d.connections;
@ -86,8 +65,12 @@ function(scope, rpc, utils) {
ctx[e] = utils.changeLength(d[e], 'B'); ctx[e] = utils.changeLength(d[e], 'B');
}); });
// raw data for graphs
ctx.dspeed = d.downloadSpeed;
ctx.uspeed = d.uploadSpeed;
ctx.eta = utils.changeTime( ctx.eta = utils.changeTime(
(d.remainingLength) / d.downloadSpeed (d.totalLength-d.completedLength) / d.downloadSpeed
); );
var percentage = (d.completedLength / d.totalLength)*100; var percentage = (d.completedLength / d.totalLength)*100;

View File

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

37
js/directives/chunkbar.js Normal file
View File

@ -0,0 +1,37 @@
var draw = function(canvas, chunks, fillStyle) {
chunks = chunks || [];
if (!canvas.getContext) {
console.log('use chunk bar on an canvas implementation!');
return;
}
var ctx = canvas.getContext('2d');
ctx.fillStyle = fillStyle || "#149BDF";
ctx.clearRect(0, 0, canvas.width, canvas.height);
var x = 0,
width = canvas.width,
height = canvas.height;
chunks.forEach(function(c) {
var dx = c.ratio * width;
if (c.show)
ctx.fillRect(x, 0, dx, height);
x += dx;
});
};
app.directive('chunkbar', ['$utils', function(utils) {
return function(scope, elem, attrs) {
var bitfield = "", pieces = 0;
var update = function() {
draw(elem[0], utils.getChunksFromHex(bitfield, pieces), attrs.fillStyle);
};
scope.$watch(attrs.bitfield, function(bf) {
bitfield = bf;
update();
});
scope.$watch(attrs.pieces, function(p) {
pieces = p;
update();
});
update();
};
}]);

74
js/directives/dgraph.js Normal file
View File

@ -0,0 +1,74 @@
app.directive('dgraph', ['$', '$utils', function($, utils) {
return function(scope, elem, attrs) {
var graphSize = 20
, dspeed = 0, uspeed = 0
, dconf = {
label: "Download Speed",
data: [],
color: "#ff0000",
lines: { show: true }
}
, uconf = {
label: "Upload Speed",
data: [],
color: "#00ff00",
lines: { show: true }
},
start = new Date
;
// jqplot doesnt draw graphs with no fixed height
elem.height(elem.width() / 2);
var graph = $.plot(elem, [dconf, uconf], {
legend: { show: true },
xaxis: {
show: true
},
yaxis: {
tickFormatter: function(val, axis) {
return utils.changeLength(val, "B/s");
}
,
min: 0
}
});
var draw = function() {
var height = elem.height();
if (height <= 0) return;
elem.height(elem.width() / 2);
var cnt = ((new Date - start)/1000).toFixed(0);
dconf.data.push([cnt, dspeed]);
if (dconf.data.length > graphSize) dconf.data.shift();
uconf.data.push([cnt, uspeed]);
if (uconf.data.length > graphSize) uconf.data.shift();
graph.setData([dconf, uconf]);
graph.resize();
graph.setupGrid();
graph.draw();
};
scope.$watch(attrs.dspeed, function(val) {
dspeed = val;
});
scope.$watch(attrs.useed, function(val) {
uspeed = val;
});
var interval = setInterval(draw, 1000);
elem.bind('$destroy', function() {
clearInterval(interval);
});
};
}]);

View File

@ -592,7 +592,7 @@ function updateGraphData(data) {
this.add(this.upSpeed, val); this.add(this.upSpeed, val);
return this; return this;
}, },
plot: that.createGraph('[data-gid=' + gid + '] .active_graph'), plot: that.createGraph('[data-gid=' + gid + '] .download-graph'),
start: new Date() start: new Date()
} }
})().addDown(downSpeed).addUp(upSpeed)); })().addDown(downSpeed).addUp(upSpeed));

View File

@ -13,11 +13,37 @@ app.factory('$utils', function() {
return str.join(""); return str.join("");
}, },
// maps the array in place to the destination
// arr, dest (optional): array
// func: a merge mapping func, see ctrls/download.js
mergeMap: function(arr, dest, func) {
if (!dest) dest = [];
for (i = 0; i < dest.length; i++) {
if (i >= arr.length) {
// remove the deleted downloads
dest.splice(i, dest.length - arr.length);
break;
}
if (!dest[i]) dest[i] = {};
func(arr[i], dest[i]);
}
// insert newly created downloads
while (i < arr.length) {
dest.push(func(arr[i++]));
}
return dest;
},
// change time units // change time units
changeTime: function(time) { changeTime: function(time) {
time = parseInt(time); time = parseFloat(time);
if (isNaN(time) || !isFinite(time)) return " infinite";
if (!time) return " infinite"; if (!time) return " infinite";
if (time < 60) return time + " s"; if (time < 60) return time.toFixed(2) + " s";
else if (time < 60*60) return (time/60).toFixed(2) + " min"; else if (time < 60*60) return (time/60).toFixed(2) + " min";
else if (time < 60*60*24) return (time/(60*60)).toFixed(2) + " hours"; else if (time < 60*60*24) return (time/(60*60)).toFixed(2) + " hours";
else return (time/(60*60*24)).toFixed(2) + " days!"; else return (time/(60*60*24)).toFixed(2) + " days!";
@ -25,8 +51,8 @@ app.factory('$utils', function() {
// change length units // change length units
changeLength: function(len, pref) { changeLength: function(len, pref) {
len = parseInt(len); len = parseFloat(len);
if(len <= (1<<10)) return len + " " + pref; if(len <= (1<<10)) return len.toFixed(1) + " " + pref;
else if(len <= (1<<20)) return (len/(1<<10)).toFixed(1) + " K" + pref; else if(len <= (1<<20)) return (len/(1<<10)).toFixed(1) + " K" + pref;
else if(len <= (1<<30)) return (len/(1<<20)).toFixed(1) + " M" + pref; else if(len <= (1<<30)) return (len/(1<<20)).toFixed(1) + " M" + pref;
else return (len/(1<<30)).toFixed(1) + " G" + pref; else return (len/(1<<30)).toFixed(1) + " G" + pref;