mirror of
https://github.com/Carve/qbittorrent-webui-cjratliff.com.git
synced 2024-11-15 11:23:18 +01:00
9493fd2827
- Icons are now FontAwesome 6 SVGs - Colors modified for new & existing icons - Colors modified for ratio - Fixed Context menu background and color - Fixed greater than 1.0 ratio color
2627 lines
103 KiB
JavaScript
2627 lines
103 KiB
JavaScript
/*
|
|
* 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
|
|
|
|
**************************************************************/
|
|
|
|
'use strict';
|
|
|
|
if (window.qBittorrent === undefined) {
|
|
window.qBittorrent = {};
|
|
}
|
|
|
|
window.qBittorrent.DynamicTable = (function() {
|
|
const exports = function() {
|
|
return {
|
|
TorrentsTable: TorrentsTable,
|
|
TorrentPeersTable: TorrentPeersTable,
|
|
SearchResultsTable: SearchResultsTable,
|
|
SearchPluginsTable: SearchPluginsTable,
|
|
TorrentTrackersTable: TorrentTrackersTable,
|
|
TorrentFilesTable: TorrentFilesTable,
|
|
RssFeedTable: RssFeedTable,
|
|
RssArticleTable: RssArticleTable,
|
|
RssDownloaderRulesTable: RssDownloaderRulesTable,
|
|
RssDownloaderFeedSelectionTable: RssDownloaderFeedSelectionTable,
|
|
RssDownloaderArticlesTable: RssDownloaderArticlesTable
|
|
};
|
|
};
|
|
|
|
const naturalSortCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
|
|
|
|
let DynamicTableHeaderContextMenuClass = null;
|
|
let ProgressColumnWidth = -1;
|
|
|
|
const DynamicTable = new Class({
|
|
|
|
initialize: function() {},
|
|
|
|
setup: function(dynamicTableDivId, dynamicTableFixedHeaderDivId, contextMenu) {
|
|
this.dynamicTableDivId = dynamicTableDivId;
|
|
this.dynamicTableFixedHeaderDivId = dynamicTableFixedHeaderDivId;
|
|
this.fixedTableHeader = $(dynamicTableFixedHeaderDivId).getElements('tr')[0];
|
|
this.hiddenTableHeader = $(dynamicTableDivId).getElements('tr')[0];
|
|
this.tableBody = $(dynamicTableDivId).getElements('tbody')[0];
|
|
this.rows = new Hash();
|
|
this.selectedRows = [];
|
|
this.columns = [];
|
|
this.contextMenu = contextMenu;
|
|
this.sortedColumn = LocalPreferences.get('sorted_column_' + this.dynamicTableDivId, 0);
|
|
this.reverseSort = LocalPreferences.get('reverse_sort_' + this.dynamicTableDivId, '0');
|
|
this.initColumns();
|
|
this.loadColumnsOrder();
|
|
this.updateTableHeaders();
|
|
this.setupCommonEvents();
|
|
this.setupHeaderEvents();
|
|
this.setupHeaderMenu();
|
|
this.setSortedColumnIcon(this.sortedColumn, null, (this.reverseSort === '1'));
|
|
},
|
|
|
|
setupCommonEvents: function() {
|
|
const scrollFn = function() {
|
|
$(this.dynamicTableFixedHeaderDivId).getElements('table')[0].style.left = -$(this.dynamicTableDivId).scrollLeft + 'px';
|
|
}.bind(this);
|
|
|
|
$(this.dynamicTableDivId).addEvent('scroll', scrollFn);
|
|
|
|
// if the table exists within a panel
|
|
if ($(this.dynamicTableDivId).getParent('.panel')) {
|
|
const resizeFn = function() {
|
|
const panel = $(this.dynamicTableDivId).getParent('.panel');
|
|
let h = panel.getBoundingClientRect().height - $(this.dynamicTableFixedHeaderDivId).getBoundingClientRect().height;
|
|
$(this.dynamicTableDivId).style.height = h + 'px';
|
|
|
|
// Workaround due to inaccurate calculation of elements heights by browser
|
|
|
|
let n = 2;
|
|
|
|
while (panel.clientWidth != panel.offsetWidth && n > 0) { // is panel vertical scrollbar visible ?
|
|
--n;
|
|
h -= 0.5;
|
|
$(this.dynamicTableDivId).style.height = h + 'px';
|
|
}
|
|
|
|
this.lastPanelHeight = panel.getBoundingClientRect().height;
|
|
}.bind(this);
|
|
|
|
$(this.dynamicTableDivId).getParent('.panel').addEvent('resize', resizeFn);
|
|
|
|
this.lastPanelHeight = 0;
|
|
|
|
// Workaround. Resize event is called not always (for example it isn't called when browser window changes it's size)
|
|
|
|
const checkResizeFn = function() {
|
|
const panel = $(this.dynamicTableDivId).getParent('.panel');
|
|
if (this.lastPanelHeight != panel.getBoundingClientRect().height) {
|
|
this.lastPanelHeight = panel.getBoundingClientRect().height;
|
|
panel.fireEvent('resize');
|
|
}
|
|
}.bind(this);
|
|
|
|
setInterval(checkResizeFn, 500);
|
|
}
|
|
},
|
|
|
|
setupHeaderEvents: function() {
|
|
this.currentHeaderAction = '';
|
|
this.canResize = false;
|
|
|
|
const resetElementBorderStyle = function(el, side) {
|
|
if (side === 'left' || side !== 'right') {
|
|
el.setStyle('border-left-style', '');
|
|
el.setStyle('border-left-color', '');
|
|
el.setStyle('border-left-width', '');
|
|
}
|
|
if (side === 'right' || side !== 'left') {
|
|
el.setStyle('border-right-style', '');
|
|
el.setStyle('border-right-color', '');
|
|
el.setStyle('border-right-width', '');
|
|
}
|
|
};
|
|
|
|
const mouseMoveFn = function(e) {
|
|
const brect = e.target.getBoundingClientRect();
|
|
const mouseXRelative = e.event.clientX - brect.left;
|
|
if (this.currentHeaderAction === '') {
|
|
if (brect.width - mouseXRelative < 5) {
|
|
this.resizeTh = e.target;
|
|
this.canResize = true;
|
|
e.target.getParent("tr").style.cursor = 'col-resize';
|
|
}
|
|
else if ((mouseXRelative < 5) && e.target.getPrevious('[class=""]')) {
|
|
this.resizeTh = e.target.getPrevious('[class=""]');
|
|
this.canResize = true;
|
|
e.target.getParent("tr").style.cursor = 'col-resize';
|
|
}
|
|
else {
|
|
this.canResize = false;
|
|
e.target.getParent("tr").style.cursor = '';
|
|
}
|
|
}
|
|
if (this.currentHeaderAction === 'drag') {
|
|
const previousVisibleSibling = e.target.getPrevious('[class=""]');
|
|
let borderChangeElement = previousVisibleSibling;
|
|
let changeBorderSide = 'right';
|
|
|
|
if (mouseXRelative > brect.width / 2) {
|
|
borderChangeElement = e.target;
|
|
this.dropSide = 'right';
|
|
}
|
|
else {
|
|
this.dropSide = 'left';
|
|
}
|
|
|
|
e.target.getParent("tr").style.cursor = 'move';
|
|
|
|
if (!previousVisibleSibling) { // right most column
|
|
borderChangeElement = e.target;
|
|
|
|
if (mouseXRelative <= brect.width / 2)
|
|
changeBorderSide = 'left';
|
|
}
|
|
|
|
borderChangeElement.setStyle('border-' + changeBorderSide + '-style', 'solid');
|
|
borderChangeElement.setStyle('border-' + changeBorderSide + '-color', '#e60');
|
|
borderChangeElement.setStyle('border-' + changeBorderSide + '-width', 'initial');
|
|
|
|
resetElementBorderStyle(borderChangeElement, changeBorderSide === 'right' ? 'left' : 'right');
|
|
|
|
borderChangeElement.getSiblings('[class=""]').each(function(el) {
|
|
resetElementBorderStyle(el);
|
|
});
|
|
}
|
|
this.lastHoverTh = e.target;
|
|
this.lastClientX = e.event.clientX;
|
|
}.bind(this);
|
|
|
|
const mouseOutFn = function(e) {
|
|
resetElementBorderStyle(e.target);
|
|
}.bind(this);
|
|
|
|
const onBeforeStart = function(el) {
|
|
this.clickedTh = el;
|
|
this.currentHeaderAction = 'start';
|
|
this.dragMovement = false;
|
|
this.dragStartX = this.lastClientX;
|
|
}.bind(this);
|
|
|
|
const onStart = function(el, event) {
|
|
if (this.canResize) {
|
|
this.currentHeaderAction = 'resize';
|
|
this.startWidth = this.resizeTh.getStyle('width').toFloat();
|
|
}
|
|
else {
|
|
this.currentHeaderAction = 'drag';
|
|
el.setStyle('background-color', '#C1D5E7');
|
|
}
|
|
}.bind(this);
|
|
|
|
const onDrag = function(el, event) {
|
|
if (this.currentHeaderAction === 'resize') {
|
|
let width = this.startWidth + (event.page.x - this.dragStartX);
|
|
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);
|
|
el.setStyle('background-color', '');
|
|
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)) {
|
|
this.saveColumnsOrder();
|
|
const val = LocalPreferences.get('columns_order_' + this.dynamicTableDivId).split(',');
|
|
val.erase(el.columnName);
|
|
let pos = val.indexOf(this.lastHoverTh.columnName);
|
|
if (this.dropSide === 'right')
|
|
++pos;
|
|
val.splice(pos, 0, el.columnName);
|
|
LocalPreferences.set('columns_order_' + this.dynamicTableDivId, val.join(','));
|
|
this.loadColumnsOrder();
|
|
this.updateTableHeaders();
|
|
while (this.tableBody.firstChild)
|
|
this.tableBody.removeChild(this.tableBody.firstChild);
|
|
this.updateTable(true);
|
|
}
|
|
if (this.currentHeaderAction === 'drag') {
|
|
resetElementBorderStyle(el);
|
|
el.getSiblings('[class=""]').each(function(el) {
|
|
resetElementBorderStyle(el);
|
|
});
|
|
}
|
|
this.currentHeaderAction = '';
|
|
}.bind(this);
|
|
|
|
const onCancel = function(el) {
|
|
this.currentHeaderAction = '';
|
|
this.setSortedColumn(el.columnName);
|
|
}.bind(this);
|
|
|
|
const onTouch = function(e) {
|
|
const column = e.target.columnName;
|
|
this.currentHeaderAction = '';
|
|
this.setSortedColumn(column);
|
|
}.bind(this);
|
|
|
|
const ths = this.fixedTableHeader.getElements('th');
|
|
|
|
for (let i = 0; i < ths.length; ++i) {
|
|
const th = ths[i];
|
|
th.addEvent('mousemove', mouseMoveFn);
|
|
th.addEvent('mouseout', mouseOutFn);
|
|
th.addEvent('touchend', onTouch);
|
|
th.makeResizable({
|
|
modifiers: {
|
|
x: '',
|
|
y: ''
|
|
},
|
|
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) {
|
|
if (this.dynamicTable.columns[i].caption === '')
|
|
continue;
|
|
if (this.dynamicTable.columns[i].visible !== '0')
|
|
this.setItemChecked(this.dynamicTable.columns[i].name, true);
|
|
else
|
|
this.setItemChecked(this.dynamicTable.columns[i].name, false);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
showColumn: function(columnName, show) {
|
|
this.columns[columnName].visible = show ? '1' : '0';
|
|
LocalPreferences.set('column_' + columnName + '_visible_' + this.dynamicTableDivId, show ? '1' : '0');
|
|
this.updateColumn(columnName);
|
|
},
|
|
|
|
setupHeaderMenu: function() {
|
|
this.setupDynamicTableHeaderContextMenuClass();
|
|
|
|
const menuId = this.dynamicTableDivId + '_headerMenu';
|
|
|
|
const ul = new Element('ul', {
|
|
id: menuId,
|
|
class: 'contextMenu scrollableMenu'
|
|
});
|
|
|
|
const createLi = function(columnName, text) {
|
|
const html = '<a href="#' + columnName + '" ><img src="images/checked-completed.svg"/>' + window.qBittorrent.Misc.escapeHtml(text) + '</a>';
|
|
return new Element('li', {
|
|
html: html
|
|
});
|
|
};
|
|
|
|
const actions = {};
|
|
|
|
const onMenuItemClicked = function(element, ref, action) {
|
|
this.showColumn(action, this.columns[action].visible === '0');
|
|
}.bind(this);
|
|
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
const text = this.columns[i].caption;
|
|
if (text === '')
|
|
continue;
|
|
ul.appendChild(createLi(this.columns[i].name, text));
|
|
actions[this.columns[i].name] = onMenuItemClicked;
|
|
}
|
|
|
|
ul.inject(document.body);
|
|
|
|
this.headerContextMenu = new DynamicTableHeaderContextMenuClass({
|
|
targets: '#' + this.dynamicTableFixedHeaderDivId + ' tr',
|
|
actions: actions,
|
|
menu: menuId,
|
|
offsets: {
|
|
x: -15,
|
|
y: 2
|
|
}
|
|
});
|
|
|
|
this.headerContextMenu.dynamicTable = this;
|
|
},
|
|
|
|
initColumns: function() {},
|
|
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
const column = {};
|
|
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) {
|
|
if (pos === undefined)
|
|
pos = 0;
|
|
return row['full_data'][this.dataProperties[pos]];
|
|
};
|
|
column['compareRows'] = function(row1, row2) {
|
|
const value1 = this.getRowValue(row1);
|
|
const value2 = this.getRowValue(row2);
|
|
return naturalSortCollator.compare(value1, value2);
|
|
};
|
|
column['updateTd'] = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
column['onResize'] = null;
|
|
this.columns.push(column);
|
|
this.columns[name] = column;
|
|
|
|
this.hiddenTableHeader.appendChild(new Element('th'));
|
|
this.fixedTableHeader.appendChild(new Element('th'));
|
|
},
|
|
|
|
loadColumnsOrder: function() {
|
|
const columnsOrder = [];
|
|
const val = LocalPreferences.get('columns_order_' + this.dynamicTableDivId);
|
|
if (val === null || val === undefined)
|
|
return;
|
|
val.split(',').forEach(function(v) {
|
|
if ((v in this.columns) && (!columnsOrder.contains(v)))
|
|
columnsOrder.push(v);
|
|
}.bind(this));
|
|
|
|
for (let i = 0; i < this.columns.length; ++i)
|
|
if (!columnsOrder.contains(this.columns[i].name))
|
|
columnsOrder.push(this.columns[i].name);
|
|
|
|
for (let i = 0; i < this.columns.length; ++i)
|
|
this.columns[i] = this.columns[columnsOrder[i]];
|
|
},
|
|
|
|
saveColumnsOrder: function() {
|
|
let val = '';
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
if (i > 0)
|
|
val += ',';
|
|
val += this.columns[i].name;
|
|
}
|
|
LocalPreferences.set('columns_order_' + this.dynamicTableDivId, val);
|
|
},
|
|
|
|
updateTableHeaders: function() {
|
|
this.updateHeader(this.hiddenTableHeader);
|
|
this.updateHeader(this.fixedTableHeader);
|
|
},
|
|
|
|
updateHeader: function(header) {
|
|
const ths = header.getElements('th');
|
|
|
|
for (let i = 0; i < ths.length; ++i) {
|
|
const th = ths[i];
|
|
th._this = this;
|
|
th.setAttribute('title', this.columns[i].caption);
|
|
th.set('text', this.columns[i].caption);
|
|
th.setAttribute('style', 'width: ' + this.columns[i].width + 'px;' + this.columns[i].style);
|
|
th.columnName = this.columns[i].name;
|
|
th.addClass('column_' + th.columnName);
|
|
if ((this.columns[i].visible == '0') || this.columns[i].force_hide)
|
|
th.addClass('invisible');
|
|
else
|
|
th.removeClass('invisible');
|
|
}
|
|
},
|
|
|
|
getColumnPos: function(columnName) {
|
|
for (let i = 0; i < this.columns.length; ++i)
|
|
if (this.columns[i].name == columnName)
|
|
return i;
|
|
return -1;
|
|
},
|
|
|
|
updateColumn: function(columnName) {
|
|
const pos = this.getColumnPos(columnName);
|
|
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;
|
|
|
|
ths[pos].setAttribute('style', style);
|
|
fths[pos].setAttribute('style', style);
|
|
|
|
if (visible) {
|
|
ths[pos].removeClass('invisible');
|
|
fths[pos].removeClass('invisible');
|
|
for (let i = 0; i < trs.length; ++i)
|
|
trs[i].getElements('td')[pos].removeClass('invisible');
|
|
}
|
|
else {
|
|
ths[pos].addClass('invisible');
|
|
fths[pos].addClass('invisible');
|
|
for (let j = 0; j < trs.length; ++j)
|
|
trs[j].getElements('td')[pos].addClass('invisible');
|
|
}
|
|
if (this.columns[pos].onResize !== null) {
|
|
this.columns[pos].onResize(columnName);
|
|
}
|
|
},
|
|
|
|
getSortedColumn: function() {
|
|
return LocalPreferences.get('sorted_column_' + this.dynamicTableDivId);
|
|
},
|
|
|
|
setSortedColumn: function(column) {
|
|
if (column != this.sortedColumn) {
|
|
const oldColumn = this.sortedColumn;
|
|
this.sortedColumn = column;
|
|
this.reverseSort = '0';
|
|
this.setSortedColumnIcon(column, oldColumn, false);
|
|
}
|
|
else {
|
|
// Toggle sort order
|
|
this.reverseSort = this.reverseSort === '0' ? '1' : '0';
|
|
this.setSortedColumnIcon(column, null, (this.reverseSort === '1'));
|
|
}
|
|
LocalPreferences.set('sorted_column_' + this.dynamicTableDivId, column);
|
|
LocalPreferences.set('reverse_sort_' + this.dynamicTableDivId, this.reverseSort);
|
|
this.updateTable(false);
|
|
},
|
|
|
|
setSortedColumnIcon: function(newColumn, oldColumn, isReverse) {
|
|
const getCol = function(headerDivId, colName) {
|
|
const colElem = $$("#" + headerDivId + " .column_" + colName);
|
|
if (colElem.length == 1)
|
|
return colElem[0];
|
|
return null;
|
|
};
|
|
|
|
const colElem = getCol(this.dynamicTableFixedHeaderDivId, newColumn);
|
|
if (colElem !== null) {
|
|
colElem.addClass('sorted');
|
|
if (isReverse)
|
|
colElem.addClass('reverse');
|
|
else
|
|
colElem.removeClass('reverse');
|
|
}
|
|
const oldColElem = getCol(this.dynamicTableFixedHeaderDivId, oldColumn);
|
|
if (oldColElem !== null) {
|
|
oldColElem.removeClass('sorted');
|
|
oldColElem.removeClass('reverse');
|
|
}
|
|
},
|
|
|
|
getSelectedRowId: function() {
|
|
if (this.selectedRows.length > 0)
|
|
return this.selectedRows[0];
|
|
return '';
|
|
},
|
|
|
|
isRowSelected: function(rowId) {
|
|
return this.selectedRows.contains(rowId);
|
|
},
|
|
|
|
altRow: function() {
|
|
if (!MUI.ieLegacySupport)
|
|
return;
|
|
|
|
const trs = this.tableBody.getElements('tr');
|
|
trs.each(function(el, i) {
|
|
if (i % 2) {
|
|
el.addClass('alt');
|
|
}
|
|
else {
|
|
el.removeClass('alt');
|
|
}
|
|
}.bind(this));
|
|
},
|
|
|
|
selectAll: function() {
|
|
this.deselectAll();
|
|
|
|
const trs = this.tableBody.getElements('tr');
|
|
for (let i = 0; i < trs.length; ++i) {
|
|
const tr = trs[i];
|
|
this.selectedRows.push(tr.rowId);
|
|
if (!tr.hasClass('selected'))
|
|
tr.addClass('selected');
|
|
}
|
|
},
|
|
|
|
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;
|
|
this.tableBody.getElements('tr').each(function(tr) {
|
|
if ((tr.rowId == rowId1) || (tr.rowId == rowId2)) {
|
|
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();
|
|
this.tableBody.getElements('tr').each(function(tr) {
|
|
if (rowIds.indexOf(tr.rowId) > -1)
|
|
tr.addClass('selected');
|
|
});
|
|
},
|
|
|
|
setRowClass: function() {
|
|
const that = this;
|
|
this.tableBody.getElements('tr').each(function(tr) {
|
|
if (that.isRowSelected(tr.rowId))
|
|
tr.addClass('selected');
|
|
else
|
|
tr.removeClass('selected');
|
|
});
|
|
},
|
|
|
|
onSelectedRowChanged: function() {},
|
|
|
|
updateRowData: function(data) {
|
|
// ensure rowId is a string
|
|
const rowId = `${data['rowId']}`;
|
|
let row;
|
|
|
|
if (!this.rows.has(rowId)) {
|
|
row = {
|
|
'full_data': {},
|
|
'rowId': rowId
|
|
};
|
|
this.rows.set(rowId, row);
|
|
}
|
|
else
|
|
row = this.rows.get(rowId);
|
|
|
|
row['data'] = data;
|
|
for (const x in data)
|
|
row['full_data'][x] = data[x];
|
|
},
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
const filteredRows = [];
|
|
|
|
const rows = this.rows.getValues();
|
|
|
|
for (let i = 0; i < rows.length; ++i) {
|
|
filteredRows.push(rows[i]);
|
|
filteredRows[rows[i].rowId] = rows[i];
|
|
}
|
|
|
|
filteredRows.sort(function(row1, row2) {
|
|
const column = this.columns[this.sortedColumn];
|
|
const res = column.compareRows(row1, row2);
|
|
if (this.reverseSort === '0')
|
|
return res;
|
|
else
|
|
return -res;
|
|
}.bind(this));
|
|
return filteredRows;
|
|
},
|
|
|
|
getTrByRowId: function(rowId) {
|
|
const trs = this.tableBody.getElements('tr');
|
|
for (let i = 0; i < trs.length; ++i)
|
|
if (trs[i].rowId == rowId)
|
|
return trs[i];
|
|
return null;
|
|
},
|
|
|
|
updateTable: function(fullUpdate) {
|
|
if (fullUpdate === undefined)
|
|
fullUpdate = false;
|
|
|
|
const rows = this.getFilteredAndSortedRows();
|
|
|
|
for (let i = 0; i < this.selectedRows.length; ++i)
|
|
if (!(this.selectedRows[i] in rows)) {
|
|
this.selectedRows.splice(i, 1);
|
|
--i;
|
|
}
|
|
|
|
const trs = this.tableBody.getElements('tr');
|
|
|
|
for (let rowPos = 0; rowPos < rows.length; ++rowPos) {
|
|
const rowId = rows[rowPos]['rowId'];
|
|
let tr_found = false;
|
|
for (let j = rowPos; j < trs.length; ++j)
|
|
if (trs[j]['rowId'] == rowId) {
|
|
tr_found = true;
|
|
if (rowPos == j)
|
|
break;
|
|
trs[j].inject(trs[rowPos], 'before');
|
|
const tmpTr = trs[j];
|
|
trs.splice(j, 1);
|
|
trs.splice(rowPos, 0, tmpTr);
|
|
break;
|
|
}
|
|
if (tr_found) // row already exists in the table
|
|
this.updateRow(trs[rowPos], fullUpdate);
|
|
else { // else create a new row in the table
|
|
const tr = new Element('tr');
|
|
// set tabindex so element receives keydown events
|
|
// more info: https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event
|
|
tr.setProperty("tabindex", "-1");
|
|
|
|
const rowId = rows[rowPos]['rowId'];
|
|
tr.setProperty("data-row-id", rowId);
|
|
tr['rowId'] = rowId;
|
|
|
|
tr._this = this;
|
|
tr.addEvent('contextmenu', function(e) {
|
|
if (!this._this.isRowSelected(this.rowId)) {
|
|
this._this.deselectAll();
|
|
this._this.selectRow(this.rowId);
|
|
}
|
|
return true;
|
|
});
|
|
tr.addEvent('click', function(e) {
|
|
e.stop();
|
|
if (e.control || e.meta) {
|
|
// CTRL/CMD ⌘ key was pressed
|
|
if (this._this.isRowSelected(this.rowId))
|
|
this._this.deselectRow(this.rowId);
|
|
else
|
|
this._this.selectRow(this.rowId);
|
|
}
|
|
else if (e.shift && (this._this.selectedRows.length == 1)) {
|
|
// 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;
|
|
});
|
|
tr.addEvent('touchstart', function(e) {
|
|
if (!this._this.isRowSelected(this.rowId)) {
|
|
this._this.deselectAll();
|
|
this._this.selectRow(this.rowId);
|
|
}
|
|
return false;
|
|
});
|
|
tr.addEvent('keydown', function(event) {
|
|
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) {
|
|
const td = new Element('td');
|
|
if ((this.columns[k].visible == '0') || this.columns[k].force_hide)
|
|
td.addClass('invisible');
|
|
td.injectInside(tr);
|
|
}
|
|
|
|
// Insert
|
|
if (rowPos >= trs.length) {
|
|
tr.inject(this.tableBody);
|
|
trs.push(tr);
|
|
}
|
|
else {
|
|
tr.inject(trs[rowPos], 'before');
|
|
trs.splice(rowPos, 0, tr);
|
|
}
|
|
|
|
// Update context menu
|
|
if (this.contextMenu)
|
|
this.contextMenu.addTarget(tr);
|
|
|
|
this.updateRow(tr, true);
|
|
}
|
|
}
|
|
|
|
let rowPos = rows.length;
|
|
|
|
while ((rowPos < trs.length) && (trs.length > 0)) {
|
|
trs[trs.length - 1].dispose();
|
|
trs.pop();
|
|
}
|
|
},
|
|
|
|
setupTr: function(tr) {},
|
|
|
|
updateRow: function(tr, fullUpdate) {
|
|
const row = this.rows.get(tr.rowId);
|
|
const data = row[fullUpdate ? 'full_data' : 'data'];
|
|
|
|
const tds = tr.getElements('td');
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0]))
|
|
this.columns[i].updateTd(tds[i], row);
|
|
}
|
|
row['data'] = {};
|
|
},
|
|
|
|
removeRow: function(rowId) {
|
|
this.selectedRows.erase(rowId);
|
|
const tr = this.getTrByRowId(rowId);
|
|
if (tr !== null) {
|
|
tr.dispose();
|
|
this.rows.erase(rowId);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
clear: function() {
|
|
this.deselectAll();
|
|
this.rows.empty();
|
|
const trs = this.tableBody.getElements('tr');
|
|
while (trs.length > 0) {
|
|
trs[trs.length - 1].dispose();
|
|
trs.pop();
|
|
}
|
|
},
|
|
|
|
selectedRowsIds: function() {
|
|
return this.selectedRows.slice();
|
|
},
|
|
|
|
getRowIds: function() {
|
|
return this.rows.getKeys();
|
|
},
|
|
|
|
selectNextRow: function() {
|
|
const visibleRows = $(this.dynamicTableDivId).getElements("tbody tr").filter(e => e.getStyle("display") !== "none");
|
|
const selectedRowId = this.getSelectedRowId();
|
|
|
|
let selectedIndex = -1;
|
|
for (let i = 0; i < visibleRows.length; ++i) {
|
|
const row = visibleRows[i];
|
|
if (row.getProperty("data-row-id") === selectedRowId) {
|
|
selectedIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const isLastRowSelected = (selectedIndex >= (visibleRows.length - 1));
|
|
if (!isLastRowSelected) {
|
|
this.deselectAll();
|
|
|
|
const newRow = visibleRows[selectedIndex + 1];
|
|
this.selectRow(newRow.getProperty("data-row-id"));
|
|
}
|
|
},
|
|
|
|
selectPreviousRow: function() {
|
|
const visibleRows = $(this.dynamicTableDivId).getElements("tbody tr").filter(e => e.getStyle("display") !== "none");
|
|
const selectedRowId = this.getSelectedRowId();
|
|
|
|
let selectedIndex = -1;
|
|
for (let i = 0; i < visibleRows.length; ++i) {
|
|
const row = visibleRows[i];
|
|
if (row.getProperty("data-row-id") === selectedRowId) {
|
|
selectedIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const isFirstRowSelected = selectedIndex <= 0;
|
|
if (!isFirstRowSelected) {
|
|
this.deselectAll();
|
|
|
|
const newRow = visibleRows[selectedIndex - 1];
|
|
this.selectRow(newRow.getProperty("data-row-id"));
|
|
}
|
|
},
|
|
});
|
|
|
|
const TorrentsTable = new Class({
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
|
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', '', 'Done', 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('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.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');
|
|
|
|
this.initColumnsFunctions();
|
|
},
|
|
|
|
initColumnsFunctions: function() {
|
|
|
|
// state_icon
|
|
this.columns['state_icon'].updateTd = function(td, row) {
|
|
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;
|
|
case "pausedDL":
|
|
state = "torrent-stop";
|
|
img_path = "images/torrent-stop.svg";
|
|
break;
|
|
case "pausedUP":
|
|
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":
|
|
case "moving":
|
|
state = "force-recheck";
|
|
img_path = "images/force-recheck.svg";
|
|
break;
|
|
case "unknown":
|
|
case "missingFiles":
|
|
state = "error";
|
|
img_path = "images/error.svg";
|
|
break;
|
|
default:
|
|
break; // do nothing
|
|
}
|
|
|
|
if (td.getChildren('img').length > 0) {
|
|
const img = td.getChildren('img')[0];
|
|
if (img.src.indexOf(img_path) < 0) {
|
|
img.set('src', img_path);
|
|
img.set('title', state);
|
|
}
|
|
}
|
|
else {
|
|
td.adopt(new Element('img', {
|
|
'src': img_path,
|
|
'class': 'stateIcon',
|
|
'title': state
|
|
}));
|
|
}
|
|
};
|
|
|
|
// status
|
|
this.columns['status'].updateTd = function(td, row) {
|
|
const state = this.getRowValue(row);
|
|
if (!state)
|
|
return;
|
|
|
|
let status;
|
|
switch (state) {
|
|
case "downloading":
|
|
status = "Downloading";
|
|
break;
|
|
case "stalledDL":
|
|
status = "Stalled";
|
|
break;
|
|
case "metaDL":
|
|
status = "Downloading metadata";
|
|
break;
|
|
case "forcedMetaDL":
|
|
status = "[F] Downloading metadata";
|
|
break;
|
|
case "forcedDL":
|
|
status = "[F] Downloading";
|
|
break;
|
|
case "uploading":
|
|
case "stalledUP":
|
|
status = "Seeding";
|
|
break;
|
|
case "forcedUP":
|
|
status = "[F] Seeding";
|
|
break;
|
|
case "queuedDL":
|
|
case "queuedUP":
|
|
status = "Queued";
|
|
break;
|
|
case "checkingDL":
|
|
case "checkingUP":
|
|
status = "Checking";
|
|
break;
|
|
case "queuedForChecking":
|
|
status = "Queued for checking";
|
|
break;
|
|
case "checkingResumeData":
|
|
status = "Checking resume data";
|
|
break;
|
|
case "pausedDL":
|
|
status = "Paused";
|
|
break;
|
|
case "pausedUP":
|
|
status = "Completed";
|
|
break;
|
|
case "moving":
|
|
status = "Moving";
|
|
break;
|
|
case "missingFiles":
|
|
status = "Missing Files";
|
|
break;
|
|
case "error":
|
|
status = "Errored";
|
|
break;
|
|
default:
|
|
status = "Unknown";
|
|
}
|
|
|
|
td.set('text', status);
|
|
td.set('title', status);
|
|
};
|
|
|
|
// priority
|
|
this.columns['priority'].updateTd = function(td, row) {
|
|
const queuePos = this.getRowValue(row);
|
|
const formattedQueuePos = (queuePos < 1) ? '*' : queuePos;
|
|
td.set('text', formattedQueuePos);
|
|
td.set('title', formattedQueuePos);
|
|
};
|
|
|
|
this.columns['priority'].compareRows = function(row1, row2) {
|
|
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;
|
|
if (row1_val < row2_val)
|
|
return -1;
|
|
else if (row1_val > row2_val)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
};
|
|
|
|
// name, category, tags
|
|
this.columns['name'].compareRows = function(row1, row2) {
|
|
const row1Val = this.getRowValue(row1);
|
|
const row2Val = this.getRowValue(row2);
|
|
return row1Val.localeCompare(row2Val, undefined, { numeric: true, sensitivity: 'base' });
|
|
};
|
|
this.columns['category'].compareRows = this.columns['name'].compareRows;
|
|
this.columns['tags'].compareRows = this.columns['name'].compareRows;
|
|
|
|
// size, total_size
|
|
this.columns['size'].updateTd = function(td, row) {
|
|
const size = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
|
td.set('text', size);
|
|
td.set('title', size);
|
|
};
|
|
this.columns['total_size'].updateTd = this.columns['size'].updateTd;
|
|
|
|
// progress
|
|
this.columns['progress'].updateTd = function(td, row) {
|
|
const progress = this.getRowValue(row);
|
|
let progressFormated = (progress * 100).round(1);
|
|
if (progressFormated == 100.0 && progress != 1.0)
|
|
progressFormated = 99.9;
|
|
|
|
if (td.getChildren('div').length > 0) {
|
|
const div = td.getChildren('div')[0];
|
|
if (td.resized) {
|
|
td.resized = false;
|
|
div.setWidth(ProgressColumnWidth - 5);
|
|
}
|
|
if (div.getValue() != progressFormated)
|
|
div.setValue(progressFormated);
|
|
}
|
|
else {
|
|
if (ProgressColumnWidth < 0)
|
|
ProgressColumnWidth = td.offsetWidth;
|
|
td.adopt(new window.qBittorrent.ProgressBar.ProgressBar(progressFormated.toFloat(), {
|
|
'width': ProgressColumnWidth - 5
|
|
}));
|
|
td.resized = false;
|
|
}
|
|
};
|
|
|
|
this.columns['progress'].onResize = function(columnName) {
|
|
const pos = this.getColumnPos(columnName);
|
|
const trs = this.tableBody.getElements('tr');
|
|
ProgressColumnWidth = -1;
|
|
for (let i = 0; i < trs.length; ++i) {
|
|
const td = trs[i].getElements('td')[pos];
|
|
if (ProgressColumnWidth < 0)
|
|
ProgressColumnWidth = td.offsetWidth;
|
|
td.resized = true;
|
|
this.columns[columnName].updateTd(td, this.rows.get(trs[i].rowId));
|
|
}
|
|
}.bind(this);
|
|
|
|
// num_seeds
|
|
this.columns['num_seeds'].updateTd = function(td, row) {
|
|
const num_seeds = this.getRowValue(row, 0);
|
|
const num_complete = this.getRowValue(row, 1);
|
|
let value = num_seeds;
|
|
if (num_complete != -1)
|
|
value += ' (' + num_complete + ')';
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
this.columns['num_seeds'].compareRows = function(row1, row2) {
|
|
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);
|
|
|
|
if (num_complete1 < num_complete2)
|
|
return -1;
|
|
else if (num_complete1 > num_complete2)
|
|
return 1;
|
|
else if (num_seeds1 < num_seeds2)
|
|
return -1;
|
|
else if (num_seeds1 > num_seeds2)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
};
|
|
|
|
// num_leechs
|
|
this.columns['num_leechs'].updateTd = this.columns['num_seeds'].updateTd;
|
|
this.columns['num_leechs'].compareRows = this.columns['num_seeds'].compareRows;
|
|
|
|
// dlspeed
|
|
this.columns['dlspeed'].updateTd = function(td, row) {
|
|
const speed = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), true);
|
|
td.set('text', speed);
|
|
td.set('title', speed);
|
|
};
|
|
|
|
// upspeed
|
|
this.columns['upspeed'].updateTd = this.columns['dlspeed'].updateTd;
|
|
|
|
// eta
|
|
this.columns['eta'].updateTd = function(td, row) {
|
|
const eta = window.qBittorrent.Misc.friendlyDuration(this.getRowValue(row), window.qBittorrent.Misc.MAX_ETA);
|
|
td.set('text', eta);
|
|
td.set('title', eta);
|
|
};
|
|
|
|
// ratio
|
|
this.columns['ratio'].updateTd = function(td, row) {
|
|
const ratio = this.getRowValue(row);
|
|
const string = (ratio === -1) ? '∞' : window.qBittorrent.Misc.toFixedPointString(ratio, 2);
|
|
td.set('text', string);
|
|
td.set('title', string);
|
|
td.classList.add('ratio');
|
|
td.set('class', 'ratio');
|
|
// color by percentage
|
|
if (string === 0 || string === "0.00") {
|
|
td.classList.add('highlight-darkred');
|
|
} else if (string > "0" && string <= "0.10") {
|
|
td.classList.add('highlight-red');
|
|
} else if (string > "0.10" && string <= "0.20") {
|
|
td.classList.add('highlight-orange');
|
|
} else if (string > "0.20" && string <= "0.40") {
|
|
td.classList.add('highlight-yellow');
|
|
} else if (string > "0.40" && string <= "0.60") {
|
|
td.classList.add('highlight-yellow');
|
|
} else if (string > "0.60" && string <= "0.80") {
|
|
td.classList.add('highlight-green');
|
|
} else if (string > "0.80" && string <= "1.0" || string > "1.0") {
|
|
td.classList.add('highlight-brightgreen');
|
|
}
|
|
};
|
|
|
|
// added on
|
|
this.columns['added_on'].updateTd = function(td, row) {
|
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
|
td.set('text', date);
|
|
td.set('title', date);
|
|
};
|
|
|
|
// completion_on
|
|
this.columns['completion_on'].updateTd = function(td, row) {
|
|
const val = this.getRowValue(row);
|
|
if ((val === 0xffffffff) || (val < 0)) {
|
|
td.set('text', '');
|
|
td.set('title', '');
|
|
}
|
|
else {
|
|
const date = new Date(this.getRowValue(row) * 1000).toLocaleString();
|
|
td.set('text', date);
|
|
td.set('title', date);
|
|
}
|
|
};
|
|
|
|
// dl_limit, up_limit
|
|
this.columns['dl_limit'].updateTd = function(td, row) {
|
|
const speed = this.getRowValue(row);
|
|
if (speed === 0) {
|
|
td.set('text', '∞');
|
|
td.set('title', '∞');
|
|
}
|
|
else {
|
|
const formattedSpeed = window.qBittorrent.Misc.friendlyUnit(speed, true);
|
|
td.set('text', formattedSpeed);
|
|
td.set('title', formattedSpeed);
|
|
}
|
|
};
|
|
|
|
this.columns['up_limit'].updateTd = this.columns['dl_limit'].updateTd;
|
|
|
|
// downloaded, uploaded, downloaded_session, uploaded_session, amount_left
|
|
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;
|
|
|
|
// time active
|
|
this.columns['time_active'].updateTd = function(td, row) {
|
|
const activeTime = this.getRowValue(row, 0);
|
|
const seedingTime = this.getRowValue(row, 1);
|
|
const time = (seedingTime > 0)
|
|
? ('%1 (seeded for %2)'
|
|
.replace('%1', window.qBittorrent.Misc.friendlyDuration(activeTime))
|
|
.replace('%2', window.qBittorrent.Misc.friendlyDuration(seedingTime)))
|
|
: window.qBittorrent.Misc.friendlyDuration(activeTime);
|
|
td.set('text', time);
|
|
td.set('title', time);
|
|
};
|
|
|
|
// completed
|
|
this.columns['completed'].updateTd = this.columns['size'].updateTd;
|
|
|
|
// max_ratio
|
|
this.columns['max_ratio'].updateTd = this.columns['ratio'].updateTd;
|
|
|
|
// seen_complete
|
|
this.columns['seen_complete'].updateTd = this.columns['completion_on'].updateTd;
|
|
|
|
// last_activity
|
|
this.columns['last_activity'].updateTd = function(td, row) {
|
|
const val = this.getRowValue(row);
|
|
if (val < 1) {
|
|
td.set('text', '∞');
|
|
td.set('title', '∞');
|
|
}
|
|
else {
|
|
const formattedVal = '%1 ago'.replace('%1', window.qBittorrent.Misc.friendlyDuration((new Date()) / 1000 - val));
|
|
td.set('text', formattedVal);
|
|
td.set('title', formattedVal);
|
|
}
|
|
};
|
|
|
|
// availability
|
|
this.columns['availability'].updateTd = function(td, row) {
|
|
const value = window.qBittorrent.Misc.toFixedPointString(this.getRowValue(row), 3);
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
},
|
|
|
|
applyFilter: function(row, filterName, categoryHash, tagHash, trackerHash, filterTerms) {
|
|
const state = row['full_data'].state;
|
|
const name = row['full_data'].name.toLowerCase();
|
|
let inactive = false;
|
|
let r;
|
|
|
|
switch (filterName) {
|
|
case 'downloading':
|
|
if ((state != 'downloading') && (state.indexOf('DL') === -1))
|
|
return false;
|
|
break;
|
|
case 'seeding':
|
|
if (state != 'uploading' && state != 'forcedUP' && state != 'stalledUP' && state != 'queuedUP' && state != 'checkingUP')
|
|
return false;
|
|
break;
|
|
case 'completed':
|
|
if ((state != 'uploading') && (state.indexOf('UP') === -1))
|
|
return false;
|
|
break;
|
|
case 'paused':
|
|
if (state.indexOf('paused') === -1)
|
|
return false;
|
|
break;
|
|
case 'resumed':
|
|
if (state.indexOf('paused') > -1)
|
|
return false;
|
|
break;
|
|
case 'stalled':
|
|
if ((state != 'stalledUP') && (state != 'stalledDL'))
|
|
return false;
|
|
break;
|
|
case 'stalled_uploading':
|
|
if (state != 'stalledUP')
|
|
return false;
|
|
break;
|
|
case 'stalled_downloading':
|
|
if (state != 'stalledDL')
|
|
return false;
|
|
break;
|
|
case 'inactive':
|
|
inactive = true;
|
|
// fallthrough
|
|
case 'active':
|
|
if (state == 'stalledDL')
|
|
r = (row['full_data'].upspeed > 0);
|
|
else
|
|
r = state == 'metaDL' || state == 'forcedMetaDL' || state == 'downloading' || state == 'forcedDL' || state == 'uploading' || state == 'forcedUP';
|
|
if (r == inactive)
|
|
return false;
|
|
break;
|
|
case 'checking':
|
|
if (state !== 'checkingUP' && state !== 'checkingDL' && state !== 'checkingResumeData')
|
|
return false;
|
|
break;
|
|
case 'errored':
|
|
if (state != 'error' && state != "unknown" && state != "missingFiles")
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
const categoryHashInt = parseInt(categoryHash);
|
|
if (!isNaN(categoryHashInt)) {
|
|
switch (categoryHashInt) {
|
|
case CATEGORIES_ALL:
|
|
break; // do nothing
|
|
case CATEGORIES_UNCATEGORIZED:
|
|
if (row['full_data'].category.length !== 0)
|
|
return false;
|
|
break; // do nothing
|
|
default:
|
|
if (categoryHashInt !== genHash(row['full_data'].category))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const tagHashInt = parseInt(tagHash);
|
|
const isNumber = !isNaN(tagHashInt);
|
|
if (isNumber) {
|
|
switch (tagHashInt) {
|
|
case TAGS_ALL:
|
|
break; // do nothing
|
|
|
|
case TAGS_UNTAGGED:
|
|
if (row['full_data'].tags.length !== 0)
|
|
return false;
|
|
break; // do nothing
|
|
|
|
default: {
|
|
let rowTags = row['full_data'].tags.split(', ');
|
|
rowTags = rowTags.map(function(tag) {
|
|
return genHash(tag);
|
|
});
|
|
if (!rowTags.contains(tagHashInt))
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const trackerHashInt = Number.parseInt(trackerHash, 10);
|
|
switch (trackerHashInt) {
|
|
case TRACKERS_ALL:
|
|
break; // do nothing
|
|
case TRACKERS_TRACKERLESS:
|
|
if (row['full_data'].trackers_count !== 0)
|
|
return false;
|
|
break;
|
|
default: {
|
|
const tracker = trackerList.get(trackerHashInt);
|
|
if (tracker && !tracker.torrents.includes(row['full_data'].rowId))
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((filterTerms !== undefined) && (filterTerms !== null)
|
|
&& (filterTerms.length > 0) && !window.qBittorrent.Misc.containsAllTerms(name, filterTerms))
|
|
return false;
|
|
|
|
return true;
|
|
},
|
|
|
|
getFilteredTorrentsNumber: function(filterName, categoryHash, tagHash, trackerHash) {
|
|
let cnt = 0;
|
|
const rows = this.rows.getValues();
|
|
|
|
for (let i = 0; i < rows.length; ++i)
|
|
if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, trackerHash, null))
|
|
++cnt;
|
|
return cnt;
|
|
},
|
|
|
|
getFilteredTorrentsHashes: function(filterName, categoryHash, tagHash, trackerHash) {
|
|
const rowsHashes = [];
|
|
const rows = this.rows.getValues();
|
|
|
|
for (let i = 0; i < rows.length; ++i)
|
|
if (this.applyFilter(rows[i], filterName, categoryHash, tagHash, trackerHash, null))
|
|
rowsHashes.push(rows[i]['rowId']);
|
|
|
|
return rowsHashes;
|
|
},
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
const filteredRows = [];
|
|
|
|
const rows = this.rows.getValues();
|
|
const filterText = $('torrentsFilterInput').value.trim().toLowerCase();
|
|
const filterTerms = (filterText.length > 0) ? filterText.split(" ") : null;
|
|
|
|
for (let i = 0; i < rows.length; ++i) {
|
|
if (this.applyFilter(rows[i], selected_filter, selected_category, selectedTag, selectedTracker, filterTerms)) {
|
|
filteredRows.push(rows[i]);
|
|
filteredRows[rows[i].rowId] = rows[i];
|
|
}
|
|
}
|
|
|
|
filteredRows.sort(function(row1, row2) {
|
|
const column = this.columns[this.sortedColumn];
|
|
const res = column.compareRows(row1, row2);
|
|
if (this.reverseSort === '0')
|
|
return res;
|
|
else
|
|
return -res;
|
|
}.bind(this));
|
|
return filteredRows;
|
|
},
|
|
|
|
setupTr: function(tr) {
|
|
tr.addEvent('dblclick', function(e) {
|
|
e.stop();
|
|
this._this.deselectAll();
|
|
this._this.selectRow(this.rowId);
|
|
const row = this._this.rows.get(this.rowId);
|
|
const state = row['full_data'].state;
|
|
if (state.indexOf('paused') > -1)
|
|
startFN();
|
|
else
|
|
pauseFN();
|
|
return true;
|
|
});
|
|
tr.addClass("torrentsTableContextMenuTarget");
|
|
},
|
|
|
|
getCurrentTorrentID: function() {
|
|
return this.getSelectedRowId();
|
|
},
|
|
|
|
onSelectedRowChanged: function() {
|
|
updatePropertiesPanel();
|
|
}
|
|
});
|
|
|
|
const TorrentPeersTable = new Class({
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
|
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('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');
|
|
this.initColumnsFunctions();
|
|
},
|
|
|
|
initColumnsFunctions: function() {
|
|
|
|
// country
|
|
this.columns['country'].updateTd = function(td, row) {
|
|
const country = this.getRowValue(row, 0);
|
|
const country_code = this.getRowValue(row, 1);
|
|
|
|
if (!country_code) {
|
|
if (td.getChildren('img').length > 0)
|
|
td.getChildren('img')[0].dispose();
|
|
return;
|
|
}
|
|
|
|
const img_path = 'images/flags/' + country_code + '.svg';
|
|
|
|
if (td.getChildren('img').length > 0) {
|
|
const img = td.getChildren('img')[0];
|
|
img.set('src', img_path);
|
|
img.set('class', 'flags');
|
|
img.set('alt', country);
|
|
img.set('title', country);
|
|
}
|
|
else
|
|
td.adopt(new Element('img', {
|
|
'src': img_path,
|
|
'class': 'flags',
|
|
'alt': country,
|
|
'title': country
|
|
}));
|
|
};
|
|
|
|
// ip
|
|
this.columns['ip'].compareRows = function(row1, row2) {
|
|
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) {
|
|
if (a[i] != b[i])
|
|
return a[i] - b[i];
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
// flags
|
|
this.columns['flags'].updateTd = function(td, row) {
|
|
td.set('text', this.getRowValue(row, 0));
|
|
td.set('title', this.getRowValue(row, 1));
|
|
};
|
|
|
|
// progress
|
|
this.columns['progress'].updateTd = function(td, row) {
|
|
const progress = this.getRowValue(row);
|
|
let progressFormated = (progress * 100).round(1);
|
|
if (progressFormated == 100.0 && progress != 1.0)
|
|
progressFormated = 99.9;
|
|
progressFormated += "%";
|
|
td.set('text', progressFormated);
|
|
td.set('title', progressFormated);
|
|
};
|
|
|
|
// dl_speed, up_speed
|
|
this.columns['dl_speed'].updateTd = function(td, row) {
|
|
const speed = this.getRowValue(row);
|
|
if (speed === 0) {
|
|
td.set('text', '');
|
|
td.set('title', '');
|
|
}
|
|
else {
|
|
const formattedSpeed = window.qBittorrent.Misc.friendlyUnit(speed, true);
|
|
td.set('text', formattedSpeed);
|
|
td.set('title', formattedSpeed);
|
|
}
|
|
};
|
|
this.columns['up_speed'].updateTd = this.columns['dl_speed'].updateTd;
|
|
|
|
// downloaded, uploaded
|
|
this.columns['downloaded'].updateTd = function(td, row) {
|
|
const downloaded = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
|
td.set('text', downloaded);
|
|
td.set('title', downloaded);
|
|
};
|
|
this.columns['uploaded'].updateTd = this.columns['downloaded'].updateTd;
|
|
|
|
// relevance
|
|
this.columns['relevance'].updateTd = this.columns['progress'].updateTd;
|
|
|
|
// files
|
|
this.columns['files'].updateTd = function(td, row) {
|
|
const value = this.getRowValue(row, 0);
|
|
td.set('text', value.replace(/\n/g, ';'));
|
|
td.set('title', value);
|
|
};
|
|
|
|
}
|
|
});
|
|
|
|
const SearchResultsTable = new Class({
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
|
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('siteUrl', '', 'Search engine', 250, true);
|
|
|
|
this.initColumnsFunctions();
|
|
},
|
|
|
|
initColumnsFunctions: function() {
|
|
const displaySize = function(td, row) {
|
|
const size = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
|
td.set('text', size);
|
|
td.set('title', size);
|
|
};
|
|
const displayNum = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
const formattedValue = (value === "-1") ? "Unknown" : value;
|
|
td.set('text', formattedValue);
|
|
td.set('title', formattedValue);
|
|
};
|
|
|
|
this.columns['fileSize'].updateTd = displaySize;
|
|
this.columns['nbSeeders'].updateTd = displayNum;
|
|
this.columns['nbLeechers'].updateTd = displayNum;
|
|
},
|
|
|
|
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 rows = this.rows.getValues();
|
|
const searchTerms = window.qBittorrent.Search.searchText.pattern.toLowerCase().split(" ");
|
|
const filterTerms = window.qBittorrent.Search.searchText.filterPattern.toLowerCase().split(" ");
|
|
const sizeFilters = getSizeFilters();
|
|
const seedsFilters = getSeedsFilters();
|
|
const searchInTorrentName = $('searchInTorrentName').get('value') === "names";
|
|
|
|
if (searchInTorrentName || (filterTerms.length > 0) || (window.qBittorrent.Search.searchSizeFilter.min > 0.00) || (window.qBittorrent.Search.searchSizeFilter.max > 0.00)) {
|
|
for (let i = 0; i < rows.length; ++i) {
|
|
const row = rows[i];
|
|
|
|
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 {
|
|
filteredRows = rows;
|
|
}
|
|
|
|
filteredRows.sort(function(row1, row2) {
|
|
const column = this.columns[this.sortedColumn];
|
|
const res = column.compareRows(row1, row2);
|
|
if (this.reverseSort === '0')
|
|
return res;
|
|
else
|
|
return -res;
|
|
}.bind(this));
|
|
|
|
return filteredRows;
|
|
},
|
|
|
|
setupTr: function(tr) {
|
|
tr.addClass("searchTableRow");
|
|
}
|
|
});
|
|
|
|
const SearchPluginsTable = new Class({
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
|
this.newColumn('fullName', '', 'Name', 175, true);
|
|
this.newColumn('version', '', 'Version', 100, true);
|
|
this.newColumn('url', '', 'Url', 175, true);
|
|
this.newColumn('enabled', '', 'Enabled', 100, true);
|
|
|
|
this.initColumnsFunctions();
|
|
},
|
|
|
|
initColumnsFunctions: function() {
|
|
this.columns['enabled'].updateTd = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
if (value) {
|
|
td.set('text', 'Yes');
|
|
td.set('title', 'Yes');
|
|
td.getParent("tr").addClass("green");
|
|
td.getParent("tr").removeClass("red");
|
|
}
|
|
else {
|
|
td.set('text', 'No');
|
|
td.set('title', 'No');
|
|
td.getParent("tr").addClass("red");
|
|
td.getParent("tr").removeClass("green");
|
|
}
|
|
};
|
|
},
|
|
|
|
setupTr: function(tr) {
|
|
tr.addClass("searchPluginsTableRow");
|
|
}
|
|
});
|
|
|
|
const TorrentTrackersTable = new Class({
|
|
Extends: DynamicTable,
|
|
|
|
initColumns: function() {
|
|
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);
|
|
},
|
|
});
|
|
|
|
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);
|
|
root.children.each(function(node) {
|
|
this._addNodeToTable(node, 0);
|
|
}.bind(this));
|
|
},
|
|
|
|
_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);
|
|
}
|
|
|
|
node.children.each(function(child) {
|
|
this._addNodeToTable(child, depth + 1);
|
|
}.bind(this));
|
|
},
|
|
|
|
getRoot: function() {
|
|
return this.fileTree.getRoot();
|
|
},
|
|
|
|
getNode: function(rowId) {
|
|
return this.fileTree.getNode(rowId);
|
|
},
|
|
|
|
getRow: function(node) {
|
|
const rowId = this.fileTree.getRowId(node);
|
|
return this.rows.get(rowId);
|
|
},
|
|
|
|
initColumns: function() {
|
|
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);
|
|
|
|
this.initColumnsFunctions();
|
|
},
|
|
|
|
initColumnsFunctions: function() {
|
|
const that = this;
|
|
const displaySize = function(td, row) {
|
|
const size = window.qBittorrent.Misc.friendlyUnit(this.getRowValue(row), false);
|
|
td.set('text', size);
|
|
td.set('title', size);
|
|
};
|
|
const displayPercentage = function(td, row) {
|
|
const value = window.qBittorrent.Misc.friendlyPercentage(this.getRowValue(row));
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
|
|
// checked
|
|
this.columns['checked'].updateTd = function(td, row) {
|
|
const id = row.rowId;
|
|
const value = this.getRowValue(row);
|
|
|
|
if (window.qBittorrent.PropFiles.isDownloadCheckboxExists(id)) {
|
|
window.qBittorrent.PropFiles.updateDownloadCheckbox(id, value);
|
|
}
|
|
else {
|
|
const treeImg = new Element('img', {
|
|
src: 'images/L.gif',
|
|
styles: {
|
|
'margin-bottom': -2
|
|
}
|
|
});
|
|
td.adopt(treeImg, window.qBittorrent.PropFiles.createDownloadCheckbox(id, row.full_data.fileId, value));
|
|
}
|
|
};
|
|
|
|
// name
|
|
this.columns['name'].updateTd = function(td, row) {
|
|
const id = row.rowId;
|
|
const fileNameId = 'filesTablefileName' + id;
|
|
const node = that.getNode(id);
|
|
|
|
if (node.isFolder) {
|
|
const value = this.getRowValue(row);
|
|
const collapseIconId = 'filesTableCollapseIcon' + id;
|
|
const dirImgId = 'filesTableDirImg' + id;
|
|
if ($(dirImgId)) {
|
|
// just update file name
|
|
$(fileNameId).set('text', value);
|
|
}
|
|
else {
|
|
const collapseIcon = new Element('img', {
|
|
src: 'images/go-down.svg',
|
|
styles: {
|
|
'margin-left': (node.depth * 20)
|
|
},
|
|
class: "filesTableCollapseIcon",
|
|
id: collapseIconId,
|
|
"data-id": id,
|
|
onclick: "qBittorrent.PropFiles.collapseIconClicked(this)"
|
|
});
|
|
const span = new Element('span', {
|
|
text: value,
|
|
id: fileNameId
|
|
});
|
|
const dirImg = new Element('img', {
|
|
src: 'images/directory.svg',
|
|
styles: {
|
|
'width': 15,
|
|
'padding-right': 5,
|
|
'margin-bottom': -3
|
|
},
|
|
id: dirImgId
|
|
});
|
|
const html = collapseIcon.outerHTML + dirImg.outerHTML + span.outerHTML;
|
|
td.set('html', html);
|
|
}
|
|
}
|
|
else {
|
|
const value = this.getRowValue(row);
|
|
const span = new Element('span', {
|
|
text: value,
|
|
id: fileNameId,
|
|
styles: {
|
|
'margin-left': ((node.depth + 1) * 20)
|
|
}
|
|
});
|
|
td.set('html', span.outerHTML);
|
|
}
|
|
};
|
|
|
|
// size
|
|
this.columns['size'].updateTd = displaySize;
|
|
|
|
// progress
|
|
this.columns['progress'].updateTd = function(td, row) {
|
|
const id = row.rowId;
|
|
const value = this.getRowValue(row);
|
|
|
|
const progressBar = $('pbf_' + id);
|
|
if (progressBar === null) {
|
|
td.adopt(new window.qBittorrent.ProgressBar.ProgressBar(value.toFloat(), {
|
|
id: 'pbf_' + id,
|
|
width: 80
|
|
}));
|
|
}
|
|
else {
|
|
progressBar.setValue(value.toFloat());
|
|
}
|
|
};
|
|
|
|
// priority
|
|
this.columns['priority'].updateTd = function(td, row) {
|
|
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
|
|
this.columns['remaining'].updateTd = displaySize;
|
|
this.columns['availability'].updateTd = displayPercentage;
|
|
},
|
|
|
|
altRow: function() {
|
|
let addClass = false;
|
|
const trs = this.tableBody.getElements('tr');
|
|
trs.each(function(tr) {
|
|
if (tr.hasClass("invisible"))
|
|
return;
|
|
|
|
if (addClass) {
|
|
tr.addClass("alt");
|
|
tr.removeClass("nonAlt");
|
|
}
|
|
else {
|
|
tr.removeClass("alt");
|
|
tr.addClass("nonAlt");
|
|
}
|
|
addClass = !addClass;
|
|
}.bind(this));
|
|
},
|
|
|
|
_sortNodesByColumn: function(nodes, column) {
|
|
nodes.sort(function(row1, row2) {
|
|
// 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);
|
|
return (this.reverseSort === '0') ? res : -res;
|
|
}.bind(this));
|
|
|
|
nodes.each(function(node) {
|
|
if (node.children.length > 0)
|
|
this._sortNodesByColumn(node.children, column);
|
|
}.bind(this));
|
|
},
|
|
|
|
_filterNodes: function(node, filterTerms, filteredRows) {
|
|
if (node.isFolder) {
|
|
const childAdded = node.children.reduce(function(acc, child) {
|
|
// 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);
|
|
}.bind(this), false);
|
|
|
|
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) {
|
|
const filterTerms = text.trim().toLowerCase().split(' ');
|
|
if ((filterTerms.length === 1) && (filterTerms[0] === ''))
|
|
this.filterTerms = [];
|
|
else
|
|
this.filterTerms = filterTerms;
|
|
},
|
|
|
|
getFilteredAndSortedRows: function() {
|
|
if (this.getRoot() === null)
|
|
return [];
|
|
|
|
const generateRowsSignature = function(rows) {
|
|
const rowsData = rows.map(function(row) {
|
|
return row.full_data;
|
|
});
|
|
return JSON.stringify(rowsData);
|
|
};
|
|
|
|
const getFilteredRows = function() {
|
|
if (this.filterTerms.length === 0) {
|
|
const nodeArray = this.fileTree.toArray();
|
|
const filteredRows = nodeArray.map(function(node) {
|
|
return this.getRow(node);
|
|
}.bind(this));
|
|
return filteredRows;
|
|
}
|
|
|
|
const filteredRows = [];
|
|
this.getRoot().children.each(function(child) {
|
|
this._filterNodes(child, this.filterTerms, filteredRows);
|
|
}.bind(this));
|
|
filteredRows.reverse();
|
|
return filteredRows;
|
|
}.bind(this);
|
|
|
|
const hasRowsChanged = function(rowsString, prevRowsStringString) {
|
|
const rowsChanged = (rowsString !== prevRowsStringString);
|
|
const isFilterTermsChanged = this.filterTerms.reduce(function(acc, term, index) {
|
|
return (acc || (term !== this.prevFilterTerms[index]));
|
|
}.bind(this), false);
|
|
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);
|
|
|
|
const rowsString = generateRowsSignature(this.rows);
|
|
if (!hasRowsChanged(rowsString, this.prevRowsString)) {
|
|
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) {
|
|
tr.addEvent('keydown', function(event) {
|
|
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() {
|
|
this.newColumn('state_icon', '', '', 30, true);
|
|
this.newColumn('name', '', 'RSS feeds', -1, true);
|
|
|
|
this.columns['state_icon'].dataProperties[0] = '';
|
|
|
|
// map name row to "[name] ([unread])"
|
|
this.columns['name'].dataProperties.push('unread');
|
|
this.columns['name'].updateTd = function(td, row) {
|
|
const name = this.getRowValue(row, 0);
|
|
const unreadCount = this.getRowValue(row, 1);
|
|
let value = name + ' (' + unreadCount + ')';
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
},
|
|
setupHeaderMenu: function() {},
|
|
setupHeaderEvents: function() {},
|
|
getFilteredAndSortedRows: function() {
|
|
return this.rows.getValues();
|
|
},
|
|
selectRow: function(rowId) {
|
|
this.selectedRows.push(rowId);
|
|
this.setRowClass();
|
|
this.onSelectedRowChanged();
|
|
|
|
const rows = this.rows.getValues();
|
|
let path = '';
|
|
for (let i = 0; i < rows.length; ++i) {
|
|
if (rows[i].rowId === rowId) {
|
|
path = rows[i].full_data.dataPath;
|
|
break;
|
|
}
|
|
}
|
|
window.qBittorrent.Rss.showRssFeed(path);
|
|
},
|
|
setupTr: function(tr) {
|
|
tr.addEvent('dblclick', function(e) {
|
|
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);
|
|
const data = row[fullUpdate ? 'full_data' : 'data'];
|
|
|
|
const tds = tr.getElements('td');
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0]))
|
|
this.columns[i].updateTd(tds[i], row);
|
|
}
|
|
row['data'] = {};
|
|
tds[0].style.overflow = 'visible';
|
|
let indentation = row.full_data.indentation;
|
|
tds[0].style.paddingLeft = (indentation * 32 + 4) + 'px';
|
|
tds[1].style.paddingLeft = (indentation * 32 + 4) + 'px';
|
|
},
|
|
updateIcons: function() {
|
|
// state_icon
|
|
this.rows.each(row => {
|
|
let img_path;
|
|
switch (row.full_data.status) {
|
|
case 'default':
|
|
img_path = 'images/application-rss.svg';
|
|
break;
|
|
case 'hasError':
|
|
img_path = 'images/task-reject.svg';
|
|
break;
|
|
case 'isLoading':
|
|
img_path = 'images/spinner.gif';
|
|
break;
|
|
case 'unread':
|
|
img_path = 'images/mail-inbox.svg';
|
|
break;
|
|
case 'isFolder':
|
|
img_path = 'images/folder-documents.svg';
|
|
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;
|
|
}
|
|
}
|
|
if (td.getChildren('img').length > 0) {
|
|
const img = td.getChildren('img')[0];
|
|
if (img.src.indexOf(img_path) < 0) {
|
|
img.set('src', img_path);
|
|
img.set('title', status);
|
|
}
|
|
}
|
|
else {
|
|
td.adopt(new Element('img', {
|
|
'src': img_path,
|
|
'class': 'stateIcon',
|
|
'height': '22px',
|
|
'width': '22px'
|
|
}));
|
|
}
|
|
});
|
|
},
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
const column = {};
|
|
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) {
|
|
if (pos === undefined)
|
|
pos = 0;
|
|
return row['full_data'][this.dataProperties[pos]];
|
|
};
|
|
column['compareRows'] = function(row1, row2) {
|
|
const value1 = this.getRowValue(row1);
|
|
const value2 = this.getRowValue(row2);
|
|
return naturalSortCollator.compare(value1, value2);
|
|
};
|
|
column['updateTd'] = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
column['onResize'] = null;
|
|
this.columns.push(column);
|
|
this.columns[name] = column;
|
|
|
|
this.hiddenTableHeader.appendChild(new Element('th'));
|
|
this.fixedTableHeader.appendChild(new Element('th'));
|
|
},
|
|
setupCommonEvents: function() {
|
|
const scrollFn = function() {
|
|
$(this.dynamicTableFixedHeaderDivId).getElements('table')[0].style.left = -$(this.dynamicTableDivId).scrollLeft + 'px';
|
|
}.bind(this);
|
|
|
|
$(this.dynamicTableDivId).addEvent('scroll', scrollFn);
|
|
}
|
|
});
|
|
|
|
const RssArticleTable = new Class({
|
|
Extends: DynamicTable,
|
|
initColumns: function() {
|
|
this.newColumn('name', '', 'Torrents: (double-click to download)', -1, true);
|
|
},
|
|
setupHeaderMenu: function() {},
|
|
setupHeaderEvents: function() {},
|
|
getFilteredAndSortedRows: function() {
|
|
return this.rows.getValues();
|
|
},
|
|
selectRow: function(rowId) {
|
|
this.selectedRows.push(rowId);
|
|
this.setRowClass();
|
|
this.onSelectedRowChanged();
|
|
|
|
const rows = this.rows.getValues();
|
|
let articleId = '';
|
|
let feedUid = '';
|
|
for (let i = 0; i < rows.length; ++i) {
|
|
if (rows[i].rowId === rowId) {
|
|
articleId = rows[i].full_data.dataId;
|
|
feedUid = rows[i].full_data.feedUid;
|
|
this.tableBody.rows[rows[i].rowId].removeClass('unreadArticle');
|
|
break;
|
|
}
|
|
}
|
|
window.qBittorrent.Rss.showDetails(feedUid, articleId);
|
|
},
|
|
setupTr: function(tr) {
|
|
tr.addEvent('dblclick', function(e) {
|
|
showDownloadPage([this._this.rows.get(this.rowId).full_data.torrentURL]);
|
|
return true;
|
|
});
|
|
tr.addClass('torrentsTableContextMenuTarget');
|
|
},
|
|
updateRow: function(tr, fullUpdate) {
|
|
const row = this.rows.get(tr.rowId);
|
|
const data = row[fullUpdate ? 'full_data' : 'data'];
|
|
if (!row.full_data.isRead)
|
|
tr.addClass('unreadArticle');
|
|
else
|
|
tr.removeClass('unreadArticle');
|
|
|
|
const tds = tr.getElements('td');
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0]))
|
|
this.columns[i].updateTd(tds[i], row);
|
|
}
|
|
row['data'] = {};
|
|
},
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
const column = {};
|
|
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) {
|
|
if (pos === undefined)
|
|
pos = 0;
|
|
return row['full_data'][this.dataProperties[pos]];
|
|
};
|
|
column['compareRows'] = function(row1, row2) {
|
|
const value1 = this.getRowValue(row1);
|
|
const value2 = this.getRowValue(row2);
|
|
return naturalSortCollator.compare(value1, value2);
|
|
};
|
|
column['updateTd'] = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
column['onResize'] = null;
|
|
this.columns.push(column);
|
|
this.columns[name] = column;
|
|
|
|
this.hiddenTableHeader.appendChild(new Element('th'));
|
|
this.fixedTableHeader.appendChild(new Element('th'));
|
|
},
|
|
setupCommonEvents: function() {
|
|
const scrollFn = function() {
|
|
$(this.dynamicTableFixedHeaderDivId).getElements('table')[0].style.left = -$(this.dynamicTableDivId).scrollLeft + 'px';
|
|
}.bind(this);
|
|
|
|
$(this.dynamicTableDivId).addEvent('scroll', scrollFn);
|
|
}
|
|
});
|
|
|
|
const RssDownloaderRulesTable = new Class({
|
|
Extends: DynamicTable,
|
|
initColumns: function() {
|
|
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.set('type', 'checkbox');
|
|
checkbox.set('id', 'cbRssDlRule' + row.rowId);
|
|
checkbox.checked = row.full_data.checked;
|
|
|
|
checkbox.addEvent('click', function(e) {
|
|
window.qBittorrent.RssDownloader.rssDownloaderRulesTable.updateRowData({
|
|
rowId: row.rowId,
|
|
checked: this.checked
|
|
});
|
|
window.qBittorrent.RssDownloader.modifyRuleState(row.full_data.name, 'enabled', this.checked);
|
|
e.stopPropagation();
|
|
});
|
|
|
|
td.append(checkbox);
|
|
}
|
|
else {
|
|
$('cbRssDlRule' + row.rowId).checked = row.full_data.checked;
|
|
}
|
|
};
|
|
},
|
|
setupHeaderMenu: function() {},
|
|
setupHeaderEvents: function() {},
|
|
getFilteredAndSortedRows: function() {
|
|
return this.rows.getValues();
|
|
},
|
|
setupTr: function(tr) {
|
|
tr.addEvent('dblclick', function(e) {
|
|
window.qBittorrent.RssDownloader.renameRule(this._this.rows.get(this.rowId).full_data.name);
|
|
return true;
|
|
});
|
|
},
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
const column = {};
|
|
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) {
|
|
if (pos === undefined)
|
|
pos = 0;
|
|
return row['full_data'][this.dataProperties[pos]];
|
|
};
|
|
column['compareRows'] = function(row1, row2) {
|
|
const value1 = this.getRowValue(row1);
|
|
const value2 = this.getRowValue(row2);
|
|
return naturalSortCollator.compare(value1, value2);
|
|
};
|
|
column['updateTd'] = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
column['onResize'] = null;
|
|
this.columns.push(column);
|
|
this.columns[name] = column;
|
|
|
|
this.hiddenTableHeader.appendChild(new Element('th'));
|
|
this.fixedTableHeader.appendChild(new Element('th'));
|
|
},
|
|
selectRow: function(rowId) {
|
|
this.selectedRows.push(rowId);
|
|
this.setRowClass();
|
|
this.onSelectedRowChanged();
|
|
|
|
const rows = this.rows.getValues();
|
|
let name = '';
|
|
for (let i = 0; i < rows.length; ++i) {
|
|
if (rows[i].rowId === rowId) {
|
|
name = rows[i].full_data.name;
|
|
break;
|
|
}
|
|
}
|
|
window.qBittorrent.RssDownloader.showRule(name);
|
|
}
|
|
});
|
|
|
|
const RssDownloaderFeedSelectionTable = new Class({
|
|
Extends: DynamicTable,
|
|
initColumns: function() {
|
|
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.set('type', 'checkbox');
|
|
checkbox.set('id', 'cbRssDlFeed' + row.rowId);
|
|
checkbox.checked = row.full_data.checked;
|
|
|
|
checkbox.addEvent('click', function(e) {
|
|
window.qBittorrent.RssDownloader.rssDownloaderFeedSelectionTable.updateRowData({
|
|
rowId: row.rowId,
|
|
checked: this.checked
|
|
});
|
|
e.stopPropagation();
|
|
});
|
|
|
|
td.append(checkbox);
|
|
}
|
|
else {
|
|
$('cbRssDlFeed' + row.rowId).checked = row.full_data.checked;
|
|
}
|
|
};
|
|
},
|
|
setupHeaderMenu: function() {},
|
|
setupHeaderEvents: function() {},
|
|
getFilteredAndSortedRows: function() {
|
|
return this.rows.getValues();
|
|
},
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
const column = {};
|
|
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) {
|
|
if (pos === undefined)
|
|
pos = 0;
|
|
return row['full_data'][this.dataProperties[pos]];
|
|
};
|
|
column['compareRows'] = function(row1, row2) {
|
|
const value1 = this.getRowValue(row1);
|
|
const value2 = this.getRowValue(row2);
|
|
return naturalSortCollator.compare(value1, value2);
|
|
};
|
|
column['updateTd'] = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
column['onResize'] = null;
|
|
this.columns.push(column);
|
|
this.columns[name] = column;
|
|
|
|
this.hiddenTableHeader.appendChild(new Element('th'));
|
|
this.fixedTableHeader.appendChild(new Element('th'));
|
|
},
|
|
selectRow: function() {}
|
|
});
|
|
|
|
const RssDownloaderArticlesTable = new Class({
|
|
Extends: DynamicTable,
|
|
initColumns: function() {
|
|
this.newColumn('name', '', '', -1, true);
|
|
},
|
|
setupHeaderMenu: function() {},
|
|
setupHeaderEvents: function() {},
|
|
getFilteredAndSortedRows: function() {
|
|
return this.rows.getValues();
|
|
},
|
|
newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
|
|
const column = {};
|
|
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) {
|
|
if (pos === undefined)
|
|
pos = 0;
|
|
return row['full_data'][this.dataProperties[pos]];
|
|
};
|
|
column['compareRows'] = function(row1, row2) {
|
|
const value1 = this.getRowValue(row1);
|
|
const value2 = this.getRowValue(row2);
|
|
return naturalSortCollator.compare(value1, value2);
|
|
};
|
|
column['updateTd'] = function(td, row) {
|
|
const value = this.getRowValue(row);
|
|
td.set('text', value);
|
|
td.set('title', value);
|
|
};
|
|
column['onResize'] = null;
|
|
this.columns.push(column);
|
|
this.columns[name] = column;
|
|
|
|
this.hiddenTableHeader.appendChild(new Element('th'));
|
|
this.fixedTableHeader.appendChild(new Element('th'));
|
|
},
|
|
selectRow: function() {},
|
|
updateRow: function(tr, fullUpdate) {
|
|
const row = this.rows.get(tr.rowId);
|
|
const data = row[fullUpdate ? 'full_data' : 'data'];
|
|
|
|
if (row.full_data.isFeed) {
|
|
tr.addClass('articleTableFeed');
|
|
tr.removeClass('articleTableArticle');
|
|
}
|
|
else {
|
|
tr.removeClass('articleTableFeed');
|
|
tr.addClass('articleTableArticle');
|
|
}
|
|
|
|
const tds = tr.getElements('td');
|
|
for (let i = 0; i < this.columns.length; ++i) {
|
|
if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0]))
|
|
this.columns[i].updateTd(tds[i], row);
|
|
}
|
|
row['data'] = {};
|
|
}
|
|
});
|
|
|
|
return exports();
|
|
})();
|
|
|
|
Object.freeze(window.qBittorrent.DynamicTable);
|
|
|
|
/*************************************************************/
|