2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2008 Christophe Dumez <chris@qbittorrent.org>
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.
29 /* -----------------------------------------------------------------
31 ATTACH MOCHA LINK EVENTS
32 Notes: Here is where you define your windows and the events that open them.
33 If you are not using links to run Mocha methods you can remove this function.
35 If you need to add link events to links within windows you are creating, do
36 it in the onContentLoaded function of the new window.
38 ----------------------------------------------------------------- */
41 /* Define localStorage object for older browsers */
42 if (typeof localStorage
== 'undefined') {
43 window
['localStorage'] = {
44 getItem: function(name
) {
45 return Cookie
.read(name
);
47 setItem: function(name
, value
) {
48 Cookie
.write(name
, value
, {
55 function getLocalStorageItem(name
, defaultVal
) {
56 let val
= localStorage
.getItem(name
);
57 if (val
=== null || val
=== undefined)
62 let saveWindowSize = function() {};
63 let loadWindowWidth = function() {};
64 let loadWindowHeight = function() {};
65 let showDownloadPage = function() {};
66 let globalUploadLimitFN = function() {};
67 let uploadLimitFN = function() {};
68 let shareRatioFN = function() {};
69 let toggleSequentialDownloadFN = function() {};
70 let toggleFirstLastPiecePrioFN = function() {};
71 let setSuperSeedingFN = function() {};
72 let setForceStartFN = function() {};
73 let globalDownloadLimitFN = function() {};
74 let StatisticsLinkFN = function() {};
75 let downloadLimitFN = function() {};
76 let deleteFN = function() {};
77 let pauseFN = function() {};
78 let startFN = function() {};
79 let autoTorrentManagementFN = function() {};
80 let recheckFN = function() {};
81 let reannounceFN = function() {};
82 let setLocationFN = function() {};
83 let renameFN = function() {};
84 let torrentNewCategoryFN = function() {};
85 let torrentSetCategoryFN = function() {};
86 let createCategoryFN = function() {};
87 let editCategoryFN = function() {};
88 let removeCategoryFN = function() {};
89 let deleteUnusedCategoriesFN = function() {};
90 let startTorrentsByCategoryFN = function() {};
91 let pauseTorrentsByCategoryFN = function() {};
92 let deleteTorrentsByCategoryFN = function() {};
93 let torrentAddTagsFN = function() {};
94 let torrentSetTagsFN = function() {};
95 let torrentRemoveAllTagsFN = function() {};
96 let createTagFN = function() {};
97 let removeTagFN = function() {};
98 let deleteUnusedTagsFN = function() {};
99 let startTorrentsByTagFN = function() {};
100 let pauseTorrentsByTagFN = function() {};
101 let deleteTorrentsByTagFN = function() {};
102 let copyNameFN = function() {};
103 let copyMagnetLinkFN = function() {};
104 let copyHashFN = function() {};
105 let setQueuePositionFN = function() {};
107 const initializeWindows = function() {
108 saveWindowSize = function(windowId
) {
109 const size
= $(windowId
).getSize();
110 localStorage
.setItem('window_' + windowId
+ '_width', size
.x
);
111 localStorage
.setItem('window_' + windowId
+ '_height', size
.y
);
114 loadWindowWidth = function(windowId
, defaultValue
) {
115 return getLocalStorageItem('window_' + windowId
+ '_width', defaultValue
);
118 loadWindowHeight = function(windowId
, defaultValue
) {
119 return getLocalStorageItem('window_' + windowId
+ '_height', defaultValue
);
122 function addClickEvent(el
, fn
) {
123 ['Link', 'Button'].each(function(item
) {
125 $(el
+ item
).addEvent('click', fn
);
130 addClickEvent('download', function(e
) {
135 showDownloadPage = function(urls
) {
136 const id
= 'downloadPage';
137 let contentUrl
= 'download.html';
138 if (urls
&& (urls
.length
> 0)) {
139 contentUrl
+= ('?urls=' + urls
.map(function(url
) {
140 return encodeURIComponent(url
);
146 title
: "QBT_TR(Download from URLs)QBT_TR[CONTEXT=downloadFromURL]",
147 loadMethod
: 'iframe',
148 contentURL
: contentUrl
,
149 addClass
: 'windowFrame', // fixes iframe scrolling on iOS Safari
154 paddingHorizontal
: 0,
155 width
: loadWindowWidth(id
, 500),
156 height
: loadWindowHeight(id
, 600),
157 onResize: function() {
164 addClickEvent('preferences', function(e
) {
166 const id
= 'preferencesPage';
169 title
: "QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog]",
172 contentURL
: 'views/preferences.html',
174 css
: ['css/Tabs.css']
176 toolbarURL
: 'views/preferencesToolbar.html',
180 paddingHorizontal
: 0,
181 width
: loadWindowWidth(id
, 700),
182 height
: loadWindowHeight(id
, 600),
183 onResize: function() {
189 addClickEvent('upload', function(e
) {
191 const id
= 'uploadPage';
194 title
: "QBT_TR(Upload local torrent)QBT_TR[CONTEXT=HttpServer]",
195 loadMethod
: 'iframe',
196 contentURL
: 'upload.html',
197 addClass
: 'windowFrame', // fixes iframe scrolling on iOS Safari
201 paddingHorizontal
: 0,
202 width
: loadWindowWidth(id
, 500),
203 height
: loadWindowHeight(id
, 460),
204 onResize: function() {
211 globalUploadLimitFN = function() {
213 id
: 'uploadLimitPage',
214 title
: "QBT_TR(Global Upload Speed Limit)QBT_TR[CONTEXT=MainWindow]",
215 loadMethod
: 'iframe',
216 contentURL
: 'uploadlimit.html?hashes=global',
221 paddingHorizontal
: 0,
227 uploadLimitFN = function() {
228 const hashes
= torrentsTable
.selectedRowsIds();
231 id
: 'uploadLimitPage',
232 title
: "QBT_TR(Torrent Upload Speed Limiting)QBT_TR[CONTEXT=TransferListWidget]",
233 loadMethod
: 'iframe',
234 contentURL
: 'uploadlimit.html?hashes=' + hashes
.join("|"),
239 paddingHorizontal
: 0,
246 shareRatioFN = function() {
247 const hashes
= torrentsTable
.selectedRowsIds();
249 let shareRatio
= null;
250 let torrentsHaveSameShareRatio
= true;
252 // check if all selected torrents have same share ratio
253 for (let i
= 0; i
< hashes
.length
; ++i
) {
254 const hash
= hashes
[i
];
255 const row
= torrentsTable
.rows
[hash
].full_data
;
256 const origValues
= row
.ratio_limit
+ "|" + row
.seeding_time_limit
+ "|" + row
.max_ratio
+ "|" + row
.max_seeding_time
;
259 if (shareRatio
=== null)
260 shareRatio
= origValues
;
262 if (origValues
!== shareRatio
) {
263 torrentsHaveSameShareRatio
= false;
268 // if all torrents have same share ratio, display that share ratio. else use the default
269 const orig
= torrentsHaveSameShareRatio
? shareRatio
: "";
271 id
: 'shareRatioPage',
272 title
: "QBT_TR(Torrent Upload/Download Ratio Limiting)QBT_TR[CONTEXT=UpDownRatioDialog]",
273 loadMethod
: 'iframe',
274 contentURL
: 'shareratio.html?hashes=' + hashes
.join("|") + '&orig=' + orig
,
278 paddingHorizontal
: 0,
285 toggleSequentialDownloadFN = function() {
286 const hashes
= torrentsTable
.selectedRowsIds();
289 url
: 'api/v2/torrents/toggleSequentialDownload',
292 hashes
: hashes
.join("|")
299 toggleFirstLastPiecePrioFN = function() {
300 const hashes
= torrentsTable
.selectedRowsIds();
303 url
: 'api/v2/torrents/toggleFirstLastPiecePrio',
306 hashes
: hashes
.join("|")
313 setSuperSeedingFN = function(val
) {
314 const hashes
= torrentsTable
.selectedRowsIds();
317 url
: 'api/v2/torrents/setSuperSeeding',
321 hashes
: hashes
.join("|")
328 setForceStartFN = function() {
329 const hashes
= torrentsTable
.selectedRowsIds();
332 url
: 'api/v2/torrents/setForceStart',
336 hashes
: hashes
.join("|")
343 globalDownloadLimitFN = function() {
345 id
: 'downloadLimitPage',
346 title
: "QBT_TR(Global Download Speed Limit)QBT_TR[CONTEXT=MainWindow]",
347 loadMethod
: 'iframe',
348 contentURL
: 'downloadlimit.html?hashes=global',
353 paddingHorizontal
: 0,
359 StatisticsLinkFN = function() {
360 const id
= 'statisticspage';
363 title
: 'QBT_TR(Statistics)QBT_TR[CONTEXT=StatsDialog]',
365 contentURL
: 'views/statistics.html',
368 width
: loadWindowWidth(id
, 275),
369 height
: loadWindowHeight(id
, 370),
370 onResize: function() {
376 downloadLimitFN = function() {
377 const hashes
= torrentsTable
.selectedRowsIds();
380 id
: 'downloadLimitPage',
381 title
: "QBT_TR(Torrent Download Speed Limiting)QBT_TR[CONTEXT=TransferListWidget]",
382 loadMethod
: 'iframe',
383 contentURL
: 'downloadlimit.html?hashes=' + hashes
.join("|"),
388 paddingHorizontal
: 0,
395 deleteFN = function() {
396 const hashes
= torrentsTable
.selectedRowsIds();
399 id
: 'confirmDeletionPage',
400 title
: "QBT_TR(Deletion confirmation)QBT_TR[CONTEXT=confirmDeletionDlg]",
401 loadMethod
: 'iframe',
402 contentURL
: 'confirmdeletion.html?hashes=' + hashes
.join("|"),
414 addClickEvent('delete', function(e
) {
419 pauseFN = function() {
420 const hashes
= torrentsTable
.selectedRowsIds();
423 url
: 'api/v2/torrents/pause',
426 hashes
: hashes
.join("|")
433 startFN = function() {
434 const hashes
= torrentsTable
.selectedRowsIds();
437 url
: 'api/v2/torrents/resume',
440 hashes
: hashes
.join("|")
447 autoTorrentManagementFN = function() {
448 const hashes
= torrentsTable
.selectedRowsIds();
451 hashes
.each(function(hash
, index
) {
452 const row
= torrentsTable
.rows
[hash
];
453 if (!row
.full_data
.auto_tmm
)
457 url
: 'api/v2/torrents/setAutoManagement',
460 hashes
: hashes
.join("|"),
468 recheckFN = function() {
469 const hashes
= torrentsTable
.selectedRowsIds();
472 url
: 'api/v2/torrents/recheck',
475 hashes
: hashes
.join("|"),
482 reannounceFN = function() {
483 const hashes
= torrentsTable
.selectedRowsIds();
486 url
: 'api/v2/torrents/reannounce',
489 hashes
: hashes
.join("|"),
496 setLocationFN = function() {
497 const hashes
= torrentsTable
.selectedRowsIds();
499 const hash
= hashes
[0];
500 const row
= torrentsTable
.rows
[hash
];
501 const path
= encodeURIComponent(row
.full_data
.save_path
);
503 id
: 'setLocationPage',
504 title
: "QBT_TR(Set location)QBT_TR[CONTEXT=TransferListWidget]",
505 loadMethod
: 'iframe',
506 contentURL
: 'setlocation.html?hashes=' + hashes
.join('|') + '&path=' + path
,
511 paddingHorizontal
: 0,
518 renameFN = function() {
519 const hashes
= torrentsTable
.selectedRowsIds();
520 if (hashes
.length
== 1) {
521 const hash
= hashes
[0];
522 const row
= torrentsTable
.rows
[hash
];
526 title
: "QBT_TR(Rename)QBT_TR[CONTEXT=TransferListWidget]",
527 loadMethod
: 'iframe',
528 contentURL
: 'rename.html?hash=' + hash
+ '&name=' + encodeURIComponent(row
.full_data
.name
),
533 paddingHorizontal
: 0,
541 torrentNewCategoryFN = function() {
542 const action
= "set";
543 const hashes
= torrentsTable
.selectedRowsIds();
546 id
: 'newCategoryPage',
547 title
: "QBT_TR(New Category)QBT_TR[CONTEXT=TransferListWidget]",
548 loadMethod
: 'iframe',
549 contentURL
: 'newcategory.html?action=' + action
+ '&hashes=' + hashes
.join('|'),
554 paddingHorizontal
: 0,
561 torrentSetCategoryFN = function(categoryHash
) {
562 let categoryName
= '';
563 if (categoryHash
!= 0)
564 categoryName
= category_list
[categoryHash
].name
;
565 const hashes
= torrentsTable
.selectedRowsIds();
568 url
: 'api/v2/torrents/setCategory',
571 hashes
: hashes
.join("|"),
572 category
: categoryName
578 createCategoryFN = function() {
579 const action
= "create";
581 id
: 'newCategoryPage',
582 title
: "QBT_TR(New Category)QBT_TR[CONTEXT=CategoryFilterWidget]",
583 loadMethod
: 'iframe',
584 contentURL
: 'newcategory.html?action=' + action
,
589 paddingHorizontal
: 0,
596 editCategoryFN = function(categoryHash
) {
597 const action
= "edit";
598 const categoryName
= category_list
[categoryHash
].name
;
599 const savePath
= category_list
[categoryHash
].savePath
;
601 id
: 'editCategoryPage',
602 title
: "QBT_TR(Edit Category)QBT_TR[CONTEXT=TransferListWidget]",
603 loadMethod
: 'iframe',
604 contentURL
: 'newcategory.html?action=' + action
+ '&categoryName=' + categoryName
+ '&savePath=' + savePath
,
609 paddingHorizontal
: 0,
616 removeCategoryFN = function(categoryHash
) {
617 const categoryName
= category_list
[categoryHash
].name
;
619 url
: 'api/v2/torrents/removeCategories',
622 categories
: categoryName
625 setCategoryFilter(CATEGORIES_ALL
);
628 deleteUnusedCategoriesFN = function() {
629 const categories
= [];
630 for (const hash
in category_list
) {
631 if (torrentsTable
.getFilteredTorrentsNumber('all', hash
, TAGS_ALL
) === 0)
632 categories
.push(category_list
[hash
].name
);
635 url
: 'api/v2/torrents/removeCategories',
638 categories
: categories
.join('\n')
641 setCategoryFilter(CATEGORIES_ALL
);
644 startTorrentsByCategoryFN = function(categoryHash
) {
645 const hashes
= torrentsTable
.getFilteredTorrentsHashes('all', categoryHash
, TAGS_ALL
);
648 url
: 'api/v2/torrents/resume',
651 hashes
: hashes
.join("|")
658 pauseTorrentsByCategoryFN = function(categoryHash
) {
659 const hashes
= torrentsTable
.getFilteredTorrentsHashes('all', categoryHash
, TAGS_ALL
);
662 url
: 'api/v2/torrents/pause',
665 hashes
: hashes
.join("|")
672 deleteTorrentsByCategoryFN = function(categoryHash
) {
673 const hashes
= torrentsTable
.getFilteredTorrentsHashes('all', categoryHash
, TAGS_ALL
);
676 id
: 'confirmDeletionPage',
677 title
: "QBT_TR(Deletion confirmation)QBT_TR[CONTEXT=confirmDeletionDlg]",
678 loadMethod
: 'iframe',
679 contentURL
: 'confirmdeletion.html?hashes=' + hashes
.join("|"),
691 torrentAddTagsFN = function() {
692 const action
= "set";
693 const hashes
= torrentsTable
.selectedRowsIds();
697 title
: "QBT_TR(Add Tags)QBT_TR[CONTEXT=TransferListWidget]",
698 loadMethod
: 'iframe',
699 contentURL
: 'newtag.html?action=' + action
+ '&hashes=' + hashes
.join('|'),
704 paddingHorizontal
: 0,
711 torrentSetTagsFN = function(tagHash
, isSet
) {
712 const tagName
= ((tagHash
=== '0') ? '' : tagList
[tagHash
].name
);
713 const hashes
= torrentsTable
.selectedRowsIds();
716 url
: (isSet
? 'api/v2/torrents/addTags' : 'api/v2/torrents/removeTags'),
719 hashes
: hashes
.join("|"),
726 torrentRemoveAllTagsFN = function() {
727 const hashes
= torrentsTable
.selectedRowsIds();
730 url
: ('api/v2/torrents/removeTags'),
733 hashes
: hashes
.join("|"),
739 createTagFN = function() {
740 const action
= "create";
743 title
: "QBT_TR(New Tag)QBT_TR[CONTEXT=TagFilterWidget]",
744 loadMethod
: 'iframe',
745 contentURL
: 'newtag.html?action=' + action
,
750 paddingHorizontal
: 0,
757 removeTagFN = function(tagHash
) {
758 const tagName
= tagList
[tagHash
].name
;
760 url
: 'api/v2/torrents/deleteTags',
766 setTagFilter(TAGS_ALL
);
769 deleteUnusedTagsFN = function() {
771 for (const hash
in tagList
) {
772 if (torrentsTable
.getFilteredTorrentsNumber('all', CATEGORIES_ALL
, hash
) === 0)
773 tags
.push(tagList
[hash
].name
);
776 url
: 'api/v2/torrents/deleteTags',
782 setTagFilter(TAGS_ALL
);
785 startTorrentsByTagFN = function(tagHash
) {
786 const hashes
= torrentsTable
.getFilteredTorrentsHashes('all', CATEGORIES_ALL
, tagHash
);
789 url
: 'api/v2/torrents/resume',
792 hashes
: hashes
.join("|")
799 pauseTorrentsByTagFN = function(tagHash
) {
800 const hashes
= torrentsTable
.getFilteredTorrentsHashes('all', CATEGORIES_ALL
, tagHash
);
803 url
: 'api/v2/torrents/pause',
806 hashes
: hashes
.join("|")
813 deleteTorrentsByTagFN = function(tagHash
) {
814 const hashes
= torrentsTable
.getFilteredTorrentsHashes('all', CATEGORIES_ALL
, tagHash
);
817 id
: 'confirmDeletionPage',
818 title
: "QBT_TR(Deletion confirmation)QBT_TR[CONTEXT=confirmDeletionDlg]",
819 loadMethod
: 'iframe',
820 contentURL
: 'confirmdeletion.html?hashes=' + hashes
.join("|"),
832 copyNameFN = function() {
833 const selectedRows
= torrentsTable
.selectedRowsIds();
835 if (selectedRows
.length
) {
836 const rows
= torrentsTable
.getFilteredAndSortedRows();
837 for (let i
= 0; i
< selectedRows
.length
; ++i
) {
838 const hash
= selectedRows
[i
];
839 names
.push(rows
[hash
].full_data
.name
);
842 return names
.join("\n");
845 copyMagnetLinkFN = function() {
846 const selectedRows
= torrentsTable
.selectedRowsIds();
848 if (selectedRows
.length
) {
849 const rows
= torrentsTable
.getFilteredAndSortedRows();
850 for (let i
= 0; i
< selectedRows
.length
; ++i
) {
851 const hash
= selectedRows
[i
];
852 magnets
.push(rows
[hash
].full_data
.magnet_uri
);
855 return magnets
.join("\n");
858 copyHashFN = function() {
859 return torrentsTable
.selectedRowsIds().join("\n");
862 ['pause', 'resume'].each(function(item
) {
863 addClickEvent(item
+ 'All', function(e
) {
866 url
: 'api/v2/torrents/' + item
,
876 ['pause', 'resume', 'recheck'].each(function(item
) {
877 addClickEvent(item
, function(e
) {
879 const hashes
= torrentsTable
.selectedRowsIds();
881 hashes
.each(function(hash
, index
) {
883 url
: 'api/v2/torrents/' + item
,
895 ['decreasePrio', 'increasePrio', 'topPrio', 'bottomPrio'].each(function(item
) {
896 addClickEvent(item
, function(e
) {
898 setQueuePositionFN(item
);
902 setQueuePositionFN = function(cmd
) {
903 const hashes
= torrentsTable
.selectedRowsIds();
906 url
: 'api/v2/torrents/' + cmd
,
909 hashes
: hashes
.join("|")
916 addClickEvent('about', function(e
) {
918 const id
= 'aboutpage';
921 title
: 'QBT_TR(About qBittorrent)QBT_TR[CONTEXT=AboutDialog]',
923 contentURL
: 'views/about.html',
925 css
: ['css/Tabs.css']
928 toolbarURL
: 'views/aboutToolbar.html',
930 width
: loadWindowWidth(id
, 550),
931 height
: loadWindowHeight(id
, 360),
932 onResize: function() {
938 addClickEvent('logout', function(e
) {
941 url
: 'api/v2/auth/logout',
943 onSuccess: function() {
944 window
.location
.reload(true);
949 addClickEvent('shutdown', function(e
) {
951 if (confirm('QBT_TR(Are you sure you want to quit qBittorrent?)QBT_TR[CONTEXT=MainWindow]')) {
953 url
: 'api/v2/app/shutdown',
954 onSuccess: function() {
955 document
.write('<!doctype html><html lang="${LANG}"><head> <meta charset="utf-8"> <title>QBT_TR(qBittorrent has been shutdown)QBT_TR[CONTEXT=HttpServer]</title></head><body> <h1 style="text-align: center;">QBT_TR(qBittorrent has been shutdown)QBT_TR[CONTEXT=HttpServer]</h1></body></html>');
962 // Deactivate menu header links
963 $$('a.returnFalse').each(function(el
) {
964 el
.addEvent('click', function(e
) {