mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-04 22:52:33 -06:00
Merge pull request #4661 from buinsky/WebUI_labels_menu
WebUI: Implement labels filter context menu
This commit is contained in:
@@ -202,32 +202,8 @@ window.addEvent('load', function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
var updateContextMenu = function () {
|
||||
var categoryList = $('contextCategoryList');
|
||||
categoryList.empty();
|
||||
categoryList.appendChild(new Element('li', {html: '<a href="javascript:newCategoryFN();"><img src="theme/list-add" alt="QBT_TR(New...)QBT_TR"/> QBT_TR(New...)QBT_TR</a>'}));
|
||||
categoryList.appendChild(new Element('li', {html: '<a href="javascript:updateCategoryFN(0);"><img src="theme/edit-clear" alt="QBT_TR(Reset)QBT_TR"/> QBT_TR(Reset)QBT_TR</a>'}));
|
||||
|
||||
var sortedCategories = []
|
||||
Object.each(category_list, function(category) {
|
||||
sortedCategories.push(category.name);
|
||||
});
|
||||
sortedCategories.sort();
|
||||
|
||||
var first = true;
|
||||
Object.each(sortedCategories, function(categoryName) {
|
||||
var categoryHash = genHash(categoryName);
|
||||
var el = new Element('li', {html: '<a href="javascript:updateCategoryFN(\'' + categoryHash + '\');"><img src="theme/inode-directory"/> ' + categoryName + '</a>'});
|
||||
if (first) {
|
||||
el.addClass('separator');
|
||||
first = false;
|
||||
}
|
||||
categoryList.appendChild(el);
|
||||
});
|
||||
};
|
||||
|
||||
var updateFilter = function(filter, filterTitle) {
|
||||
$(filter + '_filter').firstChild.childNodes[1].nodeValue = filterTitle.replace('%1', torrentsTable.getFilteredTorrentsNumber(filter));
|
||||
$(filter + '_filter').firstChild.childNodes[1].nodeValue = filterTitle.replace('%1', torrentsTable.getFilteredTorrentsNumber(filter, CATEGORIES_ALL));
|
||||
};
|
||||
|
||||
var updateFiltersList = function() {
|
||||
@@ -251,8 +227,10 @@ window.addEvent('load', function () {
|
||||
var create_link = function(hash, text, count) {
|
||||
var html = '<a href="#" onclick="setCategoryFilter(' + hash + ');return false;">' +
|
||||
'<img src="theme/inode-directory"/>' +
|
||||
text + ' (' + count + ')' + '</a>';
|
||||
return new Element('li', {id: hash, html: html});
|
||||
escapeHtml(text) + ' (' + count + ')' + '</a>';
|
||||
var el = new Element('li', {id: hash, html: html});
|
||||
categoriesFilterContextMenu.addTarget(el);
|
||||
return el;
|
||||
};
|
||||
|
||||
var all = torrentsTable.getRowIds().length;
|
||||
@@ -357,7 +335,7 @@ window.addEvent('load', function () {
|
||||
updateFiltersList();
|
||||
if (update_categories) {
|
||||
updateCategoryList();
|
||||
updateContextMenu();
|
||||
torrentsTableContextMenu.updateCategoriesSubMenu(category_list);
|
||||
}
|
||||
}
|
||||
clearTimeout(syncMainDataTimer);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
var lastShownContexMenu = null;
|
||||
var ContextMenu = new Class({
|
||||
|
||||
//implements
|
||||
Implements: [Options, Events],
|
||||
|
||||
//options
|
||||
options: {
|
||||
actions: {},
|
||||
menu: 'contextmenu',
|
||||
menu: 'menu_id',
|
||||
stopEvent: true,
|
||||
targets: 'body',
|
||||
trigger: 'contextmenu',
|
||||
@@ -54,6 +54,57 @@ var ContextMenu = new Class({
|
||||
});
|
||||
},
|
||||
|
||||
adjustMenuPosition: function(e) {
|
||||
this.updateMenuItems();
|
||||
|
||||
// draw the menu off-screen to know the menu dimentions
|
||||
this.menu.setStyles({
|
||||
left: '-999em',
|
||||
top: '-999em'
|
||||
});
|
||||
|
||||
// position the menu
|
||||
var xPos = e.page.x + this.options.offsets.x;
|
||||
var yPos = e.page.y + this.options.offsets.y;
|
||||
if (xPos + this.menu.offsetWidth > document.documentElement.clientWidth)
|
||||
xPos -= this.menu.offsetWidth;
|
||||
if (yPos + this.menu.offsetHeight > document.documentElement.clientHeight)
|
||||
yPos -= this.menu.offsetHeight;
|
||||
if (xPos < 0)
|
||||
xPos = 0;
|
||||
if (yPos < 0)
|
||||
yPos = 0;
|
||||
this.menu.setStyles({
|
||||
left: xPos,
|
||||
top: yPos,
|
||||
position: 'absolute',
|
||||
'z-index': '2000'
|
||||
});
|
||||
|
||||
// position the sub-menu
|
||||
var uls = this.menu.getElementsByTagName('ul');
|
||||
for (var i = 0; i < uls.length; i++) {
|
||||
var ul = uls[i];
|
||||
var rectParent = ul.parentNode.getBoundingClientRect();
|
||||
var xPosOrigin = rectParent.left;
|
||||
var yPosOrigin = rectParent.bottom;
|
||||
var xPos = xPosOrigin + rectParent.width - 1;
|
||||
var 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 -= (ul.offsetHeight - rectParent.height - 2);
|
||||
if (xPos < 0)
|
||||
xPos = 0;
|
||||
if (yPos < 0)
|
||||
yPos = 0;
|
||||
ul.setStyles({
|
||||
'margin-left': xPos - xPosOrigin,
|
||||
'margin-top': yPos - yPosOrigin
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
addTarget: function(t) {
|
||||
this.targets[this.targets.length] = t;
|
||||
t.addEvent(this.options.trigger, function(e) {
|
||||
@@ -65,13 +116,7 @@ var ContextMenu = new Class({
|
||||
}
|
||||
//record this as the trigger
|
||||
this.options.element = $(t);
|
||||
//position the menu
|
||||
this.menu.setStyles({
|
||||
top: (e.page.y + this.options.offsets.y),
|
||||
left: (e.page.x + this.options.offsets.x),
|
||||
position: 'absolute',
|
||||
'z-index': '2000'
|
||||
});
|
||||
this.adjustMenuPosition(e);
|
||||
//show the menu
|
||||
this.show();
|
||||
}
|
||||
@@ -95,13 +140,7 @@ var ContextMenu = new Class({
|
||||
}
|
||||
//record this as the trigger
|
||||
this.options.element = $(el);
|
||||
//position the menu
|
||||
this.menu.setStyles({
|
||||
top: (e.page.y + this.options.offsets.y),
|
||||
left: (e.page.x + this.options.offsets.x),
|
||||
position: 'absolute',
|
||||
'z-index': '2000'
|
||||
});
|
||||
this.adjustMenuPosition(e);
|
||||
//show the menu
|
||||
this.show();
|
||||
}
|
||||
@@ -128,6 +167,76 @@ var ContextMenu = new Class({
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
updateMenuItems: function () {},
|
||||
|
||||
//show menu
|
||||
show: function (trigger) {
|
||||
if (lastShownContexMenu && lastShownContexMenu != this)
|
||||
lastShownContexMenu.hide();
|
||||
this.fx.start(1);
|
||||
this.fireEvent('show');
|
||||
this.shown = true;
|
||||
lastShownContexMenu = 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);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
var TorrentsTableContextMenu = new Class({
|
||||
Extends: ContextMenu,
|
||||
|
||||
updateMenuItems: function () {
|
||||
all_are_seq_dl = true;
|
||||
there_are_seq_dl = false;
|
||||
@@ -220,69 +329,40 @@ var ContextMenu = new Class({
|
||||
this.hideItem('ForceStart');
|
||||
else if (!there_are_paused && !there_are_force_start)
|
||||
this.hideItem('Start');
|
||||
|
||||
},
|
||||
|
||||
//show menu
|
||||
show: function(trigger) {
|
||||
this.updateMenuItems();
|
||||
this.fx.start(1);
|
||||
this.fireEvent('show');
|
||||
this.shown = true;
|
||||
return this;
|
||||
},
|
||||
updateCategoriesSubMenu : function (category_list) {
|
||||
var categoryList = $('contextCategoryList');
|
||||
categoryList.empty();
|
||||
categoryList.appendChild(new Element('li', {html: '<a href="javascript:torrentNewCategoryFN();"><img src="theme/list-add" alt="QBT_TR(New...)QBT_TR"/> QBT_TR(New...)QBT_TR</a>'}));
|
||||
categoryList.appendChild(new Element('li', {html: '<a href="javascript:torrentSetCategoryFN(0);"><img src="theme/edit-clear" alt="QBT_TR(Reset)QBT_TR"/> QBT_TR(Reset)QBT_TR</a>'}));
|
||||
|
||||
//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;
|
||||
},
|
||||
var sortedCategories = []
|
||||
Object.each(category_list, function (category) {
|
||||
sortedCategories.push(category.name);
|
||||
});
|
||||
sortedCategories.sort();
|
||||
|
||||
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);
|
||||
}
|
||||
return this;
|
||||
var first = true;
|
||||
Object.each(sortedCategories, function (categoryName) {
|
||||
var categoryHash = genHash(categoryName);
|
||||
var el = new Element('li', {html: '<a href="javascript:torrentSetCategoryFN(\'' + categoryHash + '\');"><img src="theme/inode-directory"/> ' + escapeHtml(categoryName) + '</a>'});
|
||||
if (first) {
|
||||
el.addClass('separator');
|
||||
first = false;
|
||||
}
|
||||
categoryList.appendChild(el);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var CategoriesFilterContextMenu = new Class({
|
||||
Extends: ContextMenu,
|
||||
updateMenuItems: function () {
|
||||
var id = this.options.element.id;
|
||||
if (id != CATEGORIES_ALL && id != CATEGORIES_UNCATEGORIZED)
|
||||
this.showItem('DeleteCategory');
|
||||
else
|
||||
this.hideItem('DeleteCategory');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -300,7 +300,6 @@ var DynamicTable = new Class({
|
||||
else { // else create a new row in the table
|
||||
var tr = new Element('tr');
|
||||
|
||||
tr.addClass("menu-target");
|
||||
tr['rowId'] = rows[rowPos]['rowId'];
|
||||
|
||||
tr._this = this;
|
||||
@@ -358,7 +357,7 @@ var DynamicTable = new Class({
|
||||
return false;
|
||||
});
|
||||
|
||||
this.setupTrEvents(tr);
|
||||
this.setupTr(tr);
|
||||
|
||||
for (var j = 0 ; j < this.columns.length; j++) {
|
||||
var td = new Element('td');
|
||||
@@ -393,7 +392,7 @@ var DynamicTable = new Class({
|
||||
}
|
||||
},
|
||||
|
||||
setupTrEvents : function (tr) {},
|
||||
setupTr : function (tr) {},
|
||||
|
||||
updateRow : function (tr, fullUpdate) {
|
||||
var row = this.rows.get(tr.rowId);
|
||||
@@ -524,11 +523,12 @@ var TorrentsTable = new Class({
|
||||
else return 0;
|
||||
};
|
||||
|
||||
// name
|
||||
// name, category
|
||||
|
||||
this.columns['name'].updateTd = function (td, row) {
|
||||
td.set('html', escapeHtml(this.getRowValue(row)));
|
||||
};
|
||||
this.columns['category'].updateTd = this.columns['name'].updateTd;
|
||||
|
||||
// size
|
||||
|
||||
@@ -620,7 +620,7 @@ var TorrentsTable = new Class({
|
||||
};
|
||||
},
|
||||
|
||||
applyFilter : function (row, filterName, categoryName) {
|
||||
applyFilter : function (row, filterName, categoryHash) {
|
||||
var state = row['full_data'].state;
|
||||
var inactive = false;
|
||||
var r;
|
||||
@@ -662,28 +662,38 @@ var TorrentsTable = new Class({
|
||||
break;
|
||||
}
|
||||
|
||||
if (categoryName == CATEGORIES_ALL)
|
||||
if (categoryHash == CATEGORIES_ALL)
|
||||
return true;
|
||||
|
||||
if (categoryName == CATEGORIES_UNCATEGORIZED && row['full_data'].category.length === 0)
|
||||
if (categoryHash == CATEGORIES_UNCATEGORIZED && row['full_data'].category.length === 0)
|
||||
return true;
|
||||
|
||||
if (categoryName != genHash(row['full_data'].category))
|
||||
if (categoryHash != genHash(row['full_data'].category))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
getFilteredTorrentsNumber : function (filterName) {
|
||||
getFilteredTorrentsNumber : function (filterName, categoryHash) {
|
||||
var cnt = 0;
|
||||
var rows = this.rows.getValues();
|
||||
|
||||
for (i = 0; i < rows.length; i++)
|
||||
if (this.applyFilter(rows[i], filterName, CATEGORIES_ALL)) cnt++;
|
||||
|
||||
if (this.applyFilter(rows[i], filterName, categoryHash)) cnt++;
|
||||
return cnt;
|
||||
},
|
||||
|
||||
getFilteredTorrentsHashes : function (filterName, categoryHash) {
|
||||
var rowsHashes = [];
|
||||
var rows = this.rows.getValues();
|
||||
|
||||
for (i = 0; i < rows.length; i++)
|
||||
if (this.applyFilter(rows[i], filterName, categoryHash))
|
||||
rowsHashes.push(rows[i]['rowId']);
|
||||
|
||||
return rowsHashes;
|
||||
},
|
||||
|
||||
getFilteredAndSortedRows : function () {
|
||||
var filteredRows = new Array();
|
||||
|
||||
@@ -706,7 +716,7 @@ var TorrentsTable = new Class({
|
||||
return filteredRows;
|
||||
},
|
||||
|
||||
setupTrEvents : function (tr) {
|
||||
setupTr : function (tr) {
|
||||
tr.addEvent('dblclick', function (e) {
|
||||
e.stop();
|
||||
this._this.selectRow(this.rowId);
|
||||
@@ -718,6 +728,7 @@ var TorrentsTable = new Class({
|
||||
pauseFN();
|
||||
return true;
|
||||
});
|
||||
tr.addClass("torrentsTableContextMenuTarget");
|
||||
},
|
||||
|
||||
getCurrentTorrentHash : function () {
|
||||
|
||||
@@ -304,7 +304,7 @@ initializeWindows = function() {
|
||||
}
|
||||
};
|
||||
|
||||
newCategoryFN = function () {
|
||||
torrentNewCategoryFN = function () {
|
||||
var h = torrentsTable.selectedRowsIds();
|
||||
if (h.length) {
|
||||
new MochaUI.Window({
|
||||
@@ -323,7 +323,7 @@ initializeWindows = function() {
|
||||
}
|
||||
};
|
||||
|
||||
updateCategoryFN = function (categoryHash) {
|
||||
torrentSetCategoryFN = function (categoryHash) {
|
||||
var categoryName = '';
|
||||
if (categoryHash != 0)
|
||||
var categoryName = category_list[categoryHash].name;
|
||||
@@ -340,6 +340,102 @@ initializeWindows = function() {
|
||||
}
|
||||
};
|
||||
|
||||
createCategoryFN = function () {
|
||||
new MochaUI.Window({
|
||||
id: 'newCategoryPage',
|
||||
title: "QBT_TR(New Category)QBT_TR",
|
||||
loadMethod: 'iframe',
|
||||
contentURL: 'newcategory.html',
|
||||
scrollbars: false,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
paddingVertical: 0,
|
||||
paddingHorizontal: 0,
|
||||
width: 250,
|
||||
height: 100
|
||||
});
|
||||
updateMainData();
|
||||
};
|
||||
|
||||
removeCategoryFN = function (categoryHash) {
|
||||
var categoryName = category_list[categoryHash].name;
|
||||
new Request({
|
||||
url: 'command/removeCategories',
|
||||
method: 'post',
|
||||
data: {
|
||||
categories: categoryName
|
||||
}
|
||||
}).send();
|
||||
setCategoryFilter(CATEGORIES_ALL);
|
||||
};
|
||||
|
||||
deleteUnusedCategoriesFN = function () {
|
||||
var categories = [];
|
||||
for (var hash in category_list) {
|
||||
if (torrentsTable.getFilteredTorrentsNumber('all', hash) == 0)
|
||||
categories.push(category_list[hash].name);
|
||||
}
|
||||
new Request({
|
||||
url: 'command/removeCategories',
|
||||
method: 'post',
|
||||
data: {
|
||||
categories: categories.join('\n')
|
||||
}
|
||||
}).send();
|
||||
setCategoryFilter(CATEGORIES_ALL);
|
||||
};
|
||||
|
||||
startTorrentsByCategoryFN = function (categoryHash) {
|
||||
var h = torrentsTable.getFilteredTorrentsHashes('all', categoryHash);
|
||||
if (h.length) {
|
||||
h.each(function (hash, index) {
|
||||
new Request({
|
||||
url: 'command/resume',
|
||||
method: 'post',
|
||||
data: {
|
||||
hash: hash
|
||||
}
|
||||
}).send();
|
||||
});
|
||||
updateMainData();
|
||||
}
|
||||
};
|
||||
|
||||
pauseTorrentsByCategoryFN = function (categoryHash) {
|
||||
var h = torrentsTable.getFilteredTorrentsHashes('all', categoryHash);
|
||||
if (h.length) {
|
||||
h.each(function (hash, index) {
|
||||
new Request({
|
||||
url: 'command/pause',
|
||||
method: 'post',
|
||||
data: {
|
||||
hash: hash
|
||||
}
|
||||
}).send();
|
||||
});
|
||||
updateMainData();
|
||||
}
|
||||
};
|
||||
|
||||
deleteTorrentsByCategoryFN = function (categoryHash) {
|
||||
var h = torrentsTable.getFilteredTorrentsHashes('all', categoryHash);
|
||||
if (h.length) {
|
||||
new MochaUI.Window({
|
||||
id: 'confirmDeletionPage',
|
||||
title: "QBT_TR(Deletion confirmation)QBT_TR",
|
||||
loadMethod: 'iframe',
|
||||
contentURL: 'confirmdeletion.html?hashes=' + h.join("|"),
|
||||
scrollbars: false,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
padding: 10,
|
||||
width: 424,
|
||||
height: 140
|
||||
});
|
||||
updateMainData();
|
||||
}
|
||||
};
|
||||
|
||||
['pauseAll', 'resumeAll'].each(function(item) {
|
||||
addClickEvent(item, function(e) {
|
||||
new Event(e).stop();
|
||||
|
||||
Reference in New Issue
Block a user