diff --git a/src/webui/www/private/scripts/client.js b/src/webui/www/private/scripts/client.js index d1217e7fb81..0879ce5241f 100644 --- a/src/webui/www/private/scripts/client.js +++ b/src/webui/www/private/scripts/client.js @@ -466,15 +466,26 @@ window.addEventListener("DOMContentLoaded", () => { margin_left = (category_path.length - 1) * 20; } - const html = `` - + '' - + window.qBittorrent.Misc.escapeHtml(display_name) + " (" + count + ")" + ""; - const el = new Element("li", { - id: hash, - html: html + const span = document.createElement("span"); + span.classList.add("link"); + span.href = "#"; + span.style.marginLeft = `${margin_left}px`; + span.textContent = `${display_name} (${count})`; + span.addEventListener("click", (event) => { + event.preventDefault(); + setCategoryFilter(hash); }); - window.qBittorrent.Filters.categoriesFilterContextMenu.addTarget(el); - return el; + + const img = document.createElement("img"); + img.src = "images/view-categories.svg"; + span.prepend(img); + + const listItem = document.createElement("li"); + listItem.id = hash; + listItem.appendChild(span); + + window.qBittorrent.Filters.categoriesFilterContextMenu.addTarget(listItem); + return listItem; }; const all = torrentsTable.getRowIds().length; @@ -547,15 +558,25 @@ window.addEventListener("DOMContentLoaded", () => { tagFilterList.getChildren().each(c => c.destroy()); const createLink = function(hash, text, count) { - const html = `` - + '' - + window.qBittorrent.Misc.escapeHtml(text) + " (" + count + ")" + ""; - const el = new Element("li", { - id: hash, - html: html + const span = document.createElement("span"); + span.classList.add("link"); + span.href = "#"; + span.textContent = `${text} (${count})`; + span.addEventListener("click", (event) => { + event.preventDefault(); + setTagFilter(hash); }); - window.qBittorrent.Filters.tagsFilterContextMenu.addTarget(el); - return el; + + const img = document.createElement("img"); + img.src = "images/tags.svg"; + span.prepend(img); + + const listItem = document.createElement("li"); + listItem.id = hash; + listItem.appendChild(span); + + window.qBittorrent.Filters.tagsFilterContextMenu.addTarget(listItem); + return listItem; }; const torrentsCount = torrentsTable.getRowIds().length; @@ -623,15 +644,25 @@ window.addEventListener("DOMContentLoaded", () => { trackerFilterList.getChildren().each(c => c.destroy()); const createLink = function(hash, text, count) { - const html = '' - + '' - + window.qBittorrent.Misc.escapeHtml(text.replace("%1", count)) + ""; - const el = new Element("li", { - id: hash, - html: html + const span = document.createElement("span"); + span.classList.add("link"); + span.href = "#"; + span.textContent = text.replace("%1", count); + span.addEventListener("click", (event) => { + event.preventDefault(); + setTrackerFilter(hash); }); - window.qBittorrent.Filters.trackersFilterContextMenu.addTarget(el); - return el; + + const img = document.createElement("img"); + img.src = "images/trackers.svg"; + span.prepend(img); + + const listItem = document.createElement("li"); + listItem.id = hash; + listItem.appendChild(span); + + window.qBittorrent.Filters.trackersFilterContextMenu.addTarget(listItem); + return listItem; }; const torrentsCount = torrentsTable.getRowIds().length; diff --git a/src/webui/www/private/scripts/contextmenu.js b/src/webui/www/private/scripts/contextmenu.js index 75dbed0fa66..6140e7ced2b 100644 --- a/src/webui/www/private/scripts/contextmenu.js +++ b/src/webui/www/private/scripts/contextmenu.js @@ -428,7 +428,7 @@ window.qBittorrent.ContextMenu ??= (() => { const contextTagList = $("contextTagList"); tagList.forEach((tag, tagHash) => { - const checkbox = contextTagList.getElement(`a[href="#Tag/${tagHash}"] input[type="checkbox"]`); + const checkbox = contextTagList.getElement(`a[href="#Tag/${tag.name}"] input[type="checkbox"]`); const count = tagCount.get(tag.name); const hasCount = (count !== undefined); const isLesser = (count < selectedRows.length); @@ -438,7 +438,7 @@ window.qBittorrent.ContextMenu ??= (() => { const contextCategoryList = document.getElementById("contextCategoryList"); category_list.forEach((category, categoryHash) => { - const categoryIcon = contextCategoryList.querySelector(`a[href$="(${categoryHash});"] img`); + const categoryIcon = contextCategoryList.querySelector(`a[href$="#Category/${category.name}"] img`); const count = categoryCount.get(category.name); const isEqual = ((count !== undefined) && (count === selectedRows.length)); categoryIcon.classList.toggle("highlightedCategoryIcon", isEqual); @@ -448,12 +448,24 @@ window.qBittorrent.ContextMenu ??= (() => { updateCategoriesSubMenu: function(categoryList) { const contextCategoryList = $("contextCategoryList"); contextCategoryList.getChildren().each(c => c.destroy()); - contextCategoryList.appendChild(new Element("li", { - html: 'QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]' - })); - contextCategoryList.appendChild(new Element("li", { - html: 'QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]' - })); + + const createMenuItem = (text, imgURL, clickFn) => { + const anchor = document.createElement("a"); + anchor.textContent = text; + anchor.addEventListener("click", clickFn); + + const img = document.createElement("img"); + img.src = imgURL; + img.alt = text; + anchor.prepend(img); + + const item = document.createElement("li"); + item.appendChild(anchor); + + return item; + }; + contextCategoryList.appendChild(createMenuItem("QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]", "images/list-add.svg", torrentNewCategoryFN)); + contextCategoryList.appendChild(createMenuItem("QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]", "images/edit-clear.svg", () => { torrentSetCategoryFN(0); })); const sortedCategories = []; categoryList.forEach((category, hash) => sortedCategories.push({ @@ -465,14 +477,25 @@ window.qBittorrent.ContextMenu ??= (() => { let first = true; for (const { categoryName, categoryHash } of sortedCategories) { - const el = new Element("li", { - html: `${window.qBittorrent.Misc.escapeHtml(categoryName)}` + const anchor = document.createElement("a"); + anchor.href = `#Category/${categoryName}`; + anchor.textContent = categoryName; + anchor.addEventListener("click", (event) => { + torrentSetCategoryFN(categoryHash); }); + + const img = document.createElement("img"); + img.src = "images/view-categories.svg"; + anchor.prepend(img); + + const setCategoryItem = document.createElement("li"); + setCategoryItem.appendChild(anchor); if (first) { - el.addClass("separator"); + setCategoryItem.addClass("separator"); first = false; } - contextCategoryList.appendChild(el); + + contextCategoryList.appendChild(setCategoryItem); } }, @@ -481,18 +504,23 @@ window.qBittorrent.ContextMenu ??= (() => { while (contextTagList.firstChild !== null) contextTagList.removeChild(contextTagList.firstChild); - contextTagList.appendChild(new Element("li", { - html: '' - + 'QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]' - + " QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]" - + "" - })); - contextTagList.appendChild(new Element("li", { - html: '' - + 'QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]' - + " QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]" - + "" - })); + const createMenuItem = (text, imgURL, clickFn) => { + const anchor = document.createElement("a"); + anchor.textContent = text; + anchor.addEventListener("click", clickFn); + + const img = document.createElement("img"); + img.src = imgURL; + img.alt = text; + anchor.prepend(img); + + const item = document.createElement("li"); + item.appendChild(anchor); + + return item; + }; + contextTagList.appendChild(createMenuItem("QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]", "images/list-add.svg", torrentAddTagsFN)); + contextTagList.appendChild(createMenuItem("QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]", "images/edit-clear.svg", torrentRemoveAllTagsFN)); const sortedTags = []; tagList.forEach((tag, hash) => sortedTags.push({ @@ -503,14 +531,28 @@ window.qBittorrent.ContextMenu ??= (() => { for (let i = 0; i < sortedTags.length; ++i) { const { tagName, tagHash } = sortedTags[i]; - const el = new Element("li", { - html: `` - + ' ' + window.qBittorrent.Misc.escapeHtml(tagName) - + "" + + const input = document.createElement("input"); + input.type = "checkbox"; + input.addEventListener("click", (event) => { + input.checked = !input.checked; + }); + + const anchor = document.createElement("a"); + anchor.href = `#Tag/${tagName}`; + anchor.textContent = tagName; + anchor.addEventListener("click", (event) => { + event.preventDefault(); + torrentSetTagsFN(tagHash, !input.checked); }); + anchor.prepend(input); + + const setTagItem = document.createElement("li"); + setTagItem.appendChild(anchor); if (i === 0) - el.addClass("separator"); - contextTagList.appendChild(el); + setTagItem.addClass("separator"); + + contextTagList.appendChild(setTagItem); } } }); diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 375497078a9..b51dd7317a3 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -333,10 +333,18 @@ window.qBittorrent.DynamicTable ??= (() => { }); const createLi = function(columnName, text) { - const html = '' + window.qBittorrent.Misc.escapeHtml(text) + ""; - return new Element("li", { - html: html - }); + const anchor = document.createElement("a"); + anchor.href = `#${columnName}`; + anchor.textContent = text; + + const img = document.createElement("img"); + img.src = "images/checked-completed.svg"; + anchor.prepend(img); + + const listItem = document.createElement("li"); + listItem.appendChild(anchor); + + return listItem; }; const actions = {}; @@ -2076,8 +2084,7 @@ window.qBittorrent.DynamicTable ??= (() => { }, id: dirImgId }); - const html = dirImg.outerHTML + span.outerHTML; - td.innerHTML = html; + td.replaceChildren(dirImg, span); } } else { // is file @@ -2089,7 +2096,7 @@ window.qBittorrent.DynamicTable ??= (() => { "margin-left": ((node.depth + 1) * 20) } }); - td.innerHTML = span.outerHTML; + td.replaceChildren(span); } }; @@ -2103,7 +2110,7 @@ window.qBittorrent.DynamicTable ??= (() => { text: value, id: fileNameRenamedId, }); - td.innerHTML = span.outerHTML; + td.replaceChildren(span); }; }, @@ -2409,8 +2416,7 @@ window.qBittorrent.DynamicTable ??= (() => { }, id: dirImgId }); - const html = collapseIcon.outerHTML + dirImg.outerHTML + span.outerHTML; - td.innerHTML = html; + td.replaceChildren(collapseIcon, dirImg, span); } } else { @@ -2422,7 +2428,7 @@ window.qBittorrent.DynamicTable ??= (() => { "margin-left": ((node.depth + 1) * 20) } }); - td.innerHTML = span.outerHTML; + td.replaceChildren(span); } }; diff --git a/src/webui/www/private/scripts/prop-files.js b/src/webui/www/private/scripts/prop-files.js index d8111e2d597..80248caf90c 100644 --- a/src/webui/www/private/scripts/prop-files.js +++ b/src/webui/www/private/scripts/prop-files.js @@ -165,32 +165,31 @@ window.qBittorrent.PropFiles ??= (() => { return ($("comboPrio" + id) !== null); }; - const createPriorityOptionElement = function(priority, selected, html) { - const elem = new Element("option"); - elem.value = priority.toString(); - elem.innerHTML = html; - if (selected) - elem.selected = true; - return elem; - }; + const createPriorityCombo = (id, fileId, selectedPriority) => { + const createOption = (priority, isSelected, text) => { + const option = document.createElement("option"); + option.value = priority.toString(); + option.selected = isSelected; + option.textContent = text; + return option; + }; - const createPriorityCombo = function(id, fileId, selectedPriority) { - const select = new Element("select"); + const select = document.createElement("select"); select.id = "comboPrio" + id; select.setAttribute("data-id", id); select.setAttribute("data-file-id", fileId); select.addClass("combo_priority"); select.addEventListener("change", fileComboboxChanged); - createPriorityOptionElement(FilePriority.Ignored, (FilePriority.Ignored === selectedPriority), "QBT_TR(Do not download)QBT_TR[CONTEXT=PropListDelegate]").injectInside(select); - createPriorityOptionElement(FilePriority.Normal, (FilePriority.Normal === selectedPriority), "QBT_TR(Normal)QBT_TR[CONTEXT=PropListDelegate]").injectInside(select); - createPriorityOptionElement(FilePriority.High, (FilePriority.High === selectedPriority), "QBT_TR(High)QBT_TR[CONTEXT=PropListDelegate]").injectInside(select); - createPriorityOptionElement(FilePriority.Maximum, (FilePriority.Maximum === selectedPriority), "QBT_TR(Maximum)QBT_TR[CONTEXT=PropListDelegate]").injectInside(select); + select.appendChild(createOption(FilePriority.Ignored, (FilePriority.Ignored === selectedPriority), "QBT_TR(Do not download)QBT_TR[CONTEXT=PropListDelegate]")); + select.appendChild(createOption(FilePriority.Normal, (FilePriority.Normal === selectedPriority), "QBT_TR(Normal)QBT_TR[CONTEXT=PropListDelegate]")); + select.appendChild(createOption(FilePriority.High, (FilePriority.High === selectedPriority), "QBT_TR(High)QBT_TR[CONTEXT=PropListDelegate]")); + select.appendChild(createOption(FilePriority.Maximum, (FilePriority.Maximum === selectedPriority), "QBT_TR(Maximum)QBT_TR[CONTEXT=PropListDelegate]")); // "Mixed" priority is for display only; it shouldn't be selectable - const mixedPriorityOption = createPriorityOptionElement(FilePriority.Mixed, (FilePriority.Mixed === selectedPriority), "QBT_TR(Mixed)QBT_TR[CONTEXT=PropListDelegate]"); + const mixedPriorityOption = createOption(FilePriority.Mixed, (FilePriority.Mixed === selectedPriority), "QBT_TR(Mixed)QBT_TR[CONTEXT=PropListDelegate]"); mixedPriorityOption.disabled = true; - mixedPriorityOption.injectInside(select); + select.appendChild(mixedPriorityOption); return select; }; diff --git a/src/webui/www/private/scripts/prop-general.js b/src/webui/www/private/scripts/prop-general.js index 2ef547f4ff8..1b7148547c6 100644 --- a/src/webui/www/private/scripts/prop-general.js +++ b/src/webui/www/private/scripts/prop-general.js @@ -67,7 +67,7 @@ window.qBittorrent.PropGeneral ??= (() => { $("torrent_hash_v1").textContent = ""; $("torrent_hash_v2").textContent = ""; $("save_path").textContent = ""; - $("comment").innerHTML = ""; + $("comment").textContent = ""; $("private").textContent = ""; piecesBar.clear(); }; diff --git a/src/webui/www/private/scripts/prop-webseeds.js b/src/webui/www/private/scripts/prop-webseeds.js index 5c0d4e64d24..1feb430dc62 100644 --- a/src/webui/www/private/scripts/prop-webseeds.js +++ b/src/webui/www/private/scripts/prop-webseeds.js @@ -63,7 +63,7 @@ window.qBittorrent.PropWebseeds ??= (() => { updateRow: function(tr, row) { const tds = tr.getElements("td"); for (let i = 0; i < row.length; ++i) - tds[i].innerHTML = row[i]; + tds[i].textContent = row[i]; return true; }, @@ -78,9 +78,9 @@ window.qBittorrent.PropWebseeds ??= (() => { const tr = new Element("tr"); this.rows.set(url, tr); for (let i = 0; i < row.length; ++i) { - const td = new Element("td"); - td.innerHTML = row[i]; - td.injectInside(tr); + const td = document.createElement("td"); + td.textContent = row[i]; + tr.appendChild(td); } tr.injectInside(this.table); }, diff --git a/src/webui/www/private/scripts/search.js b/src/webui/www/private/scripts/search.js index c02fcfcf2cc..0bd541f5ff7 100644 --- a/src/webui/www/private/scripts/search.js +++ b/src/webui/www/private/scripts/search.js @@ -174,16 +174,15 @@ window.qBittorrent.Search ??= (() => { tabElem.appendChild(getStatusIconElement("QBT_TR(Searching...)QBT_TR[CONTEXT=SearchJobWidget]", "images/queued.svg")); - const liElement = new Element("li", { - id: newTabId, - class: "selected", - html: tabElem.outerHTML, - }); - liElement.addEventListener("click", (e) => { - setActiveTab(liElement); + const listItem = document.createElement("li"); + listItem.id = newTabId; + listItem.classList.add("selected"); + listItem.addEventListener("click", (e) => { + setActiveTab(listItem); $("startSearchButton").textContent = "QBT_TR(Search)QBT_TR[CONTEXT=SearchEngineWidget]"; }); - $("searchTabs").appendChild(liElement); + listItem.appendChild(tabElem); + $("searchTabs").appendChild(listItem); // unhide the results elements if (numSearchTabs() >= 1) { @@ -194,7 +193,7 @@ window.qBittorrent.Search ??= (() => { } // select new tab - setActiveTab(liElement); + setActiveTab(listItem); searchResultsTable.clear(); resetFilters(); @@ -577,26 +576,27 @@ window.qBittorrent.Search ??= (() => { } }; - const getSearchCategories = function() { - const populateCategorySelect = function(categories) { - const categoryHtml = []; - categories.each((category) => { - const option = new Element("option"); + const getSearchCategories = () => { + const populateCategorySelect = (categories) => { + const categoryOptions = []; + + for (const category of categories) { + const option = document.createElement("option"); option.value = category.id; option.textContent = category.name; - categoryHtml.push(option.outerHTML); - }); + categoryOptions.push(option); + }; // first category is "All Categories" - if (categoryHtml.length > 1) { + if (categoryOptions.length > 1) { // add separator - const option = new Element("option"); + const option = document.createElement("option"); option.disabled = true; option.textContent = "──────────"; - categoryHtml.splice(1, 0, option.outerHTML); + categoryOptions.splice(1, 0, option); } - $("categorySelect").innerHTML = categoryHtml.join(""); + $("categorySelect").replaceChildren(...categoryOptions); }; const selectedPlugin = $("pluginsSelect").value; @@ -629,7 +629,16 @@ window.qBittorrent.Search ??= (() => { url: new URI("api/v2/search/plugins"), method: "get", noCache: true, - onSuccess: function(response) { + onSuccess: (response) => { + const createOption = (text, value, disabled = false) => { + const option = document.createElement("option"); + if (value !== undefined) + option.value = value; + option.textContent = text; + option.disabled = disabled; + return option; + }; + if (response !== prevSearchPluginsResponse) { prevSearchPluginsResponse = response; searchPlugins.length = 0; @@ -637,9 +646,9 @@ window.qBittorrent.Search ??= (() => { searchPlugins.push(plugin); }); - const pluginsHtml = []; - pluginsHtml.push(''); - pluginsHtml.push(''); + const pluginOptions = []; + pluginOptions.push(createOption("QBT_TR(Only enabled)QBT_TR[CONTEXT=SearchEngineWidget]", "enabled")); + pluginOptions.push(createOption("QBT_TR(All plugins)QBT_TR[CONTEXT=SearchEngineWidget]", "all")); const searchPluginsEmpty = (searchPlugins.length === 0); if (!searchPluginsEmpty) { @@ -656,14 +665,14 @@ window.qBittorrent.Search ??= (() => { allPlugins.each((plugin) => { if (plugin.enabled === true) - pluginsHtml.push(""); + pluginOptions.push(createOption(plugin.fullName, plugin.name)); }); - if (pluginsHtml.length > 2) - pluginsHtml.splice(2, 0, ""); + if (pluginOptions.length > 2) + pluginOptions.splice(2, 0, createOption("──────────", undefined, true)); } - $("pluginsSelect").innerHTML = pluginsHtml.join(""); + $("pluginsSelect").replaceChildren(...pluginOptions); $("searchPattern").disabled = searchPluginsEmpty; $("categorySelect").disabled = searchPluginsEmpty;