diff --git a/private/css/style.css b/private/css/style.css index d3ae823..b3f5218 100644 --- a/private/css/style.css +++ b/private/css/style.css @@ -233,8 +233,23 @@ dd { /* Forms */ fieldset { - border: 1px solid var(--color-border-default); - border-radius: 5px; + border: solid 1px var(--darkmode-line-color); + border-radius: 8px; + padding: 4px 4px 4px 10px; +} + +fieldset legend { + font-weight: bold; + margin-left: 8px; + padding: 4px; +} + +fieldset label { + padding: 2px; +} + +fieldset+div.formRow { + margin-top: 10px; } /* Code */ @@ -613,30 +628,6 @@ label.partial { background-position: 0 -26px; } -fieldset { - border-color: var(--darkmode-line-color); -} - -fieldset.settings { - border: solid 1px var(--darkmode-line-color); - border-radius: 8px; - padding: 4px 4px 4px 10px; -} - -fieldset.settings legend { - font-weight: bold; - margin-left: 8px; - padding: 4px; -} - -fieldset.settings label { - padding: 2px; -} - -fieldset.settings+div.formRow { - margin-top: 10px; -} - div.formRow { clear: left; display: block; diff --git a/private/scripts/client.js b/private/scripts/client.js index c5ede47..4e67d00 100644 --- a/private/scripts/client.js +++ b/private/scripts/client.js @@ -1647,7 +1647,7 @@ window.addEventListener("DOMContentLoaded", () => { return; if (event.target.isContentEditable) return; - deleteFN(); + deleteSelectedTorrentsFN(); event.preventDefault(); }, "shift+delete": (event) => { @@ -1655,7 +1655,7 @@ window.addEventListener("DOMContentLoaded", () => { return; if (event.target.isContentEditable) return; - deleteFN(true); + deleteSelectedTorrentsFN(true); event.preventDefault(); } } diff --git a/private/scripts/mocha-init.js b/private/scripts/mocha-init.js index 74c001c..3fcd7f7 100644 --- a/private/scripts/mocha-init.js +++ b/private/scripts/mocha-init.js @@ -36,1414 +36,1239 @@ it in the onContentLoaded function of the new window. ----------------------------------------------------------------- */ -"use strict"; - -window.qBittorrent ??= {}; -window.qBittorrent.Dialog ??= (() => { - const exports = () => { - return { - baseModalOptions: baseModalOptions - }; - }; - - const deepFreeze = (obj) => { - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#examples - // accounts for circular refs - const frozen = new WeakSet(); - const deepFreezeSafe = (obj) => { - if (frozen.has(obj)) - return; - - frozen.add(obj); - - const keys = Reflect.ownKeys(obj); - for (const key of keys) { - const value = obj[key]; - if ((value && (typeof value === "object")) || (typeof value === "function")) - deepFreezeSafe(value); - } - Object.freeze(obj); - }; - - deepFreezeSafe(obj); - }; - - const baseModalOptions = Object.assign(Object.create(null), { - addClass: "modalDialog", - collapsible: false, - cornerRadius: 5, - draggable: true, - footerHeight: 20, - icon: "images/qbittorrent-tray.svg", - loadMethod: "xhr", - maximizable: false, - method: "post", - minimizable: false, - padding: { - top: 15, - right: 10, - bottom: 15, - left: 5 - }, - resizable: true, - width: 480, - onCloseComplete: function() { - // make sure overlay is properly hidden upon modal closing - document.getElementById("modalOverlay").style.display = "none"; - } - }); - - deepFreeze(baseModalOptions); - - return exports(); -})(); -Object.freeze(window.qBittorrent.Dialog); - -const LocalPreferences = new window.qBittorrent.LocalPreferences.LocalPreferencesClass(); - -let saveWindowSize = function() {}; -let loadWindowWidth = function() {}; -let loadWindowHeight = function() {}; -let showDownloadPage = function() {}; -let globalUploadLimitFN = function() {}; -let uploadLimitFN = function() {}; -let shareRatioFN = function() {}; -let toggleSequentialDownloadFN = function() {}; -let toggleFirstLastPiecePrioFN = function() {}; -let setSuperSeedingFN = function() {}; -let setForceStartFN = function() {}; -let globalDownloadLimitFN = function() {}; -let StatisticsLinkFN = function() {}; -let downloadLimitFN = function() {}; -let deleteFN = function() {}; -let stopFN = function() {}; -let startFN = function() {}; -let autoTorrentManagementFN = function() {}; -let recheckFN = function() {}; -let reannounceFN = function() {}; -let setLocationFN = function() {}; -let renameFN = function() {}; -let renameFilesFN = function() {}; -let torrentNewCategoryFN = function() {}; -let torrentSetCategoryFN = function() {}; -let createCategoryFN = function() {}; -let createSubcategoryFN = function() {}; -let editCategoryFN = function() {}; -let removeCategoryFN = function() {}; -let deleteUnusedCategoriesFN = function() {}; -let startTorrentsByCategoryFN = function() {}; -let stopTorrentsByCategoryFN = function() {}; -let deleteTorrentsByCategoryFN = function() {}; -let torrentAddTagsFN = function() {}; -let torrentSetTagsFN = function() {}; -let torrentRemoveAllTagsFN = function() {}; -let createTagFN = function() {}; -let removeTagFN = function() {}; -let deleteUnusedTagsFN = function() {}; -let startTorrentsByTagFN = function() {}; -let stopTorrentsByTagFN = function() {}; -let deleteTorrentsByTagFN = function() {}; -let startTorrentsByTrackerFN = function() {}; -let stopTorrentsByTrackerFN = function() {}; -let deleteTorrentsByTrackerFN = function() {}; -let deleteTrackerFN = function() {}; -let copyNameFN = function() {}; -let copyInfohashFN = function(policy) {}; -let copyMagnetLinkFN = function() {}; -let copyIdFN = function() {}; -let copyCommentFN = function() {}; -let setQueuePositionFN = function() {}; -let exportTorrentFN = function() {}; - -const initializeWindows = function() { - saveWindowSize = function(windowId) { - const size = $(windowId).getSize(); - LocalPreferences.set("window_" + windowId + "_width", size.x); - LocalPreferences.set("window_" + windowId + "_height", size.y); - }; - - loadWindowWidth = function(windowId, defaultValue) { - return LocalPreferences.get("window_" + windowId + "_width", defaultValue); - }; - - loadWindowHeight = function(windowId, defaultValue) { - return LocalPreferences.get("window_" + windowId + "_height", defaultValue); - }; - - function addClickEvent(el, fn) { - ["Link", "Button"].each((item) => { - if ($(el + item)) - $(el + item).addEventListener("click", fn); - }); - } - - addClickEvent("download", (e) => { - e.preventDefault(); - e.stopPropagation(); - showDownloadPage(); - }); - - showDownloadPage = function(urls) { - const id = "downloadPage"; - const contentUri = new URI("download.html"); - - if (urls && (urls.length > 0)) - contentUri.setData("urls", urls.map(encodeURIComponent).join("|")); - - new MochaUI.Window({ - id: id, - icon: "images/qbittorrent-tray.svg", - title: "Download from URLs", - loadMethod: "iframe", - contentURL: contentUri.toString(), - addClass: "windowFrame", // fixes iframe scrolling on iOS Safari - scrollbars: true, - maximizable: false, - closable: true, - paddingVertical: 0, - paddingHorizontal: 0, - width: loadWindowWidth(id, 500), - height: loadWindowHeight(id, 600), - onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { - saveWindowSize(id); - }) - }); - updateMainData(); - }; - - addClickEvent("preferences", (e) => { - e.preventDefault(); - e.stopPropagation(); - - const id = "preferencesPage"; - new MochaUI.Window({ - id: id, - icon: "images/qbittorrent-tray.svg", - title: "Options", - loadMethod: "xhr", - toolbar: true, - contentURL: new URI("views/preferences.html").toString(), - require: { - css: ["css/Tabs.css"] - }, - toolbarURL: "views/preferencesToolbar.html", - maximizable: false, - closable: true, - paddingVertical: 0, - paddingHorizontal: 0, - width: loadWindowWidth(id, 700), - height: loadWindowHeight(id, 600), - onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { - saveWindowSize(id); - }) - }); - }); - - addClickEvent("manageCookies", (e) => { - e.preventDefault(); - e.stopPropagation(); - - const id = "cookiesPage"; - new MochaUI.Window({ - id: id, - title: "Manage Cookies", - loadMethod: "xhr", - contentURL: new URI("views/cookies.html").toString(), - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: loadWindowWidth(id, 900), - height: loadWindowHeight(id, 400), - onResize: function() { - saveWindowSize(id); - } - }); - }); - - addClickEvent("upload", (e) => { - e.preventDefault(); - e.stopPropagation(); - - const id = "uploadPage"; - new MochaUI.Window({ - id: id, - icon: "images/qbittorrent-tray.svg", - title: "Upload local torrent", - loadMethod: "iframe", - contentURL: new URI("upload.html").toString(), - addClass: "windowFrame", // fixes iframe scrolling on iOS Safari - scrollbars: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: loadWindowWidth(id, 500), - height: loadWindowHeight(id, 460), - onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { - saveWindowSize(id); - }) - }); - updateMainData(); - }); - - globalUploadLimitFN = function() { - new MochaUI.Window({ - id: "uploadLimitPage", - icon: "images/qbittorrent-tray.svg", - title: "Global Upload Speed Limit", - loadMethod: "iframe", - contentURL: new URI("uploadlimit.html").setData("hashes", "global").toString(), - scrollbars: false, - resizable: false, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 424, - height: 80 - }); - }; - - uploadLimitFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new MochaUI.Window({ - id: "uploadLimitPage", - icon: "images/qbittorrent-tray.svg", - title: "Torrent Upload Speed Limiting", - loadMethod: "iframe", - contentURL: new URI("uploadlimit.html").setData("hashes", hashes.join("|")).toString(), - scrollbars: false, - resizable: false, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 424, - height: 80 - }); - } - }; - - shareRatioFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - let shareRatio = null; - let torrentsHaveSameShareRatio = true; - - // check if all selected torrents have same share ratio - for (let i = 0; i < hashes.length; ++i) { - const hash = hashes[i]; - const row = torrentsTable.getRow(hash).full_data; - const origValues = row.ratio_limit + "|" + row.seeding_time_limit + "|" + row.inactive_seeding_time_limit + "|" - + row.max_ratio + "|" + row.max_seeding_time + "|" + row.max_inactive_seeding_time; - - // initialize value - if (shareRatio === null) - shareRatio = origValues; - - if (origValues !== shareRatio) { - torrentsHaveSameShareRatio = false; - break; - } - } - - // if all torrents have same share ratio, display that share ratio. else use the default - const orig = torrentsHaveSameShareRatio ? shareRatio : ""; - new MochaUI.Window({ - id: "shareRatioPage", - icon: "images/qbittorrent-tray.svg", - title: "Torrent Upload/Download Ratio Limiting", - loadMethod: "iframe", - contentURL: new URI("shareratio.html").setData("hashes", hashes.join("|")).setData("orig", orig).toString(), - scrollbars: false, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 424, - height: 200 - }); - } - }; - - toggleSequentialDownloadFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/toggleSequentialDownload", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - toggleFirstLastPiecePrioFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/toggleFirstLastPiecePrio", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - setSuperSeedingFN = function(val) { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/setSuperSeeding", - method: "post", - data: { - value: val, - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - setForceStartFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/setForceStart", - method: "post", - data: { - value: "true", - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - globalDownloadLimitFN = function() { - new MochaUI.Window({ - id: "downloadLimitPage", - icon: "images/qbittorrent-tray.svg", - title: "Global Download Speed Limit", - loadMethod: "iframe", - contentURL: new URI("downloadlimit.html").setData("hashes", "global").toString(), - scrollbars: false, - resizable: false, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 424, - height: 80 - }); - }; - - StatisticsLinkFN = function() { - const id = "statisticspage"; - new MochaUI.Window({ - id: id, - icon: "images/qbittorrent-tray.svg", - title: "Statistics", - loadMethod: "xhr", - contentURL: new URI("views/statistics.html").toString(), - maximizable: false, - padding: 10, - width: loadWindowWidth(id, 285), - height: loadWindowHeight(id, 415), - onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { - saveWindowSize(id); - }) - }); - }; - - downloadLimitFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new MochaUI.Window({ - id: "downloadLimitPage", - icon: "images/qbittorrent-tray.svg", - title: "Torrent Download Speed Limiting", - loadMethod: "iframe", - contentURL: new URI("downloadlimit.html").setData("hashes", hashes.join("|")).toString(), - scrollbars: false, - resizable: false, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 424, - height: 80 - }); - } - }; - - deleteFN = function(forceDeleteFiles = false) { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length > 0) { - if (window.qBittorrent.Cache.preferences.get().confirm_torrent_deletion) { - new MochaUI.Modal({ - ...window.qBittorrent.Dialog.baseModalOptions, - id: "confirmDeletionPage", - title: "Remove torrent(s)", - data: { - hashes: hashes, - forceDeleteFiles: forceDeleteFiles - }, - contentURL: "views/confirmdeletion.html", - onContentLoaded: function(w) { - MochaUI.resizeWindow(w, { centered: true }); - MochaUI.centerWindow(w); - }, - onCloseComplete: function() { - // make sure overlay is properly hidden upon modal closing - document.getElementById("modalOverlay").style.display = "none"; - } - }); - } - else { - new Request({ - url: "api/v2/torrents/delete", - method: "post", - data: { - hashes: hashes.join("|"), - deleteFiles: forceDeleteFiles - }, - onSuccess: function() { - torrentsTable.deselectAll(); - updateMainData(); - }, - onFailure: function() { - alert("Unable to delete torrents."); - } - }).send(); - } - } - }; - - addClickEvent("delete", (e) => { - e.preventDefault(); - e.stopPropagation(); - deleteFN(); - }); - - stopFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - startFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - autoTorrentManagementFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - let enable = false; - hashes.each((hash, index) => { - const row = torrentsTable.getRow(hash); - if (!row.full_data.auto_tmm) - enable = true; - }); - new Request({ - url: "api/v2/torrents/setAutoManagement", - method: "post", - data: { - hashes: hashes.join("|"), - enable: enable - } - }).send(); - updateMainData(); - } - }; - - recheckFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length > 0) { - if (window.qBittorrent.Cache.preferences.get().confirm_torrent_recheck) { - new MochaUI.Modal({ - ...window.qBittorrent.Dialog.baseModalOptions, - id: "confirmRecheckDialog", - title: "Recheck confirmation", - data: { hashes: hashes }, - contentURL: "views/confirmRecheck.html" - }); - } - else { - new Request({ - url: "api/v2/torrents/recheck", - method: "post", - data: { - "hashes": hashes.join("|"), - }, - onSuccess: function() { - updateMainData(); - }, - onFailure: function() { - alert("Unable to recheck torrents."); - } - }).send(); - } - } - }; - - reannounceFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/reannounce", - method: "post", - data: { - hashes: hashes.join("|"), - } - }).send(); - updateMainData(); - } - }; - - setLocationFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - const hash = hashes[0]; - const row = torrentsTable.getRow(hash); - - new MochaUI.Window({ - id: "setLocationPage", - icon: "images/qbittorrent-tray.svg", - title: "Set location", - loadMethod: "iframe", - contentURL: new URI("setlocation.html").setData("hashes", hashes.join("|")).setData("path", encodeURIComponent(row.full_data.save_path)).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 400, - height: 130 - }); - } - }; - - renameFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length === 1) { - const hash = hashes[0]; - const row = torrentsTable.getRow(hash); - if (row) { - new MochaUI.Window({ - id: "renamePage", - icon: "images/qbittorrent-tray.svg", - title: "Rename", - loadMethod: "iframe", - contentURL: new URI("rename.html").setData("hash", hash).setData("name", row.full_data.name).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 400, - height: 100 - }); - } - } - }; - - renameFilesFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length === 1) { - const hash = hashes[0]; - const row = torrentsTable.getRow(hash); - if (row) { - new MochaUI.Window({ - id: "multiRenamePage", - icon: "images/qbittorrent-tray.svg", - title: "Renaming", - data: { hash: hash, selectedRows: [] }, - loadMethod: "xhr", - contentURL: "rename_files.html", - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 800, - height: 420, - resizeLimit: { "x": [800], "y": [420] } - }); - } - } - }; - - torrentNewCategoryFN = function() { - const action = "set"; - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new MochaUI.Window({ - id: "newCategoryPage", - icon: "images/qbittorrent-tray.svg", - title: "New Category", - loadMethod: "iframe", - contentURL: new URI("newcategory.html").setData("action", action).setData("hashes", hashes.join("|")).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 400, - height: 150 - }); - } - }; - - torrentSetCategoryFN = function(categoryHash) { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length <= 0) - return; - - const categoryName = category_list.has(categoryHash) - ? category_list.get(categoryHash).name - : ""; - new Request({ - url: "api/v2/torrents/setCategory", - method: "post", - data: { - hashes: hashes.join("|"), - category: categoryName - }, - onSuccess: function() { - updateMainData(); - } - }).send(); - }; - - createCategoryFN = function() { - const action = "create"; - new MochaUI.Window({ - id: "newCategoryPage", - icon: "images/qbittorrent-tray.svg", - title: "New Category", - loadMethod: "iframe", - contentURL: new URI("newcategory.html").setData("action", action).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 400, - height: 150 - }); - }; - - createSubcategoryFN = function(categoryHash) { - const action = "createSubcategory"; - const categoryName = category_list.get(categoryHash).name + "/"; - new MochaUI.Window({ - id: "newSubcategoryPage", - icon: "images/qbittorrent-tray.svg", - title: "New Category", - loadMethod: "iframe", - contentURL: new URI("newcategory.html").setData("action", action).setData("categoryName", categoryName).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 400, - height: 150 - }); - }; - - editCategoryFN = function(categoryHash) { - const action = "edit"; - const category = category_list.get(categoryHash); - new MochaUI.Window({ - id: "editCategoryPage", - icon: "images/qbittorrent-tray.svg", - title: "Edit Category", - loadMethod: "iframe", - contentURL: new URI("newcategory.html").setData("action", action).setData("categoryName", category.name).setData("savePath", category.savePath).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 400, - height: 150 - }); - }; - - removeCategoryFN = function(categoryHash) { - const categoryName = category_list.get(categoryHash).name; - new Request({ - url: "api/v2/torrents/removeCategories", - method: "post", - data: { - categories: categoryName - }, - onSuccess: function() { - setCategoryFilter(CATEGORIES_ALL); - updateMainData(); - } - }).send(); - }; - - deleteUnusedCategoriesFN = function() { - const categories = []; - category_list.forEach((category, hash) => { - if (torrentsTable.getFilteredTorrentsNumber("all", hash, TAGS_ALL, TRACKERS_ALL) === 0) - categories.push(category.name); - }); - - new Request({ - url: "api/v2/torrents/removeCategories", - method: "post", - data: { - categories: categories.join("\n") - }, - onSuccess: function() { - setCategoryFilter(CATEGORIES_ALL); - updateMainData(); - } - }).send(); - }; - - startTorrentsByCategoryFN = function(categoryHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes("all", categoryHash, TAGS_ALL, TRACKERS_ALL); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - stopTorrentsByCategoryFN = function(categoryHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes("all", categoryHash, TAGS_ALL, TRACKERS_ALL); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - deleteTorrentsByCategoryFN = function(categoryHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes("all", categoryHash, TAGS_ALL, TRACKERS_ALL); - if (hashes.length > 0) { - if (window.qBittorrent.Cache.preferences.get().confirm_torrent_deletion) { - new MochaUI.Modal({ - ...window.qBittorrent.Dialog.baseModalOptions, - id: "confirmDeletionPage", - title: "Remove torrent(s)", - data: { hashes: hashes }, - contentURL: "views/confirmdeletion.html", - onContentLoaded: function(w) { - MochaUI.resizeWindow(w, { centered: true }); - MochaUI.centerWindow(w); - }, - onCloseComplete: function() { - // make sure overlay is properly hidden upon modal closing - document.getElementById("modalOverlay").style.display = "none"; - } - }); - } - else { - new Request({ - url: "api/v2/torrents/delete", - method: "post", - data: { - hashes: hashes.join("|"), - deleteFiles: false, - }, - onSuccess: function() { - torrentsTable.deselectAll(); - updateMainData(); - }, - onFailure: function() { - alert("Unable to delete torrents."); - } - }).send(); - } - } - }; - - torrentAddTagsFN = function() { - const action = "set"; - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new MochaUI.Window({ - id: "newTagPage", - icon: "images/qbittorrent-tray.svg", - title: "Add tags", - loadMethod: "iframe", - contentURL: new URI("newtag.html").setData("action", action).setData("hashes", hashes.join("|")).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 250, - height: 100 - }); - } - }; - - torrentSetTagsFN = function(tagHash, isSet) { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length <= 0) - return; - - const tagName = tagList.has(tagHash) ? tagList.get(tagHash).name : ""; - new Request({ - url: (isSet ? "api/v2/torrents/addTags" : "api/v2/torrents/removeTags"), - method: "post", - data: { - hashes: hashes.join("|"), - tags: tagName, - } - }).send(); - }; - - torrentRemoveAllTagsFN = function() { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: ("api/v2/torrents/removeTags"), - method: "post", - data: { - hashes: hashes.join("|"), - } - }).send(); - } - }; - - createTagFN = function() { - const action = "create"; - new MochaUI.Window({ - id: "newTagPage", - icon: "images/qbittorrent-tray.svg", - title: "New Tag", - loadMethod: "iframe", - contentURL: new URI("newtag.html").setData("action", action).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - paddingVertical: 0, - paddingHorizontal: 0, - width: 250, - height: 100 - }); - updateMainData(); - }; - - removeTagFN = function(tagHash) { - const tagName = tagList.get(tagHash).name; - new Request({ - url: "api/v2/torrents/deleteTags", - method: "post", - data: { - tags: tagName - } - }).send(); - setTagFilter(TAGS_ALL); - }; - - deleteUnusedTagsFN = function() { - const tags = []; - tagList.forEach((tag, hash) => { - if (torrentsTable.getFilteredTorrentsNumber("all", CATEGORIES_ALL, hash, TRACKERS_ALL) === 0) - tags.push(tag.name); - }); - new Request({ - url: "api/v2/torrents/deleteTags", - method: "post", - data: { - tags: tags.join(",") - } - }).send(); - setTagFilter(TAGS_ALL); - }; - - startTorrentsByTagFN = function(tagHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, tagHash, TRACKERS_ALL); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - stopTorrentsByTagFN = function(tagHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, tagHash, TRACKERS_ALL); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - deleteTorrentsByTagFN = function(tagHash) { - const hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, tagHash, TRACKERS_ALL); - if (hashes.length > 0) { - if (window.qBittorrent.Cache.preferences.get().confirm_torrent_deletion) { - new MochaUI.Modal({ - ...window.qBittorrent.Dialog.baseModalOptions, - id: "confirmDeletionPage", - title: "Remove torrent(s)", - data: { hashes: hashes }, - contentURL: "views/confirmdeletion.html", - onContentLoaded: function(w) { - MochaUI.resizeWindow(w, { centered: true }); - MochaUI.centerWindow(w); - }, - onCloseComplete: function() { - // make sure overlay is properly hidden upon modal closing - document.getElementById("modalOverlay").style.display = "none"; - } - }); - } - else { - new Request({ - url: "api/v2/torrents/delete", - method: "post", - data: { - hashes: hashes.join("|"), - deleteFiles: false, - }, - onSuccess: function() { - torrentsTable.deselectAll(); - updateMainData(); - }, - onFailure: function() { - alert("Unable to delete torrents."); - } - }).send(); - } - } - }; - - startTorrentsByTrackerFN = function(trackerHash) { - const trackerHashInt = Number.parseInt(trackerHash, 10); - let hashes = []; - switch (trackerHashInt) { - case TRACKERS_ALL: - hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, TAGS_ALL, TRACKERS_ALL); - break; - case TRACKERS_TRACKERLESS: - hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS); - break; - default: { - const uniqueTorrents = new Set(); - for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) { - for (const torrent of torrents) - uniqueTorrents.add(torrent); - } - hashes = [...uniqueTorrents]; - break; - } - } - - if (hashes.length > 0) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - stopTorrentsByTrackerFN = function(trackerHash) { - const trackerHashInt = Number.parseInt(trackerHash, 10); - let hashes = []; - switch (trackerHashInt) { - case TRACKERS_ALL: - hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, TAGS_ALL, TRACKERS_ALL); - break; - case TRACKERS_TRACKERLESS: - hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS); - break; - default: { - const uniqueTorrents = new Set(); - for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) { - for (const torrent of torrents) - uniqueTorrents.add(torrent); - } - hashes = [...uniqueTorrents]; - break; - } - } - - if (hashes.length) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - deleteTorrentsByTrackerFN = function(trackerHash) { - const trackerHashInt = Number.parseInt(trackerHash, 10); - let hashes = []; - switch (trackerHashInt) { - case TRACKERS_ALL: - hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, TAGS_ALL, TRACKERS_ALL); - break; - case TRACKERS_TRACKERLESS: - hashes = torrentsTable.getFilteredTorrentsHashes("all", CATEGORIES_ALL, TAGS_ALL, TRACKERS_TRACKERLESS); - break; - default: { - const uniqueTorrents = new Set(); - for (const torrents of trackerList.get(trackerHashInt).trackerTorrentMap.values()) { - for (const torrent of torrents) - uniqueTorrents.add(torrent); - } - hashes = [...uniqueTorrents]; - break; - } - } - - if (hashes.length > 0) { - if (window.qBittorrent.Cache.preferences.get().confirm_torrent_deletion) { - new MochaUI.Modal({ - ...window.qBittorrent.Dialog.baseModalOptions, - id: "confirmDeletionPage", - title: "Remove torrent(s)", - data: { - hashes: hashes, - filterList: "tracker" - }, - contentURL: "views/confirmdeletion.html", - onContentLoaded: function(w) { - MochaUI.resizeWindow(w, { centered: true }); - MochaUI.centerWindow(w); - }, - onCloseComplete: function() { - // make sure overlay is properly hidden upon modal closing - document.getElementById("modalOverlay").style.display = "none"; - } - }); - } - else { - new Request({ - url: "api/v2/torrents/delete", - method: "post", - data: { - hashes: hashes.join("|"), - deleteFiles: false, - }, - onSuccess: function() { - torrentsTable.deselectAll(); - setTrackerFilter(TRACKERS_ALL); - updateMainData(); - }, - onFailure: function() { - alert("Unable to delete torrents."); - }, - }).send(); - } - } - }; - - deleteTrackerFN = function(trackerHash) { - const trackerHashInt = Number.parseInt(trackerHash, 10); - if ((trackerHashInt === TRACKERS_ALL) || (trackerHashInt === TRACKERS_TRACKERLESS)) - return; - - const tracker = trackerList.get(trackerHashInt); - const host = tracker.host; - const urls = [...tracker.trackerTorrentMap.keys()]; - - new MochaUI.Window({ - id: "confirmDeletionPage", - title: "Remove tracker", - loadMethod: "iframe", - contentURL: new URI("confirmtrackerdeletion.html").setData("host", host).setData("urls", urls.map(encodeURIComponent).join("|")).toString(), - scrollbars: false, - resizable: true, - maximizable: false, - padding: 10, - width: 424, - height: 100, - onCloseComplete: function() { - updateMainData(); - setTrackerFilter(TRACKERS_ALL); - } - }); - }; - - copyNameFN = function() { - const selectedRows = torrentsTable.selectedRowsIds(); - const names = []; - if (selectedRows.length > 0) { - const rows = torrentsTable.getFilteredAndSortedRows(); - for (let i = 0; i < selectedRows.length; ++i) { - const hash = selectedRows[i]; - names.push(rows[hash].full_data.name); - } - } - return names.join("\n"); - }; - - copyInfohashFN = function(policy) { - const selectedRows = torrentsTable.selectedRowsIds(); - const infohashes = []; - if (selectedRows.length > 0) { - const rows = torrentsTable.getFilteredAndSortedRows(); - switch (policy) { - case 1: - for (const id of selectedRows) { - const infohash = rows[id].full_data.infohash_v1; - if (infohash !== "") - infohashes.push(infohash); - } - break; - case 2: - for (const id of selectedRows) { - const infohash = rows[id].full_data.infohash_v2; - if (infohash !== "") - infohashes.push(infohash); - } - break; - } - } - return infohashes.join("\n"); - }; - - copyMagnetLinkFN = function() { - const selectedRows = torrentsTable.selectedRowsIds(); - const magnets = []; - if (selectedRows.length > 0) { - const rows = torrentsTable.getFilteredAndSortedRows(); - for (let i = 0; i < selectedRows.length; ++i) { - const hash = selectedRows[i]; - magnets.push(rows[hash].full_data.magnet_uri); - } - } - return magnets.join("\n"); - }; - - copyIdFN = function() { - return torrentsTable.selectedRowsIds().join("\n"); - }; - - copyCommentFN = function() { - const selectedRows = torrentsTable.selectedRowsIds(); - const comments = []; - if (selectedRows.length > 0) { - const rows = torrentsTable.getFilteredAndSortedRows(); - for (let i = 0; i < selectedRows.length; ++i) { - const hash = selectedRows[i]; - const comment = rows[hash].full_data.comment; - if (comment && (comment !== "")) - comments.push(comment); - } - } - return comments.join("\n---------\n"); - }; - - exportTorrentFN = async function() { - const hashes = torrentsTable.selectedRowsIds(); - for (const hash of hashes) { - const row = torrentsTable.getRow(hash); - if (!row) - continue; - - const name = row.full_data.name; - const url = new URI("api/v2/torrents/export"); - url.setData("hash", hash); - - // download response to file - const element = document.createElement("a"); - element.href = url; - element.download = (name + ".torrent"); - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - - // https://stackoverflow.com/questions/53560991/automatic-file-downloads-limited-to-10-files-on-chrome-browser - await window.qBittorrent.Misc.sleep(200); - } - }; - - addClickEvent("stopAll", (e) => { - e.preventDefault(); - e.stopPropagation(); - - if (confirm("Would you like to stop all torrents?")) { - new Request({ - url: "api/v2/torrents/stop", - method: "post", - data: { - hashes: "all" - } - }).send(); - updateMainData(); - } - }); - - addClickEvent("startAll", (e) => { - e.preventDefault(); - e.stopPropagation(); - - if (confirm("Would you like to start all torrents?")) { - new Request({ - url: "api/v2/torrents/start", - method: "post", - data: { - hashes: "all" - } - }).send(); - updateMainData(); - } - }); - - ["stop", "start", "recheck"].each((item) => { - addClickEvent(item, (e) => { - e.preventDefault(); - e.stopPropagation(); - - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - hashes.each((hash, index) => { - new Request({ - url: "api/v2/torrents/" + item, - method: "post", - data: { - hashes: hash - } - }).send(); - }); - updateMainData(); - } - }); - }); - - ["decreasePrio", "increasePrio", "topPrio", "bottomPrio"].each((item) => { - addClickEvent(item, (e) => { - e.preventDefault(); - e.stopPropagation(); - setQueuePositionFN(item); - }); - }); - - setQueuePositionFN = function(cmd) { - const hashes = torrentsTable.selectedRowsIds(); - if (hashes.length) { - new Request({ - url: "api/v2/torrents/" + cmd, - method: "post", - data: { - hashes: hashes.join("|") - } - }).send(); - updateMainData(); - } - }; - - addClickEvent("about", (e) => { - e.preventDefault(); - e.stopPropagation(); - - const id = "aboutpage"; - new MochaUI.Window({ - id: id, - icon: "images/qbittorrent-tray.svg", - title: "About qBittorrent", - loadMethod: "xhr", - contentURL: new URI("views/about.html").toString(), - require: { - css: ["css/Tabs.css"] - }, - toolbar: true, - toolbarURL: "views/aboutToolbar.html", - padding: 10, - width: loadWindowWidth(id, 550), - height: loadWindowHeight(id, 360), - onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { - saveWindowSize(id); - }) - }); - }); - - addClickEvent("logout", (e) => { - e.preventDefault(); - e.stopPropagation(); - - new Request({ - url: "api/v2/auth/logout", - method: "post", - onSuccess: function() { - window.location.reload(true); - } - }).send(); - }); - - addClickEvent("shutdown", (e) => { - e.preventDefault(); - e.stopPropagation(); - - if (confirm("Are you sure you want to quit qBittorrent?")) { - new Request({ - url: "api/v2/app/shutdown", - method: "post", - onSuccess: function() { - const shutdownMessage = "%1 has been shutdown".replace("%1", window.qBittorrent.Client.mainTitle()); - document.write(` ${shutdownMessage}

${shutdownMessage}

`); - document.close(); - window.stop(); - window.qBittorrent.Client.stop(); - } - }).send(); - } - }); - - // Deactivate menu header links - $$("a.returnFalse").each((el) => { - el.addEventListener("click", (e) => { - e.preventDefault(); - e.stopPropagation(); - }); - }); -}; + "use strict"; + + window.qBittorrent ??= {}; + window.qBittorrent.Dialog ??= (() => { + const exports = () => { + return { + baseModalOptions: baseModalOptions + }; + }; + + const deepFreeze = (obj) => { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#examples + // accounts for circular refs + const frozen = new WeakSet(); + const deepFreezeSafe = (obj) => { + if (frozen.has(obj)) + return; + + frozen.add(obj); + + const keys = Reflect.ownKeys(obj); + for (const key of keys) { + const value = obj[key]; + if ((value && (typeof value === "object")) || (typeof value === "function")) + deepFreezeSafe(value); + } + Object.freeze(obj); + }; + + deepFreezeSafe(obj); + }; + + const baseModalOptions = Object.assign(Object.create(null), { + addClass: "modalDialog", + collapsible: false, + cornerRadius: 5, + draggable: true, + footerHeight: 20, + icon: "images/qbittorrent-tray.svg", + loadMethod: "xhr", + maximizable: false, + method: "post", + minimizable: false, + padding: { + top: 15, + right: 10, + bottom: 15, + left: 5 + }, + resizable: true, + width: 480, + onCloseComplete: function() { + // make sure overlay is properly hidden upon modal closing + document.getElementById("modalOverlay").style.display = "none"; + } + }); + + deepFreeze(baseModalOptions); + + return exports(); + })(); + Object.freeze(window.qBittorrent.Dialog); + + const LocalPreferences = new window.qBittorrent.LocalPreferences.LocalPreferencesClass(); + + let saveWindowSize = function() {}; + let loadWindowWidth = function() {}; + let loadWindowHeight = function() {}; + let showDownloadPage = function() {}; + let globalUploadLimitFN = function() {}; + let uploadLimitFN = function() {}; + let shareRatioFN = function() {}; + let toggleSequentialDownloadFN = function() {}; + let toggleFirstLastPiecePrioFN = function() {}; + let setSuperSeedingFN = function() {}; + let setForceStartFN = function() {}; + let globalDownloadLimitFN = function() {}; + let StatisticsLinkFN = function() {}; + let downloadLimitFN = function() {}; + let deleteSelectedTorrentsFN = function() {}; + let stopFN = function() {}; + let startFN = function() {}; + let autoTorrentManagementFN = function() {}; + let recheckFN = function() {}; + let reannounceFN = function() {}; + let setLocationFN = function() {}; + let renameFN = function() {}; + let renameFilesFN = function() {}; + let startVisibleTorrentsFN = function() {}; + let stopVisibleTorrentsFN = function() {}; + let deleteVisibleTorrentsFN = function() {}; + let torrentNewCategoryFN = function() {}; + let torrentSetCategoryFN = function() {}; + let createCategoryFN = function() {}; + let createSubcategoryFN = function() {}; + let editCategoryFN = function() {}; + let removeCategoryFN = function() {}; + let deleteUnusedCategoriesFN = function() {}; + let torrentAddTagsFN = function() {}; + let torrentSetTagsFN = function() {}; + let torrentRemoveAllTagsFN = function() {}; + let createTagFN = function() {}; + let removeTagFN = function() {}; + let deleteUnusedTagsFN = function() {}; + let deleteTrackerFN = function() {}; + let copyNameFN = function() {}; + let copyInfohashFN = function(policy) {}; + let copyMagnetLinkFN = function() {}; + let copyIdFN = function() {}; + let copyCommentFN = function() {}; + let setQueuePositionFN = function() {}; + let exportTorrentFN = function() {}; + + const initializeWindows = function() { + saveWindowSize = function(windowId) { + const size = $(windowId).getSize(); + LocalPreferences.set("window_" + windowId + "_width", size.x); + LocalPreferences.set("window_" + windowId + "_height", size.y); + }; + + loadWindowWidth = function(windowId, defaultValue) { + return LocalPreferences.get("window_" + windowId + "_width", defaultValue); + }; + + loadWindowHeight = function(windowId, defaultValue) { + return LocalPreferences.get("window_" + windowId + "_height", defaultValue); + }; + + function addClickEvent(el, fn) { + ["Link", "Button"].each((item) => { + if ($(el + item)) + $(el + item).addEventListener("click", fn); + }); + } + + addClickEvent("download", (e) => { + e.preventDefault(); + e.stopPropagation(); + showDownloadPage(); + }); + + showDownloadPage = function(urls) { + const id = "downloadPage"; + const contentUri = new URI("download.html"); + + if (urls && (urls.length > 0)) + contentUri.setData("urls", urls.map(encodeURIComponent).join("|")); + + new MochaUI.Window({ + id: id, + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Download from URLs)QBT_TR[CONTEXT=downloadFromURL]", + loadMethod: "iframe", + contentURL: contentUri.toString(), + addClass: "windowFrame", // fixes iframe scrolling on iOS Safari + scrollbars: true, + maximizable: false, + closable: true, + paddingVertical: 0, + paddingHorizontal: 0, + width: loadWindowWidth(id, 500), + height: loadWindowHeight(id, 600), + onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { + saveWindowSize(id); + }) + }); + updateMainData(); + }; + + addClickEvent("preferences", (e) => { + e.preventDefault(); + e.stopPropagation(); + + const id = "preferencesPage"; + new MochaUI.Window({ + id: id, + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Options)QBT_TR[CONTEXT=OptionsDialog]", + loadMethod: "xhr", + toolbar: true, + contentURL: new URI("views/preferences.html").toString(), + require: { + css: ["css/Tabs.css"] + }, + toolbarURL: "views/preferencesToolbar.html", + maximizable: false, + closable: true, + paddingVertical: 0, + paddingHorizontal: 0, + width: loadWindowWidth(id, 730), + height: loadWindowHeight(id, 600), + onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { + saveWindowSize(id); + }) + }); + }); + + addClickEvent("manageCookies", (e) => { + e.preventDefault(); + e.stopPropagation(); + + const id = "cookiesPage"; + new MochaUI.Window({ + id: id, + title: "QBT_TR(Manage Cookies)QBT_TR[CONTEXT=CookiesDialog]", + loadMethod: "xhr", + contentURL: new URI("views/cookies.html").toString(), + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: loadWindowWidth(id, 900), + height: loadWindowHeight(id, 400), + onResize: function() { + saveWindowSize(id); + } + }); + }); + + addClickEvent("upload", (e) => { + e.preventDefault(); + e.stopPropagation(); + + const id = "uploadPage"; + new MochaUI.Window({ + id: id, + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Upload local torrent)QBT_TR[CONTEXT=HttpServer]", + loadMethod: "iframe", + contentURL: new URI("upload.html").toString(), + addClass: "windowFrame", // fixes iframe scrolling on iOS Safari + scrollbars: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: loadWindowWidth(id, 500), + height: loadWindowHeight(id, 460), + onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { + saveWindowSize(id); + }) + }); + updateMainData(); + }); + + globalUploadLimitFN = function() { + new MochaUI.Window({ + id: "uploadLimitPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Global Upload Speed Limit)QBT_TR[CONTEXT=MainWindow]", + loadMethod: "iframe", + contentURL: new URI("uploadlimit.html").setData("hashes", "global").toString(), + scrollbars: false, + resizable: false, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 424, + height: 100 + }); + }; + + uploadLimitFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new MochaUI.Window({ + id: "uploadLimitPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Torrent Upload Speed Limiting)QBT_TR[CONTEXT=TransferListWidget]", + loadMethod: "iframe", + contentURL: new URI("uploadlimit.html").setData("hashes", hashes.join("|")).toString(), + scrollbars: false, + resizable: false, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 424, + height: 100 + }); + } + }; + + shareRatioFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + let shareRatio = null; + let torrentsHaveSameShareRatio = true; + + // check if all selected torrents have same share ratio + for (let i = 0; i < hashes.length; ++i) { + const hash = hashes[i]; + const row = torrentsTable.getRow(hash).full_data; + const origValues = row.ratio_limit + "|" + row.seeding_time_limit + "|" + row.inactive_seeding_time_limit + "|" + + row.max_ratio + "|" + row.max_seeding_time + "|" + row.max_inactive_seeding_time; + + // initialize value + if (shareRatio === null) + shareRatio = origValues; + + if (origValues !== shareRatio) { + torrentsHaveSameShareRatio = false; + break; + } + } + + // if all torrents have same share ratio, display that share ratio. else use the default + const orig = torrentsHaveSameShareRatio ? shareRatio : ""; + new MochaUI.Window({ + id: "shareRatioPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Torrent Upload/Download Ratio Limiting)QBT_TR[CONTEXT=UpDownRatioDialog]", + loadMethod: "iframe", + contentURL: new URI("shareratio.html").setData("hashes", hashes.join("|")).setData("orig", orig).toString(), + scrollbars: false, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 424, + height: 220 + }); + } + }; + + toggleSequentialDownloadFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/toggleSequentialDownload", + method: "post", + data: { + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + toggleFirstLastPiecePrioFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/toggleFirstLastPiecePrio", + method: "post", + data: { + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + setSuperSeedingFN = function(val) { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/setSuperSeeding", + method: "post", + data: { + value: val, + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + setForceStartFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/setForceStart", + method: "post", + data: { + value: "true", + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + globalDownloadLimitFN = function() { + new MochaUI.Window({ + id: "downloadLimitPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Global Download Speed Limit)QBT_TR[CONTEXT=MainWindow]", + loadMethod: "iframe", + contentURL: new URI("downloadlimit.html").setData("hashes", "global").toString(), + scrollbars: false, + resizable: false, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 424, + height: 100 + }); + }; + + StatisticsLinkFN = function() { + const id = "statisticspage"; + new MochaUI.Window({ + id: id, + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Statistics)QBT_TR[CONTEXT=StatsDialog]", + loadMethod: "xhr", + contentURL: new URI("views/statistics.html").toString(), + maximizable: false, + padding: 10, + width: loadWindowWidth(id, 285), + height: loadWindowHeight(id, 415), + onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { + saveWindowSize(id); + }) + }); + }; + + downloadLimitFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new MochaUI.Window({ + id: "downloadLimitPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Torrent Download Speed Limiting)QBT_TR[CONTEXT=TransferListWidget]", + loadMethod: "iframe", + contentURL: new URI("downloadlimit.html").setData("hashes", hashes.join("|")).toString(), + scrollbars: false, + resizable: false, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 424, + height: 100 + }); + } + }; + + deleteSelectedTorrentsFN = function(forceDeleteFiles = false) { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length > 0) { + if (window.qBittorrent.Cache.preferences.get().confirm_torrent_deletion) { + new MochaUI.Modal({ + ...window.qBittorrent.Dialog.baseModalOptions, + id: "confirmDeletionPage", + title: "Remove torrent(s)", + data: { + hashes: hashes, + forceDeleteFiles: forceDeleteFiles + }, + contentURL: "views/confirmdeletion.html", + onContentLoaded: function(w) { + MochaUI.resizeWindow(w, { centered: true }); + MochaUI.centerWindow(w); + }, + onCloseComplete: function() { + // make sure overlay is properly hidden upon modal closing + document.getElementById("modalOverlay").style.display = "none"; + } + }); + } + else { + new Request({ + url: "api/v2/torrents/delete", + method: "post", + data: { + hashes: hashes.join("|"), + deleteFiles: forceDeleteFiles + }, + onSuccess: function() { + torrentsTable.deselectAll(); + updateMainData(); + updatePropertiesPanel(); + }, + onFailure: function() { + alert("Unable to delete torrents."); + } + }).send(); + } + } + }; + + addClickEvent("delete", (e) => { + e.preventDefault(); + e.stopPropagation(); + deleteSelectedTorrentsFN(); + }); + + stopFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/stop", + method: "post", + data: { + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + startFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/start", + method: "post", + data: { + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + autoTorrentManagementFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length > 0) { + const enableAutoTMM = hashes.some((hash) => !(torrentsTable.getRow(hash).full_data.auto_tmm)); + if (enableAutoTMM) { + new MochaUI.Modal({ + ...window.qBittorrent.Dialog.baseModalOptions, + id: "confirmAutoTMMDialog", + title: "QBT_TR(Enable automatic torrent management)QBT_TR[CONTEXT=confirmAutoTMMDialog]", + data: { + hashes: hashes, + enable: enableAutoTMM + }, + contentURL: "views/confirmAutoTMM.html" + }); + } + else { + new Request({ + url: "api/v2/torrents/setAutoManagement", + method: "post", + data: { + hashes: hashes.join("|"), + enable: enableAutoTMM + }, + onSuccess: () => { + updateMainData(); + }, + onFailure: () => { + alert("QBT_TR(Unable to set Auto Torrent Management for the selected torrents.)QBT_TR[CONTEXT=HttpServer]"); + } + }).send(); + } + } + }; + + recheckFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length > 0) { + if (window.qBittorrent.Cache.preferences.get().confirm_torrent_recheck) { + new MochaUI.Modal({ + ...window.qBittorrent.Dialog.baseModalOptions, + id: "confirmRecheckDialog", + title: "QBT_TR(Recheck confirmation)QBT_TR[CONTEXT=confirmRecheckDialog]", + data: { hashes: hashes }, + contentURL: "views/confirmRecheck.html" + }); + } + else { + new Request({ + url: "api/v2/torrents/recheck", + method: "post", + data: { + "hashes": hashes.join("|"), + }, + onSuccess: function() { + updateMainData(); + }, + onFailure: function() { + alert("QBT_TR(Unable to recheck torrents.)QBT_TR[CONTEXT=HttpServer]"); + } + }).send(); + } + } + }; + + reannounceFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/reannounce", + method: "post", + data: { + hashes: hashes.join("|"), + } + }).send(); + updateMainData(); + } + }; + + setLocationFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + const hash = hashes[0]; + const row = torrentsTable.getRow(hash); + + new MochaUI.Window({ + id: "setLocationPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Set location)QBT_TR[CONTEXT=TransferListWidget]", + loadMethod: "iframe", + contentURL: new URI("setlocation.html").setData("hashes", hashes.join("|")).setData("path", encodeURIComponent(row.full_data.save_path)).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 400, + height: 130 + }); + } + }; + + renameFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length === 1) { + const hash = hashes[0]; + const row = torrentsTable.getRow(hash); + if (row) { + new MochaUI.Window({ + id: "renamePage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Rename)QBT_TR[CONTEXT=TransferListWidget]", + loadMethod: "iframe", + contentURL: new URI("rename.html").setData("hash", hash).setData("name", row.full_data.name).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 400, + height: 100 + }); + } + } + }; + + renameFilesFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length === 1) { + const hash = hashes[0]; + const row = torrentsTable.getRow(hash); + if (row) { + new MochaUI.Window({ + id: "multiRenamePage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Renaming)QBT_TR[CONTEXT=TransferListWidget]", + data: { hash: hash, selectedRows: [] }, + loadMethod: "xhr", + contentURL: "rename_files.html", + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 800, + height: 420, + resizeLimit: { "x": [800], "y": [420] } + }); + } + } + }; + + startVisibleTorrentsFN = function() { + const hashes = torrentsTable.getFilteredTorrentsHashes(selectedStatus, selectedCategory, selectedTag, selectedTracker); + if (hashes.length > 0) { + new Request({ + url: "api/v2/torrents/start", + method: "post", + data: { + hashes: hashes.join("|") + }, + onSuccess: () => { + updateMainData(); + updatePropertiesPanel(); + }, + onFailure: () => { + alert("QBT_TR(Unable to start torrents.)QBT_TR[CONTEXT=HttpServer]"); + } + }).send(); + } + }; + + stopVisibleTorrentsFN = function() { + const hashes = torrentsTable.getFilteredTorrentsHashes(selectedStatus, selectedCategory, selectedTag, selectedTracker); + if (hashes.length > 0) { + new Request({ + url: "api/v2/torrents/stop", + method: "post", + data: { + hashes: hashes.join("|") + }, + onSuccess: () => { + updateMainData(); + updatePropertiesPanel(); + }, + onFailure: () => { + alert("QBT_TR(Unable to stop torrents.)QBT_TR[CONTEXT=HttpServer]"); + } + }).send(); + } + }; + + deleteVisibleTorrentsFN = function() { + const hashes = torrentsTable.getFilteredTorrentsHashes(selectedStatus, selectedCategory, selectedTag, selectedTracker); + if (hashes.length > 0) { + if (window.qBittorrent.Cache.preferences.get().confirm_torrent_deletion) { + new MochaUI.Modal({ + ...window.qBittorrent.Dialog.baseModalOptions, + id: "confirmDeletionPage", + title: "QBT_TR(Remove torrent(s))QBT_TR[CONTEXT=confirmDeletionDlg]", + data: { + hashes: hashes, + isDeletingVisibleTorrents: true + }, + contentURL: "views/confirmdeletion.html", + onContentLoaded: function(w) { + MochaUI.resizeWindow(w, { centered: true }); + MochaUI.centerWindow(w); + } + }); + } + else { + new Request({ + url: "api/v2/torrents/delete", + method: "post", + data: { + hashes: hashes.join("|"), + deleteFiles: false, + }, + onSuccess: () => { + torrentsTable.deselectAll(); + updateMainData(); + updatePropertiesPanel(); + }, + onFailure: () => { + alert("QBT_TR(Unable to delete torrents.)QBT_TR[CONTEXT=HttpServer]"); + } + }).send(); + } + } + }; + + torrentNewCategoryFN = function() { + const action = "set"; + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new MochaUI.Window({ + id: "newCategoryPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(New Category)QBT_TR[CONTEXT=TransferListWidget]", + loadMethod: "iframe", + contentURL: new URI("newcategory.html").setData("action", action).setData("hashes", hashes.join("|")).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 400, + height: 150 + }); + } + }; + + torrentSetCategoryFN = function(categoryHash) { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length <= 0) + return; + + const categoryName = category_list.has(categoryHash) + ? category_list.get(categoryHash).name + : ""; + new Request({ + url: "api/v2/torrents/setCategory", + method: "post", + data: { + hashes: hashes.join("|"), + category: categoryName + }, + onSuccess: function() { + updateMainData(); + } + }).send(); + }; + + createCategoryFN = function() { + const action = "create"; + new MochaUI.Window({ + id: "newCategoryPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(New Category)QBT_TR[CONTEXT=CategoryFilterWidget]", + loadMethod: "iframe", + contentURL: new URI("newcategory.html").setData("action", action).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 400, + height: 150 + }); + }; + + createSubcategoryFN = function(categoryHash) { + const action = "createSubcategory"; + const categoryName = category_list.get(categoryHash).name + "/"; + new MochaUI.Window({ + id: "newSubcategoryPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(New Category)QBT_TR[CONTEXT=CategoryFilterWidget]", + loadMethod: "iframe", + contentURL: new URI("newcategory.html").setData("action", action).setData("categoryName", categoryName).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 400, + height: 150 + }); + }; + + editCategoryFN = function(categoryHash) { + const action = "edit"; + const category = category_list.get(categoryHash); + new MochaUI.Window({ + id: "editCategoryPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Edit Category)QBT_TR[CONTEXT=TransferListWidget]", + loadMethod: "iframe", + contentURL: new URI("newcategory.html").setData("action", action).setData("categoryName", category.name).setData("savePath", category.savePath).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 400, + height: 150 + }); + }; + + removeCategoryFN = function(categoryHash) { + const categoryName = category_list.get(categoryHash).name; + new Request({ + url: "api/v2/torrents/removeCategories", + method: "post", + data: { + categories: categoryName + }, + onSuccess: function() { + setCategoryFilter(CATEGORIES_ALL); + updateMainData(); + } + }).send(); + }; + + deleteUnusedCategoriesFN = function() { + const categories = []; + category_list.forEach((category, hash) => { + if (torrentsTable.getFilteredTorrentsNumber("all", hash, TAGS_ALL, TRACKERS_ALL) === 0) + categories.push(category.name); + }); + + new Request({ + url: "api/v2/torrents/removeCategories", + method: "post", + data: { + categories: categories.join("\n") + }, + onSuccess: function() { + setCategoryFilter(CATEGORIES_ALL); + updateMainData(); + } + }).send(); + }; + + torrentAddTagsFN = function() { + const action = "set"; + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new MochaUI.Window({ + id: "newTagPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(Add tags)QBT_TR[CONTEXT=TransferListWidget]", + loadMethod: "iframe", + contentURL: new URI("newtag.html").setData("action", action).setData("hashes", hashes.join("|")).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 250, + height: 100 + }); + } + }; + + torrentSetTagsFN = function(tagHash, isSet) { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length <= 0) + return; + + const tagName = tagList.has(tagHash) ? tagList.get(tagHash).name : ""; + new Request({ + url: (isSet ? "api/v2/torrents/addTags" : "api/v2/torrents/removeTags"), + method: "post", + data: { + hashes: hashes.join("|"), + tags: tagName, + } + }).send(); + }; + + torrentRemoveAllTagsFN = function() { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: ("api/v2/torrents/removeTags"), + method: "post", + data: { + hashes: hashes.join("|"), + } + }).send(); + } + }; + + createTagFN = function() { + const action = "create"; + new MochaUI.Window({ + id: "newTagPage", + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(New Tag)QBT_TR[CONTEXT=TagFilterWidget]", + loadMethod: "iframe", + contentURL: new URI("newtag.html").setData("action", action).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + paddingVertical: 0, + paddingHorizontal: 0, + width: 250, + height: 100 + }); + updateMainData(); + }; + + removeTagFN = function(tagHash) { + const tagName = tagList.get(tagHash).name; + new Request({ + url: "api/v2/torrents/deleteTags", + method: "post", + data: { + tags: tagName + } + }).send(); + setTagFilter(TAGS_ALL); + }; + + deleteUnusedTagsFN = function() { + const tags = []; + tagList.forEach((tag, hash) => { + if (torrentsTable.getFilteredTorrentsNumber("all", CATEGORIES_ALL, hash, TRACKERS_ALL) === 0) + tags.push(tag.name); + }); + new Request({ + url: "api/v2/torrents/deleteTags", + method: "post", + data: { + tags: tags.join(",") + } + }).send(); + setTagFilter(TAGS_ALL); + }; + + deleteTrackerFN = function(trackerHash) { + const trackerHashInt = Number.parseInt(trackerHash, 10); + if ((trackerHashInt === TRACKERS_ALL) || (trackerHashInt === TRACKERS_TRACKERLESS)) + return; + + const tracker = trackerList.get(trackerHashInt); + const host = tracker.host; + const urls = [...tracker.trackerTorrentMap.keys()]; + + new MochaUI.Window({ + id: "confirmDeletionPage", + title: "QBT_TR(Remove tracker)QBT_TR[CONTEXT=confirmDeletionDlg]", + loadMethod: "iframe", + contentURL: new URI("confirmtrackerdeletion.html").setData("host", host).setData("urls", urls.map(encodeURIComponent).join("|")).toString(), + scrollbars: false, + resizable: true, + maximizable: false, + padding: 10, + width: 424, + height: 100, + onCloseComplete: function() { + updateMainData(); + setTrackerFilter(TRACKERS_ALL); + } + }); + }; + + copyNameFN = function() { + const selectedRows = torrentsTable.selectedRowsIds(); + const names = []; + if (selectedRows.length > 0) { + const rows = torrentsTable.getFilteredAndSortedRows(); + for (let i = 0; i < selectedRows.length; ++i) { + const hash = selectedRows[i]; + names.push(rows[hash].full_data.name); + } + } + return names.join("\n"); + }; + + copyInfohashFN = function(policy) { + const selectedRows = torrentsTable.selectedRowsIds(); + const infohashes = []; + if (selectedRows.length > 0) { + const rows = torrentsTable.getFilteredAndSortedRows(); + switch (policy) { + case 1: + for (const id of selectedRows) { + const infohash = rows[id].full_data.infohash_v1; + if (infohash !== "") + infohashes.push(infohash); + } + break; + case 2: + for (const id of selectedRows) { + const infohash = rows[id].full_data.infohash_v2; + if (infohash !== "") + infohashes.push(infohash); + } + break; + } + } + return infohashes.join("\n"); + }; + + copyMagnetLinkFN = function() { + const selectedRows = torrentsTable.selectedRowsIds(); + const magnets = []; + if (selectedRows.length > 0) { + const rows = torrentsTable.getFilteredAndSortedRows(); + for (let i = 0; i < selectedRows.length; ++i) { + const hash = selectedRows[i]; + magnets.push(rows[hash].full_data.magnet_uri); + } + } + return magnets.join("\n"); + }; + + copyIdFN = function() { + return torrentsTable.selectedRowsIds().join("\n"); + }; + + copyCommentFN = function() { + const selectedRows = torrentsTable.selectedRowsIds(); + const comments = []; + if (selectedRows.length > 0) { + const rows = torrentsTable.getFilteredAndSortedRows(); + for (let i = 0; i < selectedRows.length; ++i) { + const hash = selectedRows[i]; + const comment = rows[hash].full_data.comment; + if (comment && (comment !== "")) + comments.push(comment); + } + } + return comments.join("\n---------\n"); + }; + + exportTorrentFN = async function() { + const hashes = torrentsTable.selectedRowsIds(); + for (const hash of hashes) { + const row = torrentsTable.getRow(hash); + if (!row) + continue; + + const name = row.full_data.name; + const url = new URI("api/v2/torrents/export"); + url.setData("hash", hash); + + // download response to file + const element = document.createElement("a"); + element.href = url; + element.download = (name + ".torrent"); + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + + // https://stackoverflow.com/questions/53560991/automatic-file-downloads-limited-to-10-files-on-chrome-browser + await window.qBittorrent.Misc.sleep(200); + } + }; + + addClickEvent("stopAll", (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (confirm("QBT_TR(Would you like to stop all torrents?)QBT_TR[CONTEXT=MainWindow]")) { + new Request({ + url: "api/v2/torrents/stop", + method: "post", + data: { + hashes: "all" + } + }).send(); + updateMainData(); + } + }); + + addClickEvent("startAll", (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (confirm("QBT_TR(Would you like to start all torrents?)QBT_TR[CONTEXT=MainWindow]")) { + new Request({ + url: "api/v2/torrents/start", + method: "post", + data: { + hashes: "all" + } + }).send(); + updateMainData(); + } + }); + + ["stop", "start", "recheck"].each((item) => { + addClickEvent(item, (e) => { + e.preventDefault(); + e.stopPropagation(); + + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + hashes.each((hash, index) => { + new Request({ + url: "api/v2/torrents/" + item, + method: "post", + data: { + hashes: hash + } + }).send(); + }); + updateMainData(); + } + }); + }); + + ["decreasePrio", "increasePrio", "topPrio", "bottomPrio"].each((item) => { + addClickEvent(item, (e) => { + e.preventDefault(); + e.stopPropagation(); + setQueuePositionFN(item); + }); + }); + + setQueuePositionFN = function(cmd) { + const hashes = torrentsTable.selectedRowsIds(); + if (hashes.length) { + new Request({ + url: "api/v2/torrents/" + cmd, + method: "post", + data: { + hashes: hashes.join("|") + } + }).send(); + updateMainData(); + } + }; + + addClickEvent("about", (e) => { + e.preventDefault(); + e.stopPropagation(); + + const id = "aboutpage"; + new MochaUI.Window({ + id: id, + icon: "images/qbittorrent-tray.svg", + title: "QBT_TR(About qBittorrent)QBT_TR[CONTEXT=AboutDialog]", + loadMethod: "xhr", + contentURL: new URI("views/about.html").toString(), + require: { + css: ["css/Tabs.css"] + }, + toolbar: true, + toolbarURL: "views/aboutToolbar.html", + padding: 10, + width: loadWindowWidth(id, 570), + height: loadWindowHeight(id, 360), + onResize: window.qBittorrent.Misc.createDebounceHandler(500, (e) => { + saveWindowSize(id); + }) + }); + }); + + addClickEvent("logout", (e) => { + e.preventDefault(); + e.stopPropagation(); + + new Request({ + url: "api/v2/auth/logout", + method: "post", + onSuccess: function() { + window.location.reload(true); + } + }).send(); + }); + + addClickEvent("shutdown", (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (confirm("QBT_TR(Are you sure you want to quit qBittorrent?)QBT_TR[CONTEXT=MainWindow]")) { + new Request({ + url: "api/v2/app/shutdown", + method: "post", + onSuccess: function() { + const shutdownMessage = "QBT_TR(%1 has been shutdown)QBT_TR[CONTEXT=HttpServer]".replace("%1", window.qBittorrent.Client.mainTitle()); + document.write(` ${shutdownMessage}

${shutdownMessage}

`); + document.close(); + window.stop(); + window.qBittorrent.Client.stop(); + } + }).send(); + } + }); + + // Deactivate menu header links + $$("a.returnFalse").each((el) => { + el.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + }); + }); + }; + \ No newline at end of file diff --git a/private/views/preferences.html b/private/views/preferences.html index 8065146..1f3b8ba 100644 --- a/private/views/preferences.html +++ b/private/views/preferences.html @@ -2127,7 +2127,7 @@ Use ';' to split multiple entries. Can use wildcard '*'."> document.getElementById("hideZeroFiltersCheckbox").checked = (LocalPreferences.get("hide_zero_status_filters", "false") === "true"); $("dblclickDownloadSelect").value = LocalPreferences.get("dblclick_download", "1"); $("dblclickCompleteSelect").value = LocalPreferences.get("dblclick_complete", "1"); - document.getElementById("confirmTorrentDeletion").checked = pref.confirm_torrent_deletion; + document.getElementById("confirmTorrentDeletion").checked = pref.confirm_torrent_deletion ?? true; document.getElementById("useAltRowColorsInput").checked = (LocalPreferences.get("use_alt_row_colors", "true") === "true"); $("filelog_checkbox").checked = pref.file_log_enabled; $("filelog_save_path_input").value = pref.file_log_path; @@ -2545,7 +2545,7 @@ Use ';' to split multiple entries. Can use wildcard '*'."> LocalPreferences.set("hide_zero_status_filters", document.getElementById("hideZeroFiltersCheckbox").checked.toString()); LocalPreferences.set("dblclick_download", $("dblclickDownloadSelect").value); LocalPreferences.set("dblclick_complete", $("dblclickCompleteSelect").value); - settings["confirm_torrent_deletion"] = document.getElementById("confirmTorrentDeletion").checked; + settings["confirm_torrent_deletion"] = document.getElementById("confirmTorrentDeletion").checked ?? true; LocalPreferences.set("use_alt_row_colors", document.getElementById("useAltRowColorsInput").checked.toString()); settings["file_log_enabled"] = $("filelog_checkbox").checked; settings["file_log_path"] = $("filelog_save_path_input").value; diff --git a/private/views/transferlist.html b/private/views/transferlist.html index 97cee9b..1a16885 100644 --- a/private/views/transferlist.html +++ b/private/views/transferlist.html @@ -43,7 +43,7 @@ }, delete: function(element, ref) { - deleteFN(); + deleteSelectedTorrentsFN(); }, setLocation: function(element, ref) {