mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-31 20:58:07 -06:00
The memory leak can be reproduced easily by opening two web pages of qbittorrent so that the WebUI pages are updated with full_update = true. If you have a large number of torrents, such as 100 torrents, you can observe a rapid increase in memory usage. This is caused by the incorrect usage of dispose and empty methods in the js codes and none of them garbage collect the elements. If event listeners are added to the DOM elements, those DOM elements will not be garbage collected at all event if they are not referenced or out of the scope, which will cause memory leaks. If some elements are expected to be removed, the correct way is to use destroy method instead. https://github.com/mootools/mootools-core/blob/master/Docs/Element/Element.md#element-method-dispose-elementdispose https://github.com/mootools/mootools-core/blob/master/Docs/Element/Element.md#element-method-empty-elementempty https://github.com/mootools/mootools-core/blob/master/Docs/Element/Element.md#element-method-destroy-elementdestroy Closes #19034. PR #19969.
694 lines
27 KiB
JavaScript
694 lines
27 KiB
JavaScript
/*
|
|
* 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.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
if (window.qBittorrent === undefined) {
|
|
window.qBittorrent = {};
|
|
}
|
|
|
|
window.qBittorrent.ContextMenu = (function() {
|
|
const exports = function() {
|
|
return {
|
|
ContextMenu: ContextMenu,
|
|
TorrentsTableContextMenu: TorrentsTableContextMenu,
|
|
CategoriesFilterContextMenu: CategoriesFilterContextMenu,
|
|
TagsFilterContextMenu: TagsFilterContextMenu,
|
|
SearchPluginsTableContextMenu: SearchPluginsTableContextMenu,
|
|
RssFeedContextMenu: RssFeedContextMenu,
|
|
RssArticleContextMenu: RssArticleContextMenu,
|
|
RssDownloaderRuleContextMenu: RssDownloaderRuleContextMenu
|
|
};
|
|
};
|
|
|
|
let lastShownContextMenu = null;
|
|
const ContextMenu = new Class({
|
|
//implements
|
|
Implements: [Options, Events],
|
|
|
|
//options
|
|
options: {
|
|
actions: {},
|
|
menu: 'menu_id',
|
|
stopEvent: true,
|
|
targets: 'body',
|
|
offsets: {
|
|
x: 0,
|
|
y: 0
|
|
},
|
|
onShow: $empty,
|
|
onHide: $empty,
|
|
onClick: $empty,
|
|
fadeSpeed: 200,
|
|
touchTimer: 600
|
|
},
|
|
|
|
//initialization
|
|
initialize: function(options) {
|
|
//set options
|
|
this.setOptions(options);
|
|
|
|
//option diffs menu
|
|
this.menu = $(this.options.menu);
|
|
this.targets = $$(this.options.targets);
|
|
|
|
//fx
|
|
this.fx = new Fx.Tween(this.menu, {
|
|
property: 'opacity',
|
|
duration: this.options.fadeSpeed,
|
|
onComplete: function() {
|
|
if (this.getStyle('opacity')) {
|
|
this.setStyle('visibility', 'visible');
|
|
}
|
|
else {
|
|
this.setStyle('visibility', 'hidden');
|
|
}
|
|
}.bind(this.menu)
|
|
});
|
|
|
|
//hide and begin the listener
|
|
this.hide().startListener();
|
|
|
|
//hide the menu
|
|
this.menu.setStyles({
|
|
'position': 'absolute',
|
|
'top': '-900000px',
|
|
'display': 'block'
|
|
});
|
|
},
|
|
|
|
adjustMenuPosition: function(e) {
|
|
this.updateMenuItems();
|
|
|
|
const scrollableMenuMaxHeight = document.documentElement.clientHeight * 0.75;
|
|
|
|
if (this.menu.hasClass('scrollableMenu'))
|
|
this.menu.setStyle('max-height', scrollableMenuMaxHeight);
|
|
|
|
// draw the menu off-screen to know the menu dimensions
|
|
this.menu.setStyles({
|
|
left: '-999em',
|
|
top: '-999em'
|
|
});
|
|
|
|
// position the menu
|
|
let xPosMenu = e.page.x + this.options.offsets.x;
|
|
let yPosMenu = e.page.y + this.options.offsets.y;
|
|
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;
|
|
if (xPosMenu < 0)
|
|
xPosMenu = 0;
|
|
if (yPosMenu < 0)
|
|
yPosMenu = 0;
|
|
this.menu.setStyles({
|
|
left: xPosMenu,
|
|
top: yPosMenu,
|
|
position: 'absolute',
|
|
'z-index': '2000'
|
|
});
|
|
|
|
// position the sub-menu
|
|
const uls = this.menu.getElementsByTagName('ul');
|
|
for (let i = 0; i < uls.length; ++i) {
|
|
const ul = uls[i];
|
|
if (ul.hasClass('scrollableMenu'))
|
|
ul.setStyle('max-height', scrollableMenuMaxHeight);
|
|
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;
|
|
if (xPos + ul.offsetWidth > document.documentElement.clientWidth)
|
|
xPos -= (ul.offsetWidth + rectParent.width - 2);
|
|
if (yPos + ul.offsetHeight > document.documentElement.clientHeight)
|
|
yPos = document.documentElement.clientHeight - ul.offsetHeight;
|
|
if (xPos < 0)
|
|
xPos = 0;
|
|
if (yPos < 0)
|
|
yPos = 0;
|
|
ul.setStyles({
|
|
'margin-left': xPos - xPosOrigin,
|
|
'margin-top': yPos - yPosOrigin
|
|
});
|
|
}
|
|
},
|
|
|
|
setupEventListeners: function(elem) {
|
|
elem.addEvent('contextmenu', function(e) {
|
|
this.triggerMenu(e, elem);
|
|
}.bind(this));
|
|
elem.addEvent('click', function(e) {
|
|
this.hide();
|
|
}.bind(this));
|
|
|
|
elem.addEvent('touchstart', function(e) {
|
|
e.preventDefault();
|
|
clearTimeout(this.touchstartTimer);
|
|
this.hide();
|
|
|
|
const touchstartEvent = e;
|
|
this.touchstartTimer = setTimeout(function() {
|
|
this.triggerMenu(touchstartEvent, elem);
|
|
}.bind(this), this.options.touchTimer);
|
|
}.bind(this));
|
|
elem.addEvent('touchend', function(e) {
|
|
e.preventDefault();
|
|
clearTimeout(this.touchstartTimer);
|
|
}.bind(this));
|
|
},
|
|
|
|
addTarget: function(t) {
|
|
this.targets[this.targets.length] = t;
|
|
this.setupEventListeners(t);
|
|
},
|
|
|
|
triggerMenu: function(e, el) {
|
|
if (this.options.disabled)
|
|
return;
|
|
|
|
//prevent default, if told to
|
|
if (this.options.stopEvent) {
|
|
e.stop();
|
|
}
|
|
//record this as the trigger
|
|
this.options.element = $(el);
|
|
this.adjustMenuPosition(e);
|
|
//show the menu
|
|
this.show();
|
|
},
|
|
|
|
//get things started
|
|
startListener: function() {
|
|
/* all elements */
|
|
this.targets.each(function(el) {
|
|
this.setupEventListeners(el);
|
|
}.bind(this), this);
|
|
|
|
/* menu items */
|
|
this.menu.getElements('a').each(function(item) {
|
|
item.addEvent('click', function(e) {
|
|
e.preventDefault();
|
|
if (!item.hasClass('disabled')) {
|
|
this.execute(item.get('href').split('#')[1], $(this.options.element));
|
|
this.fireEvent('click', [item, e]);
|
|
}
|
|
}.bind(this));
|
|
}, this);
|
|
|
|
//hide on body click
|
|
$(document.body).addEvent('click', function() {
|
|
this.hide();
|
|
}.bind(this));
|
|
},
|
|
|
|
updateMenuItems: function() {},
|
|
|
|
//show menu
|
|
show: function(trigger) {
|
|
if (lastShownContextMenu && lastShownContextMenu != this)
|
|
lastShownContextMenu.hide();
|
|
this.fx.start(1);
|
|
this.fireEvent('show');
|
|
this.shown = true;
|
|
lastShownContextMenu = this;
|
|
return this;
|
|
},
|
|
|
|
//hide the menu
|
|
hide: function(trigger) {
|
|
if (this.shown) {
|
|
this.fx.start(0);
|
|
//this.menu.fade('out');
|
|
this.fireEvent('hide');
|
|
this.shown = false;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
setItemChecked: function(item, checked) {
|
|
this.menu.getElement('a[href$=' + item + ']').firstChild.style.opacity =
|
|
checked ? '1' : '0';
|
|
return this;
|
|
},
|
|
|
|
getItemChecked: function(item) {
|
|
return '0' != this.menu.getElement('a[href$=' + item + ']').firstChild.style.opacity;
|
|
},
|
|
|
|
//hide an item
|
|
hideItem: function(item) {
|
|
this.menu.getElement('a[href$=' + item + ']').parentNode.addClass('invisible');
|
|
return this;
|
|
},
|
|
|
|
//show an item
|
|
showItem: function(item) {
|
|
this.menu.getElement('a[href$=' + item + ']').parentNode.removeClass('invisible');
|
|
return this;
|
|
},
|
|
|
|
//disable the entire menu
|
|
disable: function() {
|
|
this.options.disabled = true;
|
|
return this;
|
|
},
|
|
|
|
//enable the entire menu
|
|
enable: function() {
|
|
this.options.disabled = false;
|
|
return this;
|
|
},
|
|
|
|
//execute an action
|
|
execute: function(action, element) {
|
|
if (this.options.actions[action]) {
|
|
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;
|
|
let all_are_paused = true;
|
|
let there_are_paused = false;
|
|
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;
|
|
const tagsSelectionState = Object.clone(tagList);
|
|
|
|
const h = torrentsTable.selectedRowsIds();
|
|
h.each(function(item, index) {
|
|
const data = torrentsTable.rows.get(item).full_data;
|
|
|
|
if (data['seq_dl'] !== true)
|
|
all_are_seq_dl = false;
|
|
else
|
|
there_are_seq_dl = true;
|
|
|
|
if (data['f_l_piece_prio'] !== true)
|
|
all_are_f_l_piece_prio = false;
|
|
else
|
|
there_are_f_l_piece_prio = true;
|
|
|
|
if (data['progress'] != 1.0) // not downloaded
|
|
all_are_downloaded = false;
|
|
else if (data['super_seeding'] !== true)
|
|
all_are_super_seeding = false;
|
|
|
|
if (data['state'] != 'pausedUP' && data['state'] != 'pausedDL')
|
|
all_are_paused = false;
|
|
else
|
|
there_are_paused = true;
|
|
|
|
if (data['force_start'] !== true)
|
|
all_are_force_start = false;
|
|
else
|
|
there_are_force_start = true;
|
|
|
|
if (data['auto_tmm'] === true)
|
|
there_are_auto_tmm = true;
|
|
else
|
|
all_are_auto_tmm = false;
|
|
|
|
const torrentTags = data['tags'].split(', ');
|
|
for (const key in tagsSelectionState) {
|
|
const tag = tagsSelectionState[key];
|
|
const tagExists = torrentTags.contains(tag.name);
|
|
if ((tag.checked !== undefined) && (tag.checked != tagExists))
|
|
tag.indeterminate = true;
|
|
if (tag.checked === undefined)
|
|
tag.checked = tagExists;
|
|
else
|
|
tag.checked = tag.checked && tagExists;
|
|
}
|
|
});
|
|
|
|
let show_seq_dl = true;
|
|
|
|
// hide renameFiles when more than 1 torrent is selected
|
|
if (h.length == 1) {
|
|
const data = torrentsTable.rows.get(h[0]).full_data;
|
|
let metadata_downloaded = !(data['state'] == 'metaDL' || data['state'] == 'forcedMetaDL' || data['total_size'] == -1);
|
|
|
|
// hide renameFiles when metadata hasn't been downloaded yet
|
|
metadata_downloaded
|
|
? this.showItem('renameFiles')
|
|
: this.hideItem('renameFiles');
|
|
}
|
|
else
|
|
this.hideItem('renameFiles');
|
|
|
|
if (!all_are_seq_dl && there_are_seq_dl)
|
|
show_seq_dl = false;
|
|
|
|
let show_f_l_piece_prio = true;
|
|
|
|
if (!all_are_f_l_piece_prio && there_are_f_l_piece_prio)
|
|
show_f_l_piece_prio = false;
|
|
|
|
if (all_are_downloaded) {
|
|
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);
|
|
}
|
|
else {
|
|
if (!show_seq_dl && show_f_l_piece_prio)
|
|
this.menu.getElement('a[href$=firstLastPiecePrio]').parentNode.addClass('separator');
|
|
else
|
|
this.menu.getElement('a[href$=firstLastPiecePrio]').parentNode.removeClass('separator');
|
|
|
|
if (show_seq_dl)
|
|
this.showItem('sequentialDownload');
|
|
else
|
|
this.hideItem('sequentialDownload');
|
|
|
|
if (show_f_l_piece_prio)
|
|
this.showItem('firstLastPiecePrio');
|
|
else
|
|
this.hideItem('firstLastPiecePrio');
|
|
|
|
this.setItemChecked('sequentialDownload', all_are_seq_dl);
|
|
this.setItemChecked('firstLastPiecePrio', all_are_f_l_piece_prio);
|
|
|
|
this.showItem('downloadLimit');
|
|
this.menu.getElement('a[href$=uploadLimit]').parentNode.removeClass('separator');
|
|
this.hideItem('superSeeding');
|
|
}
|
|
|
|
this.showItem('start');
|
|
this.showItem('pause');
|
|
this.showItem('forceStart');
|
|
if (all_are_paused)
|
|
this.hideItem('pause');
|
|
else if (all_are_force_start)
|
|
this.hideItem('forceStart');
|
|
else if (!there_are_paused && !there_are_force_start)
|
|
this.hideItem('start');
|
|
|
|
if (!all_are_auto_tmm && there_are_auto_tmm) {
|
|
this.hideItem('autoTorrentManagement');
|
|
}
|
|
else {
|
|
this.showItem('autoTorrentManagement');
|
|
this.setItemChecked('autoTorrentManagement', all_are_auto_tmm);
|
|
}
|
|
|
|
const contextTagList = $('contextTagList');
|
|
for (const tagHash in tagList) {
|
|
const checkbox = contextTagList.getElement('a[href=#Tag/' + tagHash + '] input[type=checkbox]');
|
|
const checkboxState = tagsSelectionState[tagHash];
|
|
checkbox.indeterminate = checkboxState.indeterminate;
|
|
checkbox.checked = checkboxState.checked;
|
|
}
|
|
},
|
|
|
|
updateCategoriesSubMenu: function(category_list) {
|
|
const categoryList = $('contextCategoryList');
|
|
categoryList.getChildren().each(c => c.destroy());
|
|
categoryList.appendChild(new Element('li', {
|
|
html: '<a href="javascript:torrentNewCategoryFN();"><img src="images/list-add.svg" alt="QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]"/> QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]</a>'
|
|
}));
|
|
categoryList.appendChild(new Element('li', {
|
|
html: '<a href="javascript:torrentSetCategoryFN(0);"><img src="images/edit-clear.svg" alt="QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]"/> QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]</a>'
|
|
}));
|
|
|
|
const sortedCategories = [];
|
|
Object.each(category_list, function(category) {
|
|
sortedCategories.push(category.name);
|
|
});
|
|
sortedCategories.sort();
|
|
|
|
let first = true;
|
|
Object.each(sortedCategories, function(categoryName) {
|
|
const categoryHash = genHash(categoryName);
|
|
const el = new Element('li', {
|
|
html: '<a href="javascript:torrentSetCategoryFN(\'' + categoryHash + '\');"><img src="images/view-categories.svg"/> ' + window.qBittorrent.Misc.escapeHtml(categoryName) + '</a>'
|
|
});
|
|
if (first) {
|
|
el.addClass('separator');
|
|
first = false;
|
|
}
|
|
categoryList.appendChild(el);
|
|
});
|
|
},
|
|
|
|
updateTagsSubMenu: function(tagList) {
|
|
const contextTagList = $('contextTagList');
|
|
while (contextTagList.firstChild !== null)
|
|
contextTagList.removeChild(contextTagList.firstChild);
|
|
|
|
contextTagList.appendChild(new Element('li', {
|
|
html: '<a href="javascript:torrentAddTagsFN();">'
|
|
+ '<img src="images/list-add.svg" alt="QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]"/>'
|
|
+ ' QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]'
|
|
+ '</a>'
|
|
}));
|
|
contextTagList.appendChild(new Element('li', {
|
|
html: '<a href="javascript:torrentRemoveAllTagsFN();">'
|
|
+ '<img src="images/edit-clear.svg" alt="QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]"/>'
|
|
+ ' QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]'
|
|
+ '</a>'
|
|
}));
|
|
|
|
const sortedTags = [];
|
|
for (const key in tagList)
|
|
sortedTags.push(tagList[key].name);
|
|
sortedTags.sort();
|
|
|
|
for (let i = 0; i < sortedTags.length; ++i) {
|
|
const tagName = sortedTags[i];
|
|
const tagHash = genHash(tagName);
|
|
const el = new Element('li', {
|
|
html: '<a href="#Tag/' + tagHash + '" onclick="event.preventDefault(); torrentSetTagsFN(\'' + tagHash + '\', !event.currentTarget.getElement(\'input[type=checkbox]\').checked);">'
|
|
+ '<input type="checkbox" onclick="this.checked = !this.checked;"> ' + window.qBittorrent.Misc.escapeHtml(tagName)
|
|
+ '</a>'
|
|
});
|
|
if (i === 0)
|
|
el.addClass('separator');
|
|
contextTagList.appendChild(el);
|
|
}
|
|
}
|
|
});
|
|
|
|
const CategoriesFilterContextMenu = new Class({
|
|
Extends: ContextMenu,
|
|
updateMenuItems: function() {
|
|
const id = 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');
|
|
}
|
|
}
|
|
else {
|
|
this.hideItem('editCategory');
|
|
this.hideItem('deleteCategory');
|
|
this.hideItem('createSubcategory');
|
|
}
|
|
}
|
|
});
|
|
|
|
const TagsFilterContextMenu = new Class({
|
|
Extends: ContextMenu,
|
|
updateMenuItems: function() {
|
|
const id = this.options.element.id;
|
|
if ((id !== TAGS_ALL.toString()) && (id !== TAGS_UNTAGGED.toString()))
|
|
this.showItem('deleteTag');
|
|
else
|
|
this.hideItem('deleteTag');
|
|
}
|
|
});
|
|
|
|
const SearchPluginsTableContextMenu = new Class({
|
|
Extends: ContextMenu,
|
|
|
|
updateMenuItems: function() {
|
|
const enabledColumnIndex = function(text) {
|
|
const columns = $("searchPluginsTableFixedHeaderRow").getChildren("th");
|
|
for (let i = 0; i < columns.length; ++i)
|
|
if (columns[i].get("html") === "Enabled")
|
|
return i;
|
|
};
|
|
|
|
this.showItem('Enabled');
|
|
this.setItemChecked('Enabled', this.options.element.getChildren("td")[enabledColumnIndex()].get("html") === "Yes");
|
|
|
|
this.showItem('Uninstall');
|
|
}
|
|
});
|
|
|
|
const RssFeedContextMenu = new Class({
|
|
Extends: ContextMenu,
|
|
updateMenuItems: function() {
|
|
let selectedRows = window.qBittorrent.Rss.rssFeedTable.selectedRowsIds();
|
|
this.menu.getElement('a[href$=newSubscription]').parentNode.addClass('separator');
|
|
switch (selectedRows.length) {
|
|
case 0:
|
|
// remove separator on top of newSubscription entry to avoid double line
|
|
this.menu.getElement('a[href$=newSubscription]').parentNode.removeClass('separator');
|
|
// menu when nothing selected
|
|
this.hideItem('update');
|
|
this.hideItem('markRead');
|
|
this.hideItem('rename');
|
|
this.hideItem('delete');
|
|
this.showItem('newSubscription');
|
|
this.showItem('newFolder');
|
|
this.showItem('updateAll');
|
|
this.hideItem('copyFeedURL');
|
|
break;
|
|
case 1:
|
|
if (selectedRows[0] === 0) {
|
|
// menu when "unread" feed selected
|
|
this.showItem('update');
|
|
this.showItem('markRead');
|
|
this.hideItem('rename');
|
|
this.hideItem('delete');
|
|
this.showItem('newSubscription');
|
|
this.hideItem('newFolder');
|
|
this.hideItem('updateAll');
|
|
this.hideItem('copyFeedURL');
|
|
}
|
|
else if (window.qBittorrent.Rss.rssFeedTable.rows[selectedRows[0]].full_data.dataUid === '') {
|
|
// menu when single folder selected
|
|
this.showItem('update');
|
|
this.showItem('markRead');
|
|
this.showItem('rename');
|
|
this.showItem('delete');
|
|
this.showItem('newSubscription');
|
|
this.showItem('newFolder');
|
|
this.hideItem('updateAll');
|
|
this.hideItem('copyFeedURL');
|
|
}
|
|
else {
|
|
// menu when single feed selected
|
|
this.showItem('update');
|
|
this.showItem('markRead');
|
|
this.showItem('rename');
|
|
this.showItem('delete');
|
|
this.showItem('newSubscription');
|
|
this.hideItem('newFolder');
|
|
this.hideItem('updateAll');
|
|
this.showItem('copyFeedURL');
|
|
}
|
|
break;
|
|
default:
|
|
// menu when multiple items selected
|
|
this.showItem('update');
|
|
this.showItem('markRead');
|
|
this.hideItem('rename');
|
|
this.showItem('delete');
|
|
this.hideItem('newSubscription');
|
|
this.hideItem('newFolder');
|
|
this.hideItem('updateAll');
|
|
this.showItem('copyFeedURL');
|
|
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
|
|
this.menu.setStyles({
|
|
left: '-999em',
|
|
top: '-999em'
|
|
});
|
|
// position the menu
|
|
let xPosMenu = e.page.x + this.options.offsets.x - $('rssdownloaderpage').offsetLeft;
|
|
let yPosMenu = e.page.y + this.options.offsets.y - $('rssdownloaderpage').offsetTop;
|
|
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);
|
|
|
|
this.menu.setStyles({
|
|
left: xPosMenu,
|
|
top: yPosMenu,
|
|
position: 'absolute',
|
|
'z-index': '2000'
|
|
});
|
|
},
|
|
updateMenuItems: function() {
|
|
let selectedRows = window.qBittorrent.RssDownloader.rssDownloaderRulesTable.selectedRowsIds();
|
|
this.showItem('addRule');
|
|
switch (selectedRows.length) {
|
|
case 0:
|
|
// menu when nothing selected
|
|
this.hideItem('deleteRule');
|
|
this.hideItem('renameRule');
|
|
this.hideItem('clearDownloadedEpisodes');
|
|
break;
|
|
case 1:
|
|
// menu when single item selected
|
|
this.showItem('deleteRule');
|
|
this.showItem('renameRule');
|
|
this.showItem('clearDownloadedEpisodes');
|
|
break;
|
|
default:
|
|
// menu when multiple items selected
|
|
this.showItem('deleteRule');
|
|
this.hideItem('renameRule');
|
|
this.showItem('clearDownloadedEpisodes');
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
return exports();
|
|
})();
|
|
|
|
Object.freeze(window.qBittorrent.ContextMenu);
|