mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-07-06 05:01:25 -07:00
WebUI: Add log viewer
The javascript implementation of multi-select menu is from the source https://github.com/PhilippeMarcMeyer/vanillaSelectBox. It is licensed under the MIT License. Some minor fixes is made to pass the lint. Co-authored-by: brvphoenix <30111323+brvphoenix@users.noreply.github.com> Co-authored-by: ttyS3 <ttys3.rust@gmail.com> PR #18290.
This commit is contained in:
parent
2b20d5b260
commit
0d376e7fd6
12 changed files with 2372 additions and 2 deletions
|
@ -34,6 +34,7 @@ repos:
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
compile_commands.json |
|
compile_commands.json |
|
||||||
|
src/webui/www/private/css/lib/.* |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/scripts/lib/.*
|
||||||
)$
|
)$
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ repos:
|
||||||
(?x)^(
|
(?x)^(
|
||||||
compile_commands.json |
|
compile_commands.json |
|
||||||
configure |
|
configure |
|
||||||
|
src/webui/www/private/css/lib/.* |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/scripts/lib/.*
|
||||||
)$
|
)$
|
||||||
exclude_types:
|
exclude_types:
|
||||||
|
@ -53,6 +55,7 @@ repos:
|
||||||
name: Check trailing whitespaces
|
name: Check trailing whitespaces
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
|
src/webui/www/private/css/lib/.* |
|
||||||
src/webui/www/private/scripts/lib/.*
|
src/webui/www/private/scripts/lib/.*
|
||||||
)$
|
)$
|
||||||
exclude_types:
|
exclude_types:
|
||||||
|
|
4
AUTHORS
4
AUTHORS
|
@ -29,6 +29,10 @@ Code from other projects:
|
||||||
copyright: Dan Haim <negativeiq@users.sourceforge.net>
|
copyright: Dan Haim <negativeiq@users.sourceforge.net>
|
||||||
license: BSD
|
license: BSD
|
||||||
|
|
||||||
|
* files src/webui/www/private/css/lib/vanillaSelectBox.css src/webui/www/private/scripts/lib/vanillaSelectBox.js
|
||||||
|
copyright: Philippe Meyer <pmg.meyer@gmail.com>
|
||||||
|
license: MIT
|
||||||
|
|
||||||
Images Authors:
|
Images Authors:
|
||||||
* files: src/icons/qbittorrent-tray.svg
|
* files: src/icons/qbittorrent-tray.svg
|
||||||
copyright: Provided by HVS <hvs linuxmail org> (raster first proposal) and Atif Afzal(@atfzl github) <atif5801@gmail.com> (vectorized and modified)
|
copyright: Provided by HVS <hvs linuxmail org> (raster first proposal) and Atif Afzal(@atfzl github) <atif5801@gmail.com> (vectorized and modified)
|
||||||
|
|
1
src/webui/www/.prettierignore
Normal file
1
src/webui/www/.prettierignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
private/css/lib/*.css
|
|
@ -3,6 +3,7 @@
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"stylelint-order"
|
"stylelint-order"
|
||||||
],
|
],
|
||||||
|
"ignoreFiles": ["private/css/lib/*.css"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"color-hex-length": null,
|
"color-hex-length": null,
|
||||||
"comment-empty-line-before": null,
|
"comment-empty-line-before": null,
|
||||||
|
|
271
src/webui/www/private/css/lib/vanillaSelectBox.css
Normal file
271
src/webui/www/private/css/lib/vanillaSelectBox.css
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
.hidden-search {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
li[data-parent].closed{
|
||||||
|
display:none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
li[data-parent].open:not(.hidden-search){
|
||||||
|
display:block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu{
|
||||||
|
cursor:pointer;
|
||||||
|
z-index:1000;
|
||||||
|
display:block;
|
||||||
|
visibility: hidden;
|
||||||
|
position:absolute;/*Don't change*/
|
||||||
|
border:1px solid #B2B2B2;
|
||||||
|
background-color: #fff;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 1px solid rgba(0,0,0,.15);
|
||||||
|
box-shadow: 0 6px 12px rgba(0,0,0,.175);
|
||||||
|
border-radius:4px;
|
||||||
|
font-size : 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-js-search-zone{
|
||||||
|
position:absolute;/*Don't change*/
|
||||||
|
z-index:1001;
|
||||||
|
width: 80%;
|
||||||
|
min-height:1.8em;
|
||||||
|
padding: 2px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-js-search-zone input{
|
||||||
|
border: 1px solid grey;
|
||||||
|
margin-left: 2px;
|
||||||
|
width: 96%;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 25px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-main{
|
||||||
|
position: relative;/*Don't change*/
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu li:hover {
|
||||||
|
background: linear-gradient(#f5f5f5, #e8e8e8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu ul{
|
||||||
|
user-select:none;
|
||||||
|
list-style:none;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin:0px;
|
||||||
|
margin-top:4px;
|
||||||
|
padding-left:10px;
|
||||||
|
padding-right:10px;
|
||||||
|
padding-bottom:3px;
|
||||||
|
color: #333;
|
||||||
|
cursor:pointer;
|
||||||
|
overflow-y:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.disabled{
|
||||||
|
cursor:not-allowed;
|
||||||
|
opacity:0.3;
|
||||||
|
background-color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.overflow{
|
||||||
|
cursor:not-allowed;
|
||||||
|
opacity:0.3;
|
||||||
|
background-color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.short{
|
||||||
|
overflow:hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-main button{
|
||||||
|
min-width: 120px;
|
||||||
|
border-radius: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
z-index: 1;
|
||||||
|
color: #333;
|
||||||
|
background: white !important;
|
||||||
|
border: 1px solid #999 !important;
|
||||||
|
line-height:20px;
|
||||||
|
font-size:14px;
|
||||||
|
padding:6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-main button.disabled{
|
||||||
|
cursor:not-allowed;
|
||||||
|
opacity:0.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-main .title {
|
||||||
|
margin-right: 6px;
|
||||||
|
user-select:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-main li:hover {
|
||||||
|
background: linear-gradient(#f5f5f5, #e8e8e8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-main ul{
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu li {
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #fff;
|
||||||
|
min-height:1.4em;
|
||||||
|
padding: 0.2em 2em 0.2em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu li.grouped-option b {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-left:10px;
|
||||||
|
transform: translate(-18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu li.grouped-option.open span {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: inherit;
|
||||||
|
margin-top:-2px;
|
||||||
|
height: 8px;
|
||||||
|
width: 8px;
|
||||||
|
transform: translate(-38px) rotate(45deg);
|
||||||
|
border-bottom: 3px solid black;
|
||||||
|
border-right: 3px solid black;
|
||||||
|
border-radius:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu li.grouped-option.closed span {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: inherit;
|
||||||
|
height: 8px;
|
||||||
|
width: 8px;
|
||||||
|
transform: translate(-38px) rotate(-45deg);
|
||||||
|
border-bottom: 3px solid black;
|
||||||
|
border-right: 3px solid black;
|
||||||
|
border-radius:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu li.grouped-option i {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: inherit;
|
||||||
|
float:left;
|
||||||
|
font-weight:bold;
|
||||||
|
margin-left:22px;
|
||||||
|
margin-right:2px;
|
||||||
|
height: 11px;
|
||||||
|
width: 8px;
|
||||||
|
border : 1px solid;
|
||||||
|
border-radius : 3px;
|
||||||
|
padding: 1px 3px 2px 3px;
|
||||||
|
margin-top:0px;
|
||||||
|
color:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu li.grouped-option.checked i::after {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
font-size: inherit;
|
||||||
|
color: #333;
|
||||||
|
float:left;
|
||||||
|
margin-left:0px;
|
||||||
|
display: inline-block;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
height: 8px;
|
||||||
|
width: 5px;
|
||||||
|
border-bottom: 3px solid black;
|
||||||
|
border-right: 3px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu :not(.multi) li.active {
|
||||||
|
margin-left:7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu :not(.multi) li.active::before {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
font-size: inherit;
|
||||||
|
margin-left:-18px;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
height: 10px;
|
||||||
|
width: 5px;
|
||||||
|
border-bottom: 3px solid black;
|
||||||
|
border-right: 3px solid black;
|
||||||
|
border-radius:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu .multi li {
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #fff;
|
||||||
|
min-height:1.4em;
|
||||||
|
padding: 0.2em 2em 0.2em 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu .multi li.grouped-option {
|
||||||
|
font-size: 15px;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.vsb-menu .multi li.grouped-option:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
color:rgb(52, 31, 112);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu .multi li:not(.grouped-option)::before{
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
font-size: inherit;
|
||||||
|
float:left;
|
||||||
|
font-weight:bold;
|
||||||
|
margin-left:-22px;
|
||||||
|
margin-right:2px;
|
||||||
|
border : 1px solid;
|
||||||
|
border-radius : 3px;
|
||||||
|
padding : 7px;
|
||||||
|
margin-top:0px;
|
||||||
|
color:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-menu .multi li:not(.grouped-option).active::after {
|
||||||
|
content: "";
|
||||||
|
display: inline-block;
|
||||||
|
font-size: inherit;
|
||||||
|
color: #333;
|
||||||
|
float:left;
|
||||||
|
margin-left:-18px;
|
||||||
|
display: inline-block;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
margin-top:1px;
|
||||||
|
height: 8px;
|
||||||
|
width: 5px;
|
||||||
|
border-bottom: 3px solid black;
|
||||||
|
border-right: 3px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caret {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
margin-left: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-top: 4px dashed;
|
||||||
|
border-top: 4px solid;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
li[data-parent]{
|
||||||
|
padding-left: 50px !important;
|
||||||
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
<li><a id="speedInBrowserTitleBarLink"><img class="MyMenuIcon" src="images/checked-completed.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/checked-completed.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="showSearchEngineLink"><img class="MyMenuIcon" src="images/checked-completed.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="showSearchEngineLink"><img class="MyMenuIcon" src="images/checked-completed.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><a id="showRssReaderLink"><img class="MyMenuIcon" src="images/checked-completed.svg" alt="QBT_TR(RSS)QBT_TR[CONTEXT=MainWindow]" width="16" height="16" />QBT_TR(RSS Reader)QBT_TR[CONTEXT=MainWindow]</a></li>
|
<li><a id="showRssReaderLink"><img class="MyMenuIcon" src="images/checked-completed.svg" alt="QBT_TR(RSS)QBT_TR[CONTEXT=MainWindow]" width="16" height="16" />QBT_TR(RSS Reader)QBT_TR[CONTEXT=MainWindow]</a></li>
|
||||||
|
<li><a id="showLogViewerLink"><img class="MyMenuIcon" src="images/checked-completed.svg" alt="QBT_TR(Log)QBT_TR[CONTEXT=MainWindow]" width="16" height="16" />QBT_TR(Log)QBT_TR[CONTEXT=MainWindow]</a></li>
|
||||||
<li class="divider"><a id="StatisticsLink"><img class="MyMenuIcon" src="images/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="StatisticsLink"><img class="MyMenuIcon" src="images/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>
|
||||||
|
@ -116,6 +117,7 @@
|
||||||
<li id="transfersTabLink" class="selected"><a class="tab">QBT_TR(Transfers)QBT_TR[CONTEXT=MainWindow]</a></li>
|
<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>
|
<li id="searchTabLink"><a class="tab">QBT_TR(Search)QBT_TR[CONTEXT=MainWindow]</a></li>
|
||||||
<li id="rssTabLink"><a class="tab">QBT_TR(RSS)QBT_TR[CONTEXT=MainWindow]</a></li>
|
<li id="rssTabLink"><a class="tab">QBT_TR(RSS)QBT_TR[CONTEXT=MainWindow]</a></li>
|
||||||
|
<li id="logTabLink"><a class="tab">QBT_TR(Execution Log)QBT_TR[CONTEXT=MainWindow]</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="clear"></div>
|
<div class="clear"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -35,6 +35,7 @@ let serverSyncMainDataInterval = 1500;
|
||||||
let customSyncMainDataInterval = null;
|
let customSyncMainDataInterval = null;
|
||||||
let searchTabInitialized = false;
|
let searchTabInitialized = false;
|
||||||
let rssTabInitialized = false;
|
let rssTabInitialized = false;
|
||||||
|
let logTabInitialized = false;
|
||||||
|
|
||||||
let syncRequestInProgress = false;
|
let syncRequestInProgress = false;
|
||||||
|
|
||||||
|
@ -190,9 +191,21 @@ window.addEvent('load', function() {
|
||||||
$("rssTabColumn").addClass("invisible");
|
$("rssTabColumn").addClass("invisible");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const buildLogTab = function() {
|
||||||
|
new MochaUI.Column({
|
||||||
|
id: 'logTabColumn',
|
||||||
|
placement: 'main',
|
||||||
|
width: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// start off hidden
|
||||||
|
$('logTabColumn').addClass('invisible');
|
||||||
|
};
|
||||||
|
|
||||||
buildTransfersTab();
|
buildTransfersTab();
|
||||||
buildSearchTab();
|
buildSearchTab();
|
||||||
buildRssTab();
|
buildRssTab();
|
||||||
|
buildLogTab();
|
||||||
MochaUI.initializeTabs('mainWindowTabsList');
|
MochaUI.initializeTabs('mainWindowTabsList');
|
||||||
|
|
||||||
setCategoryFilter = function(hash) {
|
setCategoryFilter = function(hash) {
|
||||||
|
@ -304,6 +317,7 @@ window.addEvent('load', function() {
|
||||||
// After showing/hiding the toolbar + status bar
|
// After showing/hiding the toolbar + status bar
|
||||||
let showSearchEngine = LocalPreferences.get('show_search_engine') !== "false";
|
let showSearchEngine = LocalPreferences.get('show_search_engine') !== "false";
|
||||||
let showRssReader = LocalPreferences.get('show_rss_reader') !== "false";
|
let showRssReader = LocalPreferences.get('show_rss_reader') !== "false";
|
||||||
|
let showLogViewer = LocalPreferences.get('show_log_viewer') === 'true';
|
||||||
|
|
||||||
// After Show Top Toolbar
|
// After Show Top Toolbar
|
||||||
MochaUI.Desktop.setDesktopSize();
|
MochaUI.Desktop.setDesktopSize();
|
||||||
|
@ -912,6 +926,12 @@ window.addEvent('load', function() {
|
||||||
updateTabDisplay();
|
updateTabDisplay();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('showLogViewerLink').addEvent('click', function(e) {
|
||||||
|
showLogViewer = !showLogViewer;
|
||||||
|
LocalPreferences.set('show_log_viewer', showLogViewer.toString());
|
||||||
|
updateTabDisplay();
|
||||||
|
});
|
||||||
|
|
||||||
const updateTabDisplay = function() {
|
const updateTabDisplay = function() {
|
||||||
if (showRssReader) {
|
if (showRssReader) {
|
||||||
$('showRssReaderLink').firstChild.style.opacity = '1';
|
$('showRssReaderLink').firstChild.style.opacity = '1';
|
||||||
|
@ -941,8 +961,22 @@ window.addEvent('load', function() {
|
||||||
$("transfersTabLink").click();
|
$("transfersTabLink").click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showLogViewer) {
|
||||||
|
$('showLogViewerLink').firstChild.style.opacity = '1';
|
||||||
|
$('mainWindowTabs').removeClass('invisible');
|
||||||
|
$('logTabLink').removeClass('invisible');
|
||||||
|
if (!MochaUI.Panels.instances.LogPanel)
|
||||||
|
addLogPanel();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('showLogViewerLink').firstChild.style.opacity = '0';
|
||||||
|
$('logTabLink').addClass('invisible');
|
||||||
|
if ($('logTabLink').hasClass('selected'))
|
||||||
|
$("transfersTabLink").click();
|
||||||
|
}
|
||||||
|
|
||||||
// display no tabs
|
// display no tabs
|
||||||
if (!showRssReader && !showSearchEngine)
|
if (!showRssReader && !showSearchEngine && !showLogViewer)
|
||||||
$('mainWindowTabs').addClass('invisible');
|
$('mainWindowTabs').addClass('invisible');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -954,18 +988,21 @@ window.addEvent('load', function() {
|
||||||
$("filtersColumn").removeClass("invisible");
|
$("filtersColumn").removeClass("invisible");
|
||||||
$("filtersColumn_handle").removeClass("invisible");
|
$("filtersColumn_handle").removeClass("invisible");
|
||||||
$("mainColumn").removeClass("invisible");
|
$("mainColumn").removeClass("invisible");
|
||||||
|
$('torrentsFilterToolbar').removeClass("invisible");
|
||||||
|
|
||||||
customSyncMainDataInterval = null;
|
customSyncMainDataInterval = null;
|
||||||
syncData(100);
|
syncData(100);
|
||||||
|
|
||||||
hideSearchTab();
|
hideSearchTab();
|
||||||
hideRssTab();
|
hideRssTab();
|
||||||
|
hideLogTab();
|
||||||
};
|
};
|
||||||
|
|
||||||
const hideTransfersTab = function() {
|
const hideTransfersTab = function() {
|
||||||
$("filtersColumn").addClass("invisible");
|
$("filtersColumn").addClass("invisible");
|
||||||
$("filtersColumn_handle").addClass("invisible");
|
$("filtersColumn_handle").addClass("invisible");
|
||||||
$("mainColumn").addClass("invisible");
|
$("mainColumn").addClass("invisible");
|
||||||
|
$('torrentsFilterToolbar').addClass("invisible");
|
||||||
MochaUI.Desktop.resizePanels();
|
MochaUI.Desktop.resizePanels();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -979,6 +1016,7 @@ window.addEvent('load', function() {
|
||||||
customSyncMainDataInterval = 30000;
|
customSyncMainDataInterval = 30000;
|
||||||
hideTransfersTab();
|
hideTransfersTab();
|
||||||
hideRssTab();
|
hideRssTab();
|
||||||
|
hideLogTab();
|
||||||
};
|
};
|
||||||
|
|
||||||
const hideSearchTab = function() {
|
const hideSearchTab = function() {
|
||||||
|
@ -999,14 +1037,37 @@ window.addEvent('load', function() {
|
||||||
customSyncMainDataInterval = 30000;
|
customSyncMainDataInterval = 30000;
|
||||||
hideTransfersTab();
|
hideTransfersTab();
|
||||||
hideSearchTab();
|
hideSearchTab();
|
||||||
|
hideLogTab();
|
||||||
};
|
};
|
||||||
|
|
||||||
const hideRssTab = function() {
|
const hideRssTab = function() {
|
||||||
$("rssTabColumn").addClass("invisible");
|
$("rssTabColumn").addClass("invisible");
|
||||||
window.qBittorrent.Rss.unload();
|
window.qBittorrent.Rss && window.qBittorrent.Rss.unload();
|
||||||
MochaUI.Desktop.resizePanels();
|
MochaUI.Desktop.resizePanels();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showLogTab = function() {
|
||||||
|
if (!logTabInitialized) {
|
||||||
|
window.qBittorrent.Log.init();
|
||||||
|
logTabInitialized = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
window.qBittorrent.Log.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('logTabColumn').removeClass('invisible');
|
||||||
|
customSyncMainDataInterval = 30000;
|
||||||
|
hideTransfersTab();
|
||||||
|
hideSearchTab();
|
||||||
|
hideRssTab();
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideLogTab = function() {
|
||||||
|
$('logTabColumn').addClass('invisible');
|
||||||
|
MochaUI.Desktop.resizePanels();
|
||||||
|
window.qBittorrent.Log && window.qBittorrent.Log.unload();
|
||||||
|
};
|
||||||
|
|
||||||
const addSearchPanel = function() {
|
const addSearchPanel = function() {
|
||||||
new MochaUI.Panel({
|
new MochaUI.Panel({
|
||||||
id: 'SearchPanel',
|
id: 'SearchPanel',
|
||||||
|
@ -1045,6 +1106,42 @@ window.addEvent('load', function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var addLogPanel = function() {
|
||||||
|
new MochaUI.Panel({
|
||||||
|
id: 'LogPanel',
|
||||||
|
title: 'Log',
|
||||||
|
header: true,
|
||||||
|
padding: {
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0
|
||||||
|
},
|
||||||
|
loadMethod: 'xhr',
|
||||||
|
contentURL: 'views/log.html',
|
||||||
|
require: {
|
||||||
|
css: ['css/lib/vanillaSelectBox.css'],
|
||||||
|
js: ['scripts/lib/vanillaSelectBox.js'],
|
||||||
|
},
|
||||||
|
tabsURL: 'views/logTabs.html',
|
||||||
|
tabsOnload: function() {
|
||||||
|
MochaUI.initializeTabs('panelTabs');
|
||||||
|
|
||||||
|
$('logMessageLink').addEvent('click', function(e) {
|
||||||
|
window.qBittorrent.Log.setCurrentTab('main');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('logPeerLink').addEvent('click', function(e) {
|
||||||
|
window.qBittorrent.Log.setCurrentTab('peer');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
collapsible: false,
|
||||||
|
content: '',
|
||||||
|
column: 'logTabColumn',
|
||||||
|
height: null
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
new MochaUI.Panel({
|
new MochaUI.Panel({
|
||||||
id: 'transferList',
|
id: 'transferList',
|
||||||
title: 'Panel',
|
title: 'Panel',
|
||||||
|
@ -1185,6 +1282,7 @@ window.addEvent('load', function() {
|
||||||
$('transfersTabLink').addEvent('click', showTransfersTab);
|
$('transfersTabLink').addEvent('click', showTransfersTab);
|
||||||
$('searchTabLink').addEvent('click', showSearchTab);
|
$('searchTabLink').addEvent('click', showSearchTab);
|
||||||
$('rssTabLink').addEvent('click', showRssTab);
|
$('rssTabLink').addEvent('click', showRssTab);
|
||||||
|
$('logTabLink').addEvent('click', showLogTab);
|
||||||
updateTabDisplay();
|
updateTabDisplay();
|
||||||
|
|
||||||
const registerDragAndDrop = () => {
|
const registerDragAndDrop = () => {
|
||||||
|
|
|
@ -46,6 +46,8 @@ window.qBittorrent.DynamicTable = (function() {
|
||||||
SearchPluginsTable: SearchPluginsTable,
|
SearchPluginsTable: SearchPluginsTable,
|
||||||
TorrentTrackersTable: TorrentTrackersTable,
|
TorrentTrackersTable: TorrentTrackersTable,
|
||||||
TorrentFilesTable: TorrentFilesTable,
|
TorrentFilesTable: TorrentFilesTable,
|
||||||
|
LogMessageTable: LogMessageTable,
|
||||||
|
LogPeerTable: LogPeerTable,
|
||||||
RssFeedTable: RssFeedTable,
|
RssFeedTable: RssFeedTable,
|
||||||
RssArticleTable: RssArticleTable,
|
RssArticleTable: RssArticleTable,
|
||||||
RssDownloaderRulesTable: RssDownloaderRulesTable,
|
RssDownloaderRulesTable: RssDownloaderRulesTable,
|
||||||
|
@ -2610,6 +2612,153 @@ window.qBittorrent.DynamicTable = (function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const LogMessageTable = new Class({
|
||||||
|
Extends: DynamicTable,
|
||||||
|
|
||||||
|
filterText: '',
|
||||||
|
|
||||||
|
filterdLength: function() {
|
||||||
|
return this.tableBody.getElements('tr').length;
|
||||||
|
},
|
||||||
|
|
||||||
|
initColumns: function() {
|
||||||
|
this.newColumn('rowId', '', 'QBT_TR(ID)QBT_TR[CONTEXT=ExecutionLogWidget]', 50, true);
|
||||||
|
this.newColumn('message', '', 'QBT_TR(Message)QBT_TR[CONTEXT=ExecutionLogWidget]', 350, true);
|
||||||
|
this.newColumn('timestamp', '', 'QBT_TR(Timestamp)QBT_TR[CONTEXT=ExecutionLogWidget]', 150, true);
|
||||||
|
this.newColumn('type', '', 'QBT_TR(Log Type)QBT_TR[CONTEXT=ExecutionLogWidget]', 100, true);
|
||||||
|
this.initColumnsFunctions();
|
||||||
|
},
|
||||||
|
|
||||||
|
initColumnsFunctions: function() {
|
||||||
|
this.columns['timestamp'].updateTd = function(td, row) {
|
||||||
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
||||||
|
td.set({ 'text': date, 'title': date });
|
||||||
|
};
|
||||||
|
|
||||||
|
this.columns['type'].updateTd = function(td, row) {
|
||||||
|
//Type of the message: Log::NORMAL: 1, Log::INFO: 2, Log::WARNING: 4, Log::CRITICAL: 8
|
||||||
|
let logLevel, addClass;
|
||||||
|
switch (this.getRowValue(row).toInt()) {
|
||||||
|
case 1:
|
||||||
|
logLevel = 'QBT_TR(Normal)QBT_TR[CONTEXT=ExecutionLogWidget]';
|
||||||
|
addClass = 'logNormal';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
logLevel = 'QBT_TR(Info)QBT_TR[CONTEXT=ExecutionLogWidget]';
|
||||||
|
addClass = 'logInfo';
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
logLevel = 'QBT_TR(Warning)QBT_TR[CONTEXT=ExecutionLogWidget]';
|
||||||
|
addClass = 'logWarning';
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
logLevel = 'QBT_TR(Critical)QBT_TR[CONTEXT=ExecutionLogWidget]';
|
||||||
|
addClass = 'logCritical';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logLevel = 'QBT_TR(Unknown)QBT_TR[CONTEXT=ExecutionLogWidget]';
|
||||||
|
addClass = 'logUnknown';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
td.set({ 'text': logLevel, 'title': logLevel });
|
||||||
|
td.getParent('tr').set('class', 'logTableRow ' + addClass);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getFilteredAndSortedRows: function() {
|
||||||
|
let filteredRows = [];
|
||||||
|
const rows = this.rows.getValues();
|
||||||
|
this.filterText = window.qBittorrent.Log.getFilterText();
|
||||||
|
const filterTerms = (this.filterText.length > 0) ? this.filterText.toLowerCase().split(' ') : [];
|
||||||
|
const logLevels = window.qBittorrent.Log.getSelectedLevels();
|
||||||
|
if (filterTerms.length > 0 || logLevels.length < 4) {
|
||||||
|
for (let i = 0; i < rows.length; ++i) {
|
||||||
|
if (logLevels.indexOf(rows[i].full_data.type.toString()) == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (filterTerms.length > 0 && !window.qBittorrent.Misc.containsAllTerms(rows[i].full_data.message, filterTerms))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
filteredRows.push(rows[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filteredRows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredRows.sort(function(row1, row2) {
|
||||||
|
const column = this.columns[this.sortedColumn];
|
||||||
|
const res = column.compareRows(row1, row2);
|
||||||
|
return (this.reverseSort == '0') ? res : -res;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
return filteredRows;
|
||||||
|
},
|
||||||
|
|
||||||
|
setupCommonEvents: function() {},
|
||||||
|
|
||||||
|
setupTr: function(tr) {
|
||||||
|
tr.addClass('logTableRow');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const LogPeerTable = new Class({
|
||||||
|
Extends: LogMessageTable,
|
||||||
|
|
||||||
|
initColumns: function() {
|
||||||
|
this.newColumn('rowId', '', 'QBT_TR(ID)QBT_TR[CONTEXT=ExecutionLogWidget]', 50, true);
|
||||||
|
this.newColumn('ip', '', 'QBT_TR(IP)QBT_TR[CONTEXT=ExecutionLogWidget]', 150, true);
|
||||||
|
this.newColumn('timestamp', '', 'QBT_TR(Timestamp)QBT_TR[CONTEXT=ExecutionLogWidget]', 150, true);
|
||||||
|
this.newColumn('blocked', '', 'QBT_TR(Status)QBT_TR[CONTEXT=ExecutionLogWidget]', 150, true);
|
||||||
|
this.newColumn('reason', '', 'QBT_TR(Reason)QBT_TR[CONTEXT=ExecutionLogWidget]', 150, true);
|
||||||
|
|
||||||
|
this.columns['timestamp'].updateTd = function(td, row) {
|
||||||
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
||||||
|
td.set({ 'text': date, 'title': date });
|
||||||
|
};
|
||||||
|
|
||||||
|
this.columns['blocked'].updateTd = function(td, row) {
|
||||||
|
let status, addClass;
|
||||||
|
if (this.getRowValue(row)) {
|
||||||
|
status = 'QBT_TR(Blocked)QBT_TR[CONTEXT=ExecutionLogWidget]';
|
||||||
|
addClass = 'peerBlocked';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
status = 'QBT_TR(Banned)QBT_TR[CONTEXT=ExecutionLogWidget]';
|
||||||
|
addClass = 'peerBanned';
|
||||||
|
}
|
||||||
|
td.set({ 'text': status, 'title': status });
|
||||||
|
td.getParent('tr').set('class', 'logTableRow ' + addClass);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getFilteredAndSortedRows: function() {
|
||||||
|
let filteredRows = [];
|
||||||
|
const rows = this.rows.getValues();
|
||||||
|
this.filterText = window.qBittorrent.Log.getFilterText();
|
||||||
|
const filterTerms = (this.filterText.length > 0) ? this.filterText.toLowerCase().split(' ') : [];
|
||||||
|
if (filterTerms.length > 0) {
|
||||||
|
for (let i = 0; i < rows.length; ++i) {
|
||||||
|
if (filterTerms.length > 0 && !window.qBittorrent.Misc.containsAllTerms(rows[i].full_data.ip, filterTerms))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
filteredRows.push(rows[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filteredRows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredRows.sort(function(row1, row2) {
|
||||||
|
const column = this.columns[this.sortedColumn];
|
||||||
|
const res = column.compareRows(row1, row2);
|
||||||
|
return (this.reverseSort == '0') ? res : -res;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
return filteredRows;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return exports();
|
return exports();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
1403
src/webui/www/private/scripts/lib/vanillaSelectBox.js
Normal file
1403
src/webui/www/private/scripts/lib/vanillaSelectBox.js
Normal file
File diff suppressed because it is too large
Load diff
427
src/webui/www/private/views/log.html
Normal file
427
src/webui/www/private/views/log.html
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
<style type="text/css">
|
||||||
|
#logTopBar {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logFilterBar {
|
||||||
|
margin: .5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logFilterBar>label {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logFilterBar>button {
|
||||||
|
display: inline-block;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 .5em;
|
||||||
|
margin-left: .3em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logView {
|
||||||
|
padding: 0 20px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logContentView {
|
||||||
|
display: block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logMessageTableFixedHeaderDiv .dynamicTableHeader,
|
||||||
|
#logPeerTableFixedHeaderDiv .dynamicTableHeader {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filterTextInput {
|
||||||
|
background-image: url("../images/edit-find.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: left;
|
||||||
|
background-size: 1.5em;
|
||||||
|
padding: 1px 5px 1px 2em;
|
||||||
|
margin-left: 20px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logFilterSummary {
|
||||||
|
overflow: auto;
|
||||||
|
margin: 1em 0 .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#numFilteredLogs,
|
||||||
|
#numTotalLogs {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logNormal {
|
||||||
|
color: #80766e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logInfo {
|
||||||
|
color: #1781b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logWarning {
|
||||||
|
color: #f97d1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logCritical,
|
||||||
|
.peerBlocked {
|
||||||
|
color: #ee3f4d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsb-main>button {
|
||||||
|
padding: 0 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contextMenu>li>a>img {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="logView">
|
||||||
|
<div id="logTopBar">
|
||||||
|
<div id="logFilterBar">
|
||||||
|
<label for="logLevelSelect">QBT_TR(Log Levels:)QBT_TR[CONTEXT=ExecutionLogWidget]</label>
|
||||||
|
<select multiple size="1" id="logLevelSelect" class="logLevelSelect" onchange="window.qBittorrent.Log.logLevelChanged()">
|
||||||
|
<option value="1">QBT_TR(Normal Messages)QBT_TR[CONTEXT=ExecutionLogWidget]</option>
|
||||||
|
<option value="2">QBT_TR(Information Messages)QBT_TR[CONTEXT=ExecutionLogWidget]</option>
|
||||||
|
<option value="4">QBT_TR(Warning Messages)QBT_TR[CONTEXT=ExecutionLogWidget]</option>
|
||||||
|
<option value="8">QBT_TR(Critical Messages)QBT_TR[CONTEXT=ExecutionLogWidget]</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input type="text" id="filterTextInput" onkeyup="window.qBittorrent.Log.filterTextChanged()" placeholder="QBT_TR(Filter logs)QBT_TR[CONTEXT=ExecutionLogWidget]" autocomplete="off" autocorrect="off" autocapitalize="none" />
|
||||||
|
<button title="Clear input" onclick="javascript:document.querySelector('#filterTextInput').value='';window.qBittorrent.Log.filterTextChanged();">QBT_TR(Clear)QBT_TR[CONTEXT=ExecutionLogWidget]</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="logFilterSummary">
|
||||||
|
<span>QBT_TR(Results)QBT_TR[CONTEXT=ExecutionLogWidget] (QBT_TR(showing)QBT_TR[CONTEXT=ExecutionLogWidget] <span id="numFilteredLogs">0</span> QBT_TR(out of)QBT_TR[CONTEXT=ExecutionLogWidget] <span id="numTotalLogs">0</span>):</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="logContentView">
|
||||||
|
<div id="logMessageView">
|
||||||
|
<div id="logMessageTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
|
||||||
|
<table class="dynamicTable unselectable" style="position:relative;">
|
||||||
|
<thead>
|
||||||
|
<tr class="dynamicTableHeader"></tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="logMessageTableDiv" class="dynamicTableDiv">
|
||||||
|
<table class="dynamicTable unselectable">
|
||||||
|
<thead>
|
||||||
|
<tr class="dynamicTableHeader"></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="logPeerView" class="invisible">
|
||||||
|
<div id="logPeerTableFixedHeaderDiv" class="dynamicTableFixedHeaderDiv">
|
||||||
|
<table class="dynamicTable unselectable" style="position:relative;">
|
||||||
|
<thead>
|
||||||
|
<tr class="dynamicTableHeader"></tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="logPeerTableDiv" class="dynamicTableDiv">
|
||||||
|
<table class="dynamicTable unselectable">
|
||||||
|
<thead>
|
||||||
|
<tr class="dynamicTableHeader"></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul id="logTableMenu" class="contextMenu">
|
||||||
|
<li><a href="#" class="copyLogDataToClipboard"><img src="images/edit-copy.svg" alt="QBT_TR(Copy)QBT_TR[CONTEXT=ExecutionLogWidget]" />QBT_TR(Copy)QBT_TR[CONTEXT=ExecutionLogWidget]</a></li>
|
||||||
|
<li><a href="#Clear"><img src="images/list-remove.svg" alt="QBT_TR(Clear)QBT_TR[CONTEXT=ExecutionLogWidget]" />QBT_TR(Clear)QBT_TR[CONTEXT=ExecutionLogWidget]</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.qBittorrent === undefined) {
|
||||||
|
window.qBittorrent = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.qBittorrent.Log = (() => {
|
||||||
|
const exports = () => {
|
||||||
|
return {
|
||||||
|
init: init,
|
||||||
|
unload: unload,
|
||||||
|
load: load,
|
||||||
|
setCurrentTab: setCurrentTab,
|
||||||
|
getFilterText: getFilterText,
|
||||||
|
getSelectedLevels: getSelectedLevels,
|
||||||
|
logLevelChanged: logLevelChanged,
|
||||||
|
filterTextChanged: filterTextChanged
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentSelectedTab = 'main';
|
||||||
|
let tableInfo = {
|
||||||
|
main: {
|
||||||
|
instance: new window.qBittorrent.DynamicTable.LogMessageTable(),
|
||||||
|
progress: false,
|
||||||
|
timer: null,
|
||||||
|
last_id: -1
|
||||||
|
},
|
||||||
|
peer: {
|
||||||
|
instance: new window.qBittorrent.DynamicTable.LogPeerTable(),
|
||||||
|
progress: false,
|
||||||
|
timer: null,
|
||||||
|
last_id: -1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let customSyncLogDataInterval = null;
|
||||||
|
let logFilterTimer;
|
||||||
|
let inputedFilterText = "";
|
||||||
|
let selectBox;
|
||||||
|
let selectedLogLevels = JSON.parse(LocalPreferences.get('qbt_selected_log_levels')) || ['1', '2', '4', '8'];
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
$('logLevelSelect').getElements('option').each((x) => {
|
||||||
|
if (selectedLogLevels.indexOf(x.value.toString()) !== -1) {
|
||||||
|
x.setAttribute('selected', '');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x.removeAttribute('selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
selectBox = new vanillaSelectBox('#logLevelSelect', {
|
||||||
|
maxHeight: 200,
|
||||||
|
search: false,
|
||||||
|
translations: {
|
||||||
|
all: 'QBT_TR(All)QBT_TR[CONTEXT=ExecutionLogWidget]',
|
||||||
|
item: 'QBT_TR(item)QBT_TR[CONTEXT=ExecutionLogWidget]',
|
||||||
|
items: 'QBT_TR(items)QBT_TR[CONTEXT=ExecutionLogWidget]',
|
||||||
|
selectAll: 'QBT_TR(Select All)QBT_TR[CONTEXT=ExecutionLogWidget]',
|
||||||
|
clearAll: 'QBT_TR(Clear All)QBT_TR[CONTEXT=ExecutionLogWidget]',
|
||||||
|
},
|
||||||
|
placeHolder: "QBT_TR(Choose a log level...)QBT_TR[CONTEXT=ExecutionLogWidget]",
|
||||||
|
});
|
||||||
|
|
||||||
|
const logTableContextMenu = new window.qBittorrent.ContextMenu.ContextMenu({
|
||||||
|
targets: '.logTableRow',
|
||||||
|
menu: 'logTableMenu',
|
||||||
|
actions: {
|
||||||
|
Clear: () => {
|
||||||
|
tableInfo[currentSelectedTab].instance.selectedRowsIds().forEach(function(rowId) {
|
||||||
|
tableInfo[currentSelectedTab].instance.removeRow(rowId);
|
||||||
|
});
|
||||||
|
|
||||||
|
updateLableCount();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
offsets: {
|
||||||
|
x: -16,
|
||||||
|
y: -57
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tableInfo['main'].instance.setup('logMessageTableDiv', 'logMessageTableFixedHeaderDiv', logTableContextMenu);
|
||||||
|
tableInfo['peer'].instance.setup('logPeerTableDiv', 'logPeerTableFixedHeaderDiv', logTableContextMenu);
|
||||||
|
|
||||||
|
MUI.Panels.instances.LogPanel.contentEl.setStyle('height', '100%');
|
||||||
|
$('logView').setStyle('height', 'inherit');
|
||||||
|
|
||||||
|
load();
|
||||||
|
};
|
||||||
|
|
||||||
|
const unload = () => {
|
||||||
|
for (let table in tableInfo)
|
||||||
|
resetTableTimer(table);
|
||||||
|
};
|
||||||
|
|
||||||
|
const load = () => {
|
||||||
|
customSyncLogDataInterval = null;
|
||||||
|
syncLogWithInterval(100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetTableTimer = (curTab) => {
|
||||||
|
if (curTab === undefined)
|
||||||
|
curTab = currentSelectedTab;
|
||||||
|
|
||||||
|
clearTimeout(tableInfo[curTab].timer);
|
||||||
|
tableInfo[curTab].timer = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncLogWithInterval = (interval) => {
|
||||||
|
if (!tableInfo[currentSelectedTab].progress) {
|
||||||
|
clearTimeout(tableInfo[currentSelectedTab].timer);
|
||||||
|
tableInfo[currentSelectedTab].timer = syncLogData.delay(interval, null, currentSelectedTab);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFilterText = () => {
|
||||||
|
return inputedFilterText;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedLevels = () => {
|
||||||
|
return selectedLogLevels;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSyncLogDataInterval = () => {
|
||||||
|
return customSyncLogDataInterval ? customSyncLogDataInterval : serverSyncMainDataInterval;
|
||||||
|
};
|
||||||
|
|
||||||
|
const logLevelChanged = () => {
|
||||||
|
const value = selectBox.getResult().sort();
|
||||||
|
|
||||||
|
if (selectedLogLevels !== value) {
|
||||||
|
tableInfo[currentSelectedTab].last_id = -1;
|
||||||
|
selectedLogLevels = value;
|
||||||
|
LocalPreferences.set('qbt_selected_log_levels', JSON.stringify(selectedLogLevels));
|
||||||
|
logFilterChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterTextChanged = () => {
|
||||||
|
const value = $('filterTextInput').get('value').trim();
|
||||||
|
if (inputedFilterText !== value) {
|
||||||
|
inputedFilterText = value;
|
||||||
|
logFilterChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const logFilterChanged = () => {
|
||||||
|
clearTimeout(logFilterTimer);
|
||||||
|
logFilterTimer = setTimeout((curTab) => {
|
||||||
|
tableInfo[curTab].instance.updateTable(false);
|
||||||
|
updateLableCount(curTab);
|
||||||
|
}, 400, currentSelectedTab);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCurrentTab = (tab) => {
|
||||||
|
if (tab === currentSelectedTab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
currentSelectedTab = tab;
|
||||||
|
if (currentSelectedTab === 'main') {
|
||||||
|
selectBox.enable();
|
||||||
|
$('logMessageView').removeClass('invisible');
|
||||||
|
$('logPeerView').addClass('invisible');
|
||||||
|
resetTableTimer('peer');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
selectBox.disable();
|
||||||
|
$('logMessageView').addClass('invisible');
|
||||||
|
$('logPeerView').removeClass('invisible');
|
||||||
|
resetTableTimer('main');
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTimeout(logFilterTimer);
|
||||||
|
load();
|
||||||
|
|
||||||
|
if (tableInfo[currentSelectedTab].instance.filterText !== getFilterText()) {
|
||||||
|
tableInfo[currentSelectedTab].instance.updateTable();
|
||||||
|
}
|
||||||
|
updateLableCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLableCount = (curTab) => {
|
||||||
|
if (curTab === undefined)
|
||||||
|
curTab = currentSelectedTab;
|
||||||
|
|
||||||
|
$('numFilteredLogs').set('text', tableInfo[curTab].instance.filterdLength());
|
||||||
|
$('numTotalLogs').set('text', tableInfo[curTab].instance.getRowIds().length);
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncLogData = (curTab) => {
|
||||||
|
if (curTab === undefined)
|
||||||
|
curTab = currentSelectedTab;
|
||||||
|
|
||||||
|
let url;
|
||||||
|
if (curTab === 'main') {
|
||||||
|
url = new URI('api/v2/log/main');
|
||||||
|
url.setData({
|
||||||
|
normal: selectedLogLevels.indexOf('1') !== -1,
|
||||||
|
info: selectedLogLevels.indexOf('2') !== -1,
|
||||||
|
warning: selectedLogLevels.indexOf('4') !== -1,
|
||||||
|
critical: selectedLogLevels.indexOf('8') !== -1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url = new URI('api/v2/log/peers');
|
||||||
|
}
|
||||||
|
|
||||||
|
url.setData('last_known_id', tableInfo[curTab].last_id);
|
||||||
|
tableInfo[curTab].progress = true;
|
||||||
|
|
||||||
|
new Request.JSON({
|
||||||
|
url: url,
|
||||||
|
noCache: true,
|
||||||
|
method: 'get',
|
||||||
|
onFailure: function(response) {
|
||||||
|
const errorDiv = $('error_div');
|
||||||
|
if (errorDiv)
|
||||||
|
errorDiv.set('text', 'QBT_TR(qBittorrent client is not reachable)QBT_TR[CONTEXT=HttpServer]');
|
||||||
|
tableInfo[curTab].progress = false;
|
||||||
|
syncLogWithInterval(10000);
|
||||||
|
},
|
||||||
|
onSuccess: function(response) {
|
||||||
|
$('error_div').set('text', '');
|
||||||
|
|
||||||
|
if ($('logTabColumn').hasClass('invisible'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (response.length > 0) {
|
||||||
|
clearTimeout(logFilterTimer);
|
||||||
|
for (let i = 0; i < response.length; ++i) {
|
||||||
|
let row;
|
||||||
|
if (curTab === 'main') {
|
||||||
|
row = {
|
||||||
|
rowId: response[i].id,
|
||||||
|
message: response[i].message,
|
||||||
|
timestamp: response[i].timestamp,
|
||||||
|
type: response[i].type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
row = {
|
||||||
|
rowId: response[i].id,
|
||||||
|
ip: response[i].ip,
|
||||||
|
timestamp: response[i].timestamp,
|
||||||
|
blocked: response[i].blocked,
|
||||||
|
reason: response[i].reason,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
tableInfo[curTab].instance.updateRowData(row);
|
||||||
|
tableInfo[curTab].last_id = Math.max(response[i].id.toInt(), tableInfo[curTab].last_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
tableInfo[curTab].instance.updateTable();
|
||||||
|
tableInfo[curTab].instance.altRow();
|
||||||
|
updateLableCount(curTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
tableInfo[curTab].progress = false;
|
||||||
|
syncLogWithInterval(getSyncLogDataInterval());
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
new ClipboardJS('.copyLogDataToClipboard', {
|
||||||
|
text: function() {
|
||||||
|
let msg = [];
|
||||||
|
tableInfo[currentSelectedTab].instance.selectedRowsIds().each(function(rowId) {
|
||||||
|
msg.push(tableInfo[currentSelectedTab].instance.rows.get(rowId).full_data[(currentSelectedTab === 'main') ? 'message' : 'ip']);
|
||||||
|
});
|
||||||
|
|
||||||
|
return msg.join('\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return exports();
|
||||||
|
})();
|
||||||
|
</script>
|
7
src/webui/www/private/views/logTabs.html
Normal file
7
src/webui/www/private/views/logTabs.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div class="toolbarTabs">
|
||||||
|
<ul id="panelTabs" class="tab-menu">
|
||||||
|
<li id="logMessageLink" class="selected"><a title="QBT_TR(General)QBT_TR[CONTEXT=ExecutionLogWidget]">QBT_TR(General)QBT_TR[CONTEXT=ExecutionLogWidget]</a></li>
|
||||||
|
<li id="logPeerLink"><a title="QBT_TR(Blocked IPs)QBT_TR[CONTEXT=ExecutionLogWidget]">QBT_TR(Blocked IPs)QBT_TR[CONTEXT=ExecutionLogWidget]</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
|
@ -12,6 +12,7 @@
|
||||||
<file>private/css/noscript.css</file>
|
<file>private/css/noscript.css</file>
|
||||||
<file>private/css/style.css</file>
|
<file>private/css/style.css</file>
|
||||||
<file>private/css/Tabs.css</file>
|
<file>private/css/Tabs.css</file>
|
||||||
|
<file>private/css/lib/vanillaSelectBox.css</file>
|
||||||
<file>private/css/Window.css</file>
|
<file>private/css/Window.css</file>
|
||||||
<file>private/download.html</file>
|
<file>private/download.html</file>
|
||||||
<file>private/downloadlimit.html</file>
|
<file>private/downloadlimit.html</file>
|
||||||
|
@ -392,6 +393,7 @@
|
||||||
<file>private/scripts/prop-trackers.js</file>
|
<file>private/scripts/prop-trackers.js</file>
|
||||||
<file>private/scripts/prop-webseeds.js</file>
|
<file>private/scripts/prop-webseeds.js</file>
|
||||||
<file>private/scripts/speedslider.js</file>
|
<file>private/scripts/speedslider.js</file>
|
||||||
|
<file>private/scripts/lib/vanillaSelectBox.js</file>
|
||||||
<file>private/setlocation.html</file>
|
<file>private/setlocation.html</file>
|
||||||
<file>private/shareratio.html</file>
|
<file>private/shareratio.html</file>
|
||||||
<file>private/upload.html</file>
|
<file>private/upload.html</file>
|
||||||
|
@ -400,6 +402,8 @@
|
||||||
<file>private/views/aboutToolbar.html</file>
|
<file>private/views/aboutToolbar.html</file>
|
||||||
<file>private/views/filters.html</file>
|
<file>private/views/filters.html</file>
|
||||||
<file>private/views/installsearchplugin.html</file>
|
<file>private/views/installsearchplugin.html</file>
|
||||||
|
<file>private/views/log.html</file>
|
||||||
|
<file>private/views/logTabs.html</file>
|
||||||
<file>private/views/preferences.html</file>
|
<file>private/views/preferences.html</file>
|
||||||
<file>private/views/preferencesToolbar.html</file>
|
<file>private/views/preferencesToolbar.html</file>
|
||||||
<file>private/views/properties.html</file>
|
<file>private/views/properties.html</file>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue