Browse Source

Merge pull request #9758 from Piccirello/new-search-webui-2

Add Search feature to WebUI
adaptive-webui-19844
Mike Tzou 6 years ago committed by GitHub
parent
commit
2eb213e2f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 52
      src/webui/www/private/css/style.css
  2. 13
      src/webui/www/private/download.html
  3. 10
      src/webui/www/private/index.html
  4. 69
      src/webui/www/private/installsearchplugin.html
  5. 155
      src/webui/www/private/scripts/client.js
  6. 18
      src/webui/www/private/scripts/contextmenu.js
  7. 164
      src/webui/www/private/scripts/dynamicTable.js
  8. 12
      src/webui/www/private/scripts/mocha-init.js
  9. 644
      src/webui/www/private/search.html
  10. 219
      src/webui/www/private/searchplugins.html
  11. 3
      src/webui/www/webui.qrc

52
src/webui/www/private/css/style.css

@ -320,6 +320,9 @@ a.propButton img {
#mochaToolbar { #mochaToolbar {
margin-top: 5px; margin-top: 5px;
position: relative;
height: 29px;
overflow-y: hidden;
} }
#mochaToolbar .divider { #mochaToolbar .divider {
@ -336,6 +339,11 @@ a.propButton img {
padding-right: 5px; padding-right: 5px;
} }
#mainWindowTabs {
float: right;
margin: 4px 5px 0 0;
}
/* Tri-state checkbox */ /* Tri-state checkbox */
label.tristate { label.tristate {
@ -530,3 +538,47 @@ td.statusBarSeparator {
.statisticsValue { .statisticsValue {
text-align: right; 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;
}

13
src/webui/www/private/download.html

@ -7,7 +7,9 @@
<link rel="stylesheet" href="css/style.css" type="text/css" /> <link rel="stylesheet" href="css/style.css" type="text/css" />
<link rel="stylesheet" href="css/Window.css" type="text/css" /> <link rel="stylesheet" href="css/Window.css" type="text/css" />
<script src="scripts/lib/mootools-1.2-core-yc.js"></script> <script src="scripts/lib/mootools-1.2-core-yc.js"></script>
<script src="scripts/lib/mootools-1.2-more.js"></script>
<script src="scripts/download.js"></script> <script src="scripts/download.js"></script>
<script src="scripts/misc.js"></script>
</head> </head>
<body> <body>
@ -133,6 +135,17 @@
</div> </div>
</form> </form>
<script> <script>
var encodedUrls = new URI().getData('urls');
if (encodedUrls) {
var urls = [];
encodedUrls.split('|').each(function(url) {
urls.push(decodeURIComponent(url));
});
if (urls.length)
$('urls').set('value', urls.join("\n"));
}
var submitted = false; var submitted = false;
$('downloadForm').addEventListener("submit", function() { $('downloadForm').addEventListener("submit", function() {

10
src/webui/www/private/index.html

@ -66,7 +66,8 @@
<li><a id="showTopToolbarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Top Toolbar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Top Toolbar)QBT_TR[CONTEXT=MainWindow]</a></li> <li><a id="showTopToolbarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Top Toolbar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Top Toolbar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="showStatusBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]</a></li> <li><a id="showStatusBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="speedInBrowserTitleBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]</a></li> <li><a id="speedInBrowserTitleBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id=StatisticsLink><img class="MyMenuIcon" src="images/qbt-theme/view-statistics.svg" alt="QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]</a></li> <li class="divider"><a id="showSearchEngineLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Search Engine)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Search Engine)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="StatisticsLink"><img class="MyMenuIcon" src="images/qbt-theme/view-statistics.svg" alt="QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]</a></li>
</ul> </ul>
</li> </li>
<li> <li>
@ -99,6 +100,13 @@
<a id="bottomPrioButton"><img class="mochaToolButton" title="QBT_TR(Minimum Priority)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/go-bottom.svg" alt="QBT_TR(Minimum Priority)QBT_TR[CONTEXT=MainWindow]" width="24" height="24"/></a> <a id="bottomPrioButton"><img class="mochaToolButton" title="QBT_TR(Minimum Priority)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/go-bottom.svg" alt="QBT_TR(Minimum Priority)QBT_TR[CONTEXT=MainWindow]" width="24" height="24"/></a>
</span> </span>
<a id="preferencesButton" class="divider"><img class="mochaToolButton" title="QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog]" src="images/qbt-theme/configure.svg" alt="QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog]" width="24" height="24"/></a> <a id="preferencesButton" class="divider"><img class="mochaToolButton" title="QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog]" src="images/qbt-theme/configure.svg" alt="QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog]" width="24" height="24"/></a>
<div id="mainWindowTabs" class="toolbarTabs">
<ul id="mainWindowTabsList" class="tab-menu">
<li id="transfersTabLink" class="selected"><a class="tab">QBT_TR(Transfers)QBT_TR[CONTEXT=MainWindow]</a></li>
<li id="searchTabLink"><a class="tab">QBT_TR(Search)QBT_TR[CONTEXT=MainWindow]</a></li>
</ul>
<div class="clear"></div>
</div>
</div> </div>
</div> </div>
<div id="pageWrapper"> <div id="pageWrapper">

69
src/webui/www/private/installsearchplugin.html

@ -0,0 +1,69 @@
<style type="text/css">
#installSearchPluginContainer {
margin: 10px;
}
#installSearchPluginContainer button {
padding: 3px 20px;
}
#newPluginPath {
width: 100%;
line-height: 2em;
}
</style>
<div id="installSearchPluginContainer">
<h2>QBT_TR(Plugin path:)QBT_TR[CONTEXT=PluginSourceDlg]</h2>
<div>
<input type="text" id="newPluginPath" placeholder="QBT_TR(URL or local directory)QBT_TR[CONTEXT=PluginSourceDlg]" autocorrect="off" autocapitalize="none" />
<div style="margin-top: 10px; text-align: center;">
<button id="newPluginCancel" onclick="closeSearchWindow('installSearchPlugin');">QBT_TR(Cancel)QBT_TR[CONTEXT=PluginSourceDlg]</button>
<button id="newPluginOk" onclick="newPluginOk();">QBT_TR(Ok)QBT_TR[CONTEXT=PluginSourceDlg]</button></div>
</div>
</div>
</div>
<script>
'use strict';
var initInstallSearchPlugin = function() {
var installSearchPluginKeyboardEvents = new Keyboard({
defaultEventType: 'keydown',
events: {
'enter': function (e) {
// accept enter key as a click
new Event(e).stop();
var elem = e.event.srcElement;
if ((elem.id === "newPluginPath") || (elem.id === "newPluginOk"))
newPluginOk();
else if (elem.id === "newPluginCancel")
closeSearchWindow('installSearchPlugin');
}
}
});
installSearchPluginKeyboardEvents.activate();
$('newPluginPath').select();
};
var newPluginOk = function() {
var path = $("newPluginPath").get("value").trim();
if (path)
new Request({
url: 'api/v2/search/installPlugin',
noCache: true,
method: 'post',
data: {
sources: path,
},
onRequest: function(){
closeSearchWindow('installSearchPlugin');
}
}).send();
};
initInstallSearchPlugin();
</script>

155
src/webui/www/private/scripts/client.js

@ -24,6 +24,8 @@
torrentsTable = new TorrentsTable(); torrentsTable = new TorrentsTable();
torrentPeersTable = new TorrentPeersTable(); torrentPeersTable = new TorrentPeersTable();
searchResultsTable = new SearchResultsTable();
searchPluginsTable = new SearchPluginsTable();
var updatePropertiesPanel = function() {}; var updatePropertiesPanel = function() {};
@ -36,7 +38,8 @@ var updateTorrentFilesData = function() {};
var updateMainData = function() {}; var updateMainData = function() {};
var alternativeSpeedLimits = false; var alternativeSpeedLimits = false;
var queueing_enabled = true; var queueing_enabled = true;
var syncMainDataTimerPeriod = 1500; var serverSyncMainDataInterval = 1500;
var customSyncMainDataInterval = null;
var clipboardEvent; var clipboardEvent;
@ -66,6 +69,10 @@ function genHash(string) {
return hash; return hash;
} }
function getSyncMainDataInterval() {
return customSyncMainDataInterval ? customSyncMainDataInterval : serverSyncMainDataInterval;
}
window.addEvent('load', function() { window.addEvent('load', function() {
var saveColumnSizes = function() { var saveColumnSizes = function() {
@ -76,8 +83,9 @@ window.addEvent('load', function() {
}; };
window.addEvent('resize', function() { window.addEvent('resize', function() {
// Resizing might takes some time. // only save sizes if the columns are visible
saveColumnSizes.delay(200); if (!$("mainColumn").hasClass("invisible"))
saveColumnSizes.delay(200); // Resizing might takes some time.
}); });
/*MochaUI.Desktop = new MochaUI.Desktop(); /*MochaUI.Desktop = new MochaUI.Desktop();
@ -87,6 +95,7 @@ window.addEvent('load', function() {
});*/ });*/
MochaUI.Desktop.initialize(); MochaUI.Desktop.initialize();
var buildTransfersTab = function() {
var filt_w = localStorage.getItem('filters_width'); var filt_w = localStorage.getItem('filters_width');
if ($defined(filt_w)) if ($defined(filt_w))
filt_w = filt_w.toInt(); filt_w = filt_w.toInt();
@ -99,10 +108,27 @@ window.addEvent('load', function() {
width: filt_w, width: filt_w,
resizeLimit: [1, 300] resizeLimit: [1, 300]
}); });
new MochaUI.Column({ new MochaUI.Column({
id: 'mainColumn', id: 'mainColumn',
placement: 'main' 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) { setCategoryFilter = function(hash) {
selected_category = hash; selected_category = hash;
@ -184,6 +210,15 @@ window.addEvent('load', function() {
$('speedInBrowserTitleBarLink').firstChild.style.opacity = '0'; $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0';
// After showing/hiding the toolbar + status bar // 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(); MochaUI.Desktop.setDesktopSize();
var syncMainDataLastResponseId = 0; var syncMainDataLastResponseId = 0;
@ -391,7 +426,7 @@ window.addEvent('load', function() {
torrentsTable.reselectRows(torrentsTableSelectedRows); torrentsTable.reselectRows(torrentsTableSelectedRows);
} }
clearTimeout(syncMainDataTimer); clearTimeout(syncMainDataTimer);
syncMainDataTimer = syncMainData.delay(syncMainDataTimerPeriod); syncMainDataTimer = syncMainData.delay(getSyncMainDataInterval());
} }
}).send(); }).send();
}; };
@ -472,9 +507,7 @@ window.addEvent('load', function() {
updateAltSpeedIcon(alternativeSpeedLimits); updateAltSpeedIcon(alternativeSpeedLimits);
} }
syncMainDataTimerPeriod = serverState.refresh_interval; serverSyncMainDataInterval = Math.max(serverState.refresh_interval, 500);
if (syncMainDataTimerPeriod < 500)
syncMainDataTimerPeriod = 500;
}; };
var updateAltSpeedIcon = function(enabled) { var updateAltSpeedIcon = function(enabled) {
@ -543,8 +576,89 @@ window.addEvent('load', function() {
processServerState(); 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); $('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({ new MochaUI.Panel({
id: 'transferList', id: 'transferList',
title: 'Panel', title: 'Panel',
@ -658,6 +772,12 @@ window.addEvent('load', function() {
column: 'mainColumn', column: 'mainColumn',
height: prop_h height: prop_h
}); });
if (showSearchEngine) {
addMainWindowTabsEventListener();
addSearchPanel();
}
}); });
function closeWindows() { function closeWindows() {
@ -670,21 +790,18 @@ function setupCopyEventHandler() {
clipboardEvent = new ClipboardJS('.copyToClipboard', { clipboardEvent = new ClipboardJS('.copyToClipboard', {
text: function(trigger) { text: function(trigger) {
var textToCopy;
switch (trigger.id) { switch (trigger.id) {
case "CopyName": case "CopyName":
textToCopy = copyNameFN(); return copyNameFN();
break;
case "CopyMagnetLink": case "CopyMagnetLink":
textToCopy = copyMagnetLinkFN(); return copyMagnetLinkFN();
break;
case "CopyHash": case "CopyHash":
textToCopy = copyHashFN(); return copyHashFN();
break; case "copyDescriptionPageUrl":
return copySearchTorrentUrl();
default:
return "";
} }
return textToCopy;
} }
}); });
} }
@ -720,7 +837,7 @@ var loadTorrentPeersData = function() {
syncTorrentPeersLastResponseId = 0; syncTorrentPeersLastResponseId = 0;
torrentPeersTable.clear(); torrentPeersTable.clear();
clearTimeout(loadTorrentPeersTimer); clearTimeout(loadTorrentPeersTimer);
loadTorrentPeersTimer = loadTorrentPeersData.delay(syncMainDataTimerPeriod); loadTorrentPeersTimer = loadTorrentPeersData.delay(getSyncMainDataInterval());
return; return;
} }
var url = new URI('api/v2/sync/torrentPeers'); var url = new URI('api/v2/sync/torrentPeers');
@ -774,7 +891,7 @@ var loadTorrentPeersData = function() {
torrentPeersTable.clear(); torrentPeersTable.clear();
} }
clearTimeout(loadTorrentPeersTimer); clearTimeout(loadTorrentPeersTimer);
loadTorrentPeersTimer = loadTorrentPeersData.delay(syncMainDataTimerPeriod); loadTorrentPeersTimer = loadTorrentPeersData.delay(getSyncMainDataInterval());
} }
}).send(); }).send();
}; };

18
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');
}
});

164
src/webui/www/private/scripts/dynamicTable.js

@ -66,6 +66,8 @@ var DynamicTable = new Class({
$(this.dynamicTableDivId).addEvent('scroll', scrollFn); $(this.dynamicTableDivId).addEvent('scroll', scrollFn);
// if the table exists within a panel
if ($(this.dynamicTableDivId).getParent('.panel')) {
var resizeFn = function() { var resizeFn = function() {
var panel = $(this.dynamicTableDivId).getParent('.panel'); var panel = $(this.dynamicTableDivId).getParent('.panel');
var h = panel.getBoundingClientRect().height - $(this.dynamicTableFixedHeaderDivId).getBoundingClientRect().height; var h = panel.getBoundingClientRect().height - $(this.dynamicTableFixedHeaderDivId).getBoundingClientRect().height;
@ -99,6 +101,7 @@ var DynamicTable = new Class({
}.bind(this); }.bind(this);
setInterval(checkResizeFn, 500); setInterval(checkResizeFn, 500);
}
}, },
setupHeaderEvents: function() { 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");
}
});
/*************************************************************/ /*************************************************************/

12
src/webui/www/private/scripts/mocha-init.js

@ -59,12 +59,20 @@ initializeWindows = function() {
addClickEvent('download', function(e) { addClickEvent('download', function(e) {
new Event(e).stop(); new Event(e).stop();
showDownloadPage();
});
showDownloadPage = function(urls) {
var id = 'downloadPage'; var id = 'downloadPage';
var contentUrl = 'download.html';
if (urls && urls.length)
contentUrl += '?urls=' + urls.join("|");
new MochaUI.Window({ new MochaUI.Window({
id: id, id: id,
title: "QBT_TR(Download from URLs)QBT_TR[CONTEXT=downloadFromURL]", title: "QBT_TR(Download from URLs)QBT_TR[CONTEXT=downloadFromURL]",
loadMethod: 'iframe', loadMethod: 'iframe',
contentURL: 'download.html', contentURL: contentUrl,
addClass: 'windowFrame', // fixes iframe scrolling on iOS Safari addClass: 'windowFrame', // fixes iframe scrolling on iOS Safari
scrollbars: true, scrollbars: true,
maximizable: false, maximizable: false,
@ -78,7 +86,7 @@ initializeWindows = function() {
} }
}); });
updateMainData(); updateMainData();
}); };
addClickEvent('preferences', function(e) { addClickEvent('preferences', function(e) {
new Event(e).stop(); new Event(e).stop();

644
src/webui/www/private/search.html

@ -0,0 +1,644 @@
<style type="text/css">
#searchPattern {
width: 500px;
line-height: 2em;
padding: 1px 5px 1px 2em;
background-image: url("../images/qbt-theme/edit-find.svg");
background-repeat: no-repeat;
background-size: 1.5em;
background-position: left;
}
#categorySelect {
width: 150px;
height: 20px;
}
#pluginsSelect {
width: 150px;
height: 20px;
}
#startSearchButton {
width: 90px;
height: 20px;
}
#searchResultsNoPlugins {
height: calc(100% - 110px);
}
#searchResultsNoPlugins table {
height: 100%;
width: 100%;
text-align: center;
}
#searchResultsFilters {
height: 30px;
overflow: auto;
}
#searchInNameFilter {
width: 150px;
margin-right: 20px;
}
#searchMinSeedsFilter, #searchMaxSeedsFilter, #searchMinSizeFilter, #searchMaxSizeFilter {
width: 4em;
}
#downloadSearchTorrent, #openSearchTorrentDescription, #copyDescriptionPageUrl {
line-height: 1.5em;
}
#manageSearchPlugins {
line-height: 1.5em;
float: right;
}
</style>
<div id="searchResults">
<div style="overflow: hidden; height: 70px;">
<div style="margin: 20px 0; height: 30px;">
<input type="text" id="searchPattern" class="searchInputField" placeholder="QBT_TR(Search)QBT_TR[CONTEXT=SearchEngineWidget]" autocorrect="off" autocapitalize="none" />
<select id="categorySelect" class="searchInputField" onchange="categorySelected()"></select>
<select id="pluginsSelect" class="searchInputField" onchange="pluginSelected()"></select>
<button id="startSearchButton" class="searchInputField" onclick="startStopSearch()">QBT_TR(Search)QBT_TR[CONTEXT=SearchEngineWidget]</button>
</div>
</div>
<div id="searchResultsNoPlugins">
<table>
<tbody>
<tr>
<td>
There aren't any search plugins installed.<br/>Click the &quot;Search plugins...&quot; button at the bottom right of the window to install some.
</td>
</tr>
</tbody>
</table>
<span></span>
</div>
<div id="searchResultsFilters">
<input type="text" id="searchInNameFilter" placeholder="QBT_TR(Filter)QBT_TR[CONTEXT=SearchEngineWidget]" autocorrect="off" autocapitalize="none" />
<span>QBT_TR(Results (showing)QBT_TR[CONTEXT=SearchEngineWidget] <span id="numSearchResultsVisible" class="numSearchResults">0</span> QBT_TR(out of)QBT_TR[CONTEXT=SearchEngineWidget] <span id="numSearchResultsTotal" class="numSearchResults">0</span>):</span>
<div style="display: inline-block; float: right;">
<label for="searchInTorrentName" style="margin-left: 15px;">QBT_TR(Search in:)QBT_TR[CONTEXT=SearchEngineWidget]</label>
<select id="searchInTorrentName" onchange="searchInTorrentName()">
<option value="names">QBT_TR(Torrent names only)QBT_TR[CONTEXT=SearchEngineWidget]</option>
<option value="everywhere">QBT_TR(Everywhere)QBT_TR[CONTEXT=SearchEngineWidget]</option>
</select>
<span style="margin-left: 15px;">QBT_TR(Seeds:)QBT_TR[CONTEXT=SearchEngineWidget]</span>
<input type="number" min="0" max="1000" id="searchMinSeedsFilter" value="0" onchange="searchSeedsFilterChanged()">
<span>to</span>
<input type="number" min="0" max="1000" id="searchMaxSeedsFilter" value="0" onchange="searchSeedsFilterChanged()">
<span style="margin-left: 15px;">QBT_TR(Size:)QBT_TR[CONTEXT=SearchEngineWidget]</span>
<input type="number" min="0" max="1000" step=".01" value="0.00" id="searchMinSizeFilter" onchange="searchSizeFilterChanged()">
<select id="searchMinSizePrefix" onchange="searchSizeFilterPrefixChanged()">
<option value="0">QBT_TR(B)QBT_TR[CONTEXT=misc]</option>
<option value="1">QBT_TR(KiB)QBT_TR[CONTEXT=misc]</option>
<option value="2" selected>QBT_TR(MiB)QBT_TR[CONTEXT=misc]</option>
<option value="3">QBT_TR(GiB)QBT_TR[CONTEXT=misc]</option>
<option value="4">QBT_TR(TiB)QBT_TR[CONTEXT=misc]</option>
<option value="5">QBT_TR(PiB)QBT_TR[CONTEXT=misc]</option>
<option value="6">QBT_TR(EiB)QBT_TR[CONTEXT=misc]</option>
</select>
<span>to</span>
<input type="number" min="0" max="1000" step=".01" value="0.00" id="searchMaxSizeFilter" onchange="searchSizeFilterChanged()">
<select id="searchMaxSizePrefix" onchange="searchSizeFilterPrefixChanged()">
<option value="0">QBT_TR(B)QBT_TR[CONTEXT=misc]</option>
<option value="1">QBT_TR(KiB)QBT_TR[CONTEXT=misc]</option>
<option value="2" selected>QBT_TR(MiB)QBT_TR[CONTEXT=misc]</option>
<option value="3">QBT_TR(GiB)QBT_TR[CONTEXT=misc]</option>
<option value="4">QBT_TR(TiB)QBT_TR[CONTEXT=misc]</option>
<option value="5">QBT_TR(PiB)QBT_TR[CONTEXT=misc]</option>
<option value="6">QBT_TR(EiB)QBT_TR[CONTEXT=misc]</option>
</select>
</div>
</div>
<div id="searchResultsTableContainer">
<div id="searchResultsTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
<table class="dynamicTable unselectable" style="position:relative;">
<thead>
<tr class="dynamicTableHeader"></tr>
</thead>
</table>
</div>
<div id="searchResultsTableDiv" class="dynamicTableDiv">
<table class="dynamicTable unselectable">
<thead>
<tr class="dynamicTableHeader"></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div style="height: 30px; padding-top: 10px;">
<button id="downloadSearchTorrent" onclick="downloadSearchTorrent()">QBT_TR(Download)QBT_TR[CONTEXT=SearchEngineWidget]</button>
<button id="openSearchTorrentDescription" onclick="openSearchTorrentDescription()">QBT_TR(Go to description page)QBT_TR[CONTEXT=SearchEngineWidget]</button>
<button class="copyToClipboard" id="copyDescriptionPageUrl">QBT_TR(Copy description page URL)QBT_TR[CONTEXT=SearchEngineWidget]</button>
<button id="manageSearchPlugins" onclick="manageSearchPlugins()">QBT_TR(Search plugins...)QBT_TR[CONTEXT=SearchEngineWidget]</button>
</div>
</div>
<script>
'use strict';
var loadSearchResultsTimer;
var loadSearchPluginsTimer;
var searchResultsRowId = 0;
var searchRunning = false;
var requestCount = 0;
var searchPlugins = [];
var prevSearchPluginsResponse;
var searchPattern = "";
var searchFilterPattern = "";
var searchSeedsFilter = {
min: 0,
max: 0
};
var searchSizeFilter = {
min: 0.00,
minUnit: 2, // B = 0, KiB = 1, MiB = 2, GiB = 3, TiB = 4, PiB = 5, EiB = 6
max: 0.00,
maxUnit: 3
};
var prevNameFilterValue;
var selectedCategory = "QBT_TR(All categories)QBT_TR[CONTEXT=SearchEngineWidget]";
var selectedPlugin = "all";
var prevSelectedPlugin;
var activeSearchId = null;
var initSearchTab = function() {
// load "Search in" preference from local storage
$('searchInTorrentName').set('value', (localStorage.getItem('search_in_filter') === "names") ? "names" : "everywhere");
searchResultsTable.setup('searchResultsTableDiv', 'searchResultsTableFixedHeaderDiv', null);
getPlugins();
var searchInNameFilterTimer = null;
// listen for changes to searchInNameFilter
$('searchInNameFilter').addEvent('input', function() {
var value = $('searchInNameFilter').get("value");
if (value !== prevNameFilterValue) {
prevNameFilterValue = value;
clearTimeout(searchInNameFilterTimer);
searchInNameFilterTimer = setTimeout(function() {
searchFilterPattern = value;
searchFilterChanged();
}, 400);
}
});
var searchKeyboardEvents = new Keyboard({
defaultEventType: 'keydown',
events: {
'enter': function (e) {
// accept enter key as a click
new Event(e).stop();
var elem = e.event.srcElement;
if (elem.className.contains("searchInputField")) {
$('startSearchButton').click();
return;
}
switch (elem.id) {
case "downloadSearchTorrent":
downloadSearchTorrent();
break;
case "openSearchTorrentDescription":
openSearchTorrentDescription();
break;
case "copyDescriptionPageUrl":
copySearchTorrentUrl();
break;
case "manageSearchPlugins":
manageSearchPlugins();
break;
}
}
}
});
searchKeyboardEvents.activate();
};
var startSearch = function(pattern, category, plugins) {
clearTimeout(loadSearchResultsTimer);
searchResultsTable.clear();
$('numSearchResultsVisible').set('html', searchResultsTable.getFilteredAndSortedRows().length);
$('numSearchResultsTotal').set('html', searchResultsTable.getRowIds().length);
searchResultsRowId = 0;
requestCount = 0;
var url = new URI('api/v2/search/start');
new Request.JSON({
url: url,
noCache: true,
method: 'post',
data: {
pattern: pattern,
category: category,
plugins: plugins
},
onSuccess: function(response) {
$('startSearchButton').set('text', 'QBT_TR(Stop)QBT_TR[CONTEXT=SearchEngineWidget]');
searchRunning = true;
activeSearchId = response.id;
updateSearchResultsData();
}
}).send();
};
var stopSearch = function() {
var url = new URI('api/v2/search/stop');
new Request({
url: url,
noCache: true,
method: 'post',
data: {
id: activeSearchId
},
onSuccess: function(response) {
resetSearchState();
}
}).send();
};
var startStopSearch = function() {
if (!searchRunning || !activeSearchId) {
var pattern = $('searchPattern').getProperty('value').trim();
var category = $('categorySelect').getProperty('value');
var plugins = $('pluginsSelect').getProperty('value');
if (!pattern || !category || !plugins) return;
if (category === "QBT_TR(All categories)QBT_TR[CONTEXT=SearchEngineWidget]")
category = "all";
resetFilters();
searchPattern = pattern;
startSearch(pattern, category, plugins);
}
else {
stopSearch();
}
};
var openSearchTorrentDescription = function() {
searchResultsTable.selectedRowsIds().each(function(rowId) {
window.open(searchResultsTable.rows.get(rowId).full_data.descrLink, "_blank");
});
};
var copySearchTorrentUrl = function() {
var urls = [];
searchResultsTable.selectedRowsIds().each(function(rowId) {
urls.push(searchResultsTable.rows.get(rowId).full_data.descrLink);
});
return urls.join("\n");
};
var downloadSearchTorrent = function() {
var urls = [];
searchResultsTable.selectedRowsIds().each(function(rowId) {
urls.push(encodeURIComponent(searchResultsTable.rows.get(rowId).full_data.fileUrl));
});
// only proceed if at least 1 row was selected
if (!urls.length) return;
showDownloadPage(urls);
};
var manageSearchPlugins = function() {
var id = 'searchPlugins';
if (!$(id))
new MochaUI.Window({
id: id,
title: "QBT_TR(Search plugins)QBT_TR[CONTEXT=PluginSelectDlg]",
loadMethod: 'xhr',
contentURL: 'searchplugins.html',
scrollbars: false,
maximizable: false,
paddingVertical: 0,
paddingHorizontal: 0,
width: loadWindowWidth(id, 600),
height: loadWindowHeight(id, 360),
onResize: function() {
saveWindowSize(id);
},
onBeforeBuild: function() {
loadSearchPlugins();
},
onClose: function() {
clearTimeout(loadSearchPluginsTimer);
}
});
};
var loadSearchPlugins = function() {
getPlugins();
loadSearchPluginsTimer = loadSearchPlugins.delay(2000);
};
var categorySelected = function() {
selectedCategory = $("categorySelect").get("value");
};
var pluginSelected = function() {
selectedPlugin = $("pluginsSelect").get("value");
if (selectedPlugin !== prevSelectedPlugin) {
prevSelectedPlugin = selectedPlugin;
getSearchCategories();
}
};
var reselectCategory = function() {
for (var i = 0; i < $("categorySelect").options.length; ++i)
if ($("categorySelect").options[i].get("value") === selectedCategory)
$("categorySelect").options[i].selected = true;
categorySelected();
};
var reselectPlugin = function() {
for (var i = 0; i < $("pluginsSelect").options.length; ++i)
if ($("pluginsSelect").options[i].get("value") === selectedPlugin)
$("pluginsSelect").options[i].selected = true;
pluginSelected();
};
var resetSearchState = function() {
clearTimeout(loadSearchResultsTimer);
$('startSearchButton').set('text', 'QBT_TR(Search)QBT_TR[CONTEXT=SearchEngineWidget]');
searchResultsRowId = 0;
searchRunning = false;
activeSearchId = null;
};
var getSearchCategories = function() {
var populateCategorySelect = function(categories) {
var categoryHtml = [];
categories.each(function(category) {
categoryHtml.push("<option>" + category + "</option>");
});
// first category is "All Categories"
if (categoryHtml.length > 1)
// add separator
categoryHtml.splice(1, 0, "<option disabled>──────────</option>");
$('categorySelect').set('html', categoryHtml.join(""));
};
var selectedPlugin = $('pluginsSelect').get("value");
if ((selectedPlugin === "all") || (selectedPlugin === "enabled")) {
var url = new URI('api/v2/search/categories');
url.setData('name', selectedPlugin);
new Request.JSON({
url: url,
noCache: true,
method: 'get',
onSuccess: function(response) {
populateCategorySelect(response);
}
}).send();
}
else {
var plugins = ["QBT_TR(All categories)QBT_TR[CONTEXT=SearchEngineWidget]"];
var plugin = getPlugin(selectedPlugin);
if (plugin !== null)
plugins = plugins.concat(plugin.supportedCategories);
populateCategorySelect(plugins);
}
reselectCategory();
};
var getPlugins = function() {
new Request.JSON({
url: new URI('api/v2/search/plugins'),
noCache: true,
method: 'get',
onSuccess: function(response) {
if (response !== prevSearchPluginsResponse) {
prevSearchPluginsResponse = response;
searchPlugins = response;
var pluginsHtml = [];
pluginsHtml.push('<option value="enabled">QBT_TR(Only enabled)QBT_TR[CONTEXT=SearchEngineWidget]</option>');
pluginsHtml.push('<option value="all">QBT_TR(All plugins)QBT_TR[CONTEXT=SearchEngineWidget]</option>');
var searchPluginsEmpty = (searchPlugins.length === 0);
if (searchPluginsEmpty) {
$('searchResultsNoPlugins').style.display = "block";
$('searchResultsFilters').style.display = "none";
$('searchResultsTableContainer').style.display = "none";
}
else {
$('searchResultsNoPlugins').style.display = "none";
$('searchResultsFilters').style.display = "block";
$('searchResultsTableContainer').style.display = "block";
// sort plugins alphabetically
var allPlugins = searchPlugins.sort(function(pluginA, pluginB){
var a = pluginA.fullName.toLowerCase();
var b = pluginB.fullName.toLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
allPlugins.each(function(plugin) {
if (plugin.enabled === true)
pluginsHtml.push("<option value='" + escapeHtml(plugin.name) + "'>" + escapeHtml(plugin.fullName) + "</option>");
});
if (pluginsHtml.length > 2)
pluginsHtml.splice(2, 0, "<option disabled>──────────</option>");
}
$('pluginsSelect').set('html', pluginsHtml.join(""));
$('searchPattern').setProperty('disabled', searchPluginsEmpty);
$('categorySelect').setProperty('disabled', searchPluginsEmpty);
$('pluginsSelect').setProperty('disabled', searchPluginsEmpty);
$('startSearchButton').setProperty('disabled', searchPluginsEmpty);
$('downloadSearchTorrent').setProperty('disabled', searchPluginsEmpty);
$('openSearchTorrentDescription').setProperty('disabled', searchPluginsEmpty);
$('copyDescriptionPageUrl').setProperty('disabled', searchPluginsEmpty);
if (typeof updateSearchPluginsTable === "function")
updateSearchPluginsTable();
reselectPlugin();
}
}
}).send();
};
var getPlugin = function(name) {
for (var i = 0; i < searchPlugins.length; ++i)
if (searchPlugins[i].name === name)
return searchPlugins[i];
return null;
};
var searchInTorrentName = function() {
if ($('searchInTorrentName').get('value') === "names")
localStorage.setItem('search_in_filter', "names");
else
localStorage.setItem('search_in_filter', "everywhere");
searchFilterChanged();
};
var resetFilters = function() {
// reset filters
$('searchMinSeedsFilter').set('value', '0');
$('searchMaxSeedsFilter').set('value', '0');
$('searchMinSizeFilter').set('value', '0.00');
$('searchMinSizePrefix').set('value', '2'); // MiB
$('searchMaxSizeFilter').set('value', '0.00');
$('searchMaxSizePrefix').set('value', '3'); // GiB
};
var searchSeedsFilterChanged = function() {
searchSeedsFilter.min = $('searchMinSeedsFilter').get('value');
searchSeedsFilter.max = $('searchMaxSeedsFilter').get('value');
searchFilterChanged();
};
var searchSizeFilterChanged = function() {
searchSizeFilter.min = $('searchMinSizeFilter').get('value');
searchSizeFilter.minUnit = $('searchMinSizePrefix').get('value');
searchSizeFilter.max = $('searchMaxSizeFilter').get('value');
searchSizeFilter.maxUnit = $('searchMaxSizePrefix').get('value');
searchFilterChanged();
};
var searchSizeFilterPrefixChanged = function() {
if (($('searchMinSizeFilter').get('value') != 0) || ($('searchMaxSizeFilter').get('value') != 0))
searchSizeFilterChanged();
};
var searchFilterChanged = function() {
searchResultsTable.updateTable();
$('numSearchResultsVisible').set('html', searchResultsTable.getFilteredAndSortedRows().length);
};
var setupSearchTableEvents = function(enable) {
if (enable)
$$(".searchTableRow").each(function(target) {
target.addEventListener('dblclick', downloadSearchTorrent, false);
});
else
$$(".searchTableRow").each(function(target) {
target.removeEventListener('dblclick', downloadSearchTorrent, false);
});
};
var loadSearchResultsData = function() {
var maxResults = 500;
var url = new URI('api/v2/search/results');
new Request.JSON({
url: url,
noCache: true,
method: 'post',
data: {
id: activeSearchId,
limit: maxResults,
offset: searchResultsRowId
},
onFailure: function(response) {
if (response.status === 400) {
// bad params. search id is invalid
resetSearchState();
}
else {
clearTimeout(loadSearchResultsTimer);
loadSearchResultsTimer = loadSearchResultsData.delay(3000);
}
},
onSuccess: function(response) {
$('error_div').set('html', '');
// check if user stopped the search prior to receiving the response
if (!searchRunning) {
clearTimeout(loadSearchResultsTimer);
searchResultsRowId = 0;
return;
}
if (response) {
setupSearchTableEvents(false);
if (response.results) {
var results = response.results;
for (var i = 0; i < results.length; ++i) {
var result = results[i];
var row = {
rowId: searchResultsRowId,
descrLink: result.descrLink,
fileName: result.fileName,
fileSize: result.fileSize,
fileUrl: result.fileUrl,
nbLeechers: result.nbLeechers,
nbSeeders: result.nbSeeders,
siteUrl: result.siteUrl,
};
searchResultsTable.updateRowData(row);
++searchResultsRowId;
}
$('numSearchResultsVisible').set('html', searchResultsTable.getFilteredAndSortedRows().length);
$('numSearchResultsTotal').set('html', searchResultsTable.getRowIds().length);
}
searchResultsTable.updateTable();
searchResultsTable.altRow();
if ((response.status === "Stopped") && (searchResultsRowId >= response.total)) {
resetSearchState();
return;
}
setupSearchTableEvents(true);
}
var timeout = 1000;
if (requestCount > 30)
timeout = 3000;
else if (requestCount > 10)
timeout = 2000;
clearTimeout(loadSearchResultsTimer);
loadSearchResultsTimer = loadSearchResultsData.delay(timeout);
++requestCount;
}
}).send();
};
var updateSearchResultsData = function() {
clearTimeout(loadSearchResultsTimer);
loadSearchResultsTimer = loadSearchResultsData.delay(500);
};
initSearchTab();
</script>

219
src/webui/www/private/searchplugins.html

@ -0,0 +1,219 @@
<style type="text/css">
#searchPluginsContainer {
height: calc(100% - 20px);
margin: 10px;
overflow: auto;
}
#searchPluginsContainer button {
padding: 2px;
}
#searchPluginsContainer span {
display: block;
margin-top: 10px;
}
#searchPluginsTable {
width: 100%;
height: calc(100% - 150px);
-moz-height: calc(100% - 150px);
-webkit-height: calc(100% - 150px);
}
#searchPluginsTable .dynamicTable {
width: 100%;
}
#searchPluginsTableDiv {
height: calc(100% - 26px);
-moz-height: calc(100% - 26px);
-webkit-height: calc(100% - 26px);
}
#dynamicTableFixedHeaderDiv {
height: 26px;
}
#searchPlugins_content {
height: 100%;
}
</style>
<div id="searchPluginsContainer">
<h2>QBT_TR(Installed search plugins:)QBT_TR[CONTEXT=PluginSelectDlg]</h2>
<div id="searchPluginsTable">
<div id="searchPluginsTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
<table class="dynamicTable unselectable" style="position:relative;">
<thead>
<tr class="dynamicTableHeader" id="searchPluginsTableFixedHeaderRow"></tr>
</thead>
</table>
</div>
<div id="searchPluginsTableDiv" class="dynamicTableDiv">
<table class="dynamicTable unselectable">
<thead>
<tr class="dynamicTableHeader"></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<span>QBT_TR(Warning: Be sure to comply with your country's copyright laws when downloading torrents from any of these search engines.)QBT_TR[CONTEXT=PluginSelectDlg]</span>
<span style="font-style: italic;">QBT_TR(You can get new search engine plugins here:)QBT_TR[CONTEXT=PluginSelectDlg] <a href="http://plugins.qbittorrent.org" target="_blank">http://plugins.qbittorrent.org</a></span>
<div style="width: 100%; margin-top: 10px;">
<button style="width: 33%; line-height: 1.4em;" onclick="installPlugin();">QBT_TR(Install new plugin)QBT_TR[CONTEXT=PluginSelectDlg]</button>
<button style="width: 33%; line-height: 1.4em;" onclick="checkForUpdates();">QBT_TR(Check for updates)QBT_TR[CONTEXT=PluginSelectDlg]</button>
<button style="width: 32%; line-height: 1.4em;" onclick="closeSearchWindow('searchPlugins');">QBT_TR(Close)QBT_TR[CONTEXT=PluginSelectDlg]</button>
</div>
</div>
<ul id="searchPluginsTableMenu" class="contextMenu">
<li><a href="#Enabled"><img src="images/qbt-theme/checked.svg" alt="QBT_TR(Enabled)QBT_TR[CONTEXT=PluginSelectDlg]"/> QBT_TR(Enabled)QBT_TR[CONTEXT=PluginSelectDlg]</a></li>
<li class="separator"><a href="#Uninstall"><img src="images/qbt-theme/list-remove.svg" alt="QBT_TR(Uninstall)QBT_TR[CONTEXT=PluginSelectDlg]"/> QBT_TR(Uninstall)QBT_TR[CONTEXT=PluginSelectDlg]</a></li>
</ul>
<script>
'use strict';
var searchPluginsTableContextMenu;
var prevOffsetLeft;
var prevOffsetTop;
var initSearchPlugins = function() {
searchPluginsTableContextMenu = new SearchPluginsTableContextMenu({
targets: '.searchPluginsTableRow',
menu: 'searchPluginsTableMenu',
actions: {
Enabled: enablePlugin,
Uninstall: uninstallPlugin
},
offsets : calculateContextMenuOffsets()
});
searchPluginsTable.setup('searchPluginsTableDiv', 'searchPluginsTableFixedHeaderDiv', searchPluginsTableContextMenu);
updateSearchPluginsTable();
};
var closeSearchWindow = function(id) {
window.parent.MochaUI.closeWindow(window.parent.$(id));
};
var installPlugin = function(path) {
new MochaUI.Window({
id: 'installSearchPlugin',
title: "QBT_TR(Install plugin)QBT_TR[CONTEXT=PluginSourceDlg]",
loadMethod: 'xhr',
contentURL: 'installsearchplugin.html',
scrollbars: false,
resizable: false,
maximizable: false,
paddingVertical: 0,
paddingHorizontal: 0,
width: 500,
height: 120
});
};
var uninstallPlugin = function() {
var plugins = searchPluginsTable.selectedRowsIds().join('|');
var url = new URI('api/v2/search/uninstallPlugin');
new Request({
url: url,
noCache: true,
method: 'post',
data: {
names: plugins,
}
}).send();
};
var enablePlugin = function() {
var plugins = searchPluginsTable.selectedRowsIds();
var enable = true;
if (plugins && plugins.length)
enable = !getPlugin(plugins[0]).enabled;
var url = new URI('api/v2/search/enablePlugin');
new Request({
url: url,
noCache: true,
method: 'post',
data: {
names: plugins.join('|'),
enable: enable
}
}).send();
};
var checkForUpdates = function() {
var url = new URI('api/v2/search/updatePlugins');
new Request({
url: url,
noCache: true,
method: 'post'
}).send();
};
var calculateContextMenuOffsets = function() {
prevOffsetLeft = document.getElementById("searchPlugins").getBoundingClientRect().left;
prevOffsetTop = document.getElementById("searchPlugins").getBoundingClientRect().top;
return {
x: -(prevOffsetLeft + 20),
y: -(prevOffsetTop + 2)
};
};
var updateSearchPluginsTableContextMenuOffset = function() {
// only re-calculate if window has moved
if ((prevOffsetLeft !== document.getElementById("searchPlugins").getBoundingClientRect().left) || (prevOffsetTop !== document.getElementById("searchPlugins").getBoundingClientRect().top))
searchPluginsTableContextMenu.options.offsets = calculateContextMenuOffsets();
};
var setupSearchPluginTableEvents = function(enable) {
if (enable)
$$(".searchPluginsTableRow").each(function(target) {
target.addEventListener('dblclick', enablePlugin, false);
target.addEventListener('contextmenu', updateSearchPluginsTableContextMenuOffset, true);
});
else
$$(".searchPluginsTableRow").each(function(target) {
target.removeEventListener('dblclick', enablePlugin, false);
target.removeEventListener('contextmenu', updateSearchPluginsTableContextMenuOffset, true);
});
};
var updateSearchPluginsTable = function() {
// clear event listeners
setupSearchPluginTableEvents(false);
var oldPlugins = Object.keys(searchPluginsTable.rows);
// remove old rows from the table
for (var i = 0; i < oldPlugins.length; ++i) {
var found = false;
for (var j = 0; j < searchPlugins.length; ++j) {
if (searchPlugins[j].name === oldPlugins[i]) {
found = true;
break;
}
}
if (!found)
searchPluginsTable.removeRow(oldPlugins[i]);
}
for (var i = 0; i < searchPlugins.length; ++i) {
searchPlugins[i].rowId = searchPlugins[i].name;
searchPluginsTable.updateRowData(searchPlugins[i]);
}
searchPluginsTable.updateTable();
searchPluginsTable.altRow();
// add event listeners
setupSearchPluginTableEvents(true);
};
initSearchPlugins();
</script>

3
src/webui/www/webui.qrc

@ -13,12 +13,15 @@
<file>private/downloadlimit.html</file> <file>private/downloadlimit.html</file>
<file>private/filters.html</file> <file>private/filters.html</file>
<file>private/index.html</file> <file>private/index.html</file>
<file>private/installsearchplugin.html</file>
<file>private/newcategory.html</file> <file>private/newcategory.html</file>
<file>private/preferences.html</file> <file>private/preferences.html</file>
<file>private/preferences_content.html</file> <file>private/preferences_content.html</file>
<file>private/properties.html</file> <file>private/properties.html</file>
<file>private/properties_content.html</file> <file>private/properties_content.html</file>
<file>private/rename.html</file> <file>private/rename.html</file>
<file>private/search.html</file>
<file>private/searchplugins.html</file>
<file>private/scripts/client.js</file> <file>private/scripts/client.js</file>
<file>private/scripts/contextmenu.js</file> <file>private/scripts/contextmenu.js</file>
<file>private/scripts/download.js</file> <file>private/scripts/download.js</file>

Loading…
Cancel
Save