From 2b3c92a4a8a7d428b5ff1338723adc8c837a407b Mon Sep 17 00:00:00 2001 From: Sepro Date: Wed, 29 Apr 2020 14:41:24 +0200 Subject: [PATCH] WebUI: Add RSS functionality Implemented RSS Reader and AutoDownloader in reference WebUI. --- .../www/private/confirmfeeddeletion.html | 55 ++ src/webui/www/private/confirmruleclear.html | 48 + .../www/private/confirmruledeletion.html | 56 ++ src/webui/www/private/css/Tabs.css | 1 - src/webui/www/private/index.html | 2 + src/webui/www/private/newfeed.html | 78 ++ src/webui/www/private/newfolder.html | 77 ++ src/webui/www/private/newrule.html | 70 ++ src/webui/www/private/rename_feed.html | 88 ++ src/webui/www/private/rename_rule.html | 81 ++ src/webui/www/private/scripts/client.js | 112 ++- src/webui/www/private/scripts/contextmenu.js | 129 ++- src/webui/www/private/scripts/dynamicTable.js | 495 ++++++++++- src/webui/www/private/views/preferences.html | 64 ++ .../www/private/views/preferencesToolbar.html | 5 + src/webui/www/private/views/rss.html | 823 ++++++++++++++++++ .../www/private/views/rssDownloader.html | 735 ++++++++++++++++ src/webui/www/webui.qrc | 10 + 18 files changed, 2902 insertions(+), 27 deletions(-) create mode 100644 src/webui/www/private/confirmfeeddeletion.html create mode 100644 src/webui/www/private/confirmruleclear.html create mode 100644 src/webui/www/private/confirmruledeletion.html create mode 100644 src/webui/www/private/newfeed.html create mode 100644 src/webui/www/private/newfolder.html create mode 100644 src/webui/www/private/newrule.html create mode 100644 src/webui/www/private/rename_feed.html create mode 100644 src/webui/www/private/rename_rule.html create mode 100644 src/webui/www/private/views/rss.html create mode 100644 src/webui/www/private/views/rssDownloader.html diff --git a/src/webui/www/private/confirmfeeddeletion.html b/src/webui/www/private/confirmfeeddeletion.html new file mode 100644 index 000000000..ad34122d4 --- /dev/null +++ b/src/webui/www/private/confirmfeeddeletion.html @@ -0,0 +1,55 @@ + + + + + + QBT_TR(Deletion confirmation)QBT_TR[CONTEXT=RSSWidget] + + + + + + + +
+

QBT_TR(Are you sure you want to delete the selected RSS feeds?)QBT_TR[CONTEXT=RSSWidget]

+
+ + +
+
+ + + diff --git a/src/webui/www/private/confirmruleclear.html b/src/webui/www/private/confirmruleclear.html new file mode 100644 index 000000000..75314288a --- /dev/null +++ b/src/webui/www/private/confirmruleclear.html @@ -0,0 +1,48 @@ + + + + + + QBT_TR(Clear downloaded episodes)QBT_TR[CONTEXT=AutomatedRssDownloader] + + + + + + + +
+

QBT_TR(Are you sure you want to clear the list of downloaded episodes for the selected rule?)QBT_TR[CONTEXT=AutomatedRssDownloader]

+
+ + +
+
+ + + diff --git a/src/webui/www/private/confirmruledeletion.html b/src/webui/www/private/confirmruledeletion.html new file mode 100644 index 000000000..2b0ed7090 --- /dev/null +++ b/src/webui/www/private/confirmruledeletion.html @@ -0,0 +1,56 @@ + + + + + + QBT_TR(Rule deletion confirmation)QBT_TR[CONTEXT=AutomatedRssDownloader] + + + + + + + +
+

QBT_TR(Are you sure you want to remove the selected download rules?)QBT_TR[CONTEXT=AutomatedRssDownloader]

+
+ + +
+
+ + + diff --git a/src/webui/www/private/css/Tabs.css b/src/webui/www/private/css/Tabs.css index c6c115d35..0de8965ee 100644 --- a/src/webui/www/private/css/Tabs.css +++ b/src/webui/www/private/css/Tabs.css @@ -34,7 +34,6 @@ Required by: } .tab-menu li { - display: block; float: left; margin: 0 0 5px 0; cursor: pointer; diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 80c76c841..7e97c4836 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -74,6 +74,7 @@
  • QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]
  • QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]
  • QBT_TR(Search Engine)QBT_TR[CONTEXT=MainWindow]QBT_TR(Search Engine)QBT_TR[CONTEXT=MainWindow]
  • +
  • QBT_TR(RSS)QBT_TR[CONTEXT=MainWindow]QBT_TR(RSS Reader)QBT_TR[CONTEXT=MainWindow]
  • QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]
  • @@ -112,6 +113,7 @@
    diff --git a/src/webui/www/private/newfeed.html b/src/webui/www/private/newfeed.html new file mode 100644 index 000000000..07ae77edf --- /dev/null +++ b/src/webui/www/private/newfeed.html @@ -0,0 +1,78 @@ + + + + + + QBT_TR(Please type a RSS feed URL)QBT_TR[CONTEXT=RSSWidget] + + + + + + + + +
    +

    QBT_TR(Feed URL:)QBT_TR[CONTEXT=RSSWidget]

    + +
    + +
    +
    + + + diff --git a/src/webui/www/private/newfolder.html b/src/webui/www/private/newfolder.html new file mode 100644 index 000000000..4f2cc121a --- /dev/null +++ b/src/webui/www/private/newfolder.html @@ -0,0 +1,77 @@ + + + + + + QBT_TR(Please choose a folder name)QBT_TR[CONTEXT=RSSWidget] + + + + + + + + +
    +

    QBT_TR(Folder name:)QBT_TR[CONTEXT=RSSWidget]

    + +
    + +
    +
    + + + diff --git a/src/webui/www/private/newrule.html b/src/webui/www/private/newrule.html new file mode 100644 index 000000000..cc46cb20c --- /dev/null +++ b/src/webui/www/private/newrule.html @@ -0,0 +1,70 @@ + + + + + + QBT_TR(New rule name)QBT_TR[CONTEXT=AutomatedRssDownloader] + + + + + + + + +
    +

    QBT_TR(Please type the name of the new download rule.)QBT_TR[CONTEXT=AutomatedRssDownloader]

    + +
    + +
    +
    + + + diff --git a/src/webui/www/private/rename_feed.html b/src/webui/www/private/rename_feed.html new file mode 100644 index 000000000..7153f9a0b --- /dev/null +++ b/src/webui/www/private/rename_feed.html @@ -0,0 +1,88 @@ + + + + + + QBT_TR(Please choose a new name for this RSS feed)QBT_TR[CONTEXT=RSSWidget] + + + + + + + + +
    +

    QBT_TR(New feed name:)QBT_TR[CONTEXT=RSSWidget]

    + +
    + +
    +
    + + + diff --git a/src/webui/www/private/rename_rule.html b/src/webui/www/private/rename_rule.html new file mode 100644 index 000000000..b36ca965c --- /dev/null +++ b/src/webui/www/private/rename_rule.html @@ -0,0 +1,81 @@ + + + + + + QBT_TR(Rule renaming)QBT_TR[CONTEXT=AutomatedRssDownloader] + + + + + + + + +
    +

    QBT_TR(Please type the new rule name)QBT_TR[CONTEXT=AutomatedRssDownloader]

    + +
    + +
    +
    + + + diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index 07f7f3491..d30967b45 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -34,6 +34,7 @@ let queueing_enabled = true; let serverSyncMainDataInterval = 1500; let customSyncMainDataInterval = null; let searchTabInitialized = false; +let rssTabInitialized = false; let syncRequestInProgress = false; @@ -171,8 +172,20 @@ window.addEvent('load', function() { $("searchTabColumn").addClass("invisible"); }; + const buildRssTab = function() { + new MochaUI.Column({ + id: 'rssTabColumn', + placement: 'main', + width: null + }); + + // start off hidden + $("rssTabColumn").addClass("invisible"); + }; + buildTransfersTab(); buildSearchTab(); + buildRssTab(); MochaUI.initializeTabs('mainWindowTabsList'); setCategoryFilter = function(hash) { @@ -275,12 +288,7 @@ window.addEvent('load', function() { // After showing/hiding the toolbar + status bar let showSearchEngine = LocalPreferences.get('show_search_engine') !== "false"; - if (!showSearchEngine) { - // uncheck menu option - $('showSearchEngineLink').firstChild.style.opacity = '0'; - // hide tabs - $('mainWindowTabs').addClass('invisible'); - } + let showRssReader = LocalPreferences.get('show_rss_reader') !== "false"; // After Show Top Toolbar MochaUI.Desktop.setDesktopSize(); @@ -860,22 +868,48 @@ window.addEvent('load', function() { $('showSearchEngineLink').addEvent('click', function(e) { showSearchEngine = !showSearchEngine; LocalPreferences.set('show_search_engine', showSearchEngine.toString()); + updateTabDisplay(); + }); + + $('showRssReaderLink').addEvent('click', function(e) { + showRssReader = !showRssReader; + LocalPreferences.set('show_rss_reader', showRssReader.toString()); + updateTabDisplay(); + }); + + const updateTabDisplay = function() { + if (showRssReader) { + $('showRssReaderLink').firstChild.style.opacity = '1'; + $('mainWindowTabs').removeClass('invisible'); + $('rssTabLink').removeClass('invisible'); + if (!MochaUI.Panels.instances.RssPanel) + addRssPanel(); + } + else { + $('showRssReaderLink').firstChild.style.opacity = '0'; + $('rssTabLink').addClass('invisible'); + if ($('rssTabLink').hasClass('selected')) + $("transfersTabLink").click(); + } + if (showSearchEngine) { $('showSearchEngineLink').firstChild.style.opacity = '1'; $('mainWindowTabs').removeClass('invisible'); - - addMainWindowTabsEventListener(); + $('searchTabLink').removeClass('invisible'); if (!MochaUI.Panels.instances.SearchPanel) addSearchPanel(); } else { $('showSearchEngineLink').firstChild.style.opacity = '0'; - $('mainWindowTabs').addClass('invisible'); - $("transfersTabLink").click(); - - removeMainWindowTabsEventListener(); + $('searchTabLink').addClass('invisible'); + if ($('searchTabLink').hasClass('selected')) + $("transfersTabLink").click(); } - }); + + // display no tabs + if (!showRssReader && !showSearchEngine) + $('mainWindowTabs').addClass('invisible'); + }; $('StatisticsLink').addEvent('click', StatisticsLinkFN); @@ -890,6 +924,7 @@ window.addEvent('load', function() { syncData(100); hideSearchTab(); + hideRssTab(); }; const hideTransfersTab = function() { @@ -908,6 +943,7 @@ window.addEvent('load', function() { $("searchTabColumn").removeClass("invisible"); customSyncMainDataInterval = 30000; hideTransfersTab(); + hideRssTab(); }; const hideSearchTab = function() { @@ -915,14 +951,25 @@ window.addEvent('load', function() { MochaUI.Desktop.resizePanels(); }; - const addMainWindowTabsEventListener = function() { - $('transfersTabLink').addEvent('click', showTransfersTab); - $('searchTabLink').addEvent('click', showSearchTab); + const showRssTab = function() { + if (!rssTabInitialized) { + window.qBittorrent.Rss.init(); + rssTabInitialized = true; + } + else { + window.qBittorrent.Rss.load(); + } + + $("rssTabColumn").removeClass("invisible"); + customSyncMainDataInterval = 30000; + hideTransfersTab(); + hideSearchTab(); }; - const removeMainWindowTabsEventListener = function() { - $('transfersTabLink').removeEvent('click', showTransfersTab); - $('searchTabLink').removeEvent('click', showSearchTab); + const hideRssTab = function() { + $("rssTabColumn").addClass("invisible"); + window.qBittorrent.Rss.unload(); + MochaUI.Desktop.resizePanels(); }; const addSearchPanel = function() { @@ -944,6 +991,25 @@ window.addEvent('load', function() { }); }; + const addRssPanel = function() { + new MochaUI.Panel({ + id: 'RssPanel', + title: 'Rss', + header: false, + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + }, + loadMethod: 'xhr', + contentURL: 'views/rss.html', + content: '', + column: 'rssTabColumn', + height: null + }); + }; + new MochaUI.Panel({ id: 'transferList', title: 'Panel', @@ -1081,10 +1147,10 @@ window.addEvent('load', function() { } }); - if (showSearchEngine) { - addMainWindowTabsEventListener(); - addSearchPanel(); - } + $('transfersTabLink').addEvent('click', showTransfersTab); + $('searchTabLink').addEvent('click', showSearchTab); + $('rssTabLink').addEvent('click', showRssTab); + updateTabDisplay(); }); function registerMagnetHandler() { diff --git a/src/webui/www/private/scripts/contextmenu.js b/src/webui/www/private/scripts/contextmenu.js index 5179ef470..b4fdec732 100644 --- a/src/webui/www/private/scripts/contextmenu.js +++ b/src/webui/www/private/scripts/contextmenu.js @@ -39,7 +39,10 @@ window.qBittorrent.ContextMenu = (function() { TorrentsTableContextMenu: TorrentsTableContextMenu, CategoriesFilterContextMenu: CategoriesFilterContextMenu, TagsFilterContextMenu: TagsFilterContextMenu, - SearchPluginsTableContextMenu: SearchPluginsTableContextMenu + SearchPluginsTableContextMenu: SearchPluginsTableContextMenu, + RssFeedContextMenu: RssFeedContextMenu, + RssArticleContextMenu: RssArticleContextMenu, + RssDownloaderRuleContextMenu: RssDownloaderRuleContextMenu }; }; @@ -538,5 +541,129 @@ window.qBittorrent.ContextMenu = (function() { } }); + const RssFeedContextMenu = new Class({ + Extends: ContextMenu, + updateMenuItems: function() { + let selectedRows = window.qBittorrent.Rss.rssFeedTable.selectedRowsIds(); + this.menu.getElement('a[href$=newSubscription]').parentNode.addClass('separator'); + switch (selectedRows.length) { + case 0: + // remove seperator on top of newSubscription entry to avoid double line + this.menu.getElement('a[href$=newSubscription]').parentNode.removeClass('separator'); + // menu when nothing selected + this.hideItem('update'); + this.hideItem('markRead'); + this.hideItem('rename'); + this.hideItem('delete'); + this.showItem('newSubscription'); + this.showItem('newFolder'); + this.showItem('updateAll'); + this.hideItem('copyFeedURL'); + break; + case 1: + if (selectedRows[0] === 0) { + // menu when "unread" feed selected + this.showItem('update'); + this.showItem('markRead'); + this.hideItem('rename'); + this.hideItem('delete'); + this.showItem('newSubscription'); + this.hideItem('newFolder'); + this.hideItem('updateAll'); + this.hideItem('copyFeedURL'); + } + else if (window.qBittorrent.Rss.rssFeedTable.rows[selectedRows[0]].full_data.dataUid === '') { + // menu when single folder selected + this.showItem('update'); + this.showItem('markRead'); + this.showItem('rename'); + this.showItem('delete'); + this.showItem('newSubscription'); + this.showItem('newFolder'); + this.hideItem('updateAll'); + this.hideItem('copyFeedURL'); + } + else { + // menu when single feed selected + this.showItem('update'); + this.showItem('markRead'); + this.showItem('rename'); + this.showItem('delete'); + this.showItem('newSubscription'); + this.hideItem('newFolder'); + this.hideItem('updateAll'); + this.showItem('copyFeedURL'); + } + break; + default: + // menu when multiple items selected + this.showItem('update'); + this.showItem('markRead'); + this.hideItem('rename'); + this.showItem('delete'); + this.hideItem('newSubscription'); + this.hideItem('newFolder'); + this.hideItem('updateAll'); + this.showItem('copyFeedURL'); + } + } + }); + + const RssArticleContextMenu = new Class({ + Extends: ContextMenu + }); + + const RssDownloaderRuleContextMenu = new Class({ + Extends: ContextMenu, + adjustMenuPosition: function(e) { + this.updateMenuItems(); + + // draw the menu off-screen to know the menu dimensions + this.menu.setStyles({ + left: '-999em', + top: '-999em' + }); + // position the menu + let xPosMenu = e.page.x + this.options.offsets.x - $('rssdownloaderpage').offsetLeft; + let yPosMenu = e.page.y + this.options.offsets.y - $('rssdownloaderpage').offsetTop; + if ((xPosMenu + this.menu.offsetWidth) > document.documentElement.clientWidth) + xPosMenu -= this.menu.offsetWidth; + if ((yPosMenu + this.menu.offsetHeight) > document.documentElement.clientHeight) + yPosMenu = document.documentElement.clientHeight - this.menu.offsetHeight; + xPosMenu = Math.max(xPosMenu, 0); + yPosMenu = Math.max(yPosMenu, 0); + + this.menu.setStyles({ + left: xPosMenu, + top: yPosMenu, + position: 'absolute', + 'z-index': '2000' + }); + }, + updateMenuItems: function() { + let selectedRows = window.qBittorrent.RssDownloader.rssDownloaderRulesTable.selectedRowsIds(); + this.showItem('addRule'); + switch (selectedRows.length) { + case 0: + // menu when nothing selected + this.hideItem('deleteRule'); + this.hideItem('renameRule'); + this.hideItem('clearDownloadedEpisodes'); + break; + case 1: + // menu when single item selected + this.showItem('deleteRule'); + this.showItem('renameRule'); + this.showItem('clearDownloadedEpisodes'); + break; + default: + // menu when multiple items selected + this.showItem('deleteRule'); + this.hideItem('renameRule'); + this.showItem('clearDownloadedEpisodes'); + } + } + }); + return exports(); })(); diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 7c1f25b53..6b60a8e58 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -45,7 +45,12 @@ window.qBittorrent.DynamicTable = (function() { SearchResultsTable: SearchResultsTable, SearchPluginsTable: SearchPluginsTable, TorrentTrackersTable: TorrentTrackersTable, - TorrentFilesTable: TorrentFilesTable + TorrentFilesTable: TorrentFilesTable, + RssFeedTable: RssFeedTable, + RssArticleTable: RssArticleTable, + RssDownloaderRulesTable: RssDownloaderRulesTable, + RssDownloaderFeedSelectionTable: RssDownloaderFeedSelectionTable, + RssDownloaderArticlesTable: RssDownloaderArticlesTable }; }; @@ -1867,7 +1872,7 @@ window.qBittorrent.DynamicTable = (function() { if (tr.hasClass("invisible")) return; - if (addClass){ + if (addClass) { tr.addClass("alt"); tr.removeClass("nonAlt"); } @@ -2000,6 +2005,492 @@ window.qBittorrent.DynamicTable = (function() { } }); + const RssFeedTable = new Class({ + Extends: DynamicTable, + initColumns: function() { + this.newColumn('state_icon', '', '', 30, true); + this.newColumn('name', '', 'QBT_TR(RSS feeds)QBT_TR[CONTEXT=FeedListWidget]', -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 (data.hasOwnProperty(this.columns[i].dataProperties[0])) + this.columns[i].updateTd(tds[i], row); + } + row['data'] = {}; + tds[0].style.overflow = 'visible'; + let indentaion = row.full_data.indentaion; + tds[0].style.paddingLeft = (indentaion * 32 + 4) + 'px'; + tds[1].style.paddingLeft = (indentaion * 32 + 4) + 'px'; + }, + updateIcons: function() { + // state_icon + this.rows.each(row => { + let img_path; + switch (row.full_data.status) { + case 'default': + img_path = 'icons/application-rss+xml.svg'; + break; + case 'hasError': + img_path = 'icons/unavailable.svg'; + break; + case 'isLoading': + img_path = 'images/spinner.gif'; + break; + case 'unread': + img_path = 'icons/mail-folder-inbox.svg'; + break; + case 'isFolder': + img_path = 'icons/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) { + if (this.getRowValue(row1) < this.getRowValue(row2)) + return -1; + else if (this.getRowValue(row1) > this.getRowValue(row2)) + return 1; + else return 0; + }; + 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', '', 'QBT_TR(Torrents: (double-click to download))QBT_TR[CONTEXT=RSSWidget]', -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 (data.hasOwnProperty(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) { + if (this.getRowValue(row1) < this.getRowValue(row2)) + return -1; + else if (this.getRowValue(row1) > this.getRowValue(row2)) + return 1; + else return 0; + }; + 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) { + if (this.getRowValue(row1) < this.getRowValue(row2)) + return -1; + else if (this.getRowValue(row1) > this.getRowValue(row2)) + return 1; + else return 0; + }; + 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) { + if (this.getRowValue(row1) < this.getRowValue(row2)) + return -1; + else if (this.getRowValue(row1) > this.getRowValue(row2)) + return 1; + else return 0; + }; + 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) { + if (this.getRowValue(row1) < this.getRowValue(row2)) + return -1; + else if (this.getRowValue(row1) > this.getRowValue(row2)) + return 1; + else return 0; + }; + 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 (data.hasOwnProperty(this.columns[i].dataProperties[0])) + this.columns[i].updateTd(tds[i], row); + } + row['data'] = {}; + } + }); + + return exports(); })(); diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 12ee09257..409d0fe27 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -595,6 +595,54 @@ + +