302 lines
7.9 KiB
JavaScript
302 lines
7.9 KiB
JavaScript
/**
|
|
* Copyright © Mnemosyne LLC
|
|
*
|
|
* This file is licensed under the GPLv2.
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
*/
|
|
|
|
Transmission.fmt = (function () {
|
|
const speed_K = 1000;
|
|
const speed_K_str = 'kB/s';
|
|
const speed_M_str = 'MB/s';
|
|
const speed_G_str = 'GB/s';
|
|
|
|
const size_K = 1000;
|
|
const size_B_str = 'B';
|
|
const size_K_str = 'kB';
|
|
const size_M_str = 'MB';
|
|
const size_G_str = 'GB';
|
|
const size_T_str = 'TB';
|
|
|
|
const mem_K = 1024;
|
|
const mem_B_str = 'B';
|
|
const mem_K_str = 'KiB';
|
|
const mem_M_str = 'MiB';
|
|
const mem_G_str = 'GiB';
|
|
const mem_T_str = 'TiB';
|
|
|
|
return {
|
|
/*
|
|
* Format a percentage to a string
|
|
*/
|
|
percentString: function (x) {
|
|
if (x < 10.0) {
|
|
return x.toTruncFixed(2);
|
|
} else if (x < 100.0) {
|
|
return x.toTruncFixed(1);
|
|
} else {
|
|
return x.toTruncFixed(0);
|
|
}
|
|
},
|
|
|
|
/*
|
|
* Format a ratio to a string
|
|
*/
|
|
ratioString: function (x) {
|
|
if (x === -1) {
|
|
return 'None';
|
|
}
|
|
if (x === -2) {
|
|
return '∞';
|
|
}
|
|
return this.percentString(x);
|
|
},
|
|
|
|
/**
|
|
* Formats the a memory size into a human-readable string
|
|
* @param {Number} bytes the filesize in bytes
|
|
* @return {String} human-readable string
|
|
*/
|
|
mem: function (bytes) {
|
|
if (bytes < mem_K) {
|
|
return [bytes, mem_B_str].join(' ');
|
|
}
|
|
|
|
let convertedSize;
|
|
let unit;
|
|
|
|
if (bytes < Math.pow(mem_K, 2)) {
|
|
convertedSize = bytes / mem_K;
|
|
unit = mem_K_str;
|
|
} else if (bytes < Math.pow(mem_K, 3)) {
|
|
convertedSize = bytes / Math.pow(mem_K, 2);
|
|
unit = mem_M_str;
|
|
} else if (bytes < Math.pow(mem_K, 4)) {
|
|
convertedSize = bytes / Math.pow(mem_K, 3);
|
|
unit = mem_G_str;
|
|
} else {
|
|
convertedSize = bytes / Math.pow(mem_K, 4);
|
|
unit = mem_T_str;
|
|
}
|
|
|
|
// try to have at least 3 digits and at least 1 decimal
|
|
return convertedSize <= 9.995
|
|
? [convertedSize.toTruncFixed(2), unit].join(' ')
|
|
: [convertedSize.toTruncFixed(1), unit].join(' ');
|
|
},
|
|
|
|
/**
|
|
* Formats the a disk capacity or file size into a human-readable string
|
|
* @param {Number} bytes the filesize in bytes
|
|
* @return {String} human-readable string
|
|
*/
|
|
size: function (bytes) {
|
|
if (bytes < size_K) {
|
|
return [bytes, size_B_str].join(' ');
|
|
}
|
|
|
|
let convertedSize;
|
|
let unit;
|
|
|
|
if (bytes < Math.pow(size_K, 2)) {
|
|
convertedSize = bytes / size_K;
|
|
unit = size_K_str;
|
|
} else if (bytes < Math.pow(size_K, 3)) {
|
|
convertedSize = bytes / Math.pow(size_K, 2);
|
|
unit = size_M_str;
|
|
} else if (bytes < Math.pow(size_K, 4)) {
|
|
convertedSize = bytes / Math.pow(size_K, 3);
|
|
unit = size_G_str;
|
|
} else {
|
|
convertedSize = bytes / Math.pow(size_K, 4);
|
|
unit = size_T_str;
|
|
}
|
|
|
|
// try to have at least 3 digits and at least 1 decimal
|
|
return convertedSize <= 9.995
|
|
? [convertedSize.toTruncFixed(2), unit].join(' ')
|
|
: [convertedSize.toTruncFixed(1), unit].join(' ');
|
|
},
|
|
|
|
speedBps: function (Bps) {
|
|
return this.speed(this.toKBps(Bps));
|
|
},
|
|
|
|
toKBps: function (Bps) {
|
|
return Math.floor(Bps / speed_K);
|
|
},
|
|
|
|
speed: function (KBps) {
|
|
let speed = KBps;
|
|
|
|
if (speed <= 999.95) {
|
|
// 0 KBps to 999 K
|
|
return [speed.toTruncFixed(0), speed_K_str].join(' ');
|
|
}
|
|
|
|
speed /= speed_K;
|
|
|
|
if (speed <= 99.995) {
|
|
// 1 M to 99.99 M
|
|
return [speed.toTruncFixed(2), speed_M_str].join(' ');
|
|
}
|
|
if (speed <= 999.95) {
|
|
// 100 M to 999.9 M
|
|
return [speed.toTruncFixed(1), speed_M_str].join(' ');
|
|
}
|
|
|
|
// insane speeds
|
|
speed /= speed_K;
|
|
return [speed.toTruncFixed(2), speed_G_str].join(' ');
|
|
},
|
|
|
|
timeInterval: function (seconds) {
|
|
const days = Math.floor(seconds / 86400);
|
|
const hours = Math.floor((seconds % 86400) / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
seconds = Math.floor(seconds % 60);
|
|
const d = days + ' ' + (days > 1 ? 'days' : 'day');
|
|
const h = hours + ' ' + (hours > 1 ? 'hours' : 'hour');
|
|
const m = minutes + ' ' + (minutes > 1 ? 'minutes' : 'minute');
|
|
const s = seconds + ' ' + (seconds > 1 ? 'seconds' : 'second');
|
|
|
|
if (days) {
|
|
if (days >= 4 || !hours) {
|
|
return d;
|
|
}
|
|
return d + ', ' + h;
|
|
}
|
|
if (hours) {
|
|
if (hours >= 4 || !minutes) {
|
|
return h;
|
|
}
|
|
return h + ', ' + m;
|
|
}
|
|
if (minutes) {
|
|
if (minutes >= 4 || !seconds) {
|
|
return m;
|
|
}
|
|
return m + ', ' + s;
|
|
}
|
|
return s;
|
|
},
|
|
|
|
timestamp: function (seconds) {
|
|
if (!seconds) {
|
|
return 'N/A';
|
|
}
|
|
|
|
const myDate = new Date(seconds * 1000);
|
|
const now = new Date();
|
|
|
|
let date = '';
|
|
let time = '';
|
|
|
|
const sameYear = now.getFullYear() === myDate.getFullYear();
|
|
const sameMonth = now.getMonth() === myDate.getMonth();
|
|
|
|
const dateDiff = now.getDate() - myDate.getDate();
|
|
if (sameYear && sameMonth && Math.abs(dateDiff) <= 1) {
|
|
if (dateDiff === 0) {
|
|
date = 'Today';
|
|
} else if (dateDiff === 1) {
|
|
date = 'Yesterday';
|
|
} else {
|
|
date = 'Tomorrow';
|
|
}
|
|
} else {
|
|
date = myDate.toDateString();
|
|
}
|
|
|
|
let hours = myDate.getHours();
|
|
let period = 'AM';
|
|
if (hours > 12) {
|
|
hours = hours - 12;
|
|
period = 'PM';
|
|
}
|
|
if (hours === 0) {
|
|
hours = 12;
|
|
}
|
|
if (hours < 10) {
|
|
hours = '0' + hours;
|
|
}
|
|
let minutes = myDate.getMinutes();
|
|
if (minutes < 10) {
|
|
minutes = '0' + minutes;
|
|
}
|
|
seconds = myDate.getSeconds();
|
|
if (seconds < 10) {
|
|
seconds = '0' + seconds;
|
|
}
|
|
|
|
time = [hours, minutes, seconds].join(':');
|
|
|
|
return [date, time, period].join(' ');
|
|
},
|
|
|
|
ngettext: function (msgid, msgid_plural, n) {
|
|
// TODO(i18n): http://doc.qt.digia.com/4.6/i18n-plural-rules.html
|
|
return n === 1 ? msgid : msgid_plural;
|
|
},
|
|
|
|
countString: function (msgid, msgid_plural, n) {
|
|
return [n.toStringWithCommas(), this.ngettext(msgid, msgid_plural, n)].join(' ');
|
|
},
|
|
|
|
peerStatus: function (flagStr) {
|
|
const formattedFlags = [];
|
|
for (var i = 0, flag; (flag = flagStr[i]); ++i) {
|
|
let explanation = null;
|
|
switch (flag) {
|
|
case 'O':
|
|
explanation = 'Optimistic unchoke';
|
|
break;
|
|
case 'D':
|
|
explanation = 'Downloading from this peer';
|
|
break;
|
|
case 'd':
|
|
explanation = "We would download from this peer if they'd let us";
|
|
break;
|
|
case 'U':
|
|
explanation = 'Uploading to peer';
|
|
break;
|
|
case 'u':
|
|
explanation = "We would upload to this peer if they'd ask";
|
|
break;
|
|
case 'K':
|
|
explanation = "Peer has unchoked us, but we're not interested";
|
|
break;
|
|
case '?':
|
|
explanation = "We unchoked this peer, but they're not interested";
|
|
break;
|
|
case 'E':
|
|
explanation = 'Encrypted Connection';
|
|
break;
|
|
case 'H':
|
|
explanation = 'Peer was discovered through Distributed Hash Table (DHT)';
|
|
break;
|
|
case 'X':
|
|
explanation = 'Peer was discovered through Peer Exchange (PEX)';
|
|
break;
|
|
case 'I':
|
|
explanation = 'Peer is an incoming connection';
|
|
break;
|
|
case 'T':
|
|
explanation = 'Peer is connected via uTP';
|
|
break;
|
|
}
|
|
|
|
if (!explanation) {
|
|
formattedFlags.push(flag);
|
|
} else {
|
|
formattedFlags.push(
|
|
'<span title="' + flag + ': ' + explanation + '">' + flag + '</span>'
|
|
);
|
|
}
|
|
}
|
|
|
|
return formattedFlags.join('');
|
|
},
|
|
};
|
|
})();
|