Add torrent name filtering to WebUI
[qBittorrent.git] / src / webui / www / private / scripts / client.js
blob9f211db83e2c9f86d42e3a5c4f06e70bf65c0515
1 /*
2  * MIT License
3  * Copyright (c) 2008 Ishan Arora <ishan@qbittorrent.org>,
4  * Christophe Dumez <chris@qbittorrent.org>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
25 torrentsTable = new TorrentsTable();
26 torrentPeersTable = new TorrentPeersTable();
27 searchResultsTable = new SearchResultsTable();
28 searchPluginsTable = new SearchPluginsTable();
30 var updatePropertiesPanel = function() {};
32 var updateTorrentData = function() {};
33 var updateTrackersData = function() {};
34 var updateTorrentPeersData = function() {};
35 var updateWebSeedsData = function() {};
36 var updateTorrentFilesData = function() {};
38 var updateMainData = function() {};
39 var alternativeSpeedLimits = false;
40 var queueing_enabled = true;
41 var serverSyncMainDataInterval = 1500;
42 var customSyncMainDataInterval = null;
44 var clipboardEvent;
46 var CATEGORIES_ALL = 1;
47 var CATEGORIES_UNCATEGORIZED = 2;
49 var category_list = {};
51 var selected_category = CATEGORIES_ALL;
52 var setCategoryFilter = function() {};
54 var selected_filter = getLocalStorageItem('selected_filter', 'all');
55 var setFilter = function() {};
56 var toggleFilterDisplay = function() {};
58 var loadSelectedCategory = function() {
59     selected_category = getLocalStorageItem('selected_category', CATEGORIES_ALL);
61 loadSelectedCategory();
63 function genHash(string) {
64     var hash = 0;
65     for (var i = 0; i < string.length; ++i) {
66         var c = string.charCodeAt(i);
67         hash = (c + hash * 31) | 0;
68     }
69     return hash;
72 function getSyncMainDataInterval() {
73     return customSyncMainDataInterval ? customSyncMainDataInterval : serverSyncMainDataInterval;
76 window.addEvent('load', function() {
78     var saveColumnSizes = function() {
79         var filters_width = $('Filters').getSize().x;
80         var properties_height_rel = $('propertiesPanel').getSize().y / Window.getSize().y;
81         localStorage.setItem('filters_width', filters_width);
82         localStorage.setItem('properties_height_rel', properties_height_rel);
83     };
85     window.addEvent('resize', function() {
86         // only save sizes if the columns are visible
87         if (!$("mainColumn").hasClass("invisible"))
88             saveColumnSizes.delay(200); // Resizing might takes some time.
89     });
91     /*MochaUI.Desktop = new MochaUI.Desktop();
92     MochaUI.Desktop.desktop.setStyles({
93         'background': '#fff',
94         'visibility': 'visible'
95     });*/
96     MochaUI.Desktop.initialize();
98     var buildTransfersTab = function() {
99         var filt_w = localStorage.getItem('filters_width');
100         if ($defined(filt_w))
101             filt_w = filt_w.toInt();
102         else
103             filt_w = 120;
104         new MochaUI.Column({
105             id: 'filtersColumn',
106             placement: 'left',
107             onResize: saveColumnSizes,
108             width: filt_w,
109             resizeLimit: [1, 300]
110         });
112         new MochaUI.Column({
113             id: 'mainColumn',
114             placement: 'main'
115         });
116     };
118     var buildSearchTab = function() {
119         new MochaUI.Column({
120             id: 'searchTabColumn',
121             placement: 'main',
122             width: null
123         });
125         // start off hidden
126         $("searchTabColumn").addClass("invisible");
127     };
129     buildTransfersTab();
130     buildSearchTab();
131     MochaUI.initializeTabs('mainWindowTabsList');
133     setCategoryFilter = function(hash) {
134         selected_category = hash;
135         localStorage.setItem('selected_category', selected_category);
136         highlightSelectedCategory();
137         if (typeof torrentsTable.tableBody != 'undefined')
138             updateMainData();
139     };
141     setFilter = function(f) {
142         // Visually Select the right filter
143         $("all_filter").removeClass("selectedFilter");
144         $("downloading_filter").removeClass("selectedFilter");
145         $("seeding_filter").removeClass("selectedFilter");
146         $("completed_filter").removeClass("selectedFilter");
147         $("paused_filter").removeClass("selectedFilter");
148         $("resumed_filter").removeClass("selectedFilter");
149         $("active_filter").removeClass("selectedFilter");
150         $("inactive_filter").removeClass("selectedFilter");
151         $("errored_filter").removeClass("selectedFilter");
152         $(f + "_filter").addClass("selectedFilter");
153         selected_filter = f;
154         localStorage.setItem('selected_filter', f);
155         // Reload torrents
156         if (typeof torrentsTable.tableBody != 'undefined')
157             updateMainData();
158     };
160     toggleFilterDisplay = function(filter) {
161         var element = filter + "FilterList";
162         localStorage.setItem('filter_' + filter + "_collapsed", !$(element).hasClass("invisible"));
163         $(element).toggleClass("invisible")
164         var parent = $(element).getParent(".filterWrapper");
165         var toggleIcon = $(parent).getChildren(".filterTitle img");
166         if (toggleIcon)
167             toggleIcon[0].toggleClass("rotate");
168     };
170     new MochaUI.Panel({
171         id: 'Filters',
172         title: 'Panel',
173         header: false,
174         padding: {
175             top: 0,
176             right: 0,
177             bottom: 0,
178             left: 0
179         },
180         loadMethod: 'xhr',
181         contentURL: 'filters.html',
182         onContentLoaded: function() {
183             setFilter(selected_filter);
184         },
185         column: 'filtersColumn',
186         height: 300
187     });
188     initializeWindows();
190     // Show Top Toolbar is enabled by default
191     var showTopToolbar = true;
192     if (localStorage.getItem('show_top_toolbar') !== null)
193         showTopToolbar = localStorage.getItem('show_top_toolbar') == "true";
194     if (!showTopToolbar) {
195         $('showTopToolbarLink').firstChild.style.opacity = '0';
196         $('mochaToolbar').addClass('invisible');
197     }
199     // Show Status Bar is enabled by default
200     var showStatusBar = true;
201     if (localStorage.getItem('show_status_bar') !== null)
202         showStatusBar = localStorage.getItem('show_status_bar') === "true";
203     if (!showStatusBar) {
204         $('showStatusBarLink').firstChild.style.opacity = '0';
205         $('desktopFooterWrapper').addClass('invisible');
206     }
208     var speedInTitle = localStorage.getItem('speed_in_browser_title_bar') == "true";
209     if (!speedInTitle)
210         $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0';
212     // After showing/hiding the toolbar + status bar
213     var showSearchEngine = localStorage.getItem('show_search_engine') === "true";
214     if (!showSearchEngine) {
215         // uncheck menu option
216         $('showSearchEngineLink').firstChild.style.opacity = '0';
217         // hide tabs
218         $('mainWindowTabs').addClass('invisible');
219     }
221     // After Show Top Toolbar
222     MochaUI.Desktop.setDesktopSize();
224     var syncMainDataLastResponseId = 0;
225     var serverState = {};
227     var removeTorrentFromCategoryList = function(hash) {
228         if (hash === null || hash === "")
229             return false;
230         var removed = false;
231         Object.each(category_list, function(category) {
232             if (Object.contains(category.torrents, hash)) {
233                 removed = true;
234                 category.torrents.splice(category.torrents.indexOf(hash), 1);
235             }
236         });
237         return removed;
238     };
240     var addTorrentToCategoryList = function(torrent) {
241         var category = torrent['category'];
242         if (typeof category === 'undefined')
243             return false;
244         if (category.length === 0) { // Empty category
245             removeTorrentFromCategoryList(torrent['hash']);
246             return true;
247         }
248         var categoryHash = genHash(category);
249         if (category_list[categoryHash] === null) // This should not happen
250             category_list[categoryHash] = {
251                 name: category,
252                 torrents: []
253             };
254         if (!Object.contains(category_list[categoryHash].torrents, torrent['hash'])) {
255             removeTorrentFromCategoryList(torrent['hash']);
256             category_list[categoryHash].torrents = category_list[categoryHash].torrents.combine([torrent['hash']]);
257             return true;
258         }
259         return false;
260     };
262     var updateFilter = function(filter, filterTitle) {
263         $(filter + '_filter').firstChild.childNodes[1].nodeValue = filterTitle.replace('%1', torrentsTable.getFilteredTorrentsNumber(filter, CATEGORIES_ALL));
264     };
266     var updateFiltersList = function() {
267         updateFilter('all', 'QBT_TR(All (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
268         updateFilter('downloading', 'QBT_TR(Downloading (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
269         updateFilter('seeding', 'QBT_TR(Seeding (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
270         updateFilter('completed', 'QBT_TR(Completed (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
271         updateFilter('resumed', 'QBT_TR(Resumed (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
272         updateFilter('paused', 'QBT_TR(Paused (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
273         updateFilter('active', 'QBT_TR(Active (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
274         updateFilter('inactive', 'QBT_TR(Inactive (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
275         updateFilter('errored', 'QBT_TR(Errored (%1))QBT_TR[CONTEXT=StatusFilterWidget]');
276     };
278     var updateCategoryList = function() {
279         var categoryList = $('categoryFilterList');
280         if (!categoryList)
281             return;
282         categoryList.empty();
284         var create_link = function(hash, text, count) {
285             var html = '<a href="#" onclick="setCategoryFilter(' + hash + ');return false;">'
286                 + '<img src="images/qbt-theme/inode-directory.svg"/>'
287                 + escapeHtml(text) + ' (' + count + ')' + '</a>';
288             var el = new Element('li', {
289                 id: hash,
290                 html: html
291             });
292             categoriesFilterContextMenu.addTarget(el);
293             return el;
294         };
296         var all = torrentsTable.getRowIds().length;
297         var uncategorized = 0;
298         Object.each(torrentsTable.rows, function(row) {
299             if (row['full_data'].category.length === 0)
300                 uncategorized += 1;
301         });
302         categoryList.appendChild(create_link(CATEGORIES_ALL, 'QBT_TR(All)QBT_TR[CONTEXT=CategoryFilterModel]', all));
303         categoryList.appendChild(create_link(CATEGORIES_UNCATEGORIZED, 'QBT_TR(Uncategorized)QBT_TR[CONTEXT=CategoryFilterModel]', uncategorized));
305         var sortedCategories = [];
306         Object.each(category_list, function(category) {
307             sortedCategories.push(category.name);
308         });
309         sortedCategories.sort();
311         Object.each(sortedCategories, function(categoryName) {
312             var categoryHash = genHash(categoryName);
313             var categoryCount = category_list[categoryHash].torrents.length;
314             categoryList.appendChild(create_link(categoryHash, categoryName, categoryCount));
315         });
317         highlightSelectedCategory();
318     };
320     var highlightSelectedCategory = function() {
321         var categoryList = $('categoryFilterList');
322         if (!categoryList)
323             return;
324         var childrens = categoryList.childNodes;
325         for (var i in childrens) {
326             if (childrens[i].id == selected_category)
327                 childrens[i].className = "selectedFilter";
328             else
329                 childrens[i].className = "";
330         }
331     };
333     var syncMainDataTimer;
334     var syncMainData = function() {
335         var url = new URI('api/v2/sync/maindata');
336         url.setData('rid', syncMainDataLastResponseId);
337         new Request.JSON({
338             url: url,
339             noCache: true,
340             method: 'get',
341             onFailure: function() {
342                 var errorDiv = $('error_div');
343                 if (errorDiv)
344                     errorDiv.set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR[CONTEXT=HttpServer]');
345                 clearTimeout(syncMainDataTimer);
346                 syncMainDataTimer = syncMainData.delay(2000);
347             },
348             onSuccess: function(response) {
349                 $('error_div').set('html', '');
350                 if (response) {
351                     clearTimeout(torrentsFilterInputTimer);
352                     var torrentsTableSelectedRows;
353                     var update_categories = false;
354                     var full_update = (response['full_update'] === true);
355                     if (full_update) {
356                         torrentsTableSelectedRows = torrentsTable.selectedRowsIds();
357                         torrentsTable.clear();
358                         category_list = {};
359                     }
360                     if (response['rid']) {
361                         syncMainDataLastResponseId = response['rid'];
362                     }
363                     if (response['categories']) {
364                         for (var key in response['categories']) {
365                             var category = response['categories'][key];
366                             var categoryHash = genHash(key);
367                             if (category_list[categoryHash] !== undefined) {
368                                 // only the save path can change for existing categories
369                                 category_list[categoryHash].savePath = category.savePath;
370                             }
371                             else {
372                                 category_list[categoryHash] = {
373                                     name: category.name,
374                                     savePath: category.savePath,
375                                     torrents: []
376                                 };
377                             }
378                         }
379                         update_categories = true;
380                     }
381                     if (response['categories_removed']) {
382                         response['categories_removed'].each(function(category) {
383                             var categoryHash = genHash(category);
384                             delete category_list[categoryHash];
385                         });
386                         update_categories = true;
387                     }
388                     if (response['torrents']) {
389                         var updateTorrentList = false;
390                         for (var key in response['torrents']) {
391                             response['torrents'][key]['hash'] = key;
392                             response['torrents'][key]['rowId'] = key;
393                             if (response['torrents'][key]['state'])
394                                 response['torrents'][key]['status'] = response['torrents'][key]['state'];
395                             torrentsTable.updateRowData(response['torrents'][key]);
396                             if (addTorrentToCategoryList(response['torrents'][key]))
397                                 update_categories = true;
398                             if (response['torrents'][key]['name'])
399                                 updateTorrentList = true;
400                         }
402                         if (updateTorrentList)
403                             setupCopyEventHandler();
404                     }
405                     if (response['torrents_removed'])
406                         response['torrents_removed'].each(function(hash) {
407                             torrentsTable.removeRow(hash);
408                             removeTorrentFromCategoryList(hash);
409                             update_categories = true; // Always to update All category
410                         });
411                     torrentsTable.updateTable(full_update);
412                     torrentsTable.altRow();
413                     if (response['server_state']) {
414                         var tmp = response['server_state'];
415                         for (var k in tmp)
416                             serverState[k] = tmp[k];
417                         processServerState();
418                     }
419                     updateFiltersList();
420                     if (update_categories) {
421                         updateCategoryList();
422                         torrentsTableContextMenu.updateCategoriesSubMenu(category_list);
423                     }
425                     if (full_update)
426                         // re-select previously selected rows
427                         torrentsTable.reselectRows(torrentsTableSelectedRows);
428                 }
429                 clearTimeout(syncMainDataTimer);
430                 syncMainDataTimer = syncMainData.delay(getSyncMainDataInterval());
431             }
432         }).send();
433     };
435     updateMainData = function() {
436         torrentsTable.updateTable();
437         clearTimeout(syncMainDataTimer);
438         syncMainDataTimer = syncMainData.delay(100);
439     };
441     var processServerState = function() {
442         var transfer_info = friendlyUnit(serverState.dl_info_speed, true);
443         if (serverState.dl_rate_limit > 0)
444             transfer_info += " [" + friendlyUnit(serverState.dl_rate_limit, true) + "]";
445         transfer_info += " (" + friendlyUnit(serverState.dl_info_data, false) + ")";
446         $("DlInfos").set('html', transfer_info);
447         transfer_info = friendlyUnit(serverState.up_info_speed, true);
448         if (serverState.up_rate_limit > 0)
449             transfer_info += " [" + friendlyUnit(serverState.up_rate_limit, true) + "]";
450         transfer_info += " (" + friendlyUnit(serverState.up_info_data, false) + ")";
451         $("UpInfos").set('html', transfer_info);
452         if (speedInTitle) {
453             document.title = "QBT_TR([D: %1, U: %2] qBittorrent %3)QBT_TR[CONTEXT=MainWindow]".replace("%1", friendlyUnit(serverState.dl_info_speed, true)).replace("%2", friendlyUnit(serverState.up_info_speed, true)).replace("%3", "${VERSION}");
454             document.title += " QBT_TR(Web UI)QBT_TR[CONTEXT=OptionsDialog]";
455         }
456         else
457             document.title = "qBittorrent ${VERSION} QBT_TR(Web UI)QBT_TR[CONTEXT=OptionsDialog]";
458         $('freeSpaceOnDisk').set('html', 'QBT_TR(Free space: %1)QBT_TR[CONTEXT=HttpServer]'.replace("%1", friendlyUnit(serverState.free_space_on_disk)));
459         $('DHTNodes').set('html', 'QBT_TR(DHT: %1 nodes)QBT_TR[CONTEXT=StatusBar]'.replace("%1", serverState.dht_nodes));
461         // Statistics dialog
462         if (document.getElementById("statisticspage")) {
463             $('AlltimeDL').set('html', friendlyUnit(serverState.alltime_dl, false));
464             $('AlltimeUL').set('html', friendlyUnit(serverState.alltime_ul, false));
465             $('TotalWastedSession').set('html', friendlyUnit(serverState.total_wasted_session, false));
466             $('GlobalRatio').set('html', serverState.global_ratio);
467             $('TotalPeerConnections').set('html', serverState.total_peer_connections);
468             $('ReadCacheHits').set('html', serverState.read_cache_hits + "%");
469             $('TotalBuffersSize').set('html', friendlyUnit(serverState.total_buffers_size, false));
470             $('WriteCacheOverload').set('html', serverState.write_cache_overload + "%");
471             $('ReadCacheOverload').set('html', serverState.read_cache_overload + "%");
472             $('QueuedIOJobs').set('html', serverState.queued_io_jobs);
473             $('AverageTimeInQueue').set('html', serverState.average_time_queue + " ms");
474             $('TotalQueuedSize').set('html', friendlyUnit(serverState.total_queued_size, false));
475         }
477         if (serverState.connection_status == "connected")
478             $('connectionStatus').src = 'images/skin/connected.svg';
479         else if (serverState.connection_status == "firewalled")
480             $('connectionStatus').src = 'images/skin/firewalled.svg';
481         else
482             $('connectionStatus').src = 'images/skin/disconnected.svg';
484         if (queueing_enabled != serverState.queueing) {
485             queueing_enabled = serverState.queueing;
486             torrentsTable.columns['priority'].force_hide = !queueing_enabled;
487             torrentsTable.updateColumn('priority');
488             if (queueing_enabled) {
489                 $('topPrioItem').removeClass('invisible');
490                 $('increasePrioItem').removeClass('invisible');
491                 $('decreasePrioItem').removeClass('invisible');
492                 $('bottomPrioItem').removeClass('invisible');
493                 $('queueingButtons').removeClass('invisible');
494                 $('queueingMenuItems').removeClass('invisible');
495             }
496             else {
497                 $('topPrioItem').addClass('invisible');
498                 $('increasePrioItem').addClass('invisible');
499                 $('decreasePrioItem').addClass('invisible');
500                 $('bottomPrioItem').addClass('invisible');
501                 $('queueingButtons').addClass('invisible');
502                 $('queueingMenuItems').addClass('invisible');
503             }
504         }
506         if (alternativeSpeedLimits != serverState.use_alt_speed_limits) {
507             alternativeSpeedLimits = serverState.use_alt_speed_limits;
508             updateAltSpeedIcon(alternativeSpeedLimits);
509         }
511         serverSyncMainDataInterval = Math.max(serverState.refresh_interval, 500);
512     };
514     var updateAltSpeedIcon = function(enabled) {
515         if (enabled)
516             $('alternativeSpeedLimits').src = "images/slow.png";
517         else
518             $('alternativeSpeedLimits').src = "images/slow_off.png";
519     };
521     $('alternativeSpeedLimits').addEvent('click', function() {
522         // Change icon immediately to give some feedback
523         updateAltSpeedIcon(!alternativeSpeedLimits);
525         new Request({
526             url: 'api/v2/transfer/toggleSpeedLimitsMode',
527             method: 'post',
528             onComplete: function() {
529                 alternativeSpeedLimits = !alternativeSpeedLimits;
530                 updateMainData();
531             },
532             onFailure: function() {
533                 // Restore icon in case of failure
534                 updateAltSpeedIcon(alternativeSpeedLimits);
535             }
536         }).send();
537     });
539     $('DlInfos').addEvent('click', globalDownloadLimitFN);
540     $('UpInfos').addEvent('click', globalUploadLimitFN);
542     $('showTopToolbarLink').addEvent('click', function(e) {
543         showTopToolbar = !showTopToolbar;
544         localStorage.setItem('show_top_toolbar', showTopToolbar.toString());
545         if (showTopToolbar) {
546             $('showTopToolbarLink').firstChild.style.opacity = '1';
547             $('mochaToolbar').removeClass('invisible');
548         }
549         else {
550             $('showTopToolbarLink').firstChild.style.opacity = '0';
551             $('mochaToolbar').addClass('invisible');
552         }
553         MochaUI.Desktop.setDesktopSize();
554     });
556     $('showStatusBarLink').addEvent('click', function(e) {
557         showStatusBar = !showStatusBar;
558         localStorage.setItem('show_status_bar', showStatusBar.toString());
559         if (showStatusBar) {
560             $('showStatusBarLink').firstChild.style.opacity = '1';
561             $('desktopFooterWrapper').removeClass('invisible');
562         }
563         else {
564             $('showStatusBarLink').firstChild.style.opacity = '0';
565             $('desktopFooterWrapper').addClass('invisible');
566         }
567         MochaUI.Desktop.setDesktopSize();
568     });
570     $('speedInBrowserTitleBarLink').addEvent('click', function(e) {
571         speedInTitle = !speedInTitle;
572         localStorage.setItem('speed_in_browser_title_bar', speedInTitle.toString());
573         if (speedInTitle)
574             $('speedInBrowserTitleBarLink').firstChild.style.opacity = '1';
575         else
576             $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0';
577         processServerState();
578     });
580     $('showSearchEngineLink').addEvent('click', function(e) {
581         showSearchEngine = !showSearchEngine;
582         localStorage.setItem('show_search_engine', showSearchEngine.toString());
583         if (showSearchEngine) {
584             $('showSearchEngineLink').firstChild.style.opacity = '1';
585             $('mainWindowTabs').removeClass('invisible');
587             addMainWindowTabsEventListener();
588             if (!MochaUI.Panels.instances.SearchPanel)
589                 addSearchPanel();
590         }
591         else {
592             $('showSearchEngineLink').firstChild.style.opacity = '0';
593             $('mainWindowTabs').addClass('invisible');
594             $("transfersTabLink").click();
596             removeMainWindowTabsEventListener();
597         }
598     });
600     $('StatisticsLink').addEvent('click', StatisticsLinkFN);
602     // main window tabs
604     var showTransfersTab = function() {
605         $("filtersColumn").removeClass("invisible");
606         $("filtersColumn_handle").removeClass("invisible");
607         $("mainColumn").removeClass("invisible");
609         customSyncMainDataInterval = null;
610         clearTimeout(syncMainDataTimer);
611         syncMainDataTimer = syncMainData.delay(100);
613         hideSearchTab();
614     };
616     var hideTransfersTab = function() {
617         $("filtersColumn").addClass("invisible");
618         $("filtersColumn_handle").addClass("invisible");
619         $("mainColumn").addClass("invisible");
620         MochaUI.Desktop.resizePanels();
621     };
623     var showSearchTab = function() {
624         $("searchTabColumn").removeClass("invisible");
625         customSyncMainDataInterval = 30000;
626         hideTransfersTab();
627     };
629     var hideSearchTab = function() {
630         $("searchTabColumn").addClass("invisible");
631         MochaUI.Desktop.resizePanels();
632     };
634     var addMainWindowTabsEventListener = function() {
635         $('transfersTabLink').addEvent('click', showTransfersTab);
636         $('searchTabLink').addEvent('click', showSearchTab);
637     };
639     var removeMainWindowTabsEventListener = function() {
640         $('transfersTabLink').removeEvent('click', showTransfersTab);
641         $('searchTabLink').removeEvent('click', showSearchTab);
642     };
644     var addSearchPanel = function() {
645         new MochaUI.Panel({
646             id : 'SearchPanel',
647             title : 'Search',
648             header : false,
649             padding : {
650                 top : 0,
651                 right : 0,
652                 bottom : 0,
653                 left : 0
654             },
655             loadMethod : 'xhr',
656             contentURL : 'search.html',
657             content: '',
658             column : 'searchTabColumn',
659             height : null
660         });
661     };
663     new MochaUI.Panel({
664         id: 'transferList',
665         title: 'Panel',
666         header: false,
667         padding: {
668             top: 0,
669             right: 0,
670             bottom: 0,
671             left: 0
672         },
673         loadMethod: 'xhr',
674         contentURL: 'transferlist.html',
675         onContentLoaded: function() {
676             updateMainData();
677         },
678         column: 'mainColumn',
679         onResize: saveColumnSizes,
680         height: null
681     });
682     var prop_h = localStorage.getItem('properties_height_rel');
683     if ($defined(prop_h))
684         prop_h = prop_h.toFloat() * Window.getSize().y;
685     else
686         prop_h = Window.getSize().y / 2.0;
687     new MochaUI.Panel({
688         id: 'propertiesPanel',
689         title: 'Panel',
690         header: true,
691         padding: {
692             top: 0,
693             right: 0,
694             bottom: 0,
695             left: 0
696         },
697         contentURL: 'properties_content.html',
698         require: {
699             css: ['css/Tabs.css', 'css/dynamicTable.css'],
700             js: ['scripts/prop-general.js', 'scripts/prop-trackers.js', 'scripts/prop-webseeds.js', 'scripts/prop-files.js'],
701         },
702         tabsURL: 'properties.html',
703         tabsOnload: function() {
704             MochaUI.initializeTabs('propertiesTabs');
706             updatePropertiesPanel = function() {
707                 if (!$('prop_general').hasClass('invisible'))
708                     updateTorrentData();
709                 else if (!$('prop_trackers').hasClass('invisible'))
710                     updateTrackersData();
711                 else if (!$('prop_peers').hasClass('invisible'))
712                     updateTorrentPeersData();
713                 else if (!$('prop_webseeds').hasClass('invisible'))
714                     updateWebSeedsData();
715                 else if (!$('prop_files').hasClass('invisible'))
716                     updateTorrentFilesData();
717             };
719             $('PropGeneralLink').addEvent('click', function(e) {
720                 $('prop_general').removeClass("invisible");
721                 $('prop_trackers').addClass("invisible");
722                 $('prop_webseeds').addClass("invisible");
723                 $('prop_files').addClass("invisible");
724                 $('prop_peers').addClass("invisible");
725                 updatePropertiesPanel();
726                 localStorage.setItem('selected_tab', this.id);
727             });
729             $('PropTrackersLink').addEvent('click', function(e) {
730                 $('prop_trackers').removeClass("invisible");
731                 $('prop_general').addClass("invisible");
732                 $('prop_webseeds').addClass("invisible");
733                 $('prop_files').addClass("invisible");
734                 $('prop_peers').addClass("invisible");
735                 updatePropertiesPanel();
736                 localStorage.setItem('selected_tab', this.id);
737             });
739             $('PropPeersLink').addEvent('click', function(e) {
740                 $('prop_peers').removeClass("invisible");
741                 $('prop_trackers').addClass("invisible");
742                 $('prop_general').addClass("invisible");
743                 $('prop_webseeds').addClass("invisible");
744                 $('prop_files').addClass("invisible");
745                 updatePropertiesPanel();
746                 localStorage.setItem('selected_tab', this.id);
747             });
749             $('PropWebSeedsLink').addEvent('click', function(e) {
750                 $('prop_webseeds').removeClass("invisible");
751                 $('prop_general').addClass("invisible");
752                 $('prop_trackers').addClass("invisible");
753                 $('prop_files').addClass("invisible");
754                 $('prop_peers').addClass("invisible");
755                 updatePropertiesPanel();
756                 localStorage.setItem('selected_tab', this.id);
757             });
759             $('PropFilesLink').addEvent('click', function(e) {
760                 $('prop_files').removeClass("invisible");
761                 $('prop_general').addClass("invisible");
762                 $('prop_trackers').addClass("invisible");
763                 $('prop_webseeds').addClass("invisible");
764                 $('prop_peers').addClass("invisible");
765                 updatePropertiesPanel();
766                 localStorage.setItem('selected_tab', this.id);
767             });
769             $('propertiesPanel_collapseToggle').addEvent('click', function(e) {
770                 updatePropertiesPanel();
771             });
772         },
773         column: 'mainColumn',
774         height: prop_h
775     });
777     var prevTorrentsFilterValue;
778     var torrentsFilterInputTimer = null;
779     // listen for changes to torrentsFilterInput
780     $('torrentsFilterInput').addEvent('input', function() {
781         var value = $('torrentsFilterInput').get("value");
782         if (value !== prevTorrentsFilterValue) {
783             prevTorrentsFilterValue = value;
784             clearTimeout(torrentsFilterInputTimer);
785             torrentsFilterInputTimer = setTimeout(function() {
786                 torrentsTable.updateTable(false);
787             }, 400);
788         }
789     });
791     if (showSearchEngine) {
792         addMainWindowTabsEventListener();
793         addSearchPanel();
794     }
797 function closeWindows() {
798     MochaUI.closeAll();
801 function setupCopyEventHandler() {
802     if (clipboardEvent)
803         clipboardEvent.destroy();
805     clipboardEvent = new ClipboardJS('.copyToClipboard', {
806         text: function(trigger) {
807             switch (trigger.id) {
808                 case "CopyName":
809                     return copyNameFN();
810                 case "CopyMagnetLink":
811                     return copyMagnetLinkFN();
812                 case "CopyHash":
813                     return copyHashFN();
814                 case "copyDescriptionPageUrl":
815                     return copySearchTorrentUrl();
816                 default:
817                     return "";
818             }
819         }
820     });
823 var keyboardEvents = new Keyboard({
824     defaultEventType: 'keydown',
825     events: {
826         'ctrl+a': function(event) {
827             torrentsTable.selectAll();
828             event.preventDefault();
829         },
830         'delete': function(event) {
831             deleteFN();
832             event.preventDefault();
833         }
834     }
837 keyboardEvents.activate();
839 var loadTorrentPeersTimer;
840 var syncTorrentPeersLastResponseId = 0;
841 var show_flags = true;
842 var loadTorrentPeersData = function() {
843     if ($('prop_peers').hasClass('invisible')
844         || $('propertiesPanel_collapseToggle').hasClass('panel-expand')) {
845         syncTorrentPeersLastResponseId = 0;
846         torrentPeersTable.clear();
847         return;
848     }
849     var current_hash = torrentsTable.getCurrentTorrentHash();
850     if (current_hash === "") {
851         syncTorrentPeersLastResponseId = 0;
852         torrentPeersTable.clear();
853         clearTimeout(loadTorrentPeersTimer);
854         loadTorrentPeersTimer = loadTorrentPeersData.delay(getSyncMainDataInterval());
855         return;
856     }
857     var url = new URI('api/v2/sync/torrentPeers');
858     url.setData('rid', syncTorrentPeersLastResponseId);
859     url.setData('hash', current_hash);
860     new Request.JSON({
861         url: url,
862         noCache: true,
863         method: 'get',
864         onFailure: function() {
865             $('error_div').set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR[CONTEXT=HttpServer]');
866             clearTimeout(loadTorrentPeersTimer);
867             loadTorrentPeersTimer = loadTorrentPeersData.delay(5000);
868         },
869         onSuccess: function(response) {
870             $('error_div').set('html', '');
871             if (response) {
872                 var full_update = (response['full_update'] === true);
873                 if (full_update) {
874                     torrentPeersTable.clear();
875                 }
876                 if (response['rid']) {
877                     syncTorrentPeersLastResponseId = response['rid'];
878                 }
879                 if (response['peers']) {
880                     for (var key in response['peers']) {
881                         response['peers'][key]['rowId'] = key;
883                         if (response['peers'][key]['client'])
884                             response['peers'][key]['client'] = escapeHtml(response['peers'][key]['client']);
886                         torrentPeersTable.updateRowData(response['peers'][key]);
887                     }
888                 }
889                 if (response['peers_removed'])
890                     response['peers_removed'].each(function(hash) {
891                         torrentPeersTable.removeRow(hash);
892                     });
893                 torrentPeersTable.updateTable(full_update);
894                 torrentPeersTable.altRow();
896                 if (response['show_flags']) {
897                     if (show_flags != response['show_flags']) {
898                         show_flags = response['show_flags'];
899                         torrentPeersTable.columns['country'].force_hide = !show_flags;
900                         torrentPeersTable.updateColumn('country');
901                     }
902                 }
903             }
904             else {
905                 torrentPeersTable.clear();
906             }
907             clearTimeout(loadTorrentPeersTimer);
908             loadTorrentPeersTimer = loadTorrentPeersData.delay(getSyncMainDataInterval());
909         }
910     }).send();
913 updateTorrentPeersData = function() {
914     clearTimeout(loadTorrentPeersTimer);
915     loadTorrentPeersData();