2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2014 Gabriele <pmzqla.git@gmail.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
31 window
.qBittorrent
??= {};
32 window
.qBittorrent
.Misc
??= (() => {
33 const exports
= () => {
37 createDebounceHandler
: createDebounceHandler
,
38 friendlyUnit
: friendlyUnit
,
39 friendlyDuration
: friendlyDuration
,
40 friendlyPercentage
: friendlyPercentage
,
41 friendlyFloat
: friendlyFloat
,
42 parseHtmlLinks
: parseHtmlLinks
,
43 parseVersion
: parseVersion
,
44 escapeHtml
: escapeHtml
,
45 naturalSortCollator
: naturalSortCollator
,
47 toFixedPointString
: toFixedPointString
,
48 containsAllTerms
: containsAllTerms
,
51 FILTER_INPUT_DELAY
: 400,
56 const genHash = function(string
) {
58 // https://stackoverflow.com/a/8831937
59 // https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0
61 for (let i
= 0; i
< string
.length
; ++i
)
62 hash
= ((Math
.imul(hash
, 31) + string
.charCodeAt(i
)) | 0);
66 // getHost emulate the GUI version `QString getHost(const QString &url)`
67 const getHost = function(url
) {
68 // We want the hostname.
69 // If failed to parse the domain, original input should be returned
71 if (!/^(?:https?|udp):/i.test(url
))
75 // hack: URL can not get hostname from udp protocol
76 const parsedUrl
= new URL(url
.replace(/^udp:/i, "https:"));
77 // host: "example.com:8443"
78 // hostname: "example.com"
79 const host
= parsedUrl
.hostname
;
90 const createDebounceHandler
= (delay
, func
) => {
92 return (...params
) => {
94 timer
= setTimeout(() => {
103 * JS counterpart of the function in src/misc.cpp
105 const friendlyUnit = function(value
, isSpeed
) {
107 "QBT_TR(B)QBT_TR[CONTEXT=misc]",
108 "QBT_TR(KiB)QBT_TR[CONTEXT=misc]",
109 "QBT_TR(MiB)QBT_TR[CONTEXT=misc]",
110 "QBT_TR(GiB)QBT_TR[CONTEXT=misc]",
111 "QBT_TR(TiB)QBT_TR[CONTEXT=misc]",
112 "QBT_TR(PiB)QBT_TR[CONTEXT=misc]",
113 "QBT_TR(EiB)QBT_TR[CONTEXT=misc]"
116 if ((value
=== undefined) || (value
=== null) || (value
< 0))
117 return "QBT_TR(Unknown)QBT_TR[CONTEXT=misc]";
120 while ((value
>= 1024.0) && (i
< 6)) {
125 function friendlyUnitPrecision(sizeUnit
) {
126 if (sizeUnit
<= 2) // KiB, MiB
128 else if (sizeUnit
=== 3) // GiB
130 else // TiB, PiB, EiB
136 ret
= value
+ " " + units
[i
];
139 const precision
= friendlyUnitPrecision(i
);
140 const offset
= Math
.pow(10, precision
);
142 ret
= (Math
.floor(offset
* value
) / offset
).toFixed(precision
) + " " + units
[i
];
146 ret
+= "QBT_TR(/s)QBT_TR[CONTEXT=misc]";
151 * JS counterpart of the function in src/misc.cpp
153 const friendlyDuration = function(seconds
, maxCap
= -1) {
154 if ((seconds
< 0) || ((seconds
>= maxCap
) && (maxCap
>= 0)))
159 return "QBT_TR(< 1m)QBT_TR[CONTEXT=misc]";
160 let minutes
= seconds
/ 60;
162 return "QBT_TR(%1m)QBT_TR[CONTEXT=misc]".replace("%1", Math
.floor(minutes
));
163 let hours
= minutes
/ 60;
166 return "QBT_TR(%1h %2m)QBT_TR[CONTEXT=misc]".replace("%1", Math
.floor(hours
)).replace("%2", Math
.floor(minutes
));
167 let days
= hours
/ 24;
170 return "QBT_TR(%1d %2h)QBT_TR[CONTEXT=misc]".replace("%1", Math
.floor(days
)).replace("%2", Math
.floor(hours
));
171 const years
= days
/ 365;
173 return "QBT_TR(%1y %2d)QBT_TR[CONTEXT=misc]".replace("%1", Math
.floor(years
)).replace("%2", Math
.floor(days
));
176 const friendlyPercentage = function(value
) {
177 let percentage
= (value
* 100).round(1);
178 if (isNaN(percentage
) || (percentage
< 0))
180 if (percentage
> 100)
182 return percentage
.toFixed(1) + "%";
185 const friendlyFloat = function(value
, precision
) {
186 return parseFloat(value
).toFixed(precision
);
190 * JS counterpart of the function in src/misc.cpp
192 const parseHtmlLinks = function(text
) {
193 const exp
= /(\b(https?|ftp|file):\/\/[-\w+&@#/%?=~|!:,.;]*[-\w
+&@#/%=~|])/gi;
194 return text
.replace(exp
, "<a target='_blank' rel='noopener noreferrer' href='$1'>$1</a>");
197 const parseVersion = function(versionString
) {
202 if (typeof versionString
!== "string")
205 const tryToNumber
= (str
) => {
206 const num
= Number(str
);
207 return (isNaN(num
) ? str
: num
);
210 const ver
= versionString
.split(".", 4).map(val
=> tryToNumber(val
));
220 const escapeHtml
= (() => {
221 const div
= document
.createElement("div");
223 div
.textContent
= str
;
224 return div
.innerHTML
;
228 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator#parameters
229 const naturalSortCollator
= new Intl
.Collator(undefined, { numeric
: true, usage
: "sort" });
231 const safeTrim = function(value
) {
236 if (e
instanceof TypeError
)
242 const toFixedPointString = function(number
, digits
) {
243 // Do not round up number
244 const power
= Math
.pow(10, digits
);
245 return (Math
.floor(power
* number
) / power
).toFixed(digits
);
250 * @param {String} text the text to search
251 * @param {Array<String>} terms terms to search for within the text
252 * @returns {Boolean} true if all terms match the text, false otherwise
254 const containsAllTerms = function(text
, terms
) {
255 const textToSearch
= text
.toLowerCase();
256 return terms
.every((term
) => {
257 const isTermRequired
= (term
[0] === "+");
258 const isTermExcluded
= (term
[0] === "-");
259 if (isTermRequired
|| isTermExcluded
) {
261 if (term
.length
=== 1)
264 term
= term
.substring(1);
267 const textContainsTerm
= (textToSearch
.indexOf(term
) !== -1);
268 return isTermExcluded
? !textContainsTerm
: textContainsTerm
;
272 const sleep
= (ms
) => {
273 return new Promise((resolve
) => {
274 setTimeout(resolve
, ms
);
280 Object
.freeze(window
.qBittorrent
.Misc
);