diff --git a/src/webui/www/private/css/style.css b/src/webui/www/private/css/style.css index 8a1555c24..4320a7dc2 100644 --- a/src/webui/www/private/css/style.css +++ b/src/webui/www/private/css/style.css @@ -320,6 +320,9 @@ a.propButton img { #mochaToolbar { margin-top: 5px; + position: relative; + height: 29px; + overflow-y: hidden; } #mochaToolbar .divider { @@ -336,6 +339,11 @@ a.propButton img { padding-right: 5px; } +#mainWindowTabs { + float: right; + margin: 4px 5px 0 0; +} + /* Tri-state checkbox */ label.tristate { @@ -530,3 +538,47 @@ td.statusBarSeparator { .statisticsValue { text-align: right; } + +/* Search tab */ + +#SearchPanel, #SearchPanel_wrapper, #SearchPanel_pad { + height: inherit; +} + +#searchResults { + padding: 0 20px; + height: 100%; +} + +#searchResultsTableContainer { + height: calc(100% - 140px); + -moz-height: calc(100% - 140px); + -webkit-height: calc(100% - 140px); + overflow: auto; +} + +#searchResultsTableDiv { + height: calc(100% - 26px) !important; + -moz-height: calc(100% - 26px) !important; + -webkit-height: calc(100% - 26px) !important; +} + +#searchResults .dynamicTable { + width: 100%; +} + +#searchResults .numSearchResults { + font-style: italic; +} + +.red { + color: red; +} + +.green { + color: green; +} + +.searchPluginsTableRow { + cursor: pointer; +} diff --git a/src/webui/www/private/download.html b/src/webui/www/private/download.html index 6b89a3ce8..3d2bc6b0d 100644 --- a/src/webui/www/private/download.html +++ b/src/webui/www/private/download.html @@ -7,7 +7,9 @@ + +
@@ -133,6 +135,17 @@ diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index a822ecde2..1901a6296 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -24,6 +24,8 @@ torrentsTable = new TorrentsTable(); torrentPeersTable = new TorrentPeersTable(); +searchResultsTable = new SearchResultsTable(); +searchPluginsTable = new SearchPluginsTable(); var updatePropertiesPanel = function() {}; @@ -36,7 +38,8 @@ var updateTorrentFilesData = function() {}; var updateMainData = function() {}; var alternativeSpeedLimits = false; var queueing_enabled = true; -var syncMainDataTimerPeriod = 1500; +var serverSyncMainDataInterval = 1500; +var customSyncMainDataInterval = null; var clipboardEvent; @@ -66,6 +69,10 @@ function genHash(string) { return hash; } +function getSyncMainDataInterval() { + return customSyncMainDataInterval ? customSyncMainDataInterval : serverSyncMainDataInterval; +} + window.addEvent('load', function() { var saveColumnSizes = function() { @@ -76,8 +83,9 @@ window.addEvent('load', function() { }; window.addEvent('resize', function() { - // Resizing might takes some time. - saveColumnSizes.delay(200); + // only save sizes if the columns are visible + if (!$("mainColumn").hasClass("invisible")) + saveColumnSizes.delay(200); // Resizing might takes some time. }); /*MochaUI.Desktop = new MochaUI.Desktop(); @@ -87,22 +95,40 @@ window.addEvent('load', function() { });*/ MochaUI.Desktop.initialize(); - var filt_w = localStorage.getItem('filters_width'); - if ($defined(filt_w)) - filt_w = filt_w.toInt(); - else - filt_w = 120; - new MochaUI.Column({ - id: 'filtersColumn', - placement: 'left', - onResize: saveColumnSizes, - width: filt_w, - resizeLimit: [1, 300] - }); - new MochaUI.Column({ - id: 'mainColumn', - placement: 'main' - }); + var buildTransfersTab = function() { + var filt_w = localStorage.getItem('filters_width'); + if ($defined(filt_w)) + filt_w = filt_w.toInt(); + else + filt_w = 120; + new MochaUI.Column({ + id: 'filtersColumn', + placement: 'left', + onResize: saveColumnSizes, + width: filt_w, + resizeLimit: [1, 300] + }); + + new MochaUI.Column({ + id: 'mainColumn', + placement: 'main' + }); + }; + + var buildSearchTab = function() { + new MochaUI.Column({ + id: 'searchTabColumn', + placement: 'main', + width: null + }); + + // start off hidden + $("searchTabColumn").addClass("invisible"); + }; + + buildTransfersTab(); + buildSearchTab(); + MochaUI.initializeTabs('mainWindowTabsList'); setCategoryFilter = function(hash) { selected_category = hash; @@ -184,6 +210,15 @@ window.addEvent('load', function() { $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0'; // After showing/hiding the toolbar + status bar + var showSearchEngine = localStorage.getItem('show_search_engine') === "true"; + if (!showSearchEngine) { + // uncheck menu option + $('showSearchEngineLink').firstChild.style.opacity = '0'; + // hide tabs + $('mainWindowTabs').addClass('invisible'); + } + + // After Show Top Toolbar MochaUI.Desktop.setDesktopSize(); var syncMainDataLastResponseId = 0; @@ -391,7 +426,7 @@ window.addEvent('load', function() { torrentsTable.reselectRows(torrentsTableSelectedRows); } clearTimeout(syncMainDataTimer); - syncMainDataTimer = syncMainData.delay(syncMainDataTimerPeriod); + syncMainDataTimer = syncMainData.delay(getSyncMainDataInterval()); } }).send(); }; @@ -472,9 +507,7 @@ window.addEvent('load', function() { updateAltSpeedIcon(alternativeSpeedLimits); } - syncMainDataTimerPeriod = serverState.refresh_interval; - if (syncMainDataTimerPeriod < 500) - syncMainDataTimerPeriod = 500; + serverSyncMainDataInterval = Math.max(serverState.refresh_interval, 500); }; var updateAltSpeedIcon = function(enabled) { @@ -543,8 +576,89 @@ window.addEvent('load', function() { processServerState(); }); + $('showSearchEngineLink').addEvent('click', function(e) { + showSearchEngine = !showSearchEngine; + localStorage.setItem('show_search_engine', showSearchEngine.toString()); + if (showSearchEngine) { + $('showSearchEngineLink').firstChild.style.opacity = '1'; + $('mainWindowTabs').removeClass('invisible'); + + addMainWindowTabsEventListener(); + if (!MochaUI.Panels.instances.SearchPanel) + addSearchPanel(); + } + else { + $('showSearchEngineLink').firstChild.style.opacity = '0'; + $('mainWindowTabs').addClass('invisible'); + $("transfersTabLink").click(); + + removeMainWindowTabsEventListener(); + } + }); + $('StatisticsLink').addEvent('click', StatisticsLinkFN); + // main window tabs + + var showTransfersTab = function() { + $("filtersColumn").removeClass("invisible"); + $("filtersColumn_handle").removeClass("invisible"); + $("mainColumn").removeClass("invisible"); + + customSyncMainDataInterval = null; + clearTimeout(syncMainDataTimer); + syncMainDataTimer = syncMainData.delay(100); + + hideSearchTab(); + }; + + var hideTransfersTab = function() { + $("filtersColumn").addClass("invisible"); + $("filtersColumn_handle").addClass("invisible"); + $("mainColumn").addClass("invisible"); + MochaUI.Desktop.resizePanels(); + }; + + var showSearchTab = function() { + $("searchTabColumn").removeClass("invisible"); + customSyncMainDataInterval = 30000; + hideTransfersTab(); + }; + + var hideSearchTab = function() { + $("searchTabColumn").addClass("invisible"); + MochaUI.Desktop.resizePanels(); + }; + + var addMainWindowTabsEventListener = function() { + $('transfersTabLink').addEvent('click', showTransfersTab); + $('searchTabLink').addEvent('click', showSearchTab); + }; + + var removeMainWindowTabsEventListener = function() { + $('transfersTabLink').removeEvent('click', showTransfersTab); + $('searchTabLink').removeEvent('click', showSearchTab); + }; + + var addSearchPanel = function() { + new MochaUI.Panel({ + id : 'SearchPanel', + title : 'Search', + header : false, + padding : { + top : 0, + right : 0, + bottom : 0, + left : 0 + }, + loadMethod : 'xhr', + contentURL : 'search.html', + content: '', + column : 'searchTabColumn', + height : null + }); + }; + new MochaUI.Panel({ id: 'transferList', title: 'Panel', @@ -658,6 +772,12 @@ window.addEvent('load', function() { column: 'mainColumn', height: prop_h }); + + if (showSearchEngine) { + addMainWindowTabsEventListener(); + addSearchPanel(); + } + }); function closeWindows() { @@ -670,21 +790,18 @@ function setupCopyEventHandler() { clipboardEvent = new ClipboardJS('.copyToClipboard', { text: function(trigger) { - var textToCopy; - switch (trigger.id) { case "CopyName": - textToCopy = copyNameFN(); - break; + return copyNameFN(); case "CopyMagnetLink": - textToCopy = copyMagnetLinkFN(); - break; + return copyMagnetLinkFN(); case "CopyHash": - textToCopy = copyHashFN(); - break; + return copyHashFN(); + case "copyDescriptionPageUrl": + return copySearchTorrentUrl(); + default: + return ""; } - - return textToCopy; } }); } @@ -720,7 +837,7 @@ var loadTorrentPeersData = function() { syncTorrentPeersLastResponseId = 0; torrentPeersTable.clear(); clearTimeout(loadTorrentPeersTimer); - loadTorrentPeersTimer = loadTorrentPeersData.delay(syncMainDataTimerPeriod); + loadTorrentPeersTimer = loadTorrentPeersData.delay(getSyncMainDataInterval()); return; } var url = new URI('api/v2/sync/torrentPeers'); @@ -774,7 +891,7 @@ var loadTorrentPeersData = function() { torrentPeersTable.clear(); } clearTimeout(loadTorrentPeersTimer); - loadTorrentPeersTimer = loadTorrentPeersData.delay(syncMainDataTimerPeriod); + loadTorrentPeersTimer = loadTorrentPeersData.delay(getSyncMainDataInterval()); } }).send(); }; diff --git a/src/webui/www/private/scripts/contextmenu.js b/src/webui/www/private/scripts/contextmenu.js index 2c26dc037..b41c9e538 100644 --- a/src/webui/www/private/scripts/contextmenu.js +++ b/src/webui/www/private/scripts/contextmenu.js @@ -400,3 +400,21 @@ var CategoriesFilterContextMenu = new Class({ } } }); + +var SearchPluginsTableContextMenu = new Class({ + Extends: ContextMenu, + + updateMenuItems: function () { + var enabledColumnIndex = function(text) { + var columns = $("searchPluginsTableFixedHeaderRow").getChildren("th"); + for (var i = 0; i < columns.length; ++i) + if (columns[i].get("html") === "Enabled") + return i; + }; + + this.showItem('Enabled'); + this.setItemChecked('Enabled', this.options.element.getChildren("td")[enabledColumnIndex()].get("html") === "Yes"); + + this.showItem('Uninstall'); + } +}); diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 4e1f40fd0..13cc1e724 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -66,39 +66,42 @@ var DynamicTable = new Class({ $(this.dynamicTableDivId).addEvent('scroll', scrollFn); - var resizeFn = function() { - var panel = $(this.dynamicTableDivId).getParent('.panel'); - var h = panel.getBoundingClientRect().height - $(this.dynamicTableFixedHeaderDivId).getBoundingClientRect().height; - $(this.dynamicTableDivId).style.height = h + 'px'; + // if the table exists within a panel + if ($(this.dynamicTableDivId).getParent('.panel')) { + var resizeFn = function() { + var panel = $(this.dynamicTableDivId).getParent('.panel'); + var h = panel.getBoundingClientRect().height - $(this.dynamicTableFixedHeaderDivId).getBoundingClientRect().height; + $(this.dynamicTableDivId).style.height = h + 'px'; - // Workaround due to inaccurate calculation of elements heights by browser + // Workaround due to inaccurate calculation of elements heights by browser - var n = 2; + var n = 2; - while (panel.clientWidth != panel.offsetWidth && n > 0) { // is panel vertical scrollbar visible ? - --n; - h -= 0.5; - $(this.dynamicTableDivId).style.height = h + 'px'; - } + while (panel.clientWidth != panel.offsetWidth && n > 0) { // is panel vertical scrollbar visible ? + --n; + h -= 0.5; + $(this.dynamicTableDivId).style.height = h + 'px'; + } - this.lastPanelHeight = panel.getBoundingClientRect().height; - }.bind(this); + this.lastPanelHeight = panel.getBoundingClientRect().height; + }.bind(this); - $(this.dynamicTableDivId).getParent('.panel').addEvent('resize', resizeFn); + $(this.dynamicTableDivId).getParent('.panel').addEvent('resize', resizeFn); - this.lastPanelHeight = 0; + this.lastPanelHeight = 0; - // Workaround. Resize event is called not always (for example it isn't called when browser window changes it's size) + // Workaround. Resize event is called not always (for example it isn't called when browser window changes it's size) - var checkResizeFn = function() { - var panel = $(this.dynamicTableDivId).getParent('.panel'); - if (this.lastPanelHeight != panel.getBoundingClientRect().height) { - this.lastPanelHeight = panel.getBoundingClientRect().height; - panel.fireEvent('resize'); - } - }.bind(this); + var checkResizeFn = function() { + var panel = $(this.dynamicTableDivId).getParent('.panel'); + if (this.lastPanelHeight != panel.getBoundingClientRect().height) { + this.lastPanelHeight = panel.getBoundingClientRect().height; + panel.fireEvent('resize'); + } + }.bind(this); - setInterval(checkResizeFn, 500); + setInterval(checkResizeFn, 500); + } }, setupHeaderEvents: function() { @@ -1381,4 +1384,165 @@ var TorrentPeersTable = new Class({ } }); +var SearchResultsTable = new Class({ + Extends: DynamicTable, + + initColumns: function () { + this.newColumn('fileName', '', 'QBT_TR(Name)QBT_TR[CONTEXT=SearchResultsTable]', 500, true); + this.newColumn('fileSize', '', 'QBT_TR(Size)QBT_TR[CONTEXT=SearchResultsTable]', 100, true); + this.newColumn('nbSeeders', '', 'QBT_TR(Seeders)QBT_TR[CONTEXT=SearchResultsTable]', 100, true); + this.newColumn('nbLeechers', '', 'QBT_TR(Leechers)QBT_TR[CONTEXT=SearchResultsTable]', 100, true); + this.newColumn('siteUrl', '', 'QBT_TR(Search engine)QBT_TR[CONTEXT=SearchResultsTable]', 250, true); + + this.initColumnsFunctions(); + }, + + initColumnsFunctions: function () { + var displayText = function (td, row) { + var value = this.getRowValue(row); + td.set('html', escapeHtml(value)); + } + var displaySize = function(td, row) { + var size = this.getRowValue(row); + td.set('html', friendlyUnit(size, false)); + } + var displayNum = function (td, row) { + var value = escapeHtml(this.getRowValue(row)); + td.set('html', (value === "-1") ? "Unknown" : value); + } + + this.columns['fileName'].updateTd = displayText; + this.columns['fileSize'].updateTd = displaySize; + this.columns['nbSeeders'].updateTd = displayNum; + this.columns['nbLeechers'].updateTd = displayNum; + this.columns['siteUrl'].updateTd = displayText; + }, + + getFilteredAndSortedRows: function () { + var containsAll = function(text, searchTerms) { + text = text.toLowerCase(); + for (var i = 0; i < searchTerms.length; ++i) { + if (text.indexOf(searchTerms[i].toLowerCase()) === -1) + return false; + } + + return true; + }; + + var getSizeFilters = function() { + var minSize = (searchSizeFilter.min > 0.00) ? (searchSizeFilter.min * Math.pow(1024, searchSizeFilter.minUnit)) : 0.00; + var maxSize = (searchSizeFilter.max > 0.00) ? (searchSizeFilter.max * Math.pow(1024, searchSizeFilter.maxUnit)) : 0.00; + + if ((minSize > maxSize) && (maxSize > 0.00)) { + var tmp = minSize; + minSize = maxSize; + maxSize = tmp; + } + + return { + min: minSize, + max: maxSize + } + }; + + var getSeedsFilters = function() { + var minSeeds = (searchSeedsFilter.min > 0) ? searchSeedsFilter.min : 0; + var maxSeeds = (searchSeedsFilter.max > 0) ? searchSeedsFilter.max : 0; + + if ((minSeeds > maxSeeds) && (maxSeeds > 0)) { + var tmp = minSeeds; + minSeeds = maxSeeds; + maxSeeds = tmp; + } + + return { + min: minSeeds, + max: maxSeeds + } + } + + var filteredRows = []; + var rows = this.rows.getValues(); + var searchTerms = searchPattern.toLowerCase().split(" "); + var filterTerms = searchFilterPattern.toLowerCase().split(" "); + var sizeFilters = getSizeFilters(); + var seedsFilters = getSeedsFilters(); + var searchInTorrentName = $('searchInTorrentName').get('value') === "names"; + + if (searchInTorrentName || filterTerms.length || (searchSizeFilter.min > 0.00) || (searchSizeFilter.max > 0.00)) { + for (var i = 0; i < rows.length; ++i) { + var row = rows[i]; + + if (searchInTorrentName && !containsAll(row.full_data.fileName, searchTerms)) continue; + if (filterTerms.length && !containsAll(row.full_data.fileName, filterTerms)) continue; + if ((sizeFilters.min > 0.00) && (row.full_data.fileSize < sizeFilters.min)) continue; + if ((sizeFilters.max > 0.00) && (row.full_data.fileSize > sizeFilters.max)) continue; + if ((seedsFilters.min > 0) && (row.full_data.nbSeeders < seedsFilters.min)) continue; + if ((seedsFilters.max > 0) && (row.full_data.nbSeeders > seedsFilters.max)) continue; + + filteredRows.push(row); + } + } + else { + filteredRows = rows; + } + + filteredRows.sort(function (row1, row2) { + var column = this.columns[this.sortedColumn]; + var res = column.compareRows(row1, row2); + if (this.reverseSort == '0') + return res; + else + return -res; + }.bind(this)); + + return filteredRows; + }, + + setupTr: function (tr) { + tr.addClass("searchTableRow"); + } + }); + +var SearchPluginsTable = new Class({ + Extends: DynamicTable, + + initColumns: function () { + this.newColumn('fullName', '', 'QBT_TR(Name)QBT_TR[CONTEXT=SearchPluginsTable]', 175, true); + this.newColumn('version', '', 'QBT_TR(Version)QBT_TR[CONTEXT=SearchPluginsTable]', 100, true); + this.newColumn('url', '', 'QBT_TR(Url)QBT_TR[CONTEXT=SearchPluginsTable]', 175, true); + this.newColumn('enabled', '', 'QBT_TR(Enabled)QBT_TR[CONTEXT=SearchPluginsTable]', 100, true); + + this.initColumnsFunctions(); + }, + + initColumnsFunctions: function () { + var displayText = function (td, row) { + var value = this.getRowValue(row); + td.set('html', escapeHtml(value)); + } + + this.columns['fullName'].updateTd = displayText; + this.columns['version'].updateTd = displayText; + this.columns['url'].updateTd = displayText; + this.columns['enabled'].updateTd = function(td, row) { + var value = this.getRowValue(row); + if (value) { + td.set('html', "Yes"); + td.getParent("tr").addClass("green"); + td.getParent("tr").removeClass("red"); + } + else { + td.set('html', "No"); + td.getParent("tr").addClass("red"); + td.getParent("tr").removeClass("green"); + } + }; + }, + + setupTr: function (tr) { + tr.addClass("searchPluginsTableRow"); + } + }); + /*************************************************************/ diff --git a/src/webui/www/private/scripts/mocha-init.js b/src/webui/www/private/scripts/mocha-init.js index 0b8988109..9ca0d8fd4 100644 --- a/src/webui/www/private/scripts/mocha-init.js +++ b/src/webui/www/private/scripts/mocha-init.js @@ -59,12 +59,20 @@ initializeWindows = function() { addClickEvent('download', function(e) { new Event(e).stop(); + showDownloadPage(); + }); + + showDownloadPage = function(urls) { var id = 'downloadPage'; + var contentUrl = 'download.html'; + if (urls && urls.length) + contentUrl += '?urls=' + urls.join("|"); + new MochaUI.Window({ id: id, title: "QBT_TR(Download from URLs)QBT_TR[CONTEXT=downloadFromURL]", loadMethod: 'iframe', - contentURL: 'download.html', + contentURL: contentUrl, addClass: 'windowFrame', // fixes iframe scrolling on iOS Safari scrollbars: true, maximizable: false, @@ -78,7 +86,7 @@ initializeWindows = function() { } }); updateMainData(); - }); + }; addClickEvent('preferences', function(e) { new Event(e).stop(); diff --git a/src/webui/www/private/search.html b/src/webui/www/private/search.html new file mode 100644 index 000000000..c7921e7f3 --- /dev/null +++ b/src/webui/www/private/search.html @@ -0,0 +1,644 @@ + + +
+ There aren't any search plugins installed. Click the "Search plugins..." button at the bottom right of the window to install some. + |
+