2022-10-18 22:39:32 -04:00
|
|
|
/*
|
|
|
|
* MIT License
|
|
|
|
* Copyright (c) 2008 Ishan Arora <ishan@qbittorrent.org> & Christophe Dumez <chris@qbittorrent.org>
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**************************************************************
|
|
|
|
|
|
|
|
Script : Dynamic Table
|
|
|
|
Version : 0.5
|
|
|
|
Authors : Ishan Arora & Christophe Dumez
|
|
|
|
Desc : Programmable sortable table
|
|
|
|
Licence : Open Source MIT Licence
|
|
|
|
|
|
|
|
**************************************************************/
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
"use strict";
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
window.qBittorrent ??= {};
|
|
|
|
window.qBittorrent.DynamicTable ??= (() => {
|
|
|
|
const exports = () => {
|
2022-10-18 22:39:32 -04:00
|
|
|
return {
|
|
|
|
TorrentsTable: TorrentsTable,
|
|
|
|
TorrentPeersTable: TorrentPeersTable,
|
|
|
|
SearchResultsTable: SearchResultsTable,
|
|
|
|
SearchPluginsTable: SearchPluginsTable,
|
|
|
|
TorrentTrackersTable: TorrentTrackersTable,
|
2023-07-18 11:58:05 -04:00
|
|
|
BulkRenameTorrentFilesTable: BulkRenameTorrentFilesTable,
|
2022-10-18 22:39:32 -04:00
|
|
|
TorrentFilesTable: TorrentFilesTable,
|
2023-07-18 11:58:05 -04:00
|
|
|
LogMessageTable: LogMessageTable,
|
|
|
|
LogPeerTable: LogPeerTable,
|
2022-10-18 22:39:32 -04:00
|
|
|
RssFeedTable: RssFeedTable,
|
|
|
|
RssArticleTable: RssArticleTable,
|
|
|
|
RssDownloaderRulesTable: RssDownloaderRulesTable,
|
|
|
|
RssDownloaderFeedSelectionTable: RssDownloaderFeedSelectionTable,
|
2024-10-03 08:37:11 -04:00
|
|
|
RssDownloaderArticlesTable: RssDownloaderArticlesTable,
|
|
|
|
TorrentWebseedsTable: TorrentWebseedsTable
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-07-18 11:58:05 -04:00
|
|
|
const compareNumbers = (val1, val2) => {
|
|
|
|
if (val1 < val2)
|
|
|
|
return -1;
|
|
|
|
if (val1 > val2)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
};
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
let DynamicTableHeaderContextMenuClass = null;
|
|
|
|
let ProgressColumnWidth = -1;
|
|
|
|
|
|
|
|
const DynamicTable = new Class({
|
|
|
|
|
|
|
|
initialize: function() {},
|
|
|
|
|
|
|
|
setup: function(dynamicTableDivId, dynamicTableFixedHeaderDivId, contextMenu) {
|
|
|
|
this.dynamicTableDivId = dynamicTableDivId;
|
|
|
|
this.dynamicTableFixedHeaderDivId = dynamicTableFixedHeaderDivId;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.fixedTableHeader = $(dynamicTableFixedHeaderDivId).getElements("tr")[0];
|
|
|
|
this.hiddenTableHeader = $(dynamicTableDivId).getElements("tr")[0];
|
|
|
|
this.tableBody = $(dynamicTableDivId).getElements("tbody")[0];
|
2024-10-01 14:25:40 -04:00
|
|
|
this.rows = new Map();
|
2022-10-18 22:39:32 -04:00
|
|
|
this.selectedRows = [];
|
|
|
|
this.columns = [];
|
|
|
|
this.contextMenu = contextMenu;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.sortedColumn = LocalPreferences.get("sorted_column_" + this.dynamicTableDivId, 0);
|
|
|
|
this.reverseSort = LocalPreferences.get("reverse_sort_" + this.dynamicTableDivId, "0");
|
2022-10-18 22:39:32 -04:00
|
|
|
this.initColumns();
|
|
|
|
this.loadColumnsOrder();
|
|
|
|
this.updateTableHeaders();
|
|
|
|
this.setupCommonEvents();
|
|
|
|
this.setupHeaderEvents();
|
|
|
|
this.setupHeaderMenu();
|
2024-10-03 08:37:11 -04:00
|
|
|
this.setSortedColumnIcon(this.sortedColumn, null, (this.reverseSort === "1"));
|
|
|
|
this.setupAltRow();
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
setupCommonEvents: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const tableDiv = $(this.dynamicTableDivId);
|
|
|
|
const tableFixedHeaderDiv = $(this.dynamicTableFixedHeaderDivId);
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const tableElement = tableFixedHeaderDiv.querySelector("table");
|
|
|
|
tableDiv.addEventListener("scroll", () => {
|
|
|
|
tableElement.style.left = `${-tableDiv.scrollLeft}px`;
|
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// if the table exists within a panel
|
2024-10-03 08:37:11 -04:00
|
|
|
const parentPanel = tableDiv.getParent(".panel");
|
|
|
|
if (parentPanel) {
|
|
|
|
const resizeFn = (entries) => {
|
|
|
|
const panel = entries[0].target;
|
|
|
|
let h = panel.getBoundingClientRect().height - tableFixedHeaderDiv.getBoundingClientRect().height;
|
|
|
|
tableDiv.style.height = `${h}px`;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// Workaround due to inaccurate calculation of elements heights by browser
|
|
|
|
let n = 2;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// is panel vertical scrollbar visible or does panel content not fit?
|
|
|
|
while (((panel.clientWidth !== panel.offsetWidth) || (panel.clientHeight !== panel.scrollHeight)) && (n > 0)) {
|
2022-10-18 22:39:32 -04:00
|
|
|
--n;
|
|
|
|
h -= 0.5;
|
2024-10-03 08:37:11 -04:00
|
|
|
tableDiv.style.height = `${h}px`;
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
};
|
2023-07-18 11:58:05 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const resizeDebouncer = window.qBittorrent.Misc.createDebounceHandler(100, (entries) => {
|
|
|
|
resizeFn(entries);
|
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const resizeObserver = new ResizeObserver(resizeDebouncer);
|
|
|
|
resizeObserver.observe(parentPanel, { box: "border-box" });
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
setupHeaderEvents: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.currentHeaderAction = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
this.canResize = false;
|
|
|
|
|
|
|
|
const resetElementBorderStyle = function(el, side) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((side === "left") || (side !== "right"))
|
|
|
|
el.style.borderLeft = "";
|
|
|
|
if ((side === "right") || (side !== "left"))
|
|
|
|
el.style.borderRight = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const mouseMoveFn = function(e) {
|
|
|
|
const brect = e.target.getBoundingClientRect();
|
2024-10-03 08:37:11 -04:00
|
|
|
const mouseXRelative = e.clientX - brect.left;
|
|
|
|
if (this.currentHeaderAction === "") {
|
|
|
|
if ((brect.width - mouseXRelative) < 5) {
|
2022-10-18 22:39:32 -04:00
|
|
|
this.resizeTh = e.target;
|
|
|
|
this.canResize = true;
|
2024-10-03 08:37:11 -04:00
|
|
|
e.target.getParent("tr").style.cursor = "col-resize";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else if ((mouseXRelative < 5) && e.target.getPrevious('[class=""]')) {
|
|
|
|
this.resizeTh = e.target.getPrevious('[class=""]');
|
|
|
|
this.canResize = true;
|
2024-10-03 08:37:11 -04:00
|
|
|
e.target.getParent("tr").style.cursor = "col-resize";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.canResize = false;
|
2024-10-03 08:37:11 -04:00
|
|
|
e.target.getParent("tr").style.cursor = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.currentHeaderAction === "drag") {
|
2022-10-18 22:39:32 -04:00
|
|
|
const previousVisibleSibling = e.target.getPrevious('[class=""]');
|
|
|
|
let borderChangeElement = previousVisibleSibling;
|
2024-10-03 08:37:11 -04:00
|
|
|
let changeBorderSide = "right";
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (mouseXRelative > (brect.width / 2)) {
|
2022-10-18 22:39:32 -04:00
|
|
|
borderChangeElement = e.target;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.dropSide = "right";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.dropSide = "left";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
e.target.getParent("tr").style.cursor = "move";
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
if (!previousVisibleSibling) { // right most column
|
|
|
|
borderChangeElement = e.target;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (mouseXRelative <= (brect.width / 2))
|
|
|
|
changeBorderSide = "left";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const borderStyle = "initial solid #e60";
|
|
|
|
if (changeBorderSide === "left")
|
|
|
|
borderChangeElement.style.borderLeft = borderStyle;
|
|
|
|
else
|
|
|
|
borderChangeElement.style.borderRight = borderStyle;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
resetElementBorderStyle(borderChangeElement, ((changeBorderSide === "right") ? "left" : "right"));
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
borderChangeElement.getSiblings('[class=""]').each((el) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
resetElementBorderStyle(el);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
this.lastHoverTh = e.target;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.lastClientX = e.clientX;
|
2022-10-18 22:39:32 -04:00
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const mouseOutFn = function(e) {
|
|
|
|
resetElementBorderStyle(e.target);
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const onBeforeStart = function(el) {
|
|
|
|
this.clickedTh = el;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.currentHeaderAction = "start";
|
2022-10-18 22:39:32 -04:00
|
|
|
this.dragMovement = false;
|
|
|
|
this.dragStartX = this.lastClientX;
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const onStart = function(el, event) {
|
|
|
|
if (this.canResize) {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.currentHeaderAction = "resize";
|
|
|
|
this.startWidth = parseInt(this.resizeTh.style.width, 10);
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.currentHeaderAction = "drag";
|
|
|
|
el.style.backgroundColor = "#C1D5E7";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const onDrag = function(el, event) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.currentHeaderAction === "resize") {
|
|
|
|
let width = this.startWidth + (event.event.pageX - this.dragStartX);
|
2022-10-18 22:39:32 -04:00
|
|
|
if (width < 16)
|
|
|
|
width = 16;
|
|
|
|
this.columns[this.resizeTh.columnName].width = width;
|
|
|
|
this.updateColumn(this.resizeTh.columnName);
|
|
|
|
}
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const onComplete = function(el, event) {
|
|
|
|
resetElementBorderStyle(this.lastHoverTh);
|
2024-10-03 08:37:11 -04:00
|
|
|
el.style.backgroundColor = "";
|
|
|
|
if (this.currentHeaderAction === "resize")
|
|
|
|
LocalPreferences.set("column_" + this.resizeTh.columnName + "_width_" + this.dynamicTableDivId, this.columns[this.resizeTh.columnName].width);
|
|
|
|
if ((this.currentHeaderAction === "drag") && (el !== this.lastHoverTh)) {
|
2022-10-18 22:39:32 -04:00
|
|
|
this.saveColumnsOrder();
|
2024-10-03 08:37:11 -04:00
|
|
|
const val = LocalPreferences.get("columns_order_" + this.dynamicTableDivId).split(",");
|
2022-10-18 22:39:32 -04:00
|
|
|
val.erase(el.columnName);
|
|
|
|
let pos = val.indexOf(this.lastHoverTh.columnName);
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.dropSide === "right")
|
2022-10-18 22:39:32 -04:00
|
|
|
++pos;
|
|
|
|
val.splice(pos, 0, el.columnName);
|
2024-10-03 08:37:11 -04:00
|
|
|
LocalPreferences.set("columns_order_" + this.dynamicTableDivId, val.join(","));
|
2022-10-18 22:39:32 -04:00
|
|
|
this.loadColumnsOrder();
|
|
|
|
this.updateTableHeaders();
|
|
|
|
while (this.tableBody.firstChild)
|
|
|
|
this.tableBody.removeChild(this.tableBody.firstChild);
|
|
|
|
this.updateTable(true);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.currentHeaderAction === "drag") {
|
2022-10-18 22:39:32 -04:00
|
|
|
resetElementBorderStyle(el);
|
2024-10-03 08:37:11 -04:00
|
|
|
el.getSiblings('[class=""]').each((el) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
resetElementBorderStyle(el);
|
|
|
|
});
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
this.currentHeaderAction = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const onCancel = function(el) {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.currentHeaderAction = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
this.setSortedColumn(el.columnName);
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const onTouch = function(e) {
|
|
|
|
const column = e.target.columnName;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.currentHeaderAction = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
this.setSortedColumn(column);
|
|
|
|
}.bind(this);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const ths = this.fixedTableHeader.getElements("th");
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
for (let i = 0; i < ths.length; ++i) {
|
|
|
|
const th = ths[i];
|
2024-10-03 08:37:11 -04:00
|
|
|
th.addEventListener("mousemove", mouseMoveFn);
|
|
|
|
th.addEventListener("mouseout", mouseOutFn);
|
|
|
|
th.addEventListener("touchend", onTouch, { passive: true });
|
2022-10-18 22:39:32 -04:00
|
|
|
th.makeResizable({
|
|
|
|
modifiers: {
|
2024-10-03 08:37:11 -04:00
|
|
|
x: "",
|
|
|
|
y: ""
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
onBeforeStart: onBeforeStart,
|
|
|
|
onStart: onStart,
|
|
|
|
onDrag: onDrag,
|
|
|
|
onComplete: onComplete,
|
|
|
|
onCancel: onCancel
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
setupDynamicTableHeaderContextMenuClass: function() {
|
|
|
|
if (!DynamicTableHeaderContextMenuClass) {
|
|
|
|
DynamicTableHeaderContextMenuClass = new Class({
|
|
|
|
Extends: window.qBittorrent.ContextMenu.ContextMenu,
|
|
|
|
updateMenuItems: function() {
|
|
|
|
for (let i = 0; i < this.dynamicTable.columns.length; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.dynamicTable.columns[i].caption === "")
|
2022-10-18 22:39:32 -04:00
|
|
|
continue;
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.dynamicTable.columns[i].visible !== "0")
|
2022-10-18 22:39:32 -04:00
|
|
|
this.setItemChecked(this.dynamicTable.columns[i].name, true);
|
|
|
|
else
|
|
|
|
this.setItemChecked(this.dynamicTable.columns[i].name, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
showColumn: function(columnName, show) {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns[columnName].visible = show ? "1" : "0";
|
|
|
|
LocalPreferences.set("column_" + columnName + "_visible_" + this.dynamicTableDivId, show ? "1" : "0");
|
2022-10-18 22:39:32 -04:00
|
|
|
this.updateColumn(columnName);
|
|
|
|
},
|
|
|
|
|
|
|
|
setupHeaderMenu: function() {
|
|
|
|
this.setupDynamicTableHeaderContextMenuClass();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const menuId = this.dynamicTableDivId + "_headerMenu";
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2023-07-18 11:58:05 -04:00
|
|
|
// reuse menu if already exists
|
2024-10-03 08:37:11 -04:00
|
|
|
const ul = $(menuId) ?? new Element("ul", {
|
2022-10-18 22:39:32 -04:00
|
|
|
id: menuId,
|
2024-10-03 08:37:11 -04:00
|
|
|
class: "contextMenu scrollableMenu"
|
2022-10-18 22:39:32 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
const createLi = function(columnName, text) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const anchor = document.createElement("a");
|
|
|
|
anchor.href = `#${columnName}`;
|
|
|
|
anchor.textContent = text;
|
|
|
|
|
|
|
|
const img = document.createElement("img");
|
|
|
|
img.src = "images/checked-completed.svg";
|
|
|
|
anchor.prepend(img);
|
|
|
|
|
|
|
|
const listItem = document.createElement("li");
|
|
|
|
listItem.appendChild(anchor);
|
|
|
|
|
|
|
|
return listItem;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const actions = {};
|
|
|
|
|
|
|
|
const onMenuItemClicked = function(element, ref, action) {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showColumn(action, this.columns[action].visible === "0");
|
2022-10-18 22:39:32 -04:00
|
|
|
}.bind(this);
|
|
|
|
|
2023-07-18 11:58:05 -04:00
|
|
|
// recreate child nodes when reusing (enables the context menu to work correctly)
|
|
|
|
if (ul.hasChildNodes()) {
|
2024-10-03 08:37:11 -04:00
|
|
|
while (ul.firstChild)
|
2023-07-18 11:58:05 -04:00
|
|
|
ul.removeChild(ul.lastChild);
|
|
|
|
}
|
|
|
|
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
|
|
const text = this.columns[i].caption;
|
2024-10-03 08:37:11 -04:00
|
|
|
if (text === "")
|
2022-10-18 22:39:32 -04:00
|
|
|
continue;
|
|
|
|
ul.appendChild(createLi(this.columns[i].name, text));
|
|
|
|
actions[this.columns[i].name] = onMenuItemClicked;
|
|
|
|
}
|
|
|
|
|
|
|
|
ul.inject(document.body);
|
|
|
|
|
|
|
|
this.headerContextMenu = new DynamicTableHeaderContextMenuClass({
|
2024-10-03 08:37:11 -04:00
|
|
|
targets: "#" + this.dynamicTableFixedHeaderDivId + " tr",
|
2022-10-18 22:39:32 -04:00
|
|
|
actions: actions,
|
|
|
|
menu: menuId,
|
|
|
|
offsets: {
|
|
|
|
x: -15,
|
|
|
|
y: 2
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
this.headerContextMenu.dynamicTable = this;
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumns: function() {},
|
|
|
|
|
|
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
|
|
const column = {};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["name"] = name;
|
|
|
|
column["title"] = name;
|
|
|
|
column["visible"] = LocalPreferences.get("column_" + name + "_visible_" + this.dynamicTableDivId, defaultVisible ? "1" : "0");
|
|
|
|
column["force_hide"] = false;
|
|
|
|
column["caption"] = caption;
|
|
|
|
column["style"] = style;
|
|
|
|
column["width"] = LocalPreferences.get("column_" + name + "_width_" + this.dynamicTableDivId, defaultWidth);
|
|
|
|
column["dataProperties"] = [name];
|
|
|
|
column["getRowValue"] = function(row, pos) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (pos === undefined)
|
|
|
|
pos = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
return row["full_data"][this.dataProperties[pos]];
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["compareRows"] = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value1 = this.getRowValue(row1);
|
|
|
|
const value2 = this.getRowValue(row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
2023-07-18 11:58:05 -04:00
|
|
|
return compareNumbers(value1, value2);
|
|
|
|
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["updateTd"] = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["onResize"] = null;
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns.push(column);
|
|
|
|
this.columns[name] = column;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hiddenTableHeader.appendChild(new Element("th"));
|
|
|
|
this.fixedTableHeader.appendChild(new Element("th"));
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
loadColumnsOrder: function() {
|
|
|
|
const columnsOrder = [];
|
2024-10-03 08:37:11 -04:00
|
|
|
const val = LocalPreferences.get("columns_order_" + this.dynamicTableDivId);
|
|
|
|
if ((val === null) || (val === undefined))
|
2022-10-18 22:39:32 -04:00
|
|
|
return;
|
2024-10-03 08:37:11 -04:00
|
|
|
val.split(",").forEach((v) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
if ((v in this.columns) && (!columnsOrder.contains(v)))
|
|
|
|
columnsOrder.push(v);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (!columnsOrder.contains(this.columns[i].name))
|
|
|
|
columnsOrder.push(this.columns[i].name);
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
for (let i = 0; i < this.columns.length; ++i)
|
|
|
|
this.columns[i] = this.columns[columnsOrder[i]];
|
|
|
|
},
|
|
|
|
|
|
|
|
saveColumnsOrder: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
let val = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
|
|
if (i > 0)
|
2024-10-03 08:37:11 -04:00
|
|
|
val += ",";
|
2022-10-18 22:39:32 -04:00
|
|
|
val += this.columns[i].name;
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
LocalPreferences.set("columns_order_" + this.dynamicTableDivId, val);
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
updateTableHeaders: function() {
|
|
|
|
this.updateHeader(this.hiddenTableHeader);
|
|
|
|
this.updateHeader(this.fixedTableHeader);
|
|
|
|
},
|
|
|
|
|
|
|
|
updateHeader: function(header) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const ths = header.getElements("th");
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
for (let i = 0; i < ths.length; ++i) {
|
|
|
|
const th = ths[i];
|
|
|
|
th._this = this;
|
2024-10-03 08:37:11 -04:00
|
|
|
th.title = this.columns[i].caption;
|
|
|
|
th.textContent = this.columns[i].caption;
|
|
|
|
th.setAttribute("style", "width: " + this.columns[i].width + "px;" + this.columns[i].style);
|
2022-10-18 22:39:32 -04:00
|
|
|
th.columnName = this.columns[i].name;
|
2024-10-03 08:37:11 -04:00
|
|
|
th.addClass("column_" + th.columnName);
|
|
|
|
if ((this.columns[i].visible === "0") || this.columns[i].force_hide)
|
|
|
|
th.addClass("invisible");
|
2022-10-18 22:39:32 -04:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
th.removeClass("invisible");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getColumnPos: function(columnName) {
|
2024-10-03 08:37:11 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
|
|
if (this.columns[i].name === columnName)
|
2022-10-18 22:39:32 -04:00
|
|
|
return i;
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
return -1;
|
|
|
|
},
|
|
|
|
|
|
|
|
updateColumn: function(columnName) {
|
|
|
|
const pos = this.getColumnPos(columnName);
|
2024-10-03 08:37:11 -04:00
|
|
|
const visible = ((this.columns[pos].visible !== "0") && !this.columns[pos].force_hide);
|
|
|
|
const ths = this.hiddenTableHeader.getElements("th");
|
|
|
|
const fths = this.fixedTableHeader.getElements("th");
|
|
|
|
const trs = this.tableBody.getElements("tr");
|
|
|
|
const style = "width: " + this.columns[pos].width + "px;" + this.columns[pos].style;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
ths[pos].setAttribute("style", style);
|
|
|
|
fths[pos].setAttribute("style", style);
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
if (visible) {
|
2024-10-03 08:37:11 -04:00
|
|
|
ths[pos].removeClass("invisible");
|
|
|
|
fths[pos].removeClass("invisible");
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < trs.length; ++i)
|
2024-10-03 08:37:11 -04:00
|
|
|
trs[i].getElements("td")[pos].removeClass("invisible");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
ths[pos].addClass("invisible");
|
|
|
|
fths[pos].addClass("invisible");
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let j = 0; j < trs.length; ++j)
|
2024-10-03 08:37:11 -04:00
|
|
|
trs[j].getElements("td")[pos].addClass("invisible");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.columns[pos].onResize !== null)
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns[pos].onResize(columnName);
|
|
|
|
},
|
|
|
|
|
|
|
|
getSortedColumn: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
return LocalPreferences.get("sorted_column_" + this.dynamicTableDivId);
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
/**
|
|
|
|
* @param {string} column name to sort by
|
|
|
|
* @param {string|null} reverse defaults to implementation-specific behavior when not specified. Should only be passed when restoring previous state.
|
|
|
|
*/
|
|
|
|
setSortedColumn: function(column, reverse = null) {
|
|
|
|
if (column !== this.sortedColumn) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const oldColumn = this.sortedColumn;
|
|
|
|
this.sortedColumn = column;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.reverseSort = reverse ?? "0";
|
2022-10-18 22:39:32 -04:00
|
|
|
this.setSortedColumnIcon(column, oldColumn, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Toggle sort order
|
2024-10-03 08:37:11 -04:00
|
|
|
this.reverseSort = reverse ?? (this.reverseSort === "0" ? "1" : "0");
|
|
|
|
this.setSortedColumnIcon(column, null, (this.reverseSort === "1"));
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
LocalPreferences.set("sorted_column_" + this.dynamicTableDivId, column);
|
|
|
|
LocalPreferences.set("reverse_sort_" + this.dynamicTableDivId, this.reverseSort);
|
2022-10-18 22:39:32 -04:00
|
|
|
this.updateTable(false);
|
|
|
|
},
|
|
|
|
|
|
|
|
setSortedColumnIcon: function(newColumn, oldColumn, isReverse) {
|
|
|
|
const getCol = function(headerDivId, colName) {
|
|
|
|
const colElem = $$("#" + headerDivId + " .column_" + colName);
|
2024-10-03 08:37:11 -04:00
|
|
|
if (colElem.length === 1)
|
2022-10-18 22:39:32 -04:00
|
|
|
return colElem[0];
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
const colElem = getCol(this.dynamicTableFixedHeaderDivId, newColumn);
|
|
|
|
if (colElem !== null) {
|
2024-10-03 08:37:11 -04:00
|
|
|
colElem.addClass("sorted");
|
2022-10-18 22:39:32 -04:00
|
|
|
if (isReverse)
|
2024-10-03 08:37:11 -04:00
|
|
|
colElem.addClass("reverse");
|
2022-10-18 22:39:32 -04:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
colElem.removeClass("reverse");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
const oldColElem = getCol(this.dynamicTableFixedHeaderDivId, oldColumn);
|
|
|
|
if (oldColElem !== null) {
|
2024-10-03 08:37:11 -04:00
|
|
|
oldColElem.removeClass("sorted");
|
|
|
|
oldColElem.removeClass("reverse");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getSelectedRowId: function() {
|
|
|
|
if (this.selectedRows.length > 0)
|
|
|
|
return this.selectedRows[0];
|
2024-10-03 08:37:11 -04:00
|
|
|
return "";
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
isRowSelected: function(rowId) {
|
|
|
|
return this.selectedRows.contains(rowId);
|
|
|
|
},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
setupAltRow: function() {
|
|
|
|
const useAltRowColors = (LocalPreferences.get("use_alt_row_colors", "true") === "true");
|
|
|
|
if (useAltRowColors)
|
|
|
|
document.getElementById(this.dynamicTableDivId).classList.add("altRowColors");
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
selectAll: function() {
|
|
|
|
this.deselectAll();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const trs = this.tableBody.getElements("tr");
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < trs.length; ++i) {
|
|
|
|
const tr = trs[i];
|
|
|
|
this.selectedRows.push(tr.rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
if (!tr.hasClass("selected"))
|
|
|
|
tr.addClass("selected");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
deselectAll: function() {
|
|
|
|
this.selectedRows.empty();
|
|
|
|
},
|
|
|
|
|
|
|
|
selectRow: function(rowId) {
|
|
|
|
this.selectedRows.push(rowId);
|
|
|
|
this.setRowClass();
|
|
|
|
this.onSelectedRowChanged();
|
|
|
|
},
|
|
|
|
|
|
|
|
deselectRow: function(rowId) {
|
|
|
|
this.selectedRows.erase(rowId);
|
|
|
|
this.setRowClass();
|
|
|
|
this.onSelectedRowChanged();
|
|
|
|
},
|
|
|
|
|
|
|
|
selectRows: function(rowId1, rowId2) {
|
|
|
|
this.deselectAll();
|
|
|
|
if (rowId1 === rowId2) {
|
|
|
|
this.selectRow(rowId1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let select = false;
|
|
|
|
const that = this;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.tableBody.getElements("tr").each((tr) => {
|
|
|
|
if ((tr.rowId === rowId1) || (tr.rowId === rowId2)) {
|
2022-10-18 22:39:32 -04:00
|
|
|
select = !select;
|
|
|
|
that.selectedRows.push(tr.rowId);
|
|
|
|
}
|
|
|
|
else if (select) {
|
|
|
|
that.selectedRows.push(tr.rowId);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.setRowClass();
|
|
|
|
this.onSelectedRowChanged();
|
|
|
|
},
|
|
|
|
|
|
|
|
reselectRows: function(rowIds) {
|
|
|
|
this.deselectAll();
|
|
|
|
this.selectedRows = rowIds.slice();
|
2024-10-03 08:37:11 -04:00
|
|
|
this.tableBody.getElements("tr").each((tr) => {
|
|
|
|
if (rowIds.includes(tr.rowId))
|
|
|
|
tr.addClass("selected");
|
2022-10-18 22:39:32 -04:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
setRowClass: function() {
|
|
|
|
const that = this;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.tableBody.getElements("tr").each((tr) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (that.isRowSelected(tr.rowId))
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addClass("selected");
|
2022-10-18 22:39:32 -04:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.removeClass("selected");
|
2022-10-18 22:39:32 -04:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onSelectedRowChanged: function() {},
|
|
|
|
|
|
|
|
updateRowData: function(data) {
|
|
|
|
// ensure rowId is a string
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowId = `${data["rowId"]}`;
|
2022-10-18 22:39:32 -04:00
|
|
|
let row;
|
|
|
|
|
|
|
|
if (!this.rows.has(rowId)) {
|
|
|
|
row = {
|
2024-10-03 08:37:11 -04:00
|
|
|
"full_data": {},
|
|
|
|
"rowId": rowId
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
this.rows.set(rowId, row);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
else {
|
2022-10-18 22:39:32 -04:00
|
|
|
row = this.rows.get(rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
row["data"] = data;
|
|
|
|
for (const x in data) {
|
|
|
|
if (!Object.hasOwn(data, x))
|
|
|
|
continue;
|
|
|
|
row["full_data"][x] = data[x];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getRow: function(rowId) {
|
|
|
|
return this.rows.get(rowId);
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
|
|
const filteredRows = [];
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
for (const row of this.getRowValues()) {
|
|
|
|
filteredRows.push(row);
|
|
|
|
filteredRows[row.rowId] = row;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows.sort((row1, row2) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
const column = this.columns[this.sortedColumn];
|
|
|
|
const res = column.compareRows(row1, row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.reverseSort === "0")
|
2022-10-18 22:39:32 -04:00
|
|
|
return res;
|
|
|
|
else
|
|
|
|
return -res;
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
return filteredRows;
|
|
|
|
},
|
|
|
|
|
|
|
|
getTrByRowId: function(rowId) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const trs = this.tableBody.getElements("tr");
|
|
|
|
for (let i = 0; i < trs.length; ++i) {
|
|
|
|
if (trs[i].rowId === rowId)
|
2022-10-18 22:39:32 -04:00
|
|
|
return trs[i];
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
updateTable: function(fullUpdate = false) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const rows = this.getFilteredAndSortedRows();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
for (let i = 0; i < this.selectedRows.length; ++i) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (!(this.selectedRows[i] in rows)) {
|
|
|
|
this.selectedRows.splice(i, 1);
|
|
|
|
--i;
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const trs = this.tableBody.getElements("tr");
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
for (let rowPos = 0; rowPos < rows.length; ++rowPos) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowId = rows[rowPos]["rowId"];
|
2022-10-18 22:39:32 -04:00
|
|
|
let tr_found = false;
|
2024-10-03 08:37:11 -04:00
|
|
|
for (let j = rowPos; j < trs.length; ++j) {
|
|
|
|
if (trs[j]["rowId"] === rowId) {
|
2022-10-18 22:39:32 -04:00
|
|
|
tr_found = true;
|
2024-10-03 08:37:11 -04:00
|
|
|
if (rowPos === j)
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
trs[j].inject(trs[rowPos], "before");
|
2022-10-18 22:39:32 -04:00
|
|
|
const tmpTr = trs[j];
|
|
|
|
trs.splice(j, 1);
|
|
|
|
trs.splice(rowPos, 0, tmpTr);
|
|
|
|
break;
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
|
|
|
if (tr_found) { // row already exists in the table
|
2022-10-18 22:39:32 -04:00
|
|
|
this.updateRow(trs[rowPos], fullUpdate);
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
else { // else create a new row in the table
|
2024-10-03 08:37:11 -04:00
|
|
|
const tr = new Element("tr");
|
2022-10-18 22:39:32 -04:00
|
|
|
// set tabindex so element receives keydown events
|
|
|
|
// more info: https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.tabindex = "-1";
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowId = rows[rowPos]["rowId"];
|
|
|
|
tr.setAttribute("data-row-id", rowId);
|
|
|
|
tr["rowId"] = rowId;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
tr._this = this;
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("contextmenu", function(e) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (!this._this.isRowSelected(this.rowId)) {
|
|
|
|
this._this.deselectAll();
|
|
|
|
this._this.selectRow(this.rowId);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("click", function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
if (e.ctrlKey || e.metaKey) {
|
2022-10-18 22:39:32 -04:00
|
|
|
// CTRL/CMD ⌘ key was pressed
|
|
|
|
if (this._this.isRowSelected(this.rowId))
|
|
|
|
this._this.deselectRow(this.rowId);
|
|
|
|
else
|
|
|
|
this._this.selectRow(this.rowId);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
else if (e.shiftKey && (this._this.selectedRows.length === 1)) {
|
2022-10-18 22:39:32 -04:00
|
|
|
// Shift key was pressed
|
|
|
|
this._this.selectRows(this._this.getSelectedRowId(), this.rowId);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Simple selection
|
|
|
|
this._this.deselectAll();
|
|
|
|
this._this.selectRow(this.rowId);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("touchstart", function(e) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (!this._this.isRowSelected(this.rowId)) {
|
|
|
|
this._this.deselectAll();
|
|
|
|
this._this.selectRow(this.rowId);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
}, { passive: true });
|
|
|
|
tr.addEventListener("keydown", function(event) {
|
2022-10-18 22:39:32 -04:00
|
|
|
switch (event.key) {
|
|
|
|
case "up":
|
|
|
|
this._this.selectPreviousRow();
|
|
|
|
return false;
|
|
|
|
case "down":
|
|
|
|
this._this.selectNextRow();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
this.setupTr(tr);
|
|
|
|
|
|
|
|
for (let k = 0; k < this.columns.length; ++k) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const td = new Element("td");
|
|
|
|
if ((this.columns[k].visible === "0") || this.columns[k].force_hide)
|
|
|
|
td.addClass("invisible");
|
2022-10-18 22:39:32 -04:00
|
|
|
td.injectInside(tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert
|
|
|
|
if (rowPos >= trs.length) {
|
|
|
|
tr.inject(this.tableBody);
|
|
|
|
trs.push(tr);
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.inject(trs[rowPos], "before");
|
2022-10-18 22:39:32 -04:00
|
|
|
trs.splice(rowPos, 0, tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update context menu
|
|
|
|
if (this.contextMenu)
|
|
|
|
this.contextMenu.addTarget(tr);
|
|
|
|
|
|
|
|
this.updateRow(tr, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowPos = rows.length;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
while ((rowPos < trs.length) && (trs.length > 0))
|
2024-01-07 17:04:03 -05:00
|
|
|
trs.pop().destroy();
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
setupTr: function(tr) {},
|
|
|
|
|
|
|
|
updateRow: function(tr, fullUpdate) {
|
|
|
|
const row = this.rows.get(tr.rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
const data = row[fullUpdate ? "full_data" : "data"];
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const tds = tr.getElements("td");
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (Object.hasOwn(data, this.columns[i].dataProperties[0]))
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns[i].updateTd(tds[i], row);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
row["data"] = {};
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
removeRow: function(rowId) {
|
|
|
|
this.selectedRows.erase(rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
this.rows.delete(rowId);
|
2022-10-18 22:39:32 -04:00
|
|
|
const tr = this.getTrByRowId(rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
tr?.destroy();
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
clear: function() {
|
|
|
|
this.deselectAll();
|
2024-10-01 14:25:40 -04:00
|
|
|
this.rows.clear();
|
2024-10-03 08:37:11 -04:00
|
|
|
const trs = this.tableBody.getElements("tr");
|
|
|
|
while (trs.length > 0)
|
2024-01-07 17:04:03 -05:00
|
|
|
trs.pop().destroy();
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
selectedRowsIds: function() {
|
|
|
|
return this.selectedRows.slice();
|
|
|
|
},
|
|
|
|
|
|
|
|
getRowIds: function() {
|
2024-10-01 14:25:40 -04:00
|
|
|
return this.rows.keys();
|
|
|
|
},
|
|
|
|
|
|
|
|
getRowValues: function() {
|
|
|
|
return this.rows.values();
|
|
|
|
},
|
|
|
|
|
|
|
|
getRowItems: function() {
|
|
|
|
return this.rows.entries();
|
|
|
|
},
|
|
|
|
|
|
|
|
getRowSize: function() {
|
|
|
|
return this.rows.size;
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
selectNextRow: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const visibleRows = $(this.dynamicTableDivId).getElements("tbody tr").filter(e => e.style.display !== "none");
|
2022-10-18 22:39:32 -04:00
|
|
|
const selectedRowId = this.getSelectedRowId();
|
|
|
|
|
|
|
|
let selectedIndex = -1;
|
|
|
|
for (let i = 0; i < visibleRows.length; ++i) {
|
|
|
|
const row = visibleRows[i];
|
2024-10-03 08:37:11 -04:00
|
|
|
if (row.getAttribute("data-row-id") === selectedRowId) {
|
2022-10-18 22:39:32 -04:00
|
|
|
selectedIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const isLastRowSelected = (selectedIndex >= (visibleRows.length - 1));
|
|
|
|
if (!isLastRowSelected) {
|
|
|
|
this.deselectAll();
|
|
|
|
|
|
|
|
const newRow = visibleRows[selectedIndex + 1];
|
2024-10-03 08:37:11 -04:00
|
|
|
this.selectRow(newRow.getAttribute("data-row-id"));
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
selectPreviousRow: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const visibleRows = $(this.dynamicTableDivId).getElements("tbody tr").filter(e => e.style.display !== "none");
|
2022-10-18 22:39:32 -04:00
|
|
|
const selectedRowId = this.getSelectedRowId();
|
|
|
|
|
|
|
|
let selectedIndex = -1;
|
|
|
|
for (let i = 0; i < visibleRows.length; ++i) {
|
|
|
|
const row = visibleRows[i];
|
2024-10-03 08:37:11 -04:00
|
|
|
if (row.getAttribute("data-row-id") === selectedRowId) {
|
2022-10-18 22:39:32 -04:00
|
|
|
selectedIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const isFirstRowSelected = selectedIndex <= 0;
|
|
|
|
if (!isFirstRowSelected) {
|
|
|
|
this.deselectAll();
|
|
|
|
|
|
|
|
const newRow = visibleRows[selectedIndex - 1];
|
2024-10-03 08:37:11 -04:00
|
|
|
this.selectRow(newRow.getAttribute("data-row-id"));
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const TorrentsTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("priority", "", "#", 30, true);
|
|
|
|
this.newColumn("state_icon", "cursor: default", "", 22, true);
|
|
|
|
this.newColumn("name", "", "Name", 200, true);
|
|
|
|
this.newColumn("size", "", "Size", 100, true);
|
|
|
|
this.newColumn("total_size", "", "Total Size", 100, false);
|
|
|
|
this.newColumn("progress", "", "Progress", 85, true);
|
|
|
|
this.newColumn("status", "", "Status", 100, true);
|
|
|
|
this.newColumn("num_seeds", "", "Seeds", 100, true);
|
|
|
|
this.newColumn("num_leechs", "", "Peers", 100, true);
|
|
|
|
this.newColumn("dlspeed", "", "Down Speed", 100, true);
|
|
|
|
this.newColumn("upspeed", "", "Up Speed", 100, true);
|
|
|
|
this.newColumn("eta", "", "ETA", 100, true);
|
|
|
|
this.newColumn("ratio", "", "Ratio", 100, true);
|
|
|
|
this.newColumn("popularity", "", "Popularity", 100, true);
|
|
|
|
this.newColumn("category", "", "Category", 100, true);
|
|
|
|
this.newColumn("tags", "", "Tags", 100, true);
|
|
|
|
this.newColumn("added_on", "", "Added On", 100, true);
|
|
|
|
this.newColumn("completion_on", "", "Completed On", 100, false);
|
|
|
|
this.newColumn("tracker", "", "Tracker", 100, false);
|
|
|
|
this.newColumn("dl_limit", "", "Down Limit", 100, false);
|
|
|
|
this.newColumn("up_limit", "", "Up Limit", 100, false);
|
|
|
|
this.newColumn("downloaded", "", "Downloaded", 100, false);
|
|
|
|
this.newColumn("uploaded", "", "Uploaded", 100, false);
|
|
|
|
this.newColumn("downloaded_session", "", "Session Download", 100, false);
|
|
|
|
this.newColumn("uploaded_session", "", "Session Upload", 100, false);
|
|
|
|
this.newColumn("amount_left", "", "Remaining", 100, false);
|
|
|
|
this.newColumn("time_active", "", "Time Active", 100, false);
|
|
|
|
this.newColumn("save_path", "", "Save path", 100, false);
|
|
|
|
this.newColumn("completed", "", "Completed", 100, false);
|
|
|
|
this.newColumn("max_ratio", "", "Ratio Limit", 100, false);
|
|
|
|
this.newColumn("seen_complete", "", "Last Seen Complete", 100, false);
|
|
|
|
this.newColumn("last_activity", "", "Last Activity", 100, false);
|
|
|
|
this.newColumn("availability", "", "Availability", 100, false);
|
|
|
|
this.newColumn("download_path", "", "Incomplete Save Path", 100, false);
|
|
|
|
this.newColumn("infohash_v1", "", "Info Hash v1", 100, false);
|
|
|
|
this.newColumn("infohash_v2", "", "Info Hash v2", 100, false);
|
|
|
|
this.newColumn("reannounce", "", "Reannounce In", 100, false);
|
|
|
|
this.newColumn("private", "", "Private", 100, false);
|
|
|
|
|
|
|
|
this.columns["state_icon"].onclick = "";
|
|
|
|
this.columns["state_icon"].dataProperties[0] = "state";
|
|
|
|
|
|
|
|
this.columns["num_seeds"].dataProperties.push("num_complete");
|
|
|
|
this.columns["num_leechs"].dataProperties.push("num_incomplete");
|
|
|
|
this.columns["time_active"].dataProperties.push("seeding_time");
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
this.initColumnsFunctions();
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumnsFunctions: function() {
|
|
|
|
|
|
|
|
// state_icon
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["state_icon"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
let state = this.getRowValue(row);
|
|
|
|
let img_path;
|
|
|
|
// normalize states
|
|
|
|
switch (state) {
|
|
|
|
case "forcedDL":
|
|
|
|
case "metaDL":
|
|
|
|
case "forcedMetaDL":
|
|
|
|
case "downloading":
|
|
|
|
state = "downloading";
|
|
|
|
img_path = "images/downloading.svg";
|
|
|
|
break;
|
|
|
|
case "forcedUP":
|
|
|
|
case "uploading":
|
|
|
|
state = "uploading";
|
|
|
|
img_path = "images/upload.svg";
|
|
|
|
break;
|
|
|
|
case "stalledUP":
|
|
|
|
state = "stalledUP";
|
|
|
|
img_path = "images/stalledUP.svg";
|
|
|
|
break;
|
|
|
|
case "stalledDL":
|
|
|
|
state = "stalledDL";
|
|
|
|
img_path = "images/stalledDL.svg";
|
|
|
|
break;
|
2024-10-01 14:25:40 -04:00
|
|
|
case "stoppedDL":
|
2022-10-18 22:39:32 -04:00
|
|
|
state = "torrent-stop";
|
2024-10-01 14:25:40 -04:00
|
|
|
img_path = "images/stopped.svg";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-01 14:25:40 -04:00
|
|
|
case "stoppedUP":
|
2022-10-18 22:39:32 -04:00
|
|
|
state = "checked-completed";
|
|
|
|
img_path = "images/checked-completed.svg";
|
|
|
|
break;
|
|
|
|
case "queuedDL":
|
|
|
|
case "queuedUP":
|
|
|
|
state = "queued";
|
|
|
|
img_path = "images/queued.svg";
|
|
|
|
break;
|
|
|
|
case "checkingDL":
|
|
|
|
case "checkingUP":
|
|
|
|
case "queuedForChecking":
|
|
|
|
case "checkingResumeData":
|
|
|
|
state = "force-recheck";
|
|
|
|
img_path = "images/force-recheck.svg";
|
|
|
|
break;
|
2024-01-07 17:04:03 -05:00
|
|
|
case "moving":
|
|
|
|
state = "moving";
|
|
|
|
img_path = "images/set-location.svg";
|
|
|
|
break;
|
|
|
|
case "error":
|
2022-10-18 22:39:32 -04:00
|
|
|
case "unknown":
|
|
|
|
case "missingFiles":
|
|
|
|
state = "error";
|
|
|
|
img_path = "images/error.svg";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break; // do nothing
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (td.getChildren("img").length > 0) {
|
|
|
|
const img = td.getChildren("img")[0];
|
|
|
|
if (!img.src.includes(img_path)) {
|
|
|
|
img.src = img_path;
|
|
|
|
img.title = state;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.adopt(new Element("img", {
|
|
|
|
"src": img_path,
|
|
|
|
"class": "stateIcon",
|
|
|
|
"title": state
|
2022-10-18 22:39:32 -04:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// status
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["status"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const state = this.getRowValue(row);
|
|
|
|
if (!state)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let status;
|
|
|
|
switch (state) {
|
|
|
|
case "downloading":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Downloading";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "stalledDL":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Stalled";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "metaDL":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Downloading metadata";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "forcedMetaDL":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "[F] Downloading metadata";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "forcedDL":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "[F] Downloading";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "uploading":
|
|
|
|
case "stalledUP":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Seeding";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "forcedUP":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "[F] Seeding";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "queuedDL":
|
|
|
|
case "queuedUP":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Queued";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "checkingDL":
|
|
|
|
case "checkingUP":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Checking";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "queuedForChecking":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Queued for checking";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "checkingResumeData":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Checking resume data";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-01 14:25:40 -04:00
|
|
|
case "stoppedDL":
|
|
|
|
status = "Stopped";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-01 14:25:40 -04:00
|
|
|
case "stoppedUP":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Completed";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "moving":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Moving";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "missingFiles":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Missing Files";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
case "error":
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Errored";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
default:
|
2022-11-30 13:49:48 -05:00
|
|
|
status = "Unknown";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = status;
|
|
|
|
td.title = status;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// priority
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["priority"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const queuePos = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
const formattedQueuePos = (queuePos < 1) ? "*" : queuePos;
|
|
|
|
td.textContent = formattedQueuePos;
|
|
|
|
td.title = formattedQueuePos;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["priority"].compareRows = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
let row1_val = this.getRowValue(row1);
|
|
|
|
let row2_val = this.getRowValue(row2);
|
|
|
|
if (row1_val < 1)
|
|
|
|
row1_val = 1000000;
|
|
|
|
if (row2_val < 1)
|
|
|
|
row2_val = 1000000;
|
2023-07-18 11:58:05 -04:00
|
|
|
return compareNumbers(row1_val, row2_val);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// name, category, tags
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["name"].compareRows = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const row1Val = this.getRowValue(row1);
|
|
|
|
const row2Val = this.getRowValue(row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
return row1Val.localeCompare(row2Val, undefined, { numeric: true, sensitivity: "base" });
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["category"].compareRows = this.columns["name"].compareRows;
|
|
|
|
this.columns["tags"].compareRows = this.columns["name"].compareRows;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// size, total_size
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["size"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const size = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = size;
|
|
|
|
td.title = size;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["total_size"].updateTd = this.columns["size"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// progress
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["progress"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const progress = this.getRowValue(row);
|
2023-07-18 11:58:05 -04:00
|
|
|
let progressFormatted = (progress * 100).round(1);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((progressFormatted === 100.0) && (progress !== 1.0))
|
2023-07-18 11:58:05 -04:00
|
|
|
progressFormatted = 99.9;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (td.getChildren("div").length > 0) {
|
|
|
|
const div = td.getChildren("div")[0];
|
2022-10-18 22:39:32 -04:00
|
|
|
if (td.resized) {
|
|
|
|
td.resized = false;
|
|
|
|
div.setWidth(ProgressColumnWidth - 5);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
if (div.getValue() !== progressFormatted)
|
2023-07-18 11:58:05 -04:00
|
|
|
div.setValue(progressFormatted);
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (ProgressColumnWidth < 0)
|
|
|
|
ProgressColumnWidth = td.offsetWidth;
|
2023-07-18 11:58:05 -04:00
|
|
|
td.adopt(new window.qBittorrent.ProgressBar.ProgressBar(progressFormatted.toFloat(), {
|
2024-10-03 08:37:11 -04:00
|
|
|
"width": ProgressColumnWidth - 5
|
2022-10-18 22:39:32 -04:00
|
|
|
}));
|
|
|
|
td.resized = false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["progress"].onResize = function(columnName) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const pos = this.getColumnPos(columnName);
|
2024-10-03 08:37:11 -04:00
|
|
|
const trs = this.tableBody.getElements("tr");
|
2022-10-18 22:39:32 -04:00
|
|
|
ProgressColumnWidth = -1;
|
|
|
|
for (let i = 0; i < trs.length; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const td = trs[i].getElements("td")[pos];
|
2022-10-18 22:39:32 -04:00
|
|
|
if (ProgressColumnWidth < 0)
|
|
|
|
ProgressColumnWidth = td.offsetWidth;
|
|
|
|
td.resized = true;
|
|
|
|
this.columns[columnName].updateTd(td, this.rows.get(trs[i].rowId));
|
|
|
|
}
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
// num_seeds
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["num_seeds"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const num_seeds = this.getRowValue(row, 0);
|
|
|
|
const num_complete = this.getRowValue(row, 1);
|
|
|
|
let value = num_seeds;
|
2024-10-03 08:37:11 -04:00
|
|
|
if (num_complete !== -1)
|
|
|
|
value += " (" + num_complete + ")";
|
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["num_seeds"].compareRows = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const num_seeds1 = this.getRowValue(row1, 0);
|
|
|
|
const num_complete1 = this.getRowValue(row1, 1);
|
|
|
|
|
|
|
|
const num_seeds2 = this.getRowValue(row2, 0);
|
|
|
|
const num_complete2 = this.getRowValue(row2, 1);
|
|
|
|
|
2023-07-18 11:58:05 -04:00
|
|
|
const result = compareNumbers(num_complete1, num_complete2);
|
|
|
|
if (result !== 0)
|
|
|
|
return result;
|
|
|
|
return compareNumbers(num_seeds1, num_seeds2);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// num_leechs
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["num_leechs"].updateTd = this.columns["num_seeds"].updateTd;
|
|
|
|
this.columns["num_leechs"].compareRows = this.columns["num_seeds"].compareRows;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// dlspeed
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["dlspeed"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const speed = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), true);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = speed;
|
|
|
|
td.title = speed;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// upspeed
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["upspeed"].updateTd = this.columns["dlspeed"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// eta
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["eta"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const eta = window.qBittorrent.Misc.friendlyDuration(this.getRowValue(row), window.qBittorrent.Misc.MAX_ETA);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = eta;
|
|
|
|
td.title = eta;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// ratio
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["ratio"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const ratio = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
const string = (ratio === -1) ? "∞" : window.qBittorrent.Misc.toFixedPointString(ratio, 2);
|
|
|
|
td.textContent = string;
|
|
|
|
td.title = string;
|
2024-10-03 13:17:34 -04:00
|
|
|
// ratio colors by percentage
|
|
|
|
td.classList.add('ratio');
|
|
|
|
if (string === 0 || string === "0.00" || string <= "0.09") {
|
|
|
|
td.classList.add('highlight-darkred');
|
|
|
|
} else if (string >= "0.10" && string <= "0.19") {
|
|
|
|
td.classList.add('highlight-red');
|
|
|
|
} else if (string >= "0.20" && string <= "0.39") {
|
|
|
|
td.classList.add('highlight-orange');
|
|
|
|
} else if (string >= "0.40" && string <= "0.59") {
|
|
|
|
td.classList.add('highlight-yellow');
|
|
|
|
} else if (string >= "0.60" && string <= "0.89") {
|
|
|
|
td.classList.add('highlight-green');
|
|
|
|
} else if (string >= "0.90" && string <= "0.99") {
|
|
|
|
td.classList.add('highlight-brightgreen');
|
|
|
|
} else if (string >= "1.0" && string <= "1.5") {
|
|
|
|
td.classList.add('highlight-blue');
|
|
|
|
} else if (string >= "1.5") {
|
|
|
|
td.classList.add('highlight-purple');
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// popularity
|
|
|
|
this.columns["popularity"].updateTd = function(td, row) {
|
|
|
|
const value = this.getRowValue(row);
|
|
|
|
const popularity = (value === -1) ? "∞" : window.qBittorrent.Misc.toFixedPointString(value, 2);
|
|
|
|
td.textContent = popularity;
|
|
|
|
td.title = popularity;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// added on
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["added_on"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = date;
|
|
|
|
td.title = date;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// completion_on
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["completion_on"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const val = this.getRowValue(row);
|
|
|
|
if ((val === 0xffffffff) || (val < 0)) {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = "";
|
|
|
|
td.title = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = date;
|
|
|
|
td.title = date;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// tracker
|
|
|
|
this.columns["tracker"].updateTd = function(td, row) {
|
|
|
|
const value = this.getRowValue(row);
|
|
|
|
const tracker = displayFullURLTrackerColumn ? value : window.qBittorrent.Misc.getHost(value);
|
|
|
|
td.textContent = tracker;
|
|
|
|
td.title = value;
|
|
|
|
};
|
|
|
|
|
2022-10-18 22:39:32 -04:00
|
|
|
// dl_limit, up_limit
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["dl_limit"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const speed = this.getRowValue(row);
|
|
|
|
if (speed === 0) {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = "∞";
|
|
|
|
td.title = "∞";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const formattedSpeed = window.qBittorrent.Misc.friendlyUnit(speed, true);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = formattedSpeed;
|
|
|
|
td.title = formattedSpeed;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["up_limit"].updateTd = this.columns["dl_limit"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// downloaded, uploaded, downloaded_session, uploaded_session, amount_left
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["downloaded"].updateTd = this.columns["size"].updateTd;
|
|
|
|
this.columns["uploaded"].updateTd = this.columns["size"].updateTd;
|
|
|
|
this.columns["downloaded_session"].updateTd = this.columns["size"].updateTd;
|
|
|
|
this.columns["uploaded_session"].updateTd = this.columns["size"].updateTd;
|
|
|
|
this.columns["amount_left"].updateTd = this.columns["size"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// time active
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["time_active"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const activeTime = this.getRowValue(row, 0);
|
|
|
|
const seedingTime = this.getRowValue(row, 1);
|
|
|
|
const time = (seedingTime > 0)
|
2024-10-03 08:37:11 -04:00
|
|
|
? ("%1 (seeded for %2)"
|
|
|
|
.replace("%1", window.qBittorrent.Misc.friendlyDuration(activeTime))
|
|
|
|
.replace("%2", window.qBittorrent.Misc.friendlyDuration(seedingTime)))
|
2022-10-18 22:39:32 -04:00
|
|
|
: window.qBittorrent.Misc.friendlyDuration(activeTime);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = time;
|
|
|
|
td.title = time;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// completed
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["completed"].updateTd = this.columns["size"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// max_ratio
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["max_ratio"].updateTd = this.columns["ratio"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// seen_complete
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["seen_complete"].updateTd = this.columns["completion_on"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// last_activity
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["last_activity"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const val = this.getRowValue(row);
|
|
|
|
if (val < 1) {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = "∞";
|
|
|
|
td.title = "∞";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
const formattedVal = "%1 ago".replace("%1", window.qBittorrent.Misc.friendlyDuration((new Date() / 1000) - val));
|
|
|
|
td.textContent = formattedVal;
|
|
|
|
td.title = formattedVal;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// availability
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["availability"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = window.qBittorrent.Misc.toFixedPointString(this.getRowValue(row), 3);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
|
|
|
};
|
|
|
|
|
|
|
|
// infohash_v1
|
|
|
|
this.columns["infohash_v1"].updateTd = function(td, row) {
|
|
|
|
const sourceInfohashV1 = this.getRowValue(row);
|
|
|
|
const infohashV1 = (sourceInfohashV1 !== "") ? sourceInfohashV1 : "N/A";
|
|
|
|
td.textContent = infohashV1;
|
|
|
|
td.title = infohashV1;
|
|
|
|
};
|
|
|
|
|
|
|
|
// infohash_v2
|
|
|
|
this.columns["infohash_v2"].updateTd = function(td, row) {
|
|
|
|
const sourceInfohashV2 = this.getRowValue(row);
|
|
|
|
const infohashV2 = (sourceInfohashV2 !== "") ? sourceInfohashV2 : "N/A";
|
|
|
|
td.textContent = infohashV2;
|
|
|
|
td.title = infohashV2;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
// reannounce
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["reannounce"].updateTd = function(td, row) {
|
2024-01-07 17:04:03 -05:00
|
|
|
const time = window.qBittorrent.Misc.friendlyDuration(this.getRowValue(row));
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = time;
|
|
|
|
td.title = time;
|
|
|
|
};
|
|
|
|
|
|
|
|
// private
|
|
|
|
this.columns["private"].updateTd = function(td, row) {
|
|
|
|
const hasMetadata = row["full_data"].has_metadata;
|
|
|
|
const isPrivate = this.getRowValue(row);
|
|
|
|
const string = hasMetadata
|
|
|
|
? (isPrivate
|
|
|
|
? "Yes"
|
|
|
|
: "No")
|
|
|
|
: "N/A";
|
|
|
|
td.textContent = string;
|
|
|
|
td.title = string;
|
2024-01-07 17:04:03 -05:00
|
|
|
};
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
applyFilter: function(row, filterName, categoryHash, tagHash, trackerHash, filterTerms) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const state = row["full_data"].state;
|
2022-10-18 22:39:32 -04:00
|
|
|
let inactive = false;
|
|
|
|
|
|
|
|
switch (filterName) {
|
2024-10-03 08:37:11 -04:00
|
|
|
case "downloading":
|
|
|
|
if ((state !== "downloading") && !state.includes("DL"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "seeding":
|
|
|
|
if ((state !== "uploading") && (state !== "forcedUP") && (state !== "stalledUP") && (state !== "queuedUP") && (state !== "checkingUP"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "completed":
|
|
|
|
if ((state !== "uploading") && !state.includes("UP"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "stopped":
|
2024-10-01 14:25:40 -04:00
|
|
|
if (!state.includes("stopped"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "running":
|
2024-10-01 14:25:40 -04:00
|
|
|
if (state.includes("stopped"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "stalled":
|
|
|
|
if ((state !== "stalledUP") && (state !== "stalledDL"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "stalled_uploading":
|
|
|
|
if (state !== "stalledUP")
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "stalled_downloading":
|
|
|
|
if (state !== "stalledDL")
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "inactive":
|
2022-10-18 22:39:32 -04:00
|
|
|
inactive = true;
|
|
|
|
// fallthrough
|
2024-10-03 08:37:11 -04:00
|
|
|
case "active": {
|
|
|
|
let r;
|
|
|
|
if (state === "stalledDL")
|
|
|
|
r = (row["full_data"].upspeed > 0);
|
2022-10-18 22:39:32 -04:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
r = (state === "metaDL") || (state === "forcedMetaDL") || (state === "downloading") || (state === "forcedDL") || (state === "uploading") || (state === "forcedUP");
|
|
|
|
if (r === inactive)
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
|
|
|
case "checking":
|
|
|
|
if ((state !== "checkingUP") && (state !== "checkingDL") && (state !== "checkingResumeData"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "moving":
|
|
|
|
if (state !== "moving")
|
2024-01-07 17:04:03 -05:00
|
|
|
return false;
|
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "errored":
|
|
|
|
if ((state !== "error") && (state !== "unknown") && (state !== "missingFiles"))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
switch (categoryHash) {
|
|
|
|
case CATEGORIES_ALL:
|
|
|
|
break; // do nothing
|
|
|
|
case CATEGORIES_UNCATEGORIZED:
|
|
|
|
if (row["full_data"].category.length !== 0)
|
|
|
|
return false;
|
|
|
|
break; // do nothing
|
|
|
|
default:
|
|
|
|
if (!useSubcategories) {
|
|
|
|
if (categoryHash !== window.qBittorrent.Misc.genHash(row["full_data"].category))
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const selectedCategory = category_list.get(categoryHash);
|
|
|
|
if (selectedCategory !== undefined) {
|
|
|
|
const selectedCategoryName = selectedCategory.name + "/";
|
|
|
|
const torrentCategoryName = row["full_data"].category + "/";
|
2023-07-18 11:58:05 -04:00
|
|
|
if (!torrentCategoryName.startsWith(selectedCategoryName))
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
|
|
|
break;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
switch (tagHash) {
|
|
|
|
case TAGS_ALL:
|
|
|
|
break; // do nothing
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
case TAGS_UNTAGGED:
|
|
|
|
if (row["full_data"].tags.length !== 0)
|
|
|
|
return false;
|
|
|
|
break; // do nothing
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
default: {
|
|
|
|
const tagHashes = row["full_data"].tags.split(", ").map(tag => window.qBittorrent.Misc.genHash(tag));
|
|
|
|
if (!tagHashes.contains(tagHash))
|
|
|
|
return false;
|
|
|
|
break;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
switch (trackerHash) {
|
2022-10-18 22:39:32 -04:00
|
|
|
case TRACKERS_ALL:
|
|
|
|
break; // do nothing
|
|
|
|
case TRACKERS_TRACKERLESS:
|
2024-10-03 08:37:11 -04:00
|
|
|
if (row["full_data"].trackers_count !== 0)
|
2022-10-18 22:39:32 -04:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
default: {
|
2024-10-03 08:37:11 -04:00
|
|
|
const tracker = trackerList.get(trackerHash);
|
|
|
|
if (tracker) {
|
|
|
|
let found = false;
|
|
|
|
for (const torrents of tracker.trackerTorrentMap.values()) {
|
|
|
|
if (torrents.has(row["full_data"].rowId)) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return false;
|
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((filterTerms !== undefined) && (filterTerms !== null)) {
|
|
|
|
const filterBy = document.getElementById("torrentsFilterSelect").value;
|
|
|
|
const textToSearch = row["full_data"][filterBy].toLowerCase();
|
|
|
|
if (filterTerms instanceof RegExp) {
|
|
|
|
if (!filterTerms.test(textToSearch))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((filterTerms.length > 0) && !window.qBittorrent.Misc.containsAllTerms(textToSearch, filterTerms))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredTorrentsNumber: function(filterName, categoryHash, tagHash, trackerHash) {
|
|
|
|
let cnt = 0;
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
for (const row of this.rows.values()) {
|
|
|
|
if (this.applyFilter(row, filterName, categoryHash, tagHash, trackerHash, null))
|
2022-10-18 22:39:32 -04:00
|
|
|
++cnt;
|
2024-10-01 14:25:40 -04:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
return cnt;
|
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredTorrentsHashes: function(filterName, categoryHash, tagHash, trackerHash) {
|
|
|
|
const rowsHashes = [];
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
for (const row of this.rows.values()) {
|
|
|
|
if (this.applyFilter(row, filterName, categoryHash, tagHash, trackerHash, null))
|
|
|
|
rowsHashes.push(row["rowId"]);
|
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
return rowsHashes;
|
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
|
|
const filteredRows = [];
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
const useRegex = $("torrentsFilterRegexBox").checked;
|
|
|
|
const filterText = $("torrentsFilterInput").value.trim().toLowerCase();
|
|
|
|
let filterTerms;
|
|
|
|
try {
|
|
|
|
filterTerms = (filterText.length > 0)
|
|
|
|
? (useRegex ? new RegExp(filterText) : filterText.split(" "))
|
|
|
|
: null;
|
|
|
|
}
|
|
|
|
catch (e) { // SyntaxError: Invalid regex pattern
|
|
|
|
return filteredRows;
|
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
for (const row of this.rows.values()) {
|
|
|
|
if (this.applyFilter(row, selectedStatus, selectedCategory, selectedTag, selectedTracker, filterTerms)) {
|
|
|
|
filteredRows.push(row);
|
|
|
|
filteredRows[row.rowId] = row;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
filteredRows.sort((row1, row2) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
const column = this.columns[this.sortedColumn];
|
|
|
|
const res = column.compareRows(row1, row2);
|
2024-10-01 14:25:40 -04:00
|
|
|
if (this.reverseSort === "0")
|
2022-10-18 22:39:32 -04:00
|
|
|
return res;
|
|
|
|
else
|
|
|
|
return -res;
|
2024-10-01 14:25:40 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
return filteredRows;
|
|
|
|
},
|
|
|
|
|
|
|
|
setupTr: function(tr) {
|
2024-10-01 14:25:40 -04:00
|
|
|
tr.addEventListener("dblclick", function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
2022-10-18 22:39:32 -04:00
|
|
|
this._this.deselectAll();
|
|
|
|
this._this.selectRow(this.rowId);
|
|
|
|
const row = this._this.rows.get(this.rowId);
|
2024-10-01 14:25:40 -04:00
|
|
|
const state = row["full_data"].state;
|
|
|
|
|
|
|
|
const prefKey =
|
|
|
|
(state !== "uploading")
|
|
|
|
&& (state !== "stoppedUP")
|
|
|
|
&& (state !== "forcedUP")
|
|
|
|
&& (state !== "stalledUP")
|
|
|
|
&& (state !== "queuedUP")
|
|
|
|
&& (state !== "checkingUP")
|
|
|
|
? "dblclick_download"
|
|
|
|
: "dblclick_complete";
|
|
|
|
|
|
|
|
if (LocalPreferences.get(prefKey, "1") !== "1")
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (state.includes("stopped"))
|
2022-10-18 22:39:32 -04:00
|
|
|
startFN();
|
|
|
|
else
|
2024-10-01 14:25:40 -04:00
|
|
|
stopFN();
|
2022-10-18 22:39:32 -04:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
tr.addClass("torrentsTableContextMenuTarget");
|
|
|
|
},
|
|
|
|
|
|
|
|
getCurrentTorrentID: function() {
|
|
|
|
return this.getSelectedRowId();
|
|
|
|
},
|
|
|
|
|
|
|
|
onSelectedRowChanged: function() {
|
|
|
|
updatePropertiesPanel();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const TorrentPeersTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("country", "", "Country/Region", 22, true);
|
|
|
|
this.newColumn("ip", "", "IP", 80, true);
|
|
|
|
this.newColumn("port", "", "Port", 35, true);
|
|
|
|
this.newColumn("connection", "", "Connection", 50, true);
|
|
|
|
this.newColumn("flags", "", "Flags", 50, true);
|
|
|
|
this.newColumn("client", "", "Client", 140, true);
|
|
|
|
this.newColumn("peer_id_client", "", "Peer ID Client", 60, false);
|
|
|
|
this.newColumn("progress", "", "Progress", 50, true);
|
|
|
|
this.newColumn("dl_speed", "", "Down Speed", 50, true);
|
|
|
|
this.newColumn("up_speed", "", "Up Speed", 50, true);
|
|
|
|
this.newColumn("downloaded", "", "Downloaded", 50, true);
|
|
|
|
this.newColumn("uploaded", "", "Uploaded", 50, true);
|
|
|
|
this.newColumn("relevance", "", "Relevance", 30, true);
|
|
|
|
this.newColumn("files", "", "Files", 100, true);
|
|
|
|
|
|
|
|
this.columns["country"].dataProperties.push("country_code");
|
|
|
|
this.columns["flags"].dataProperties.push("flags_desc");
|
2022-10-18 22:39:32 -04:00
|
|
|
this.initColumnsFunctions();
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumnsFunctions: function() {
|
|
|
|
|
|
|
|
// country
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["country"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const country = this.getRowValue(row, 0);
|
|
|
|
const country_code = this.getRowValue(row, 1);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
let span = td.firstElementChild;
|
|
|
|
if (span === null) {
|
|
|
|
span = document.createElement("span");
|
|
|
|
span.classList.add("flags");
|
|
|
|
td.append(span);
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
span.style.backgroundImage = `url('images/flags/${country_code ?? "xx"}.svg')`;
|
|
|
|
span.textContent = country;
|
|
|
|
td.title = country;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// ip
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["ip"].compareRows = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const ip1 = this.getRowValue(row1);
|
|
|
|
const ip2 = this.getRowValue(row2);
|
|
|
|
|
|
|
|
const a = ip1.split(".");
|
|
|
|
const b = ip2.split(".");
|
|
|
|
|
|
|
|
for (let i = 0; i < 4; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (a[i] !== b[i])
|
2022-10-18 22:39:32 -04:00
|
|
|
return a[i] - b[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
// flags
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["flags"].updateTd = function(td, row) {
|
|
|
|
td.textContent = this.getRowValue(row, 0);
|
|
|
|
td.title = this.getRowValue(row, 1);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// progress
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["progress"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const progress = this.getRowValue(row);
|
2023-07-18 11:58:05 -04:00
|
|
|
let progressFormatted = (progress * 100).round(1);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((progressFormatted === 100.0) && (progress !== 1.0))
|
2023-07-18 11:58:05 -04:00
|
|
|
progressFormatted = 99.9;
|
|
|
|
progressFormatted += "%";
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = progressFormatted;
|
|
|
|
td.title = progressFormatted;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// dl_speed, up_speed
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["dl_speed"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const speed = this.getRowValue(row);
|
|
|
|
if (speed === 0) {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = "";
|
|
|
|
td.title = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
const formattedSpeed = window.qBittorrent.Misc.friendlyUnit(speed, true);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = formattedSpeed;
|
|
|
|
td.title = formattedSpeed;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["up_speed"].updateTd = this.columns["dl_speed"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// downloaded, uploaded
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["downloaded"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const downloaded = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = downloaded;
|
|
|
|
td.title = downloaded;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["uploaded"].updateTd = this.columns["downloaded"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// relevance
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["relevance"].updateTd = this.columns["progress"].updateTd;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// files
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["files"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row, 0);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value.replace(/\n/g, ";");
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const SearchResultsTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("fileName", "", "Name", 500, true);
|
|
|
|
this.newColumn("fileSize", "", "Size", 100, true);
|
|
|
|
this.newColumn("nbSeeders", "", "Seeders", 100, true);
|
|
|
|
this.newColumn("nbLeechers", "", "Leechers", 100, true);
|
|
|
|
this.newColumn("engineName", "", "Engine", 100, true);
|
|
|
|
this.newColumn("siteUrl", "", "Engine URL", 250, true);
|
|
|
|
this.newColumn("pubDate", "", "Published On", 200, true);
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
this.initColumnsFunctions();
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumnsFunctions: function() {
|
|
|
|
const displaySize = function(td, row) {
|
|
|
|
const size = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = size;
|
|
|
|
td.title = size;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
const displayNum = function(td, row) {
|
|
|
|
const value = this.getRowValue(row);
|
|
|
|
const formattedValue = (value === "-1") ? "Unknown" : value;
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = formattedValue;
|
|
|
|
td.title = formattedValue;
|
|
|
|
};
|
|
|
|
const displayDate = function(td, row) {
|
|
|
|
const value = this.getRowValue(row) * 1000;
|
|
|
|
const formattedValue = (isNaN(value) || (value <= 0)) ? "" : (new Date(value).toLocaleString());
|
|
|
|
td.textContent = formattedValue;
|
|
|
|
td.title = formattedValue;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["fileSize"].updateTd = displaySize;
|
|
|
|
this.columns["nbSeeders"].updateTd = displayNum;
|
|
|
|
this.columns["nbLeechers"].updateTd = displayNum;
|
|
|
|
this.columns["pubDate"].updateTd = displayDate;
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
|
|
const getSizeFilters = function() {
|
|
|
|
let minSize = (window.qBittorrent.Search.searchSizeFilter.min > 0.00) ? (window.qBittorrent.Search.searchSizeFilter.min * Math.pow(1024, window.qBittorrent.Search.searchSizeFilter.minUnit)) : 0.00;
|
|
|
|
let maxSize = (window.qBittorrent.Search.searchSizeFilter.max > 0.00) ? (window.qBittorrent.Search.searchSizeFilter.max * Math.pow(1024, window.qBittorrent.Search.searchSizeFilter.maxUnit)) : 0.00;
|
|
|
|
|
|
|
|
if ((minSize > maxSize) && (maxSize > 0.00)) {
|
|
|
|
const tmp = minSize;
|
|
|
|
minSize = maxSize;
|
|
|
|
maxSize = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
min: minSize,
|
|
|
|
max: maxSize
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const getSeedsFilters = function() {
|
|
|
|
let minSeeds = (window.qBittorrent.Search.searchSeedsFilter.min > 0) ? window.qBittorrent.Search.searchSeedsFilter.min : 0;
|
|
|
|
let maxSeeds = (window.qBittorrent.Search.searchSeedsFilter.max > 0) ? window.qBittorrent.Search.searchSeedsFilter.max : 0;
|
|
|
|
|
|
|
|
if ((minSeeds > maxSeeds) && (maxSeeds > 0)) {
|
|
|
|
const tmp = minSeeds;
|
|
|
|
minSeeds = maxSeeds;
|
|
|
|
maxSeeds = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
min: minSeeds,
|
|
|
|
max: maxSeeds
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
let filteredRows = [];
|
|
|
|
const searchTerms = window.qBittorrent.Search.searchText.pattern.toLowerCase().split(" ");
|
|
|
|
const filterTerms = window.qBittorrent.Search.searchText.filterPattern.toLowerCase().split(" ");
|
|
|
|
const sizeFilters = getSizeFilters();
|
|
|
|
const seedsFilters = getSeedsFilters();
|
2024-10-03 08:37:11 -04:00
|
|
|
const searchInTorrentName = $("searchInTorrentName").value === "names";
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
if (searchInTorrentName || (filterTerms.length > 0) || (window.qBittorrent.Search.searchSizeFilter.min > 0.00) || (window.qBittorrent.Search.searchSizeFilter.max > 0.00)) {
|
2024-10-03 08:37:11 -04:00
|
|
|
for (const row of this.getRowValues()) {
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
if (searchInTorrentName && !window.qBittorrent.Misc.containsAllTerms(row.full_data.fileName, searchTerms))
|
|
|
|
continue;
|
|
|
|
if ((filterTerms.length > 0) && !window.qBittorrent.Misc.containsAllTerms(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 {
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows = [...this.getRowValues()];
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows.sort((row1, row2) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
const column = this.columns[this.sortedColumn];
|
|
|
|
const res = column.compareRows(row1, row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.reverseSort === "0")
|
2022-10-18 22:39:32 -04:00
|
|
|
return res;
|
|
|
|
else
|
|
|
|
return -res;
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
return filteredRows;
|
|
|
|
},
|
|
|
|
|
|
|
|
setupTr: function(tr) {
|
|
|
|
tr.addClass("searchTableRow");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const SearchPluginsTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("fullName", "", "Name", 175, true);
|
|
|
|
this.newColumn("version", "", "Version", 100, true);
|
|
|
|
this.newColumn("url", "", "Url", 175, true);
|
|
|
|
this.newColumn("enabled", "", "Enabled", 100, true);
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
this.initColumnsFunctions();
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumnsFunctions: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["enabled"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row);
|
|
|
|
if (value) {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = "Yes";
|
|
|
|
td.title = "Yes";
|
2022-10-18 22:39:32 -04:00
|
|
|
td.getParent("tr").addClass("green");
|
|
|
|
td.getParent("tr").removeClass("red");
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = "No";
|
|
|
|
td.title = "No";
|
2022-10-18 22:39:32 -04:00
|
|
|
td.getParent("tr").addClass("red");
|
|
|
|
td.getParent("tr").removeClass("green");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
setupTr: function(tr) {
|
|
|
|
tr.addClass("searchPluginsTableRow");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const TorrentTrackersTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("tier", "", "Tier", 35, true);
|
|
|
|
this.newColumn("url", "", "URL", 250, true);
|
|
|
|
this.newColumn("status", "", "Status", 125, true);
|
|
|
|
this.newColumn("peers", "", "Peers", 75, true);
|
|
|
|
this.newColumn("seeds", "", "Seeds", 75, true);
|
|
|
|
this.newColumn("leeches", "", "Leeches", 75, true);
|
|
|
|
this.newColumn("downloaded", "", "Times Downloaded", 100, true);
|
|
|
|
this.newColumn("message", "", "Message", 250, true);
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-07-18 11:58:05 -04:00
|
|
|
const BulkRenameTorrentFilesTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
filterTerms: [],
|
|
|
|
prevFilterTerms: [],
|
|
|
|
prevRowsString: null,
|
|
|
|
prevFilteredRows: [],
|
|
|
|
prevSortedColumn: null,
|
|
|
|
prevReverseSort: null,
|
|
|
|
fileTree: new window.qBittorrent.FileTree.FileTree(),
|
|
|
|
|
|
|
|
populateTable: function(root) {
|
|
|
|
this.fileTree.setRoot(root);
|
2024-10-03 08:37:11 -04:00
|
|
|
root.children.each((node) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
this._addNodeToTable(node, 0);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_addNodeToTable: function(node, depth) {
|
|
|
|
node.depth = depth;
|
|
|
|
|
|
|
|
if (node.isFolder) {
|
|
|
|
const data = {
|
|
|
|
rowId: node.rowId,
|
|
|
|
fileId: -1,
|
|
|
|
checked: node.checked,
|
|
|
|
path: node.path,
|
|
|
|
original: node.original,
|
|
|
|
renamed: node.renamed
|
|
|
|
};
|
|
|
|
|
|
|
|
node.data = data;
|
|
|
|
node.full_data = data;
|
|
|
|
this.updateRowData(data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
node.data.rowId = node.rowId;
|
|
|
|
node.full_data = node.data;
|
|
|
|
this.updateRowData(node.data);
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
node.children.each((child) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
this._addNodeToTable(child, depth + 1);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
getRoot: function() {
|
|
|
|
return this.fileTree.getRoot();
|
|
|
|
},
|
|
|
|
|
|
|
|
getNode: function(rowId) {
|
|
|
|
return this.fileTree.getNode(rowId);
|
|
|
|
},
|
|
|
|
|
|
|
|
getRow: function(node) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowId = this.fileTree.getRowId(node).toString();
|
2023-07-18 11:58:05 -04:00
|
|
|
return this.rows.get(rowId);
|
|
|
|
},
|
|
|
|
|
|
|
|
getSelectedRows: function() {
|
|
|
|
const nodes = this.fileTree.toArray();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
return nodes.filter(x => x.checked === 0);
|
2023-07-18 11:58:05 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
initColumns: function() {
|
|
|
|
// Blocks saving header width (because window width isn't saved)
|
2024-10-03 08:37:11 -04:00
|
|
|
LocalPreferences.remove("column_" + "checked" + "_width_" + this.dynamicTableDivId);
|
|
|
|
LocalPreferences.remove("column_" + "original" + "_width_" + this.dynamicTableDivId);
|
|
|
|
LocalPreferences.remove("column_" + "renamed" + "_width_" + this.dynamicTableDivId);
|
|
|
|
this.newColumn("checked", "", "", 50, true);
|
|
|
|
this.newColumn("original", "", "Original", 270, true);
|
|
|
|
this.newColumn("renamed", "", "Renamed", 220, true);
|
2023-07-18 11:58:05 -04:00
|
|
|
|
|
|
|
this.initColumnsFunctions();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggles the global checkbox and all checkboxes underneath
|
|
|
|
*/
|
|
|
|
toggleGlobalCheckbox: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const checkbox = $("rootMultiRename_cb");
|
|
|
|
const checkboxes = $$("input.RenamingCB");
|
2023-07-18 11:58:05 -04:00
|
|
|
|
|
|
|
for (let i = 0; i < checkboxes.length; ++i) {
|
|
|
|
const node = this.getNode(i);
|
|
|
|
|
|
|
|
if (checkbox.checked || checkbox.indeterminate) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const cb = checkboxes[i];
|
2023-07-18 11:58:05 -04:00
|
|
|
cb.checked = true;
|
|
|
|
cb.indeterminate = false;
|
|
|
|
cb.state = "checked";
|
|
|
|
node.checked = 0;
|
|
|
|
node.full_data.checked = node.checked;
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
const cb = checkboxes[i];
|
2023-07-18 11:58:05 -04:00
|
|
|
cb.checked = false;
|
|
|
|
cb.indeterminate = false;
|
|
|
|
cb.state = "unchecked";
|
|
|
|
node.checked = 1;
|
|
|
|
node.full_data.checked = node.checked;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.updateGlobalCheckbox();
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleNodeTreeCheckbox: function(rowId, checkState) {
|
|
|
|
const node = this.getNode(rowId);
|
|
|
|
node.checked = checkState;
|
|
|
|
node.full_data.checked = checkState;
|
|
|
|
const checkbox = $(`cbRename${rowId}`);
|
2024-10-03 08:37:11 -04:00
|
|
|
checkbox.checked = node.checked === 0;
|
2023-07-18 11:58:05 -04:00
|
|
|
checkbox.state = checkbox.checked ? "checked" : "unchecked";
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
for (let i = 0; i < node.children.length; ++i)
|
2023-07-18 11:58:05 -04:00
|
|
|
this.toggleNodeTreeCheckbox(node.children[i].rowId, checkState);
|
|
|
|
},
|
|
|
|
|
|
|
|
updateGlobalCheckbox: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const checkbox = $("rootMultiRename_cb");
|
|
|
|
const checkboxes = $$("input.RenamingCB");
|
2023-07-18 11:58:05 -04:00
|
|
|
const isAllChecked = function() {
|
|
|
|
for (let i = 0; i < checkboxes.length; ++i) {
|
|
|
|
if (!checkboxes[i].checked)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
const isAllUnchecked = function() {
|
|
|
|
for (let i = 0; i < checkboxes.length; ++i) {
|
|
|
|
if (checkboxes[i].checked)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
if (isAllChecked()) {
|
|
|
|
checkbox.state = "checked";
|
|
|
|
checkbox.indeterminate = false;
|
|
|
|
checkbox.checked = true;
|
|
|
|
}
|
|
|
|
else if (isAllUnchecked()) {
|
|
|
|
checkbox.state = "unchecked";
|
|
|
|
checkbox.indeterminate = false;
|
|
|
|
checkbox.checked = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
checkbox.state = "partial";
|
|
|
|
checkbox.indeterminate = true;
|
|
|
|
checkbox.checked = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumnsFunctions: function() {
|
|
|
|
const that = this;
|
|
|
|
|
|
|
|
// checked
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["checked"].updateTd = function(td, row) {
|
2023-07-18 11:58:05 -04:00
|
|
|
const id = row.rowId;
|
|
|
|
const value = this.getRowValue(row);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const treeImg = new Element("img", {
|
|
|
|
src: "images/L.gif",
|
2023-07-18 11:58:05 -04:00
|
|
|
styles: {
|
2024-10-03 08:37:11 -04:00
|
|
|
"margin-bottom": -2
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
const checkbox = new Element("input");
|
|
|
|
checkbox.type = "checkbox";
|
|
|
|
checkbox.id = "cbRename" + id;
|
|
|
|
checkbox.setAttribute("data-id", id);
|
|
|
|
checkbox.className = "RenamingCB";
|
|
|
|
checkbox.addEventListener("click", (e) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
const node = that.getNode(id);
|
|
|
|
node.checked = e.target.checked ? 0 : 1;
|
|
|
|
node.full_data.checked = node.checked;
|
|
|
|
that.updateGlobalCheckbox();
|
|
|
|
that.onRowSelectionChange(node);
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
checkbox.checked = (value === 0);
|
2023-07-18 11:58:05 -04:00
|
|
|
checkbox.state = checkbox.checked ? "checked" : "unchecked";
|
|
|
|
checkbox.indeterminate = false;
|
|
|
|
td.adopt(treeImg, checkbox);
|
|
|
|
};
|
|
|
|
|
|
|
|
// original
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["original"].updateTd = function(td, row) {
|
2023-07-18 11:58:05 -04:00
|
|
|
const id = row.rowId;
|
2024-10-03 08:37:11 -04:00
|
|
|
const fileNameId = "filesTablefileName" + id;
|
2023-07-18 11:58:05 -04:00
|
|
|
const node = that.getNode(id);
|
|
|
|
|
|
|
|
if (node.isFolder) {
|
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
const dirImgId = "renameTableDirImg" + id;
|
2023-07-18 11:58:05 -04:00
|
|
|
if ($(dirImgId)) {
|
|
|
|
// just update file name
|
2024-10-03 08:37:11 -04:00
|
|
|
$(fileNameId).textContent = value;
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
const span = new Element("span", {
|
2023-07-18 11:58:05 -04:00
|
|
|
text: value,
|
|
|
|
id: fileNameId
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
const dirImg = new Element("img", {
|
|
|
|
src: "images/directory.svg",
|
2023-07-18 11:58:05 -04:00
|
|
|
styles: {
|
2024-10-03 08:37:11 -04:00
|
|
|
"width": 15,
|
|
|
|
"padding-right": 5,
|
|
|
|
"margin-bottom": -3,
|
|
|
|
"margin-left": (node.depth * 20)
|
2023-07-18 11:58:05 -04:00
|
|
|
},
|
|
|
|
id: dirImgId
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
td.replaceChildren(dirImg, span);
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // is file
|
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
const span = new Element("span", {
|
2023-07-18 11:58:05 -04:00
|
|
|
text: value,
|
|
|
|
id: fileNameId,
|
|
|
|
styles: {
|
2024-10-03 08:37:11 -04:00
|
|
|
"margin-left": ((node.depth + 1) * 20)
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
td.replaceChildren(span);
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// renamed
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["renamed"].updateTd = function(td, row) {
|
2023-07-18 11:58:05 -04:00
|
|
|
const id = row.rowId;
|
2024-10-03 08:37:11 -04:00
|
|
|
const fileNameRenamedId = "filesTablefileRenamed" + id;
|
2023-07-18 11:58:05 -04:00
|
|
|
const value = this.getRowValue(row);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const span = new Element("span", {
|
2023-07-18 11:58:05 -04:00
|
|
|
text: value,
|
|
|
|
id: fileNameRenamedId,
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
td.replaceChildren(span);
|
2023-07-18 11:58:05 -04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
onRowSelectionChange: function(row) {},
|
|
|
|
|
|
|
|
selectRow: function() {
|
|
|
|
return;
|
|
|
|
},
|
|
|
|
|
|
|
|
reselectRows: function(rowIds) {
|
|
|
|
const that = this;
|
|
|
|
this.deselectAll();
|
2024-10-03 08:37:11 -04:00
|
|
|
this.tableBody.getElements("tr").each((tr) => {
|
|
|
|
if (rowIds.includes(tr.rowId)) {
|
2023-07-18 11:58:05 -04:00
|
|
|
const node = that.getNode(tr.rowId);
|
|
|
|
node.checked = 0;
|
|
|
|
node.full_data.checked = 0;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const checkbox = tr.children[0].getElement("input");
|
2023-07-18 11:58:05 -04:00
|
|
|
checkbox.state = "checked";
|
|
|
|
checkbox.indeterminate = false;
|
|
|
|
checkbox.checked = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
this.updateGlobalCheckbox();
|
|
|
|
},
|
|
|
|
|
|
|
|
_sortNodesByColumn: function(nodes, column) {
|
2024-10-03 08:37:11 -04:00
|
|
|
nodes.sort((row1, row2) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
// list folders before files when sorting by name
|
|
|
|
if (column.name === "original") {
|
|
|
|
const node1 = this.getNode(row1.data.rowId);
|
|
|
|
const node2 = this.getNode(row2.data.rowId);
|
|
|
|
if (node1.isFolder && !node2.isFolder)
|
|
|
|
return -1;
|
|
|
|
if (node2.isFolder && !node1.isFolder)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const res = column.compareRows(row1, row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
return (this.reverseSort === "0") ? res : -res;
|
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
nodes.each((node) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
if (node.children.length > 0)
|
|
|
|
this._sortNodesByColumn(node.children, column);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_filterNodes: function(node, filterTerms, filteredRows) {
|
|
|
|
if (node.isFolder) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const childAdded = node.children.reduce((acc, child) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
// we must execute the function before ORing w/ acc or we'll stop checking child nodes after the first successful match
|
|
|
|
return (this._filterNodes(child, filterTerms, filteredRows) || acc);
|
2024-10-03 08:37:11 -04:00
|
|
|
}, false);
|
2023-07-18 11:58:05 -04:00
|
|
|
|
|
|
|
if (childAdded) {
|
|
|
|
const row = this.getRow(node);
|
|
|
|
filteredRows.push(row);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window.qBittorrent.Misc.containsAllTerms(node.original, filterTerms)) {
|
|
|
|
const row = this.getRow(node);
|
|
|
|
filteredRows.push(row);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
setFilter: function(text) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const filterTerms = text.trim().toLowerCase().split(" ");
|
|
|
|
if ((filterTerms.length === 1) && (filterTerms[0] === ""))
|
2023-07-18 11:58:05 -04:00
|
|
|
this.filterTerms = [];
|
|
|
|
else
|
|
|
|
this.filterTerms = filterTerms;
|
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
|
|
if (this.getRoot() === null)
|
|
|
|
return [];
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const generateRowsSignature = () => {
|
|
|
|
const rowsData = [];
|
|
|
|
for (const { full_data } of this.getRowValues())
|
|
|
|
rowsData.push(full_data);
|
2023-07-18 11:58:05 -04:00
|
|
|
return JSON.stringify(rowsData);
|
|
|
|
};
|
|
|
|
|
|
|
|
const getFilteredRows = function() {
|
|
|
|
if (this.filterTerms.length === 0) {
|
|
|
|
const nodeArray = this.fileTree.toArray();
|
2024-10-03 08:37:11 -04:00
|
|
|
const filteredRows = nodeArray.map((node) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
return this.getRow(node);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
return filteredRows;
|
|
|
|
}
|
|
|
|
|
|
|
|
const filteredRows = [];
|
2024-10-03 08:37:11 -04:00
|
|
|
this.getRoot().children.each((child) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
this._filterNodes(child, this.filterTerms, filteredRows);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
filteredRows.reverse();
|
|
|
|
return filteredRows;
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const hasRowsChanged = function(rowsString, prevRowsStringString) {
|
|
|
|
const rowsChanged = (rowsString !== prevRowsStringString);
|
2024-10-03 08:37:11 -04:00
|
|
|
const isFilterTermsChanged = this.filterTerms.reduce((acc, term, index) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
return (acc || (term !== this.prevFilterTerms[index]));
|
2024-10-03 08:37:11 -04:00
|
|
|
}, false);
|
2023-07-18 11:58:05 -04:00
|
|
|
const isFilterChanged = ((this.filterTerms.length !== this.prevFilterTerms.length)
|
|
|
|
|| ((this.filterTerms.length > 0) && isFilterTermsChanged));
|
|
|
|
const isSortedColumnChanged = (this.prevSortedColumn !== this.sortedColumn);
|
|
|
|
const isReverseSortChanged = (this.prevReverseSort !== this.reverseSort);
|
|
|
|
|
|
|
|
return (rowsChanged || isFilterChanged || isSortedColumnChanged || isReverseSortChanged);
|
|
|
|
}.bind(this);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowsString = generateRowsSignature();
|
|
|
|
if (!hasRowsChanged(rowsString, this.prevRowsString))
|
2023-07-18 11:58:05 -04:00
|
|
|
return this.prevFilteredRows;
|
|
|
|
|
|
|
|
// sort, then filter
|
|
|
|
const column = this.columns[this.sortedColumn];
|
|
|
|
this._sortNodesByColumn(this.getRoot().children, column);
|
|
|
|
const filteredRows = getFilteredRows();
|
|
|
|
|
|
|
|
this.prevFilterTerms = this.filterTerms;
|
|
|
|
this.prevRowsString = rowsString;
|
|
|
|
this.prevFilteredRows = filteredRows;
|
|
|
|
this.prevSortedColumn = this.sortedColumn;
|
|
|
|
this.prevReverseSort = this.reverseSort;
|
|
|
|
return filteredRows;
|
|
|
|
},
|
|
|
|
|
|
|
|
setIgnored: function(rowId, ignore) {
|
|
|
|
const row = this.rows.get(rowId);
|
|
|
|
if (ignore)
|
|
|
|
row.full_data.remaining = 0;
|
|
|
|
else
|
|
|
|
row.full_data.remaining = (row.full_data.size * (1.0 - (row.full_data.progress / 100)));
|
|
|
|
},
|
|
|
|
|
|
|
|
setupTr: function(tr) {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("keydown", function(event) {
|
2023-07-18 11:58:05 -04:00
|
|
|
switch (event.key) {
|
|
|
|
case "left":
|
|
|
|
qBittorrent.PropFiles.collapseFolder(this._this.getSelectedRowId());
|
|
|
|
return false;
|
|
|
|
case "right":
|
|
|
|
qBittorrent.PropFiles.expandFolder(this._this.getSelectedRowId());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-10-18 22:39:32 -04:00
|
|
|
const TorrentFilesTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
filterTerms: [],
|
|
|
|
prevFilterTerms: [],
|
|
|
|
prevRowsString: null,
|
|
|
|
prevFilteredRows: [],
|
|
|
|
prevSortedColumn: null,
|
|
|
|
prevReverseSort: null,
|
|
|
|
fileTree: new window.qBittorrent.FileTree.FileTree(),
|
|
|
|
|
|
|
|
populateTable: function(root) {
|
|
|
|
this.fileTree.setRoot(root);
|
2024-10-03 08:37:11 -04:00
|
|
|
root.children.each((node) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
this._addNodeToTable(node, 0);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_addNodeToTable: function(node, depth) {
|
|
|
|
node.depth = depth;
|
|
|
|
|
|
|
|
if (node.isFolder) {
|
|
|
|
const data = {
|
|
|
|
rowId: node.rowId,
|
|
|
|
size: node.size,
|
|
|
|
checked: node.checked,
|
|
|
|
remaining: node.remaining,
|
|
|
|
progress: node.progress,
|
|
|
|
priority: window.qBittorrent.PropFiles.normalizePriority(node.priority),
|
|
|
|
availability: node.availability,
|
|
|
|
fileId: -1,
|
|
|
|
name: node.name
|
|
|
|
};
|
|
|
|
|
|
|
|
node.data = data;
|
|
|
|
node.full_data = data;
|
|
|
|
this.updateRowData(data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
node.data.rowId = node.rowId;
|
|
|
|
node.full_data = node.data;
|
|
|
|
this.updateRowData(node.data);
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
node.children.each((child) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
this._addNodeToTable(child, depth + 1);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
getRoot: function() {
|
|
|
|
return this.fileTree.getRoot();
|
|
|
|
},
|
|
|
|
|
|
|
|
getNode: function(rowId) {
|
|
|
|
return this.fileTree.getNode(rowId);
|
|
|
|
},
|
|
|
|
|
|
|
|
getRow: function(node) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowId = this.fileTree.getRowId(node).toString();
|
2022-10-18 22:39:32 -04:00
|
|
|
return this.rows.get(rowId);
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("checked", "", "", 50, true);
|
|
|
|
this.newColumn("name", "", "Name", 300, true);
|
|
|
|
this.newColumn("size", "", "Total Size", 75, true);
|
|
|
|
this.newColumn("progress", "", "Progress", 100, true);
|
|
|
|
this.newColumn("priority", "", "Download Priority", 150, true);
|
|
|
|
this.newColumn("remaining", "", "Remaining", 75, true);
|
|
|
|
this.newColumn("availability", "", "Availability", 75, true);
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
this.initColumnsFunctions();
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumnsFunctions: function() {
|
|
|
|
const that = this;
|
|
|
|
const displaySize = function(td, row) {
|
|
|
|
const size = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = size;
|
|
|
|
td.title = size;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
const displayPercentage = function(td, row) {
|
|
|
|
const value = window.qBittorrent.Misc.friendlyPercentage(this.getRowValue(row));
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// checked
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["checked"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const id = row.rowId;
|
|
|
|
const value = this.getRowValue(row);
|
|
|
|
|
|
|
|
if (window.qBittorrent.PropFiles.isDownloadCheckboxExists(id)) {
|
|
|
|
window.qBittorrent.PropFiles.updateDownloadCheckbox(id, value);
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
const treeImg = new Element("img", {
|
|
|
|
src: "images/L.gif",
|
2022-10-18 22:39:32 -04:00
|
|
|
styles: {
|
2024-10-03 08:37:11 -04:00
|
|
|
"margin-bottom": -2
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
td.adopt(treeImg, window.qBittorrent.PropFiles.createDownloadCheckbox(id, row.full_data.fileId, value));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// name
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["name"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const id = row.rowId;
|
2024-10-03 08:37:11 -04:00
|
|
|
const fileNameId = "filesTablefileName" + id;
|
2022-10-18 22:39:32 -04:00
|
|
|
const node = that.getNode(id);
|
|
|
|
|
|
|
|
if (node.isFolder) {
|
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
const collapseIconId = "filesTableCollapseIcon" + id;
|
|
|
|
const dirImgId = "filesTableDirImg" + id;
|
2022-10-18 22:39:32 -04:00
|
|
|
if ($(dirImgId)) {
|
|
|
|
// just update file name
|
2024-10-03 08:37:11 -04:00
|
|
|
$(fileNameId).textContent = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
const collapseIcon = new Element("img", {
|
|
|
|
src: "images/go-down.svg",
|
2022-10-18 22:39:32 -04:00
|
|
|
styles: {
|
2024-10-03 08:37:11 -04:00
|
|
|
"margin-left": (node.depth * 20)
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
class: "filesTableCollapseIcon",
|
|
|
|
id: collapseIconId,
|
|
|
|
"data-id": id,
|
|
|
|
onclick: "qBittorrent.PropFiles.collapseIconClicked(this)"
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
const span = new Element("span", {
|
2022-10-18 22:39:32 -04:00
|
|
|
text: value,
|
|
|
|
id: fileNameId
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
const dirImg = new Element("img", {
|
|
|
|
src: "images/directory.svg",
|
2022-10-18 22:39:32 -04:00
|
|
|
styles: {
|
2024-10-03 08:37:11 -04:00
|
|
|
"width": 15,
|
|
|
|
"padding-right": 5,
|
|
|
|
"margin-bottom": -3
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
id: dirImgId
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
td.replaceChildren(collapseIcon, dirImg, span);
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
const span = new Element("span", {
|
2022-10-18 22:39:32 -04:00
|
|
|
text: value,
|
|
|
|
id: fileNameId,
|
|
|
|
styles: {
|
2024-10-03 08:37:11 -04:00
|
|
|
"margin-left": ((node.depth + 1) * 20)
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
td.replaceChildren(span);
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// size
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["size"].updateTd = displaySize;
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// progress
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["progress"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const id = row.rowId;
|
|
|
|
const value = this.getRowValue(row);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const progressBar = $("pbf_" + id);
|
2022-10-18 22:39:32 -04:00
|
|
|
if (progressBar === null) {
|
|
|
|
td.adopt(new window.qBittorrent.ProgressBar.ProgressBar(value.toFloat(), {
|
2024-10-03 08:37:11 -04:00
|
|
|
id: "pbf_" + id,
|
2022-10-18 22:39:32 -04:00
|
|
|
width: 80
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
progressBar.setValue(value.toFloat());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// priority
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["priority"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const id = row.rowId;
|
|
|
|
const value = this.getRowValue(row);
|
|
|
|
|
|
|
|
if (window.qBittorrent.PropFiles.isPriorityComboExists(id))
|
|
|
|
window.qBittorrent.PropFiles.updatePriorityCombo(id, value);
|
|
|
|
else
|
|
|
|
td.adopt(window.qBittorrent.PropFiles.createPriorityCombo(id, row.full_data.fileId, value));
|
|
|
|
};
|
|
|
|
|
|
|
|
// remaining, availability
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["remaining"].updateTd = displaySize;
|
|
|
|
this.columns["availability"].updateTd = displayPercentage;
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_sortNodesByColumn: function(nodes, column) {
|
2024-10-03 08:37:11 -04:00
|
|
|
nodes.sort((row1, row2) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
// list folders before files when sorting by name
|
|
|
|
if (column.name === "name") {
|
|
|
|
const node1 = this.getNode(row1.data.rowId);
|
|
|
|
const node2 = this.getNode(row2.data.rowId);
|
|
|
|
if (node1.isFolder && !node2.isFolder)
|
|
|
|
return -1;
|
|
|
|
if (node2.isFolder && !node1.isFolder)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const res = column.compareRows(row1, row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
return (this.reverseSort === "0") ? res : -res;
|
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
nodes.each((node) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (node.children.length > 0)
|
|
|
|
this._sortNodesByColumn(node.children, column);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_filterNodes: function(node, filterTerms, filteredRows) {
|
|
|
|
if (node.isFolder) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const childAdded = node.children.reduce((acc, child) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
// we must execute the function before ORing w/ acc or we'll stop checking child nodes after the first successful match
|
|
|
|
return (this._filterNodes(child, filterTerms, filteredRows) || acc);
|
2024-10-03 08:37:11 -04:00
|
|
|
}, false);
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
if (childAdded) {
|
|
|
|
const row = this.getRow(node);
|
|
|
|
filteredRows.push(row);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window.qBittorrent.Misc.containsAllTerms(node.name, filterTerms)) {
|
|
|
|
const row = this.getRow(node);
|
|
|
|
filteredRows.push(row);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
setFilter: function(text) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const filterTerms = text.trim().toLowerCase().split(" ");
|
|
|
|
if ((filterTerms.length === 1) && (filterTerms[0] === ""))
|
2022-10-18 22:39:32 -04:00
|
|
|
this.filterTerms = [];
|
|
|
|
else
|
|
|
|
this.filterTerms = filterTerms;
|
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
|
|
if (this.getRoot() === null)
|
|
|
|
return [];
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const generateRowsSignature = () => {
|
|
|
|
const rowsData = [];
|
|
|
|
for (const { full_data } of this.getRowValues())
|
|
|
|
rowsData.push(full_data);
|
2022-10-18 22:39:32 -04:00
|
|
|
return JSON.stringify(rowsData);
|
|
|
|
};
|
|
|
|
|
|
|
|
const getFilteredRows = function() {
|
|
|
|
if (this.filterTerms.length === 0) {
|
|
|
|
const nodeArray = this.fileTree.toArray();
|
2024-10-03 08:37:11 -04:00
|
|
|
const filteredRows = nodeArray.map((node) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
return this.getRow(node);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
return filteredRows;
|
|
|
|
}
|
|
|
|
|
|
|
|
const filteredRows = [];
|
2024-10-03 08:37:11 -04:00
|
|
|
this.getRoot().children.each((child) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
this._filterNodes(child, this.filterTerms, filteredRows);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2022-10-18 22:39:32 -04:00
|
|
|
filteredRows.reverse();
|
|
|
|
return filteredRows;
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
const hasRowsChanged = function(rowsString, prevRowsStringString) {
|
|
|
|
const rowsChanged = (rowsString !== prevRowsStringString);
|
2024-10-03 08:37:11 -04:00
|
|
|
const isFilterTermsChanged = this.filterTerms.reduce((acc, term, index) => {
|
2022-10-18 22:39:32 -04:00
|
|
|
return (acc || (term !== this.prevFilterTerms[index]));
|
2024-10-03 08:37:11 -04:00
|
|
|
}, false);
|
2022-10-18 22:39:32 -04:00
|
|
|
const isFilterChanged = ((this.filterTerms.length !== this.prevFilterTerms.length)
|
|
|
|
|| ((this.filterTerms.length > 0) && isFilterTermsChanged));
|
|
|
|
const isSortedColumnChanged = (this.prevSortedColumn !== this.sortedColumn);
|
|
|
|
const isReverseSortChanged = (this.prevReverseSort !== this.reverseSort);
|
|
|
|
|
|
|
|
return (rowsChanged || isFilterChanged || isSortedColumnChanged || isReverseSortChanged);
|
|
|
|
}.bind(this);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const rowsString = generateRowsSignature();
|
|
|
|
if (!hasRowsChanged(rowsString, this.prevRowsString))
|
2022-10-18 22:39:32 -04:00
|
|
|
return this.prevFilteredRows;
|
|
|
|
|
|
|
|
// sort, then filter
|
|
|
|
const column = this.columns[this.sortedColumn];
|
|
|
|
this._sortNodesByColumn(this.getRoot().children, column);
|
|
|
|
const filteredRows = getFilteredRows();
|
|
|
|
|
|
|
|
this.prevFilterTerms = this.filterTerms;
|
|
|
|
this.prevRowsString = rowsString;
|
|
|
|
this.prevFilteredRows = filteredRows;
|
|
|
|
this.prevSortedColumn = this.sortedColumn;
|
|
|
|
this.prevReverseSort = this.reverseSort;
|
|
|
|
return filteredRows;
|
|
|
|
},
|
|
|
|
|
|
|
|
setIgnored: function(rowId, ignore) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const row = this.rows.get(rowId.toString());
|
2022-10-18 22:39:32 -04:00
|
|
|
if (ignore)
|
|
|
|
row.full_data.remaining = 0;
|
|
|
|
else
|
|
|
|
row.full_data.remaining = (row.full_data.size * (1.0 - (row.full_data.progress / 100)));
|
|
|
|
},
|
|
|
|
|
|
|
|
setupTr: function(tr) {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("keydown", function(event) {
|
2022-10-18 22:39:32 -04:00
|
|
|
switch (event.key) {
|
|
|
|
case "left":
|
|
|
|
qBittorrent.PropFiles.collapseFolder(this._this.getSelectedRowId());
|
|
|
|
return false;
|
|
|
|
case "right":
|
|
|
|
qBittorrent.PropFiles.expandFolder(this._this.getSelectedRowId());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssFeedTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("state_icon", "", "", 30, true);
|
|
|
|
this.newColumn("name", "", "RSS feeds", -1, true);
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["state_icon"].dataProperties[0] = "";
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
// map name row to "[name] ([unread])"
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["name"].dataProperties.push("unread");
|
|
|
|
this.columns["name"].updateTd = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const name = this.getRowValue(row, 0);
|
|
|
|
const unreadCount = this.getRowValue(row, 1);
|
2024-10-03 08:37:11 -04:00
|
|
|
const value = name + " (" + unreadCount + ")";
|
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
setupHeaderMenu: function() {},
|
|
|
|
setupHeaderEvents: function() {},
|
|
|
|
getFilteredAndSortedRows: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
return [...this.getRowValues()];
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
selectRow: function(rowId) {
|
|
|
|
this.selectedRows.push(rowId);
|
|
|
|
this.setRowClass();
|
|
|
|
this.onSelectedRowChanged();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
let path = "";
|
|
|
|
for (const row of this.getRowValues()) {
|
|
|
|
if (row.rowId === rowId) {
|
|
|
|
path = row.full_data.dataPath;
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
window.qBittorrent.Rss.showRssFeed(path);
|
|
|
|
},
|
|
|
|
setupTr: function(tr) {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("dblclick", function(e) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (this.rowId !== 0) {
|
|
|
|
window.qBittorrent.Rss.moveItem(this._this.rows.get(this.rowId).full_data.dataPath);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
updateRow: function(tr, fullUpdate) {
|
|
|
|
const row = this.rows.get(tr.rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
const data = row[fullUpdate ? "full_data" : "data"];
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const tds = tr.getElements("td");
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (Object.hasOwn(data, this.columns[i].dataProperties[0]))
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns[i].updateTd(tds[i], row);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
row["data"] = {};
|
|
|
|
tds[0].style.overflow = "visible";
|
|
|
|
const indentation = row.full_data.indentation;
|
|
|
|
tds[0].style.paddingLeft = (indentation * 32 + 4) + "px";
|
|
|
|
tds[1].style.paddingLeft = (indentation * 32 + 4) + "px";
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
updateIcons: function() {
|
|
|
|
// state_icon
|
2024-10-03 08:37:11 -04:00
|
|
|
for (const row of this.getRowValues()) {
|
2022-10-18 22:39:32 -04:00
|
|
|
let img_path;
|
|
|
|
switch (row.full_data.status) {
|
2024-10-03 08:37:11 -04:00
|
|
|
case "default":
|
|
|
|
img_path = "images/application-rss.svg";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "hasError":
|
|
|
|
img_path = "images/task-reject.svg";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "isLoading":
|
|
|
|
img_path = "images/spinner.gif";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "unread":
|
|
|
|
img_path = "images/mail-inbox.svg";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
2024-10-03 08:37:11 -04:00
|
|
|
case "isFolder":
|
|
|
|
img_path = "images/folder-documents.svg";
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
let td;
|
|
|
|
for (let i = 0; i < this.tableBody.rows.length; ++i) {
|
|
|
|
if (this.tableBody.rows[i].rowId === row.rowId) {
|
|
|
|
td = this.tableBody.rows[i].children[0];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
if (td.getChildren("img").length > 0) {
|
|
|
|
const img = td.getChildren("img")[0];
|
|
|
|
if (!img.src.includes(img_path)) {
|
|
|
|
img.src = img_path;
|
|
|
|
img.title = status;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
td.adopt(new Element("img", {
|
|
|
|
"src": img_path,
|
|
|
|
"class": "stateIcon",
|
|
|
|
"height": "22px",
|
|
|
|
"width": "22px"
|
2022-10-18 22:39:32 -04:00
|
|
|
}));
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
};
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
|
|
const column = {};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["name"] = name;
|
|
|
|
column["title"] = name;
|
|
|
|
column["visible"] = defaultVisible;
|
|
|
|
column["force_hide"] = false;
|
|
|
|
column["caption"] = caption;
|
|
|
|
column["style"] = style;
|
|
|
|
if (defaultWidth !== -1)
|
|
|
|
column["width"] = defaultWidth;
|
|
|
|
|
|
|
|
column["dataProperties"] = [name];
|
|
|
|
column["getRowValue"] = function(row, pos) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (pos === undefined)
|
|
|
|
pos = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
return row["full_data"][this.dataProperties[pos]];
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["compareRows"] = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value1 = this.getRowValue(row1);
|
|
|
|
const value2 = this.getRowValue(row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
2023-07-18 11:58:05 -04:00
|
|
|
return compareNumbers(value1, value2);
|
|
|
|
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["updateTd"] = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["onResize"] = null;
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns.push(column);
|
|
|
|
this.columns[name] = column;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hiddenTableHeader.appendChild(new Element("th"));
|
|
|
|
this.fixedTableHeader.appendChild(new Element("th"));
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssArticleTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("name", "", "Torrents: (double-click to download)", -1, true);
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
setupHeaderMenu: function() {},
|
|
|
|
setupHeaderEvents: function() {},
|
|
|
|
getFilteredAndSortedRows: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
return [...this.getRowValues()];
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
selectRow: function(rowId) {
|
|
|
|
this.selectedRows.push(rowId);
|
|
|
|
this.setRowClass();
|
|
|
|
this.onSelectedRowChanged();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
let articleId = "";
|
|
|
|
let feedUid = "";
|
|
|
|
for (const row of this.getRowValues()) {
|
|
|
|
if (row.rowId === rowId) {
|
|
|
|
articleId = row.full_data.dataId;
|
|
|
|
feedUid = row.full_data.feedUid;
|
|
|
|
this.tableBody.rows[row.rowId].removeClass("unreadArticle");
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
window.qBittorrent.Rss.showDetails(feedUid, articleId);
|
|
|
|
},
|
|
|
|
setupTr: function(tr) {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("dblclick", function(e) {
|
2022-10-18 22:39:32 -04:00
|
|
|
showDownloadPage([this._this.rows.get(this.rowId).full_data.torrentURL]);
|
|
|
|
return true;
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addClass("torrentsTableContextMenuTarget");
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
updateRow: function(tr, fullUpdate) {
|
|
|
|
const row = this.rows.get(tr.rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
const data = row[fullUpdate ? "full_data" : "data"];
|
2022-10-18 22:39:32 -04:00
|
|
|
if (!row.full_data.isRead)
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addClass("unreadArticle");
|
2022-10-18 22:39:32 -04:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.removeClass("unreadArticle");
|
2022-10-18 22:39:32 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const tds = tr.getElements("td");
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (Object.hasOwn(data, this.columns[i].dataProperties[0]))
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns[i].updateTd(tds[i], row);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
row["data"] = {};
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
|
|
const column = {};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["name"] = name;
|
|
|
|
column["title"] = name;
|
|
|
|
column["visible"] = defaultVisible;
|
|
|
|
column["force_hide"] = false;
|
|
|
|
column["caption"] = caption;
|
|
|
|
column["style"] = style;
|
|
|
|
if (defaultWidth !== -1)
|
|
|
|
column["width"] = defaultWidth;
|
|
|
|
|
|
|
|
column["dataProperties"] = [name];
|
|
|
|
column["getRowValue"] = function(row, pos) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (pos === undefined)
|
|
|
|
pos = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
return row["full_data"][this.dataProperties[pos]];
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["compareRows"] = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value1 = this.getRowValue(row1);
|
|
|
|
const value2 = this.getRowValue(row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
2023-07-18 11:58:05 -04:00
|
|
|
return compareNumbers(value1, value2);
|
|
|
|
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["updateTd"] = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["onResize"] = null;
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns.push(column);
|
|
|
|
this.columns[name] = column;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hiddenTableHeader.appendChild(new Element("th"));
|
|
|
|
this.fixedTableHeader.appendChild(new Element("th"));
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssDownloaderRulesTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("checked", "", "", 30, true);
|
|
|
|
this.newColumn("name", "", "", -1, true);
|
|
|
|
|
|
|
|
this.columns["checked"].updateTd = function(td, row) {
|
|
|
|
if ($("cbRssDlRule" + row.rowId) === null) {
|
|
|
|
const checkbox = new Element("input");
|
|
|
|
checkbox.type = "checkbox";
|
|
|
|
checkbox.id = "cbRssDlRule" + row.rowId;
|
2022-10-18 22:39:32 -04:00
|
|
|
checkbox.checked = row.full_data.checked;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
checkbox.addEventListener("click", function(e) {
|
2022-10-18 22:39:32 -04:00
|
|
|
window.qBittorrent.RssDownloader.rssDownloaderRulesTable.updateRowData({
|
|
|
|
rowId: row.rowId,
|
|
|
|
checked: this.checked
|
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
window.qBittorrent.RssDownloader.modifyRuleState(row.full_data.name, "enabled", this.checked);
|
2022-10-18 22:39:32 -04:00
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
|
|
|
td.append(checkbox);
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
$("cbRssDlRule" + row.rowId).checked = row.full_data.checked;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
setupHeaderMenu: function() {},
|
|
|
|
setupHeaderEvents: function() {},
|
|
|
|
getFilteredAndSortedRows: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
return [...this.getRowValues()];
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
setupTr: function(tr) {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addEventListener("dblclick", function(e) {
|
2022-10-18 22:39:32 -04:00
|
|
|
window.qBittorrent.RssDownloader.renameRule(this._this.rows.get(this.rowId).full_data.name);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
|
|
const column = {};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["name"] = name;
|
|
|
|
column["title"] = name;
|
|
|
|
column["visible"] = defaultVisible;
|
|
|
|
column["force_hide"] = false;
|
|
|
|
column["caption"] = caption;
|
|
|
|
column["style"] = style;
|
|
|
|
if (defaultWidth !== -1)
|
|
|
|
column["width"] = defaultWidth;
|
|
|
|
|
|
|
|
column["dataProperties"] = [name];
|
|
|
|
column["getRowValue"] = function(row, pos) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (pos === undefined)
|
|
|
|
pos = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
return row["full_data"][this.dataProperties[pos]];
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["compareRows"] = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value1 = this.getRowValue(row1);
|
|
|
|
const value2 = this.getRowValue(row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
2023-07-18 11:58:05 -04:00
|
|
|
return compareNumbers(value1, value2);
|
|
|
|
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["updateTd"] = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["onResize"] = null;
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns.push(column);
|
|
|
|
this.columns[name] = column;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hiddenTableHeader.appendChild(new Element("th"));
|
|
|
|
this.fixedTableHeader.appendChild(new Element("th"));
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
selectRow: function(rowId) {
|
|
|
|
this.selectedRows.push(rowId);
|
|
|
|
this.setRowClass();
|
|
|
|
this.onSelectedRowChanged();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
let name = "";
|
|
|
|
for (const row of this.getRowValues()) {
|
|
|
|
if (row.rowId === rowId) {
|
|
|
|
name = row.full_data.name;
|
2022-10-18 22:39:32 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
window.qBittorrent.RssDownloader.showRule(name);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssDownloaderFeedSelectionTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("checked", "", "", 30, true);
|
|
|
|
this.newColumn("name", "", "", -1, true);
|
|
|
|
|
|
|
|
this.columns["checked"].updateTd = function(td, row) {
|
|
|
|
if ($("cbRssDlFeed" + row.rowId) === null) {
|
|
|
|
const checkbox = new Element("input");
|
|
|
|
checkbox.type = "checkbox";
|
|
|
|
checkbox.id = "cbRssDlFeed" + row.rowId;
|
2022-10-18 22:39:32 -04:00
|
|
|
checkbox.checked = row.full_data.checked;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
checkbox.addEventListener("click", function(e) {
|
2022-10-18 22:39:32 -04:00
|
|
|
window.qBittorrent.RssDownloader.rssDownloaderFeedSelectionTable.updateRowData({
|
|
|
|
rowId: row.rowId,
|
|
|
|
checked: this.checked
|
|
|
|
});
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
|
|
|
td.append(checkbox);
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
$("cbRssDlFeed" + row.rowId).checked = row.full_data.checked;
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
|
|
|
setupHeaderMenu: function() {},
|
|
|
|
setupHeaderEvents: function() {},
|
|
|
|
getFilteredAndSortedRows: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
return [...this.getRowValues()];
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
|
|
const column = {};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["name"] = name;
|
|
|
|
column["title"] = name;
|
|
|
|
column["visible"] = defaultVisible;
|
|
|
|
column["force_hide"] = false;
|
|
|
|
column["caption"] = caption;
|
|
|
|
column["style"] = style;
|
|
|
|
if (defaultWidth !== -1)
|
|
|
|
column["width"] = defaultWidth;
|
|
|
|
|
|
|
|
column["dataProperties"] = [name];
|
|
|
|
column["getRowValue"] = function(row, pos) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (pos === undefined)
|
|
|
|
pos = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
return row["full_data"][this.dataProperties[pos]];
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["compareRows"] = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value1 = this.getRowValue(row1);
|
|
|
|
const value2 = this.getRowValue(row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
2023-07-18 11:58:05 -04:00
|
|
|
return compareNumbers(value1, value2);
|
|
|
|
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["updateTd"] = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["onResize"] = null;
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns.push(column);
|
|
|
|
this.columns[name] = column;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hiddenTableHeader.appendChild(new Element("th"));
|
|
|
|
this.fixedTableHeader.appendChild(new Element("th"));
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
selectRow: function() {}
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssDownloaderArticlesTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("name", "", "", -1, true);
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
setupHeaderMenu: function() {},
|
|
|
|
setupHeaderEvents: function() {},
|
|
|
|
getFilteredAndSortedRows: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
return [...this.getRowValues()];
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
|
|
const column = {};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["name"] = name;
|
|
|
|
column["title"] = name;
|
|
|
|
column["visible"] = defaultVisible;
|
|
|
|
column["force_hide"] = false;
|
|
|
|
column["caption"] = caption;
|
|
|
|
column["style"] = style;
|
|
|
|
if (defaultWidth !== -1)
|
|
|
|
column["width"] = defaultWidth;
|
|
|
|
|
|
|
|
column["dataProperties"] = [name];
|
|
|
|
column["getRowValue"] = function(row, pos) {
|
2022-10-18 22:39:32 -04:00
|
|
|
if (pos === undefined)
|
|
|
|
pos = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
return row["full_data"][this.dataProperties[pos]];
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["compareRows"] = function(row1, row2) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value1 = this.getRowValue(row1);
|
|
|
|
const value2 = this.getRowValue(row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((typeof(value1) === "number") && (typeof(value2) === "number"))
|
2023-07-18 11:58:05 -04:00
|
|
|
return compareNumbers(value1, value2);
|
|
|
|
return window.qBittorrent.Misc.naturalSortCollator.compare(value1, value2);
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["updateTd"] = function(td, row) {
|
2022-10-18 22:39:32 -04:00
|
|
|
const value = this.getRowValue(row);
|
2024-10-03 08:37:11 -04:00
|
|
|
td.textContent = value;
|
|
|
|
td.title = value;
|
2022-10-18 22:39:32 -04:00
|
|
|
};
|
2024-10-03 08:37:11 -04:00
|
|
|
column["onResize"] = null;
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns.push(column);
|
|
|
|
this.columns[name] = column;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hiddenTableHeader.appendChild(new Element("th"));
|
|
|
|
this.fixedTableHeader.appendChild(new Element("th"));
|
2022-10-18 22:39:32 -04:00
|
|
|
},
|
|
|
|
selectRow: function() {},
|
|
|
|
updateRow: function(tr, fullUpdate) {
|
|
|
|
const row = this.rows.get(tr.rowId);
|
2024-10-03 08:37:11 -04:00
|
|
|
const data = row[fullUpdate ? "full_data" : "data"];
|
2022-10-18 22:39:32 -04:00
|
|
|
|
|
|
|
if (row.full_data.isFeed) {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addClass("articleTableFeed");
|
|
|
|
tr.removeClass("articleTableArticle");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.removeClass("articleTableFeed");
|
|
|
|
tr.addClass("articleTableArticle");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const tds = tr.getElements("td");
|
2022-10-18 22:39:32 -04:00
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (Object.hasOwn(data, this.columns[i].dataProperties[0]))
|
2022-10-18 22:39:32 -04:00
|
|
|
this.columns[i].updateTd(tds[i], row);
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
row["data"] = {};
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-07-18 11:58:05 -04:00
|
|
|
const LogMessageTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
filterText: "",
|
2023-07-18 11:58:05 -04:00
|
|
|
|
|
|
|
filteredLength: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
return this.tableBody.getElements("tr").length;
|
2023-07-18 11:58:05 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("rowId", "", "ID", 50, true);
|
|
|
|
this.newColumn("message", "", "Message", 350, true);
|
|
|
|
this.newColumn("timestamp", "", "Timestamp", 150, true);
|
|
|
|
this.newColumn("type", "", "Log Type", 100, true);
|
2023-07-18 11:58:05 -04:00
|
|
|
this.initColumnsFunctions();
|
|
|
|
},
|
|
|
|
|
|
|
|
initColumnsFunctions: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["timestamp"].updateTd = function(td, row) {
|
2023-07-18 11:58:05 -04:00
|
|
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
2024-10-03 08:37:11 -04:00
|
|
|
td.set({ "text": date, "title": date });
|
2023-07-18 11:58:05 -04:00
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["type"].updateTd = function(td, row) {
|
|
|
|
// Type of the message: Log::NORMAL: 1, Log::INFO: 2, Log::WARNING: 4, Log::CRITICAL: 8
|
2023-07-18 11:58:05 -04:00
|
|
|
let logLevel, addClass;
|
|
|
|
switch (this.getRowValue(row).toInt()) {
|
|
|
|
case 1:
|
2024-10-03 08:37:11 -04:00
|
|
|
logLevel = "Normal";
|
|
|
|
addClass = "logNormal";
|
2023-07-18 11:58:05 -04:00
|
|
|
break;
|
|
|
|
case 2:
|
2024-10-03 08:37:11 -04:00
|
|
|
logLevel = "Info";
|
|
|
|
addClass = "logInfo";
|
2023-07-18 11:58:05 -04:00
|
|
|
break;
|
|
|
|
case 4:
|
2024-10-03 08:37:11 -04:00
|
|
|
logLevel = "Warning";
|
|
|
|
addClass = "logWarning";
|
2023-07-18 11:58:05 -04:00
|
|
|
break;
|
|
|
|
case 8:
|
2024-10-03 08:37:11 -04:00
|
|
|
logLevel = "Critical";
|
|
|
|
addClass = "logCritical";
|
2023-07-18 11:58:05 -04:00
|
|
|
break;
|
|
|
|
default:
|
2024-10-03 08:37:11 -04:00
|
|
|
logLevel = "Unknown";
|
|
|
|
addClass = "logUnknown";
|
2023-07-18 11:58:05 -04:00
|
|
|
break;
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
td.set({ "text": logLevel, "title": logLevel });
|
|
|
|
td.getParent("tr").className = `logTableRow${addClass}`;
|
2023-07-18 11:58:05 -04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
|
|
let filteredRows = [];
|
|
|
|
this.filterText = window.qBittorrent.Log.getFilterText();
|
2024-10-03 08:37:11 -04:00
|
|
|
const filterTerms = (this.filterText.length > 0) ? this.filterText.toLowerCase().split(" ") : [];
|
2023-07-18 11:58:05 -04:00
|
|
|
const logLevels = window.qBittorrent.Log.getSelectedLevels();
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((filterTerms.length > 0) || (logLevels.length < 4)) {
|
|
|
|
for (const row of this.getRowValues()) {
|
|
|
|
if (!logLevels.includes(row.full_data.type.toString()))
|
2023-07-18 11:58:05 -04:00
|
|
|
continue;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((filterTerms.length > 0) && !window.qBittorrent.Misc.containsAllTerms(row.full_data.message, filterTerms))
|
2023-07-18 11:58:05 -04:00
|
|
|
continue;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows.push(row);
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows = [...this.getRowValues()];
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows.sort((row1, row2) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
const column = this.columns[this.sortedColumn];
|
|
|
|
const res = column.compareRows(row1, row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
return (this.reverseSort === "0") ? res : -res;
|
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
|
|
|
|
return filteredRows;
|
|
|
|
},
|
|
|
|
|
|
|
|
setupCommonEvents: function() {},
|
|
|
|
|
|
|
|
setupTr: function(tr) {
|
2024-10-03 08:37:11 -04:00
|
|
|
tr.addClass("logTableRow");
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const LogPeerTable = new Class({
|
|
|
|
Extends: LogMessageTable,
|
|
|
|
|
|
|
|
initColumns: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.newColumn("rowId", "", "ID", 50, true);
|
|
|
|
this.newColumn("ip", "", "IP", 150, true);
|
|
|
|
this.newColumn("timestamp", "", "Timestamp", 150, true);
|
|
|
|
this.newColumn("blocked", "", "Status", 150, true);
|
|
|
|
this.newColumn("reason", "", "Reason", 150, true);
|
2023-07-18 11:58:05 -04:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["timestamp"].updateTd = function(td, row) {
|
2023-07-18 11:58:05 -04:00
|
|
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
2024-10-03 08:37:11 -04:00
|
|
|
td.set({ "text": date, "title": date });
|
2023-07-18 11:58:05 -04:00
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.columns["blocked"].updateTd = function(td, row) {
|
2023-07-18 11:58:05 -04:00
|
|
|
let status, addClass;
|
|
|
|
if (this.getRowValue(row)) {
|
2024-10-03 08:37:11 -04:00
|
|
|
status = "Blocked";
|
|
|
|
addClass = "peerBlocked";
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
status = "Banned";
|
|
|
|
addClass = "peerBanned";
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
td.set({ "text": status, "title": status });
|
|
|
|
td.getParent("tr").className = `logTableRow${addClass}`;
|
2023-07-18 11:58:05 -04:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
|
|
let filteredRows = [];
|
|
|
|
this.filterText = window.qBittorrent.Log.getFilterText();
|
2024-10-03 08:37:11 -04:00
|
|
|
const filterTerms = (this.filterText.length > 0) ? this.filterText.toLowerCase().split(" ") : [];
|
2023-07-18 11:58:05 -04:00
|
|
|
if (filterTerms.length > 0) {
|
2024-10-03 08:37:11 -04:00
|
|
|
for (const row of this.getRowValues()) {
|
|
|
|
if ((filterTerms.length > 0) && !window.qBittorrent.Misc.containsAllTerms(row.full_data.ip, filterTerms))
|
2023-07-18 11:58:05 -04:00
|
|
|
continue;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows.push(row);
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows = [...this.getRowValues()];
|
2023-07-18 11:58:05 -04:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
filteredRows.sort((row1, row2) => {
|
2023-07-18 11:58:05 -04:00
|
|
|
const column = this.columns[this.sortedColumn];
|
|
|
|
const res = column.compareRows(row1, row2);
|
2024-10-03 08:37:11 -04:00
|
|
|
return (this.reverseSort === "0") ? res : -res;
|
|
|
|
});
|
2023-07-18 11:58:05 -04:00
|
|
|
|
|
|
|
return filteredRows;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const TorrentWebseedsTable = new Class({
|
|
|
|
Extends: DynamicTable,
|
|
|
|
|
|
|
|
initColumns: function() {
|
|
|
|
this.newColumn("url", "", "URL", 500, true);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-10-18 22:39:32 -04:00
|
|
|
return exports();
|
|
|
|
})();
|
|
|
|
Object.freeze(window.qBittorrent.DynamicTable);
|
|
|
|
|
|
|
|
/*************************************************************/
|