diff --git a/.github/workflows/webui_ci.yaml b/.github/workflows/webui_ci.yaml new file mode 100644 index 000000000..aa836523d --- /dev/null +++ b/.github/workflows/webui_ci.yaml @@ -0,0 +1,31 @@ +name: WebUI CI + +on: [pull_request, push] + +jobs: + check_webui: + name: Check WebUI + runs-on: ubuntu-20.04 + defaults: + run: + working-directory: src/webui/www + + steps: + - name: checkout repository + uses: actions/checkout@v2 + + - name: setup nodejs + uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: install tools + run: npm install + + - name: lint code + run: npm run lint + + - name: format code + run: | + npm run format + git diff --exit-code diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index 66db65dc0..59b686161 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -47,6 +47,18 @@ namespace return (left < right) ? -1 : 1; } + int customCompare(const QDateTime &left, const QDateTime &right) + { + const bool isLeftValid = left.isValid(); + const bool isRightValid = right.isValid(); + + if (isLeftValid && isRightValid) + return threeWayCompare(left, right); + if (!isLeftValid && !isRightValid) + return 0; + return isLeftValid ? -1 : 1; + } + template int customCompare(const T left, const T right) { @@ -158,7 +170,7 @@ int TransferListSortModel::compare(const QModelIndex &left, const QModelIndex &r case TransferListModel::TR_ADD_DATE: case TransferListModel::TR_SEED_DATE: case TransferListModel::TR_SEEN_COMPLETE_DATE: - return threeWayCompare(leftValue.toDateTime(), rightValue.toDateTime()); + return customCompare(leftValue.toDateTime(), rightValue.toDateTime()); case TransferListModel::TR_DLLIMIT: case TransferListModel::TR_DLSPEED: diff --git a/src/webui/www/.eslintrc.json b/src/webui/www/.eslintrc.json new file mode 100644 index 000000000..c2cb0a275 --- /dev/null +++ b/src/webui/www/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "env": { + "browser": true, + "es2020": true + }, + "extends": "eslint:recommended", + "plugins": [ + "html" + ], + "rules": { + "no-undef": "off", + "no-unused-vars": "off" + } +} diff --git a/src/webui/www/.jsbeautifyrc b/src/webui/www/.jsbeautifyrc index 63cfb7984..8aa593440 100644 --- a/src/webui/www/.jsbeautifyrc +++ b/src/webui/www/.jsbeautifyrc @@ -11,7 +11,7 @@ "space_in_empty_paren": false, "jslint_happy": false, "space_after_anon_function": false, - "brace_style": "end-expand", + "brace_style": "end-expand,preserve-inline", "unindent_chained_methods": false, "break_chained_methods": false, "keep_array_indentation": false, diff --git a/src/webui/www/package.json b/src/webui/www/package.json new file mode 100644 index 000000000..e94124882 --- /dev/null +++ b/src/webui/www/package.json @@ -0,0 +1,17 @@ +{ + "name": "webui", + "description": "qBittorrent WebUI", + "repository": { + "type": "git", + "url": "https://github.com/qbittorrent/qBittorrent.git" + }, + "scripts": { + "format": "js-beautify private/*.html private/scripts/*.js private/views/*.html public/*.html public/scripts/*.js", + "lint": "eslint private/*.html private/scripts/*.js private/views/*.html public/*.html public/scripts/*.js" + }, + "devDependencies": { + "eslint": "*", + "eslint-plugin-html": "*", + "js-beautify": "*" + } +} diff --git a/src/webui/www/private/addpeers.html b/src/webui/www/private/addpeers.html index fb19ae181..c2075f763 100644 --- a/src/webui/www/private/addpeers.html +++ b/src/webui/www/private/addpeers.html @@ -1,69 +1,71 @@ - - - QBT_TR(Add Peers)QBT_TR[CONTEXT=PeersAdditionDialog] - - - - + + - + }); + + - -
-

QBT_TR(List of peers to add (one IP per line):)QBT_TR[CONTEXT=PeersAdditionDialog]

- -
- - -
+ +
+

QBT_TR(List of peers to add (one IP per line):)QBT_TR[CONTEXT=PeersAdditionDialog]

+ +
+ +
- +
+ + diff --git a/src/webui/www/private/addtrackers.html b/src/webui/www/private/addtrackers.html index e78a24091..7321d5a75 100644 --- a/src/webui/www/private/addtrackers.html +++ b/src/webui/www/private/addtrackers.html @@ -47,10 +47,10 @@
-
+

QBT_TR(List of trackers to add (one per line):)QBT_TR[CONTEXT=TrackersAdditionDialog]

-
+
diff --git a/src/webui/www/private/confirmdeletion.html b/src/webui/www/private/confirmdeletion.html index 60aa45f16..6ee9050de 100644 --- a/src/webui/www/private/confirmdeletion.html +++ b/src/webui/www/private/confirmdeletion.html @@ -42,10 +42,10 @@ -
+

  QBT_TR(Are you sure you want to delete the selected torrents from the transfer list?)QBT_TR[CONTEXT=HttpServer]

-     

+     

    
diff --git a/src/webui/www/private/download.html b/src/webui/www/private/download.html index b7a4a50a5..91cbba935 100644 --- a/src/webui/www/private/download.html +++ b/src/webui/www/private/download.html @@ -16,7 +16,7 @@
-
+

QBT_TR(Download Torrents from their URLs or Magnet links)QBT_TR[CONTEXT=HttpServer]

QBT_TR(Only one link per line)QBT_TR[CONTEXT=HttpServer]

diff --git a/src/webui/www/private/edittracker.html b/src/webui/www/private/edittracker.html index c4ffc1ac9..56420fd16 100644 --- a/src/webui/www/private/edittracker.html +++ b/src/webui/www/private/edittracker.html @@ -58,12 +58,12 @@
-
+

QBT_TR(Tracker URL:)QBT_TR[CONTEXT=TrackerListWidget]

-
+
diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 948914d75..aa01441e5 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -46,69 +46,69 @@
  • QBT_TR(File)QBT_TR[CONTEXT=MainWindow]
  • QBT_TR(Edit)QBT_TR[CONTEXT=MainWindow]
  • QBT_TR(View)QBT_TR[CONTEXT=MainWindow]
  • QBT_TR(Tools)QBT_TR[CONTEXT=MainWindow]
  • QBT_TR(Help)QBT_TR[CONTEXT=MainWindow]
  •    - QBT_TR(Add Torrent Link...)QBT_TR[CONTEXT=MainWindow] - QBT_TR(Add Torrent File...)QBT_TR[CONTEXT=MainWindow] - QBT_TR(Delete)QBT_TR[CONTEXT=TransferListWidget] - QBT_TR(Resume)QBT_TR[CONTEXT=TransferListWidget] - QBT_TR(Pause)QBT_TR[CONTEXT=TransferListWidget] + QBT_TR(Add Torrent Link...)QBT_TR[CONTEXT=MainWindow] + QBT_TR(Add Torrent File...)QBT_TR[CONTEXT=MainWindow] + QBT_TR(Delete)QBT_TR[CONTEXT=TransferListWidget] + QBT_TR(Resume)QBT_TR[CONTEXT=TransferListWidget] + QBT_TR(Pause)QBT_TR[CONTEXT=TransferListWidget] - QBT_TR(Top of Queue)QBT_TR[CONTEXT=MainWindow] - QBT_TR(Move Up Queue)QBT_TR[CONTEXT=MainWindow] - QBT_TR(Move Down Queue)QBT_TR[CONTEXT=MainWindow] - QBT_TR(Bottom of Queue)QBT_TR[CONTEXT=MainWindow] + QBT_TR(Top of Queue)QBT_TR[CONTEXT=MainWindow] + QBT_TR(Move Up Queue)QBT_TR[CONTEXT=MainWindow] + QBT_TR(Move Down Queue)QBT_TR[CONTEXT=MainWindow] + QBT_TR(Bottom of Queue)QBT_TR[CONTEXT=MainWindow] - QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog] + QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog]
      -
    • QBT_TR(Rename...)QBT_TR[CONTEXT=PropertiesWidget] QBT_TR(Rename...)QBT_TR[CONTEXT=PropertiesWidget]
    • +
    • QBT_TR(Rename...)QBT_TR[CONTEXT=PropertiesWidget] QBT_TR(Rename...)QBT_TR[CONTEXT=PropertiesWidget]
    • QBT_TR(Priority)QBT_TR[CONTEXT=PropertiesWidget]
        @@ -227,7 +227,7 @@ QBT_TR(Connection status)QBT_TR[CONTEXT=MainWindow] - QBT_TR(Alternative speed limits)QBT_TR[CONTEXT=MainWindow] + QBT_TR(Alternative speed limits)QBT_TR[CONTEXT=MainWindow] QBT_TR(Download speed icon)QBT_TR[CONTEXT=MainWindow] diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index 4600f40fa..5ee928af4 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -464,7 +464,7 @@ window.addEvent('load', function() { const torrentsCount = torrentsTable.getRowIds().length; let untagged = 0; for (const key in torrentsTable.rows) { - if (torrentsTable.rows.hasOwnProperty(key) && torrentsTable.rows[key]['full_data'].tags.length === 0) + if (Object.prototype.hasOwnProperty.call(torrentsTable.rows, key) && (torrentsTable.rows[key]['full_data'].tags.length === 0)) untagged += 1; } tagFilterList.appendChild(createLink(TAGS_ALL, 'QBT_TR(All)QBT_TR[CONTEXT=TagFilterModel]', torrentsCount)); @@ -519,7 +519,7 @@ window.addEvent('load', function() { trackerFilterList.appendChild(createLink(TRACKERS_ALL, 'QBT_TR(All (%1))QBT_TR[CONTEXT=TrackerFiltersList]', torrentsCount)); let trackerlessTorrentsCount = 0; for (const key in torrentsTable.rows) { - if (torrentsTable.rows.hasOwnProperty(key) && (torrentsTable.rows[key]['full_data'].trackers_count === 0)) + if (Object.prototype.hasOwnProperty.call(torrentsTable.rows, key) && (torrentsTable.rows[key]['full_data'].trackers_count === 0)) trackerlessTorrentsCount += 1; } trackerFilterList.appendChild(createLink(TRACKERS_TRACKERLESS, 'QBT_TR(Trackerless (%1))QBT_TR[CONTEXT=TrackerFiltersList]', trackerlessTorrentsCount)); @@ -701,7 +701,7 @@ window.addEvent('load', function() { }; const syncData = function(delay) { - if (!syncRequestInProgress){ + if (!syncRequestInProgress) { clearTimeout(syncMainDataTimer); syncMainDataTimer = syncMainData.delay(delay); } @@ -744,21 +744,18 @@ window.addEvent('load', function() { } switch (serverState.connection_status) { - case 'connected': { + case 'connected': $('connectionStatus').src = 'icons/connected.svg'; $('connectionStatus').alt = 'QBT_TR(Connection status: Connected)QBT_TR[CONTEXT=MainWindow]'; - } - break; - case 'firewalled': { + break; + case 'firewalled': $('connectionStatus').src = 'icons/firewalled.svg'; $('connectionStatus').alt = 'QBT_TR(Connection status: Firewalled)QBT_TR[CONTEXT=MainWindow]'; - } - break; - default: { + break; + default: $('connectionStatus').src = 'icons/disconnected.svg'; $('connectionStatus').alt = 'QBT_TR(Connection status: Disconnected)QBT_TR[CONTEXT=MainWindow]'; - } - break; + break; } if (queueing_enabled != serverState.queueing) { diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index dc7778888..e2d8c90ab 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -778,7 +778,7 @@ window.qBittorrent.DynamicTable = (function() { const tds = tr.getElements('td'); for (let i = 0; i < this.columns.length; ++i) { - if (data.hasOwnProperty(this.columns[i].dataProperties[0])) + if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0])) this.columns[i].updateTd(tds[i], row); } row['data'] = {}; @@ -1007,7 +1007,7 @@ window.qBittorrent.DynamicTable = (function() { 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'}); + 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; @@ -1264,11 +1264,11 @@ window.qBittorrent.DynamicTable = (function() { if (!isNaN(categoryHashInt)) { switch (categoryHashInt) { case CATEGORIES_ALL: - break; // do nothing + break; // do nothing case CATEGORIES_UNCATEGORIZED: if (row['full_data'].category.length !== 0) return false; - break; // do nothing + break; // do nothing default: if (categoryHashInt !== genHash(row['full_data'].category)) return false; @@ -1280,20 +1280,22 @@ window.qBittorrent.DynamicTable = (function() { if (isNumber) { switch (tagHashInt) { case TAGS_ALL: - break; // do nothing + break; // do nothing case TAGS_UNTAGGED: if (row['full_data'].tags.length !== 0) return false; - break; // do nothing + break; // do nothing - default: + default: { let rowTags = row['full_data'].tags.split(', '); rowTags = rowTags.map(function(tag) { return genHash(tag); }); if (!rowTags.contains(tagHashInt)) return false; + break; + } } } @@ -1305,11 +1307,12 @@ window.qBittorrent.DynamicTable = (function() { if (row['full_data'].trackers_count !== 0) return false; break; - default: + default: { const tracker = trackerList.get(trackerHashInt); if (tracker && !tracker.torrents.includes(row['full_data'].rowId)) return false; break; + } } if ((filterTerms !== undefined) && (filterTerms !== null) @@ -1588,8 +1591,8 @@ window.qBittorrent.DynamicTable = (function() { 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 (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; @@ -1909,7 +1912,7 @@ window.qBittorrent.DynamicTable = (function() { _filterNodes: function(node, filterTerms, filteredRows) { if (node.isFolder) { - const childAdded = node.children.reduce(function (acc, child) { + 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); @@ -2058,7 +2061,7 @@ window.qBittorrent.DynamicTable = (function() { const tds = tr.getElements('td'); for (let i = 0; i < this.columns.length; ++i) { - if (data.hasOwnProperty(this.columns[i].dataProperties[0])) + if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0])) this.columns[i].updateTd(tds[i], row); } row['data'] = {}; @@ -2203,7 +2206,7 @@ window.qBittorrent.DynamicTable = (function() { const tds = tr.getElements('td'); for (let i = 0; i < this.columns.length; ++i) { - if (data.hasOwnProperty(this.columns[i].dataProperties[0])) + if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0])) this.columns[i].updateTd(tds[i], row); } row['data'] = {}; @@ -2484,14 +2487,13 @@ window.qBittorrent.DynamicTable = (function() { const tds = tr.getElements('td'); for (let i = 0; i < this.columns.length; ++i) { - if (data.hasOwnProperty(this.columns[i].dataProperties[0])) + if (Object.prototype.hasOwnProperty.call(data, this.columns[i].dataProperties[0])) this.columns[i].updateTd(tds[i], row); } row['data'] = {}; } }); - return exports(); })(); diff --git a/src/webui/www/private/scripts/misc.js b/src/webui/www/private/scripts/misc.js index f9fd8f9f8..1504f6663 100644 --- a/src/webui/www/private/scripts/misc.js +++ b/src/webui/www/private/scripts/misc.js @@ -49,8 +49,8 @@ window.qBittorrent.Misc = (function() { }; /* - * JS counterpart of the function in src/misc.cpp - */ + * JS counterpart of the function in src/misc.cpp + */ const friendlyUnit = function(value, isSpeed) { const units = [ "QBT_TR(B)QBT_TR[CONTEXT=misc]", @@ -93,8 +93,8 @@ window.qBittorrent.Misc = (function() { } /* - * JS counterpart of the function in src/misc.cpp - */ + * JS counterpart of the function in src/misc.cpp + */ const friendlyDuration = function(seconds, maxCap = -1) { if (seconds < 0 || ((seconds >= maxCap) && (maxCap >= 0))) return "∞"; @@ -132,8 +132,8 @@ window.qBittorrent.Misc = (function() { } /* - * From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString - */ + * From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString + */ if (!Date.prototype.toISOString) { (function() { @@ -159,10 +159,10 @@ window.qBittorrent.Misc = (function() { } /* - * JS counterpart of the function in src/misc.cpp - */ + * JS counterpart of the function in src/misc.cpp + */ const parseHtmlLinks = function(text) { - const exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; + const exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/ig; return text.replace(exp, "$1"); } diff --git a/src/webui/www/private/scripts/prop-files.js b/src/webui/www/private/scripts/prop-files.js index 1e1ac0af5..2d7e91f0b 100644 --- a/src/webui/www/private/scripts/prop-files.js +++ b/src/webui/www/private/scripts/prop-files.js @@ -444,8 +444,8 @@ window.qBittorrent.PropFiles = (function() { if (folderNode === null) { folderNode = new window.qBittorrent.FileTree.FolderNode(); folderNode.path = (parent.path === "") - ? folderName - : [parent.path, folderName].join(window.qBittorrent.Filesystem.PathSeparator); + ? folderName + : [parent.path, folderName].join(window.qBittorrent.Filesystem.PathSeparator); folderNode.name = folderName; folderNode.rowId = rowId; folderNode.root = parent; @@ -542,7 +542,7 @@ window.qBittorrent.PropFiles = (function() { title: "QBT_TR(Renaming)QBT_TR[CONTEXT=TorrentContentTreeView]", loadMethod: 'iframe', contentURL: 'rename_file.html?hash=' + hash + '&isFolder=' + node.isFolder - + '&path=' + encodeURIComponent(path), + + '&path=' + encodeURIComponent(path), scrollbars: false, resizable: false, maximizable: false, diff --git a/src/webui/www/private/scripts/speedslider.js b/src/webui/www/private/scripts/speedslider.js index 32797a83d..da71b62c2 100644 --- a/src/webui/www/private/scripts/speedslider.js +++ b/src/webui/www/private/scripts/speedslider.js @@ -84,7 +84,7 @@ MochaUI.extend({ else { new Request.JSON({ url: 'api/v2/torrents/uploadLimit', - noCache : true, + noCache: true, method: 'post', data: { hashes: hashes.join('|') @@ -92,7 +92,7 @@ MochaUI.extend({ onSuccess: function(data) { if (data) { let up_limit = data[hashes[0]]; - for(const key in data) + for (const key in data) if (up_limit != data[key]) { up_limit = 0; break; @@ -186,7 +186,7 @@ MochaUI.extend({ else { new Request.JSON({ url: 'api/v2/torrents/downloadLimit', - noCache : true, + noCache: true, method: 'post', data: { hashes: hashes.join('|') @@ -194,7 +194,7 @@ MochaUI.extend({ onSuccess: function(data) { if (data) { let dl_limit = data[hashes[0]]; - for(const key in data) + for (const key in data) if (dl_limit != data[key]) { dl_limit = 0; break; diff --git a/src/webui/www/private/views/about.html b/src/webui/www/private/views/about.html index 06572b5ea..fa9cea9a0 100644 --- a/src/webui/www/private/views/about.html +++ b/src/webui/www/private/views/about.html @@ -83,8 +83,8 @@
        - +
        @@ -803,7 +803,8 @@
        - + +
        @@ -1199,7 +1200,7 @@ - + (?) @@ -1207,31 +1208,31 @@ - - + +   % - - + +   % - - + +   s - +
    diff --git a/src/webui/www/private/views/propertiesToolbar.html b/src/webui/www/private/views/propertiesToolbar.html index 789dc320e..aa0c924ad 100644 --- a/src/webui/www/private/views/propertiesToolbar.html +++ b/src/webui/www/private/views/propertiesToolbar.html @@ -1,7 +1,7 @@
    - +