2022-10-18 22:39:32 -04:00
|
|
|
/*
|
|
|
|
* Bittorrent Client using Qt and libtorrent.
|
|
|
|
* Copyright (C) 2009 Christophe Dumez <chris@qbittorrent.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* In addition, as a special exception, the copyright holders give permission to
|
|
|
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
|
|
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
|
|
* and distribute the linked executables. You must obey the GNU General Public
|
|
|
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
|
|
|
* modify file(s), you may extend this exception to your version of the file(s),
|
|
|
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
|
|
|
* exception statement from your version.
|
|
|
|
*/
|
|
|
|
|
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.ContextMenu ??= (() => {
|
|
|
|
const exports = () => {
|
2024-01-07 17:04:03 -05:00
|
|
|
return {
|
|
|
|
ContextMenu: ContextMenu,
|
|
|
|
TorrentsTableContextMenu: TorrentsTableContextMenu,
|
|
|
|
CategoriesFilterContextMenu: CategoriesFilterContextMenu,
|
|
|
|
TagsFilterContextMenu: TagsFilterContextMenu,
|
2024-10-01 14:25:40 -04:00
|
|
|
TrackersFilterContextMenu: TrackersFilterContextMenu,
|
2024-01-07 17:04:03 -05:00
|
|
|
SearchPluginsTableContextMenu: SearchPluginsTableContextMenu,
|
|
|
|
RssFeedContextMenu: RssFeedContextMenu,
|
|
|
|
RssArticleContextMenu: RssArticleContextMenu,
|
|
|
|
RssDownloaderRuleContextMenu: RssDownloaderRuleContextMenu
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
let lastShownContextMenu = null;
|
|
|
|
const ContextMenu = new Class({
|
2024-10-03 08:37:11 -04:00
|
|
|
// implements
|
2024-01-07 17:04:03 -05:00
|
|
|
Implements: [Options, Events],
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// options
|
2024-01-07 17:04:03 -05:00
|
|
|
options: {
|
|
|
|
actions: {},
|
2024-10-03 08:37:11 -04:00
|
|
|
menu: "menu_id",
|
2024-01-07 17:04:03 -05:00
|
|
|
stopEvent: true,
|
2024-10-03 08:37:11 -04:00
|
|
|
targets: "body",
|
2024-01-07 17:04:03 -05:00
|
|
|
offsets: {
|
|
|
|
x: 0,
|
|
|
|
y: 0
|
|
|
|
},
|
|
|
|
onShow: () => {},
|
|
|
|
onHide: () => {},
|
|
|
|
onClick: () => {},
|
|
|
|
fadeSpeed: 200,
|
|
|
|
touchTimer: 600
|
|
|
|
},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// initialization
|
2024-01-07 17:04:03 -05:00
|
|
|
initialize: function(options) {
|
2024-10-03 08:37:11 -04:00
|
|
|
// set options
|
2024-01-07 17:04:03 -05:00
|
|
|
this.setOptions(options);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// option diffs menu
|
2024-01-07 17:04:03 -05:00
|
|
|
this.menu = $(this.options.menu);
|
|
|
|
this.targets = $$(this.options.targets);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// fx
|
2024-01-07 17:04:03 -05:00
|
|
|
this.fx = new Fx.Tween(this.menu, {
|
2024-10-03 08:37:11 -04:00
|
|
|
property: "opacity",
|
2024-01-07 17:04:03 -05:00
|
|
|
duration: this.options.fadeSpeed,
|
2024-10-03 08:37:11 -04:00
|
|
|
onComplete: () => {
|
|
|
|
this.menu.style.visibility = (getComputedStyle(this.menu).opacity > 0) ? "visible" : "hidden";
|
|
|
|
}
|
2024-01-07 17:04:03 -05:00
|
|
|
});
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// hide and begin the listener
|
2024-01-07 17:04:03 -05:00
|
|
|
this.hide().startListener();
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// hide the menu
|
|
|
|
this.menu.style.position = "absolute";
|
|
|
|
this.menu.style.top = "-900000px";
|
|
|
|
this.menu.style.display = "block";
|
2024-01-07 17:04:03 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
adjustMenuPosition: function(e) {
|
|
|
|
this.updateMenuItems();
|
|
|
|
|
|
|
|
const scrollableMenuMaxHeight = document.documentElement.clientHeight * 0.75;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (this.menu.hasClass("scrollableMenu"))
|
|
|
|
this.menu.style.maxHeight = `${scrollableMenuMaxHeight}px`;
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
// draw the menu off-screen to know the menu dimensions
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.style.left = "-999em";
|
|
|
|
this.menu.style.top = "-999em";
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
// position the menu
|
2024-10-03 08:37:11 -04:00
|
|
|
let xPosMenu = e.pageX + this.options.offsets.x;
|
|
|
|
let yPosMenu = e.pageY + this.options.offsets.y;
|
|
|
|
if ((xPosMenu + this.menu.offsetWidth) > document.documentElement.clientWidth)
|
2024-01-07 17:04:03 -05:00
|
|
|
xPosMenu -= this.menu.offsetWidth;
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((yPosMenu + this.menu.offsetHeight) > document.documentElement.clientHeight)
|
2024-01-07 17:04:03 -05:00
|
|
|
yPosMenu = document.documentElement.clientHeight - this.menu.offsetHeight;
|
|
|
|
if (xPosMenu < 0)
|
|
|
|
xPosMenu = 0;
|
|
|
|
if (yPosMenu < 0)
|
|
|
|
yPosMenu = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.style.left = `${xPosMenu}px`;
|
|
|
|
this.menu.style.top = `${yPosMenu}px`;
|
|
|
|
this.menu.style.position = "absolute";
|
|
|
|
this.menu.style.zIndex = "2000";
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
// position the sub-menu
|
2024-10-03 08:37:11 -04:00
|
|
|
const uls = this.menu.getElementsByTagName("ul");
|
2024-01-07 17:04:03 -05:00
|
|
|
for (let i = 0; i < uls.length; ++i) {
|
|
|
|
const ul = uls[i];
|
2024-10-03 08:37:11 -04:00
|
|
|
if (ul.hasClass("scrollableMenu"))
|
|
|
|
ul.style.maxHeight = `${scrollableMenuMaxHeight}px`;
|
2024-01-07 17:04:03 -05:00
|
|
|
const rectParent = ul.parentNode.getBoundingClientRect();
|
|
|
|
const xPosOrigin = rectParent.left;
|
|
|
|
const yPosOrigin = rectParent.bottom;
|
|
|
|
let xPos = xPosOrigin + rectParent.width - 1;
|
|
|
|
let yPos = yPosOrigin - rectParent.height - 1;
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((xPos + ul.offsetWidth) > document.documentElement.clientWidth)
|
2024-01-07 17:04:03 -05:00
|
|
|
xPos -= (ul.offsetWidth + rectParent.width - 2);
|
2024-10-03 08:37:11 -04:00
|
|
|
if ((yPos + ul.offsetHeight) > document.documentElement.clientHeight)
|
2024-01-07 17:04:03 -05:00
|
|
|
yPos = document.documentElement.clientHeight - ul.offsetHeight;
|
|
|
|
if (xPos < 0)
|
|
|
|
xPos = 0;
|
|
|
|
if (yPos < 0)
|
|
|
|
yPos = 0;
|
2024-10-03 08:37:11 -04:00
|
|
|
ul.style.marginLeft = `${xPos - xPosOrigin}px`;
|
|
|
|
ul.style.marginTop = `${yPos - yPosOrigin}px`;
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
setupEventListeners: function(elem) {
|
2024-10-03 08:37:11 -04:00
|
|
|
elem.addEventListener("contextmenu", (e) => {
|
2024-01-07 17:04:03 -05:00
|
|
|
this.triggerMenu(e, elem);
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
|
|
|
elem.addEventListener("click", (e) => {
|
2024-01-07 17:04:03 -05:00
|
|
|
this.hide();
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
elem.addEventListener("touchstart", (e) => {
|
2024-01-07 17:04:03 -05:00
|
|
|
this.hide();
|
2024-10-03 08:37:11 -04:00
|
|
|
this.touchStartAt = performance.now();
|
|
|
|
this.touchStartEvent = e;
|
|
|
|
}, { passive: true });
|
|
|
|
elem.addEventListener("touchend", (e) => {
|
|
|
|
const now = performance.now();
|
|
|
|
const touchStartAt = this.touchStartAt;
|
|
|
|
const touchStartEvent = this.touchStartEvent;
|
|
|
|
|
|
|
|
this.touchStartAt = null;
|
|
|
|
this.touchStartEvent = null;
|
|
|
|
|
|
|
|
const isTargetUnchanged = (Math.abs(e.event.pageX - touchStartEvent.event.pageX) <= 10) && (Math.abs(e.event.pageY - touchStartEvent.event.pageY) <= 10);
|
|
|
|
if (((now - touchStartAt) >= this.options.touchTimer) && isTargetUnchanged)
|
|
|
|
this.triggerMenu(touchStartEvent, elem);
|
|
|
|
}, { passive: true });
|
2024-01-07 17:04:03 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
addTarget: function(t) {
|
2024-10-03 08:37:11 -04:00
|
|
|
// prevent long press from selecting this text
|
|
|
|
t.style.userSelect = "none";
|
|
|
|
|
2024-01-07 17:04:03 -05:00
|
|
|
this.targets[this.targets.length] = t;
|
|
|
|
this.setupEventListeners(t);
|
|
|
|
},
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
searchAndAddTargets: function() {
|
|
|
|
document.querySelectorAll(this.options.targets).forEach((target) => { this.addTarget(target); });
|
|
|
|
},
|
|
|
|
|
2024-01-07 17:04:03 -05:00
|
|
|
triggerMenu: function(e, el) {
|
|
|
|
if (this.options.disabled)
|
|
|
|
return;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// prevent default, if told to
|
2024-01-07 17:04:03 -05:00
|
|
|
if (this.options.stopEvent) {
|
2024-10-03 08:37:11 -04:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
// record this as the trigger
|
2024-01-07 17:04:03 -05:00
|
|
|
this.options.element = $(el);
|
|
|
|
this.adjustMenuPosition(e);
|
2024-10-03 08:37:11 -04:00
|
|
|
// show the menu
|
2024-01-07 17:04:03 -05:00
|
|
|
this.show();
|
|
|
|
},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// get things started
|
2024-01-07 17:04:03 -05:00
|
|
|
startListener: function() {
|
|
|
|
/* all elements */
|
2024-10-03 08:37:11 -04:00
|
|
|
this.targets.each((el) => {
|
2024-01-07 17:04:03 -05:00
|
|
|
this.setupEventListeners(el);
|
2024-10-03 08:37:11 -04:00
|
|
|
}, this);
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
/* menu items */
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.addEventListener("click", (e) => {
|
|
|
|
const menuItem = e.target.closest("li");
|
|
|
|
if (!menuItem)
|
|
|
|
return;
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
e.preventDefault();
|
|
|
|
if (!menuItem.classList.contains("disabled")) {
|
|
|
|
const anchor = menuItem.firstElementChild;
|
|
|
|
this.execute(anchor.href.split("#")[1], this.options.element);
|
|
|
|
this.fireEvent("click", [anchor, e]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// hide on body click
|
|
|
|
$(document.body).addEventListener("click", () => {
|
2024-01-07 17:04:03 -05:00
|
|
|
this.hide();
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2024-01-07 17:04:03 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
updateMenuItems: function() {},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// show menu
|
2024-01-07 17:04:03 -05:00
|
|
|
show: function(trigger) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (lastShownContextMenu && (lastShownContextMenu !== this))
|
2024-01-07 17:04:03 -05:00
|
|
|
lastShownContextMenu.hide();
|
|
|
|
this.fx.start(1);
|
2024-10-03 08:37:11 -04:00
|
|
|
this.fireEvent("show");
|
2024-01-07 17:04:03 -05:00
|
|
|
lastShownContextMenu = this;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
// hide the menu
|
2024-01-07 17:04:03 -05:00
|
|
|
hide: function(trigger) {
|
2024-10-03 08:37:11 -04:00
|
|
|
if (lastShownContextMenu && (lastShownContextMenu.menu.style.visibility !== "hidden")) {
|
2024-01-07 17:04:03 -05:00
|
|
|
this.fx.start(0);
|
2024-10-03 08:37:11 -04:00
|
|
|
// this.menu.fade('out');
|
|
|
|
this.fireEvent("hide");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
setItemChecked: function(item, checked) {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.getElement("a[href$=" + item + "]").firstChild.style.opacity =
|
|
|
|
checked ? "1" : "0";
|
2024-01-07 17:04:03 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
getItemChecked: function(item) {
|
2024-10-03 08:37:11 -04:00
|
|
|
return this.menu.getElement("a[href$=" + item + "]").firstChild.style.opacity !== "0";
|
2024-01-07 17:04:03 -05:00
|
|
|
},
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
// hide an item
|
2024-01-07 17:04:03 -05:00
|
|
|
hideItem: function(item) {
|
2024-10-01 14:25:40 -04:00
|
|
|
this.menu.getElement("a[href$=" + item + "]").parentNode.addClass("invisible");
|
2024-01-07 17:04:03 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
// show an item
|
2024-01-07 17:04:03 -05:00
|
|
|
showItem: function(item) {
|
2024-10-01 14:25:40 -04:00
|
|
|
this.menu.getElement("a[href$=" + item + "]").parentNode.removeClass("invisible");
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
// enable/disable an item
|
|
|
|
setEnabled: function(item, enabled) {
|
|
|
|
this.menu.querySelector(`:scope a[href$="${item}"]`).parentElement.classList.toggle("disabled", !enabled);
|
2024-01-07 17:04:03 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
// disable the entire menu
|
2024-01-07 17:04:03 -05:00
|
|
|
disable: function() {
|
|
|
|
this.options.disabled = true;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
// enable the entire menu
|
2024-01-07 17:04:03 -05:00
|
|
|
enable: function() {
|
|
|
|
this.options.disabled = false;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
// execute an action
|
2024-01-07 17:04:03 -05:00
|
|
|
execute: function(action, element) {
|
2024-10-01 14:25:40 -04:00
|
|
|
if (this.options.actions[action])
|
2024-01-07 17:04:03 -05:00
|
|
|
this.options.actions[action](element, this, action);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const TorrentsTableContextMenu = new Class({
|
|
|
|
Extends: ContextMenu,
|
|
|
|
|
|
|
|
updateMenuItems: function() {
|
|
|
|
let all_are_seq_dl = true;
|
|
|
|
let there_are_seq_dl = false;
|
|
|
|
let all_are_f_l_piece_prio = true;
|
|
|
|
let there_are_f_l_piece_prio = false;
|
|
|
|
let all_are_downloaded = true;
|
2024-10-01 14:25:40 -04:00
|
|
|
let all_are_stopped = true;
|
|
|
|
let there_are_stopped = false;
|
2024-01-07 17:04:03 -05:00
|
|
|
let all_are_force_start = true;
|
|
|
|
let there_are_force_start = false;
|
|
|
|
let all_are_super_seeding = true;
|
|
|
|
let all_are_auto_tmm = true;
|
|
|
|
let there_are_auto_tmm = false;
|
2024-10-03 08:37:11 -04:00
|
|
|
let thereAreV1Hashes = false;
|
|
|
|
let thereAreV2Hashes = false;
|
|
|
|
const tagCount = new Map();
|
|
|
|
const categoryCount = new Map();
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const selectedRows = torrentsTable.selectedRowsIds();
|
|
|
|
selectedRows.forEach((item, index) => {
|
|
|
|
const data = torrentsTable.getRow(item).full_data;
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (data["seq_dl"] !== true)
|
2024-01-07 17:04:03 -05:00
|
|
|
all_are_seq_dl = false;
|
|
|
|
else
|
|
|
|
there_are_seq_dl = true;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (data["f_l_piece_prio"] !== true)
|
2024-01-07 17:04:03 -05:00
|
|
|
all_are_f_l_piece_prio = false;
|
|
|
|
else
|
|
|
|
there_are_f_l_piece_prio = true;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (data["progress"] !== 1.0) // not downloaded
|
2024-01-07 17:04:03 -05:00
|
|
|
all_are_downloaded = false;
|
2024-10-03 08:37:11 -04:00
|
|
|
else if (data["super_seeding"] !== true)
|
2024-01-07 17:04:03 -05:00
|
|
|
all_are_super_seeding = false;
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
if ((data["state"] !== "stoppedUP") && (data["state"] !== "stoppedDL"))
|
|
|
|
all_are_stopped = false;
|
2024-01-07 17:04:03 -05:00
|
|
|
else
|
2024-10-01 14:25:40 -04:00
|
|
|
there_are_stopped = true;
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (data["force_start"] !== true)
|
2024-01-07 17:04:03 -05:00
|
|
|
all_are_force_start = false;
|
|
|
|
else
|
|
|
|
there_are_force_start = true;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (data["auto_tmm"] === true)
|
2024-01-07 17:04:03 -05:00
|
|
|
there_are_auto_tmm = true;
|
|
|
|
else
|
|
|
|
all_are_auto_tmm = false;
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
if (data["infohash_v1"] !== "")
|
|
|
|
thereAreV1Hashes = true;
|
|
|
|
|
|
|
|
if (data["infohash_v2"] !== "")
|
|
|
|
thereAreV2Hashes = true;
|
|
|
|
|
|
|
|
const torrentTags = data["tags"].split(", ");
|
|
|
|
for (const tag of torrentTags) {
|
|
|
|
const count = tagCount.get(tag);
|
|
|
|
tagCount.set(tag, ((count !== undefined) ? (count + 1) : 1));
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const torrentCategory = data["category"];
|
|
|
|
const count = categoryCount.get(torrentCategory);
|
|
|
|
categoryCount.set(torrentCategory, ((count !== undefined) ? (count + 1) : 1));
|
|
|
|
});
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
// hide renameFiles when more than 1 torrent is selected
|
2024-10-03 08:37:11 -04:00
|
|
|
if (selectedRows.length === 1) {
|
|
|
|
const data = torrentsTable.getRow(selectedRows[0]).full_data;
|
|
|
|
const metadata_downloaded = !((data["state"] === "metaDL") || (data["state"] === "forcedMetaDL") || (data["total_size"] === -1));
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
// hide renameFiles when metadata hasn't been downloaded yet
|
|
|
|
metadata_downloaded
|
2024-10-03 08:37:11 -04:00
|
|
|
? this.showItem("renameFiles")
|
|
|
|
: this.hideItem("renameFiles");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.hideItem("renameFiles");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (all_are_downloaded) {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("downloadLimit");
|
|
|
|
this.menu.getElement("a[href$=uploadLimit]").parentNode.addClass("separator");
|
|
|
|
this.hideItem("sequentialDownload");
|
|
|
|
this.hideItem("firstLastPiecePrio");
|
|
|
|
this.showItem("superSeeding");
|
|
|
|
this.setItemChecked("superSeeding", all_are_super_seeding);
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
const show_seq_dl = (all_are_seq_dl || !there_are_seq_dl);
|
|
|
|
const show_f_l_piece_prio = (all_are_f_l_piece_prio || !there_are_f_l_piece_prio);
|
|
|
|
|
2024-01-07 17:04:03 -05:00
|
|
|
if (!show_seq_dl && show_f_l_piece_prio)
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.getElement("a[href$=firstLastPiecePrio]").parentNode.addClass("separator");
|
2024-01-07 17:04:03 -05:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.getElement("a[href$=firstLastPiecePrio]").parentNode.removeClass("separator");
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
if (show_seq_dl)
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("sequentialDownload");
|
2024-01-07 17:04:03 -05:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("sequentialDownload");
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
if (show_f_l_piece_prio)
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("firstLastPiecePrio");
|
2024-01-07 17:04:03 -05:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("firstLastPiecePrio");
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.setItemChecked("sequentialDownload", all_are_seq_dl);
|
|
|
|
this.setItemChecked("firstLastPiecePrio", all_are_f_l_piece_prio);
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("downloadLimit");
|
|
|
|
this.menu.getElement("a[href$=uploadLimit]").parentNode.removeClass("separator");
|
|
|
|
this.hideItem("superSeeding");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("start");
|
|
|
|
this.showItem("stop");
|
|
|
|
this.showItem("forceStart");
|
2024-10-01 14:25:40 -04:00
|
|
|
if (all_are_stopped)
|
|
|
|
this.hideItem("stop");
|
2024-01-07 17:04:03 -05:00
|
|
|
else if (all_are_force_start)
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("forceStart");
|
2024-10-01 14:25:40 -04:00
|
|
|
else if (!there_are_stopped && !there_are_force_start)
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("start");
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
if (!all_are_auto_tmm && there_are_auto_tmm) {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("autoTorrentManagement");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
else {
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("autoTorrentManagement");
|
|
|
|
this.setItemChecked("autoTorrentManagement", all_are_auto_tmm);
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.setEnabled("copyInfohash1", thereAreV1Hashes);
|
|
|
|
this.setEnabled("copyInfohash2", thereAreV2Hashes);
|
|
|
|
|
|
|
|
const contextTagList = $("contextTagList");
|
|
|
|
tagList.forEach((tag, tagHash) => {
|
|
|
|
const checkbox = contextTagList.getElement(`a[href="#Tag/${tag.name}"] input[type="checkbox"]`);
|
|
|
|
const count = tagCount.get(tag.name);
|
|
|
|
const hasCount = (count !== undefined);
|
|
|
|
const isLesser = (count < selectedRows.length);
|
|
|
|
checkbox.indeterminate = (hasCount ? isLesser : false);
|
|
|
|
checkbox.checked = (hasCount ? !isLesser : false);
|
|
|
|
});
|
|
|
|
|
|
|
|
const contextCategoryList = document.getElementById("contextCategoryList");
|
|
|
|
category_list.forEach((category, categoryHash) => {
|
|
|
|
const categoryIcon = contextCategoryList.querySelector(`a[href$="#Category/${category.name}"] img`);
|
|
|
|
const count = categoryCount.get(category.name);
|
|
|
|
const isEqual = ((count !== undefined) && (count === selectedRows.length));
|
|
|
|
categoryIcon.classList.toggle("highlightedCategoryIcon", isEqual);
|
2024-10-10 09:01:53 -04:00
|
|
|
// swap SVG to view-categories-selected.svg when selected
|
|
|
|
categoryIcon.src = isEqual
|
|
|
|
? "images/view-categories-selected.svg"
|
|
|
|
: "images/view-categories.svg";
|
2024-10-03 08:37:11 -04:00
|
|
|
});
|
2024-01-07 17:04:03 -05:00
|
|
|
},
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
updateCategoriesSubMenu: function(categoryList) {
|
|
|
|
const contextCategoryList = $("contextCategoryList");
|
|
|
|
contextCategoryList.getChildren().each(c => c.destroy());
|
|
|
|
|
|
|
|
const createMenuItem = (text, imgURL, clickFn) => {
|
|
|
|
const anchor = document.createElement("a");
|
|
|
|
anchor.textContent = text;
|
|
|
|
anchor.addEventListener("click", () => { clickFn(); });
|
|
|
|
|
|
|
|
const img = document.createElement("img");
|
|
|
|
img.src = imgURL;
|
|
|
|
img.alt = text;
|
|
|
|
anchor.prepend(img);
|
|
|
|
|
|
|
|
const item = document.createElement("li");
|
|
|
|
item.appendChild(anchor);
|
|
|
|
|
|
|
|
return item;
|
|
|
|
};
|
|
|
|
contextCategoryList.appendChild(createMenuItem("New...", "images/list-add.svg", torrentNewCategoryFN));
|
|
|
|
contextCategoryList.appendChild(createMenuItem("Reset", "images/edit-clear.svg", () => { torrentSetCategoryFN(0); }));
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
const sortedCategories = [];
|
2024-10-03 08:37:11 -04:00
|
|
|
categoryList.forEach((category, hash) => sortedCategories.push({
|
|
|
|
categoryName: category.name,
|
|
|
|
categoryHash: hash
|
|
|
|
}));
|
|
|
|
sortedCategories.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(
|
|
|
|
left.categoryName, right.categoryName));
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
let first = true;
|
2024-10-03 08:37:11 -04:00
|
|
|
for (const { categoryName, categoryHash } of sortedCategories) {
|
|
|
|
const anchor = document.createElement("a");
|
|
|
|
anchor.href = `#Category/${categoryName}`;
|
|
|
|
anchor.textContent = categoryName;
|
|
|
|
anchor.addEventListener("click", (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
torrentSetCategoryFN(categoryHash);
|
2024-01-07 17:04:03 -05:00
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
|
|
|
|
const img = document.createElement("img");
|
|
|
|
img.src = "images/view-categories.svg";
|
|
|
|
anchor.prepend(img);
|
|
|
|
|
|
|
|
const setCategoryItem = document.createElement("li");
|
|
|
|
setCategoryItem.appendChild(anchor);
|
2024-01-07 17:04:03 -05:00
|
|
|
if (first) {
|
2024-10-03 08:37:11 -04:00
|
|
|
setCategoryItem.addClass("separator");
|
2024-01-07 17:04:03 -05:00
|
|
|
first = false;
|
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
|
|
|
|
contextCategoryList.appendChild(setCategoryItem);
|
|
|
|
}
|
2024-01-07 17:04:03 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
updateTagsSubMenu: function(tagList) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const contextTagList = $("contextTagList");
|
2024-01-07 17:04:03 -05:00
|
|
|
while (contextTagList.firstChild !== null)
|
|
|
|
contextTagList.removeChild(contextTagList.firstChild);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
const createMenuItem = (text, imgURL, clickFn) => {
|
|
|
|
const anchor = document.createElement("a");
|
|
|
|
anchor.textContent = text;
|
|
|
|
anchor.addEventListener("click", () => { clickFn(); });
|
|
|
|
|
|
|
|
const img = document.createElement("img");
|
|
|
|
img.src = imgURL;
|
|
|
|
img.alt = text;
|
|
|
|
anchor.prepend(img);
|
|
|
|
|
|
|
|
const item = document.createElement("li");
|
|
|
|
item.appendChild(anchor);
|
|
|
|
|
|
|
|
return item;
|
|
|
|
};
|
|
|
|
contextTagList.appendChild(createMenuItem("Add...", "images/list-add.svg", torrentAddTagsFN));
|
|
|
|
contextTagList.appendChild(createMenuItem("Remove All", "images/edit-clear.svg", torrentRemoveAllTagsFN));
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
const sortedTags = [];
|
2024-10-03 08:37:11 -04:00
|
|
|
tagList.forEach((tag, hash) => sortedTags.push({
|
|
|
|
tagName: tag.name,
|
|
|
|
tagHash: hash
|
|
|
|
}));
|
|
|
|
sortedTags.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.tagName, right.tagName));
|
2024-01-07 17:04:03 -05:00
|
|
|
|
|
|
|
for (let i = 0; i < sortedTags.length; ++i) {
|
2024-10-03 08:37:11 -04:00
|
|
|
const { tagName, tagHash } = sortedTags[i];
|
|
|
|
|
|
|
|
const input = document.createElement("input");
|
|
|
|
input.type = "checkbox";
|
|
|
|
input.addEventListener("click", (event) => {
|
|
|
|
input.checked = !input.checked;
|
2024-01-07 17:04:03 -05:00
|
|
|
});
|
2024-10-03 08:37:11 -04:00
|
|
|
|
|
|
|
const anchor = document.createElement("a");
|
|
|
|
anchor.href = `#Tag/${tagName}`;
|
|
|
|
anchor.textContent = tagName;
|
|
|
|
anchor.addEventListener("click", (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
torrentSetTagsFN(tagHash, !input.checked);
|
|
|
|
});
|
|
|
|
anchor.prepend(input);
|
|
|
|
|
|
|
|
const setTagItem = document.createElement("li");
|
|
|
|
setTagItem.appendChild(anchor);
|
2024-01-07 17:04:03 -05:00
|
|
|
if (i === 0)
|
2024-10-03 08:37:11 -04:00
|
|
|
setTagItem.addClass("separator");
|
|
|
|
|
|
|
|
contextTagList.appendChild(setTagItem);
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
|
|
|
}
|
2024-01-07 17:04:03 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
const CategoriesFilterContextMenu = new Class({
|
|
|
|
Extends: ContextMenu,
|
|
|
|
updateMenuItems: function() {
|
2024-10-01 14:25:40 -04:00
|
|
|
const id = Number(this.options.element.id);
|
|
|
|
if ((id !== CATEGORIES_ALL) && (id !== CATEGORIES_UNCATEGORIZED)) {
|
|
|
|
this.showItem("editCategory");
|
|
|
|
this.showItem("deleteCategory");
|
|
|
|
if (useSubcategories)
|
|
|
|
this.showItem("createSubcategory");
|
|
|
|
else
|
|
|
|
this.hideItem("createSubcategory");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
else {
|
2024-10-01 14:25:40 -04:00
|
|
|
this.hideItem("editCategory");
|
|
|
|
this.hideItem("deleteCategory");
|
|
|
|
this.hideItem("createSubcategory");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
2024-01-07 17:04:03 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
const TagsFilterContextMenu = new Class({
|
|
|
|
Extends: ContextMenu,
|
|
|
|
updateMenuItems: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const id = Number(this.options.element.id);
|
|
|
|
if ((id !== TAGS_ALL) && (id !== TAGS_UNTAGGED))
|
|
|
|
this.showItem("deleteTag");
|
2024-01-07 17:04:03 -05:00
|
|
|
else
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("deleteTag");
|
2022-10-18 22:39:32 -04:00
|
|
|
}
|
2024-01-07 17:04:03 -05:00
|
|
|
});
|
|
|
|
|
2024-10-01 14:25:40 -04:00
|
|
|
const TrackersFilterContextMenu = new Class({
|
|
|
|
Extends: ContextMenu,
|
|
|
|
updateMenuItems: function() {
|
|
|
|
const id = Number(this.options.element.id);
|
|
|
|
if ((id !== TRACKERS_ALL) && (id !== TRACKERS_TRACKERLESS))
|
|
|
|
this.showItem("deleteTracker");
|
|
|
|
else
|
|
|
|
this.hideItem("deleteTracker");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-01-07 17:04:03 -05:00
|
|
|
const SearchPluginsTableContextMenu = new Class({
|
|
|
|
Extends: ContextMenu,
|
|
|
|
|
|
|
|
updateMenuItems: function() {
|
|
|
|
const enabledColumnIndex = function(text) {
|
|
|
|
const columns = $("searchPluginsTableFixedHeaderRow").getChildren("th");
|
2024-10-03 08:37:11 -04:00
|
|
|
for (let i = 0; i < columns.length; ++i) {
|
|
|
|
if (columns[i].textContent === "Enabled")
|
2024-01-07 17:04:03 -05:00
|
|
|
return i;
|
2024-10-03 08:37:11 -04:00
|
|
|
}
|
2024-01-07 17:04:03 -05:00
|
|
|
};
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("Enabled");
|
|
|
|
this.setItemChecked("Enabled", (this.options.element.getChildren("td")[enabledColumnIndex()].textContent === "Yes"));
|
2024-01-07 17:04:03 -05:00
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("Uninstall");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssFeedContextMenu = new Class({
|
|
|
|
Extends: ContextMenu,
|
|
|
|
updateMenuItems: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const selectedRows = window.qBittorrent.Rss.rssFeedTable.selectedRowsIds();
|
|
|
|
this.menu.getElement("a[href$=newSubscription]").parentNode.addClass("separator");
|
2024-01-07 17:04:03 -05:00
|
|
|
switch (selectedRows.length) {
|
|
|
|
case 0:
|
|
|
|
// remove separator on top of newSubscription entry to avoid double line
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.getElement("a[href$=newSubscription]").parentNode.removeClass("separator");
|
2024-01-07 17:04:03 -05:00
|
|
|
// menu when nothing selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("update");
|
|
|
|
this.hideItem("markRead");
|
|
|
|
this.hideItem("rename");
|
|
|
|
this.hideItem("delete");
|
|
|
|
this.showItem("newSubscription");
|
|
|
|
this.showItem("newFolder");
|
|
|
|
this.showItem("updateAll");
|
|
|
|
this.hideItem("copyFeedURL");
|
2024-01-07 17:04:03 -05:00
|
|
|
break;
|
|
|
|
case 1:
|
2024-10-03 08:37:11 -04:00
|
|
|
if (selectedRows[0] === "0") {
|
2024-01-07 17:04:03 -05:00
|
|
|
// menu when "unread" feed selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("update");
|
|
|
|
this.showItem("markRead");
|
|
|
|
this.hideItem("rename");
|
|
|
|
this.hideItem("delete");
|
|
|
|
this.showItem("newSubscription");
|
|
|
|
this.hideItem("newFolder");
|
|
|
|
this.hideItem("updateAll");
|
|
|
|
this.hideItem("copyFeedURL");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
2024-10-03 08:37:11 -04:00
|
|
|
else if (window.qBittorrent.Rss.rssFeedTable.getRow(selectedRows[0]).full_data.dataUid === "") {
|
2024-01-07 17:04:03 -05:00
|
|
|
// menu when single folder selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("update");
|
|
|
|
this.showItem("markRead");
|
|
|
|
this.showItem("rename");
|
|
|
|
this.showItem("delete");
|
|
|
|
this.showItem("newSubscription");
|
|
|
|
this.showItem("newFolder");
|
|
|
|
this.hideItem("updateAll");
|
|
|
|
this.hideItem("copyFeedURL");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// menu when single feed selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("update");
|
|
|
|
this.showItem("markRead");
|
|
|
|
this.showItem("rename");
|
|
|
|
this.showItem("delete");
|
|
|
|
this.showItem("newSubscription");
|
|
|
|
this.hideItem("newFolder");
|
|
|
|
this.hideItem("updateAll");
|
|
|
|
this.showItem("copyFeedURL");
|
2024-01-07 17:04:03 -05:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// menu when multiple items selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("update");
|
|
|
|
this.showItem("markRead");
|
|
|
|
this.hideItem("rename");
|
|
|
|
this.showItem("delete");
|
|
|
|
this.hideItem("newSubscription");
|
|
|
|
this.hideItem("newFolder");
|
|
|
|
this.hideItem("updateAll");
|
|
|
|
this.showItem("copyFeedURL");
|
2024-01-07 17:04:03 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssArticleContextMenu = new Class({
|
|
|
|
Extends: ContextMenu
|
|
|
|
});
|
|
|
|
|
|
|
|
const RssDownloaderRuleContextMenu = new Class({
|
|
|
|
Extends: ContextMenu,
|
|
|
|
adjustMenuPosition: function(e) {
|
|
|
|
this.updateMenuItems();
|
|
|
|
|
|
|
|
// draw the menu off-screen to know the menu dimensions
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.style.left = "-999em";
|
|
|
|
this.menu.style.top = "-999em";
|
2024-01-07 17:04:03 -05:00
|
|
|
// position the menu
|
2024-10-03 08:37:11 -04:00
|
|
|
let xPosMenu = e.pageX + this.options.offsets.x - $("rssdownloaderpage").offsetLeft;
|
|
|
|
let yPosMenu = e.pageY + this.options.offsets.y - $("rssdownloaderpage").offsetTop;
|
2024-01-07 17:04:03 -05:00
|
|
|
if ((xPosMenu + this.menu.offsetWidth) > document.documentElement.clientWidth)
|
|
|
|
xPosMenu -= this.menu.offsetWidth;
|
|
|
|
if ((yPosMenu + this.menu.offsetHeight) > document.documentElement.clientHeight)
|
|
|
|
yPosMenu = document.documentElement.clientHeight - this.menu.offsetHeight;
|
|
|
|
xPosMenu = Math.max(xPosMenu, 0);
|
|
|
|
yPosMenu = Math.max(yPosMenu, 0);
|
|
|
|
|
2024-10-03 08:37:11 -04:00
|
|
|
this.menu.style.left = `${xPosMenu}px`;
|
|
|
|
this.menu.style.top = `${yPosMenu}px`;
|
|
|
|
this.menu.style.position = "absolute";
|
|
|
|
this.menu.style.zIndex = "2000";
|
2024-01-07 17:04:03 -05:00
|
|
|
},
|
|
|
|
updateMenuItems: function() {
|
2024-10-03 08:37:11 -04:00
|
|
|
const selectedRows = window.qBittorrent.RssDownloader.rssDownloaderRulesTable.selectedRowsIds();
|
|
|
|
this.showItem("addRule");
|
2024-01-07 17:04:03 -05:00
|
|
|
switch (selectedRows.length) {
|
|
|
|
case 0:
|
|
|
|
// menu when nothing selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.hideItem("deleteRule");
|
|
|
|
this.hideItem("renameRule");
|
|
|
|
this.hideItem("clearDownloadedEpisodes");
|
2024-01-07 17:04:03 -05:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// menu when single item selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("deleteRule");
|
|
|
|
this.showItem("renameRule");
|
|
|
|
this.showItem("clearDownloadedEpisodes");
|
2024-01-07 17:04:03 -05:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// menu when multiple items selected
|
2024-10-03 08:37:11 -04:00
|
|
|
this.showItem("deleteRule");
|
|
|
|
this.hideItem("renameRule");
|
|
|
|
this.showItem("clearDownloadedEpisodes");
|
2024-01-07 17:04:03 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return exports();
|
|
|
|
})();
|
|
|
|
Object.freeze(window.qBittorrent.ContextMenu);
|