Compare commits

..

3 Enmetoj

Author SHA1 Message Date
Jaidyn Ann 2c73b5a956 Write placeholders for download-command inputs 2024-03-03 22:56:30 -06:00
Jaidyn Ann 0ba9d32915 Add support for download-commands
Now you can set commands to run on the beginning
or completion of a file-download.
2024-03-03 22:48:34 -06:00
Jaidyn Ann 596b383a54 Add options-page section for “Download commands”
… though it is completely non-functional, as yet.
2024-03-03 22:13:04 -06:00
6 changed files with 290 additions and 26 deletions

View File

@ -101,6 +101,51 @@
"description": "Displayed placeholder for options-page input for regex." "description": "Displayed placeholder for options-page input for regex."
}, },
"optionsTitleDownload": {
"message": "Download commands",
"description": "Title of options-page section for download commands."
},
"optionsDescDownload": {
"message": "Assign commands to be executed for certain URLs (by regex) when downloads are initiated or finished. In commands, $URL will be replaced with the downloads URL, and $FILE with the target file.",
"description": "Description of options-page section for download commands."
},
"optionsHeadDownloadRule": {
"message": "URL regex",
"description": "Header of table-element for options-page section for download commands."
},
"optionsHeadDownloadCommand": {
"message": "Shell command",
"description": "Header of table-element for options-page section for download commands."
},
"optionsHeadDownloadType": {
"message": "Run when…",
"description": "Header of table-element for options-page section for download commands."
},
"optionsDownloadWhenStarted": {
"message": "… started",
"description": "Member of menu-list for options-page section for download commands. Completes the sentence, “Run when…”"
},
"optionsDownloadWhenFinished": {
"message": "… finished",
"description": "Member of menu-list for options-page section for download commands. Completes the sentence, “Run when…”"
},
"optionsPlaceholderDownloadRegex": {
"message": ".*.png",
"description": "Displayed placeholder for options-page input for download commands regex."
},
"optionsPlaceholderDownloadCommand": {
"message": "echo $URL > $FILE.url",
"description": "Displayed placeholder for options-page input for download command."
},
"optionsSaveButton": { "optionsSaveButton": {
"message": "Apply", "message": "Apply",
"description": "Name of button in options-page to save changes." "description": "Name of button in options-page to save changes."

View File

@ -101,6 +101,51 @@
"description": "Provizora valoro ĉe agordopaĝo por enigo de reteja regula esprimo." "description": "Provizora valoro ĉe agordopaĝo por enigo de reteja regula esprimo."
}, },
"optionsTitleDownload": {
"message": "Elŝutaj ŝel-ordonoj",
"description": "Titolo de agordo-parto por elŝutaj ordonoj."
},
"optionsDescDownload": {
"message": "Agordi ordonojn kiuj ruliĝu dum komenciĝo aŭ finiĝo de elŝutado, laŭ URL (regulesprime). En ordonoj, «$URL» anstataŭiĝos per la elŝuta URL, kaj «$FILE» per la elŝuta dosiervojo.",
"description": "Priskribo de agordo-parto por elŝutaj ordonoj."
},
"optionsHeadDownloadRule": {
"message": "URL regula esprimo",
"description": "Titolo de tabelo-kolumno ĉe agordo-parto por elŝutaj ordonoj."
},
"optionsHeadDownloadCommand": {
"message": "Ŝel-ordono",
"description": "Titolo de tabelo-kolumno ĉe agordo-parto por elŝutaj ordonoj."
},
"optionsHeadDownloadType": {
"message": "Rulu je…",
"description": "Titolo de tabelo-kolumno ĉe agordo-parto por elŝutaj ordonoj."
},
"optionsDownloadWhenStarted": {
"message": "… komenciĝo",
"description": "Menuero ĉe agordo-parto por elŝutaj ordonoj. Finas la frazon, «Rulu je…»"
},
"optionsDownloadWhenFinished": {
"message": "… finiĝo",
"description": "Menuero ĉe agordo-parto por elŝutaj ordonoj. Finas la frazon, «Rulu je…»"
},
"optionsPlaceholderDownloadRegex": {
"message": ".*.png",
"description": "Provizora valoro ĉe agordopaĝo por enigo de regula esprimo por elŝuta ordono."
},
"optionsPlaceholderDownloadCommand": {
"message": "echo $URL > $FILE.url",
"description": "Provizora valoro ĉe agordopaĝo por enigo de elŝuta ordono."
},
"optionsSaveButton": { "optionsSaveButton": {
"message": "Konservi", "message": "Konservi",
"description": "Nomo de butono ĉe agordopaĝo por konservi ŝanĝojn." "description": "Nomo de butono ĉe agordopaĝo por konservi ŝanĝojn."

View File

@ -48,12 +48,42 @@ function getUrlCommands(url) {
} }
// Execute the given command string, subsituting “$URL” with the given url. // Return the download-command string (if any) associated with a URL and type
function runCommand(command, url) { // integer (0→started; 1→finished).
function getDownloadCommand(url, type) {
let matchCommand = undefined;
let matchRegex = "";
try {
let savedDownloads = savedArray("downloadCommands");
// Find the most-applicable command.
for (regexCommandType of savedDownloads) {
let regex = regexCommandType[0];
let match = url.match(regex);
let compared = compareRegexComplexity(matchRegex, regex);
if ((match && (compared == 0 || compared == 1))
&& (regexCommandType[2] == type))
{
matchCommand = regexCommandType[1];
matchRegex = regex;
}
}
} catch {};
return matchCommand;
}
// Execute the given command string, subsituting “$URL” with url and
// “$FILE” with filepath.
function runCommand(command, url, filepath) {
if (!port) if (!port)
initShellfoxProgram(); initShellfoxProgram();
if (command && port) if (command && port)
port.postMessage(command.replaceAll("$URL", url)); port.postMessage(command
.replaceAll("$URL", url)
.replaceAll("${URL}", url)
.replaceAll("$FILE", filepath)
.replaceAll("${FILE}", filepath));
} }
@ -101,8 +131,9 @@ function hideLinkContextMenuItem() {
} }
// (Re-)Create the menu-items for each context menu.
function createCommandMenuItems() { function createCommandMenuItems() {
let savedCommands = savedArray("commands"); let savedCommands = savedArray("commands") || [];
for (let i = 0; i < savedCommands.length; i++) { for (let i = 0; i < savedCommands.length; i++) {
let nameCommandPair = savedCommands[i]; let nameCommandPair = savedCommands[i];
let name = nameCommandPair[0]; let name = nameCommandPair[0];
@ -215,6 +246,26 @@ browser.menus.onClicked.addListener((info, tab) => {
}); });
// When a download starts, run any applicable download commands.
browser.downloads.onCreated.addListener((downloadItem) => {
let command = getDownloadCommand(downloadItem.url, 0);
if (command)
runCommand(command, downloadItem.url, downloadItem.filename);
});
// When a download completes, run any applicable download commands.
browser.downloads.onChanged.addListener((downloadDelta) => {
browser.downloads.search({ "id": downloadDelta.id }).then((downloadItems) => {
if (downloadDelta.state.current == "complete" && downloadItems.length > 0) {
let command = getDownloadCommand(downloadItems[0].url, 1);
if (command)
runCommand(command, downloadItems[0].url, downloadItems[0].filename);
}
})
});
// Whenever settings (commands) are updated, repopulate context-menus items. // Whenever settings (commands) are updated, repopulate context-menus items.
window.addEventListener("storage", (e) => { window.addEventListener("storage", (e) => {
createCommandMenuItems(); createCommandMenuItems();

View File

@ -1,7 +1,7 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "__MSG_extensionName__", "name": "__MSG_extensionName__",
"version": "0.1", "version": "0.12",
"description": "__MSG_extensionDescription__", "description": "__MSG_extensionDescription__",
"homepage_url": "https://hak.xwx.moe/jadedctrl/shellfox", "homepage_url": "https://hak.xwx.moe/jadedctrl/shellfox",
@ -25,6 +25,7 @@
"permissions": [ "permissions": [
"activeTab", "activeTab",
"downloads",
"nativeMessaging", "nativeMessaging",
"menus", "menus",
"tabs" "tabs"

View File

@ -4,6 +4,7 @@
<meta charset="utf8"> <meta charset="utf8">
</head> </head>
<body> <body>
<article>
<section> <section>
<h3 id="commandTitle">Shell commands</h3> <h3 id="commandTitle">Shell commands</h3>
<p id="commandP">Add shell commands, to be executed with a pages URL as its argument. In a command, $URL will be replaced with the target pages URL.</p> <p id="commandP">Add shell commands, to be executed with a pages URL as its argument. In a command, $URL will be replaced with the target pages URL.</p>
@ -28,6 +29,22 @@
</table> </table>
<button style="width: 5em;" id="save-regex">Apply</button> <button style="width: 5em;" id="save-regex">Apply</button>
</section> </section>
</article>
<hr/>
<article>
<h3 id="downloadTitle">Download commands</h3>
<p id="downloadP">Assign commands to be executed for certain URLs (by regex) when downloads are initiated or finished. In commands, $URL will be replaced with the downloads URL, and $FILE with the target file.</p>
<table id="downloadTable">
<tr>
<th id="downloadTypeTh">On start/finish</th>
<th id="downloadRegexTh">URL regex</th>
<th id="downloadShellTh">Shell command</th>
</tr>
</table>
<button style="width: 5em;" id="save-downloads">Apply</button>
</article>
<script src="options.js"></script> <script src="options.js"></script>

View File

@ -35,6 +35,22 @@ function saveRegexRules() {
} }
// Iterate over the commands-table and save each of the users valid
// name+command rows to storage.
function saveDownloadCommands() {
let downloads = [];
for (downloadTr of document.getElementsByClassName("downloadCommandRow")) {
let regex = downloadTr.getElementsByClassName("regex")[0].value;
let command = downloadTr.getElementsByClassName("command")[0].value;
let type = downloadTr.getElementsByClassName("startFinishMenu")[0].value;
if (regex && command && type)
downloads.push([regex, command, type]);
}
console.log(downloads);
localStorage.setItem("downloadCommands", JSON.stringify(downloads));
}
// Read the users saved name+command pairs from storage, and populate the // Read the users saved name+command pairs from storage, and populate the
// command-table with them. // command-table with them.
function populateCommandTable() { function populateCommandTable() {
@ -67,13 +83,29 @@ function populateRegexTable() {
} }
// Read the users saved type+name+command pairs from storage, and populate the
// downloads-table with them.
function populateDownloadTable() {
let downloadTable = document.getElementById("downloadTable");
try {
let savedDownloads = savedArray("downloadCommands") || [];
for (regexCommandType of savedDownloads) {
let downloadTr = createDownloadTr(regexCommandType[0], regexCommandType[1], regexCommandType[2]);
downloadTable.appendChild(downloadTr);
}
} catch {};
// And yet again! Have spares!!!
downloadTable.appendChild(createDownloadTr("", "", 0));
}
// Create a <select> drop-down menu containing all of the users commands. // Create a <select> drop-down menu containing all of the users commands.
function createCommandMenu() { function createCommandMenu() {
let commandMenu = document.createElement("SELECT"); let commandMenu = document.createElement("SELECT");
commandMenu.setAttribute("class", "commandMenu"); commandMenu.setAttribute("class", "commandMenu");
commandMenu.setAttribute("type", "text"); commandMenu.setAttribute("type", "text");
let savedCommands = savedArray("commands"); let savedCommands = savedArray("commands") || [];
for (let i = 0; i < savedCommands.length; i++) { for (let i = 0; i < savedCommands.length; i++) {
let commandOption = document.createElement("OPTION"); let commandOption = document.createElement("OPTION");
commandOption.setAttribute("value", i); commandOption.setAttribute("value", i);
@ -84,6 +116,66 @@ function createCommandMenu() {
} }
function createDownloadTypeMenu(type) {
let typeMenu = document.createElement("SELECT");
typeMenu.setAttribute("class", "startFinishMenu");
typeMenu.setAttribute("type", "text");
let onStartOption = document.createElement("OPTION");
onStartOption.setAttribute("value", 0);
onStartOption.text = browser.i18n.getMessage("optionsDownloadWhenStarted");
typeMenu.appendChild(onStartOption);
let onEndOption = document.createElement("OPTION");
onEndOption.setAttribute("value", 1);
onEndOption.text = browser.i18n.getMessage("optionsDownloadWhenFinished");
typeMenu.appendChild(onEndOption);
try {
typeMenu.childNodes[type].setAttribute("selected", true);
} catch { };
return typeMenu;
}
function createDownloadTr(regex, command, type) {
let typeMenu = createDownloadTypeMenu(type);
let typeTd = document.createElement("TD");
typeTd.appendChild(typeMenu);
let regexInput = document.createElement("INPUT");
regexInput.setAttribute("class", "regex");
regexInput.setAttribute("type", "text");
regexInput.setAttribute("placeholder",
browser.i18n.getMessage("optionsPlaceholderDownloadRegex"));
if (regex && command && type)
regexInput.setAttribute("value", regex);
let regexTd = document.createElement("TD");
regexTd.appendChild(regexInput);
let commandInput = document.createElement("INPUT");
commandInput.setAttribute("class", "command");
commandInput.setAttribute("type", "text");
commandInput.setAttribute("placeholder",
browser.i18n.getMessage("optionsPlaceholderDownloadCommand"));
if (regex && command && type)
commandInput.setAttribute("value", command);
let commandTd = document.createElement("TD");
commandTd.appendChild(commandInput);
let tr = document.createElement("TR");
tr.setAttribute("class", "downloadCommandRow");
tr.appendChild(typeTd);
tr.appendChild(regexTd);
tr.appendChild(commandTd);
return tr;
}
// Create a table-row for the command-table, with the command & regex inputs // Create a table-row for the command-table, with the command & regex inputs
// values set to the given parameters. If they are undefined, the inputs will // values set to the given parameters. If they are undefined, the inputs will
// have no value. // have no value.
@ -97,11 +189,13 @@ function createRegexTr(regex, command_i, commandMenu) {
let regexTd = document.createElement("TD"); let regexTd = document.createElement("TD");
regexTd.appendChild(regexInput); regexTd.appendChild(regexInput);
let commandTd = document.createElement("TD"); let commandTd = document.createElement("TD");
try { try {
commandMenu.childNodes[command_i].setAttribute("selected", true); commandMenu.childNodes[command_i].setAttribute("selected", true);
} catch { }; } catch { };
commandTd.appendChild(commandMenu); commandTd.appendChild(commandMenu);
let tr = document.createElement("TR"); let tr = document.createElement("TR");
tr.setAttribute("class", "regexCommandRow"); tr.setAttribute("class", "regexCommandRow");
tr.appendChild(regexTd); tr.appendChild(regexTd);
@ -156,8 +250,15 @@ function i18nPage() {
document.getElementById("ruleRegexTh").innerText = browser.i18n.getMessage("optionsHeadRuleName"); document.getElementById("ruleRegexTh").innerText = browser.i18n.getMessage("optionsHeadRuleName");
document.getElementById("ruleShellTh").innerText = browser.i18n.getMessage("optionsHeadRuleCommand"); document.getElementById("ruleShellTh").innerText = browser.i18n.getMessage("optionsHeadRuleCommand");
document.getElementById("downloadTitle").innerText = browser.i18n.getMessage("optionsTitleDownload");
document.getElementById("downloadP").innerText = browser.i18n.getMessage("optionsDescDownload");
document.getElementById("downloadRegexTh").innerText = browser.i18n.getMessage("optionsHeadDownloadRule");
document.getElementById("downloadTypeTh").innerText = browser.i18n.getMessage("optionsHeadDownloadType");
document.getElementById("downloadShellTh").innerText = browser.i18n.getMessage("optionsHeadDownloadCommand");
document.getElementById("save-cmd").innerText = browser.i18n.getMessage("optionsSaveButton"); document.getElementById("save-cmd").innerText = browser.i18n.getMessage("optionsSaveButton");
document.getElementById("save-regex").innerText = browser.i18n.getMessage("optionsSaveButton"); document.getElementById("save-regex").innerText = browser.i18n.getMessage("optionsSaveButton");
document.getElementById("save-downloads").innerText = browser.i18n.getMessage("optionsSaveButton");
} }
@ -168,10 +269,14 @@ document.addEventListener("click", e => {
} else if (e.target.id == ("save-regex")) { } else if (e.target.id == ("save-regex")) {
saveRegexRules(); saveRegexRules();
location.reload(); location.reload();
} } else if (e.target.id == ("save-downloads")) {
saveDownloadCommands();
location.reload();
};
}); });
populateCommandTable(); populateCommandTable();
populateRegexTable(); populateRegexTable();
populateDownloadTable();
i18nPage(); i18nPage();