diff --git a/background_scripts/all_commands.js b/background_scripts/all_commands.js index 5abab2a16..18aee871e 100644 --- a/background_scripts/all_commands.js +++ b/background_scripts/all_commands.js @@ -130,6 +130,10 @@ const allCommands = [ desc: "Go up the URL hierarchy", group: "navigation", advanced: true, + options: { + popAnchor: "Remove the anchor/fragment/hash from the URL, if present.", + popQuery: "Remove query parameters from the URL, if present.", + }, }, { diff --git a/content_scripts/mode_normal.js b/content_scripts/mode_normal.js index eaa649c3f..8351b2c0f 100644 --- a/content_scripts/mode_normal.js +++ b/content_scripts/mode_normal.js @@ -134,17 +134,35 @@ const NormalModeCommands = { }, // Url manipulation. - goUp(count) { - let url = globalThis.location.href; - if (url.endsWith("/")) { - url = url.substring(0, url.length - 1); + goUp(count, { registryEntry }) { + let c = count; + const url = new URL(globalThis.location.href); + + // Pop anchor. + if (c > 0 && registryEntry.options.popAnchor && url.hash !== "") { + url.hash = ""; + --c; } - let urlsplit = url.split("/"); - // make sure we haven't hit the base domain yet - if (urlsplit.length > 3) { - urlsplit = urlsplit.slice(0, Math.max(3, urlsplit.length - count)); - globalThis.location.href = urlsplit.join("/"); + // Pop query params. + if (c > 0 && registryEntry.options.popQuery && url.search !== "") { + url.search = ""; + url.hash = ""; + --c; + } + + // Pop path segments. + if (c > 0 && url.pathname !== "/") { + const initialSegments = url.pathname.substring(1).split("/"); + const segments = initialSegments.slice(0, -c); + url.pathname = `/${segments.join("/")}`; + url.search = ""; + url.hash = ""; + c -= initialSegments.length - segments.length; + } + + if (globalThis.location.href !== url.toString()) { + globalThis.location.href = url.toString(); } },