Initial commit

This commit is contained in:
Ward Truyen 2024-09-21 16:23:13 +02:00
commit 7246af80e1
24 changed files with 2596 additions and 0 deletions

1
.gitignore vendored Executable file
View File

@ -0,0 +1 @@
/.idea/

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Ward Truyen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

66
README.md Normal file
View File

@ -0,0 +1,66 @@
```
* _ . _ _____ .----..----. .-. .-..-..-. .-. .--. .-.
* | |/ \| |[_ _]| {__ | {) }| .`-'. ||~|| .`| | / {} \ | |
* | ,-, | | | | {__ | .-. \| |\ /| || || |\ |/ /\ \| `--.
* '-' `-' '-' `----'`-' `-'`-' ` `-'`-'`-' `-'`-' `-'`----'
```
# For terminal fun on the web.
## About
WTerminal is written in JavaScript and CSS. For use in websites. No compiling needed. 1 big class.
This can be used as a dropdown terminal and as a terminal on the page(static), at the same time.
It provides easy tools for printing- and changing public variables(javascript) and cookies.
The terminal is useful for testing/logging-debugging without opening the console every time.
WTerminal instances can be augmented with extention-commands and extention-aliases without changing the terminal.js code.
## Screenshots
Sample terminal as a static element
![Screenshot static](public/screenshot1.png)
Sample terminal as a dropdown after playing ping pong
![Screenshot dropdown](public/screenshot2.png)
Sample terminal as a dropdown + darkmode
![Screenshot dropdown](public/screenshot5.png)
Terminal for testing
![Screenshot dropdown](public/screenshot3.png)
Terminal for testing + darkmode
![Screenshot dropdown](public/screenshot4.png)
Terminal as extention (surfing youtube)
![Screenshot dropdown](public/screenshot6.png)
## Testing
To test WTerminal I use serve on the src folder, [the npm package serve](https://www.npmjs.com/package/serve).
```
serve src/
```
To install serve i suggest the following command:
```
npm install --global serve
```
## Terminal as WebExtension
You can also use this terminal-project as a (Temporary) browser-extension. Get a terminal everywhere you surf.
- [firefox addon paage](https://addons.mozilla.org/en-US/firefox/addon/wterminal/)
How to documentation:
- [MDN WebExtension](https://developer.mozilla.org/en-US/diocs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension)
- [firefox info](https://firefox-source-docs.mozilla.org/devtools-user/about_colon_debugging/index.html#this-firefox)
- [firefox about:debugging](about:debugging)

BIN
icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

25
manifest.json Normal file
View File

@ -0,0 +1,25 @@
{
"manifest_version": 2,
"name": "WTerminal",
"version": "1.0.1",
"description": "Adds a dropdown terminal to all webpages",
"author": "Ward Truyen",
"icons": {
"48": "icons/icon.png"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["src/wterminal.js", "src/wterminal-addon.js",
"src/termext/ext-cookies.js", "src/termext/ext-eval.js",
"src/termext/ext-popvar.js", "src/termext/ext-passw.js", "src/termext/ext-timerdebug.js",
"src/termext/ext-transfer.js", "src/termext/ext-stresstest.js", "src/termext/ext-variables.js"],
"css": ["src/wterminal.css"]
}
]
}

BIN
public/screenshot1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
public/screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
public/screenshot3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

BIN
public/screenshot4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

BIN
public/screenshot5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
public/screenshot6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

103
src/termext/ext-cookies.js Normal file
View File

@ -0,0 +1,103 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: Cookie data managing
*/
if (WTerminal) {
const initTerminalCookieCommands = function() {
//getcookie
WTerminal.terminalAddCommand("getcookie", (term, argLine) => {
function getCookie(name, defaultValue = undefined) {
if (name === null || name === undefined || typeof name != "string" || name == '') {
console.log('error: cookie needs a name');
return;
}
let pair = document.cookie.split(/; */).find((row) => row.startsWith(name + '='))
//console.log("cookie pair: ", pair);
if (pair === undefined)
return defaultValue;
else
return pair.split('=')[1];
}
return getCookie(...WTerminal.splitToArguments(argLine));
});
//setcookie
WTerminal.terminalAddCommand("setcookie", (term, argLine) => {
function setCookie(name, value = 1, days = 7) {
let date = new Date();
date.setDate(date.getDate() + days); // add x days to date
document.cookie = name + "=" + value + "; expires=" + date.toUTCString() + "; SameSite=strict; Secure";
}
return setCookie(...WTerminal.splitToArguments(argLine));
});
//removecookies
WTerminal.terminalAddCommand("removecookies", function(term) {
function removeCookie(name) {
document.cookie = name + "=0; max-age=-1" + "; SameSite=strict; Secure";
}
if (document.cookie == '') {
term.printError("No cookies found.");
return;
}
const cookies = document.cookie.split(/; */);
term.printLn("Cookies found: " + cookies.length);
cookies.forEach(c => {
const name = c.split('=')[0];
term.printLn("removing: " + name);
removeCookie(name);
});
});
//cookies
WTerminal.terminalAddCommand("cookies",
function(term, argLine) {
if (document.cookie === '') {
if (!argLine.includes("-s")) term.printError("No cookies found.")
} else {
term.printList(document.cookie.split(/; */), false);
}
},
function(term) {
term.printLn("Usage:");
term.printLn(" cookies //Prints all cookies.");
term.printLn(" cookies -s //(silent)Prints only cookies, no error.");
});
//doCookiesWork
WTerminal.terminalAddCommand("docookieswork", (term) => {
function getCookie(name, defaultValue = undefined) {
if (name === null || name === undefined || typeof name != "string" || name == '') {
console.log('error: cookie needs a name');
return;
}
let pair = document.cookie.split(/; */).find((row) => row.startsWith(name + '='))
//console.log("cookie pair: ", pair);
if (pair === undefined)
return defaultValue;
else
return pair.split('=')[1];
}
function setCookie(name, value = 1, days = 7) {
let date = new Date();
date.setDate(date.getDate() + days); // add x days to date
document.cookie = name + "=" + value + "; expires=" + date.toUTCString() + "; SameSite=strict; Secure";
}
function removeCookie(name) {
document.cookie = name + "=0; max-age=-1" + "; SameSite=strict; Secure";
}
const name = "testCookie";
setCookie(name);
let itWorks = getCookie(name) !== undefined;
if (itWorks) {
removeCookie(name);
}
term.printLn(itWorks);
return itWorks;
});
}
//init
if (document.body) {
initTerminalCookieCommands();
} else {
window.addEventListener("load", initTerminalCookieCommands);
}
}

32
src/termext/ext-eval.js Normal file
View File

@ -0,0 +1,32 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This adds the eval command to the terminal.
*/
{
const evalHelp = function(term) {
term.printLn("Uses the function eval(string) on the argLine");
}
const evalRun = function(term, argLine) {
try {
const result = eval(argLine);
term.printVar(result, '`' + argLine + '`');
return result;
} catch (error) {
term.printError(`Eval error: \`${argLine}\` -> ${error.message}`);
}
};
const initTerminalEvalCommand = function() {
if (WTerminal === undefined) { //is WTerminal not available?
console.error("WTerminal is missing!");
return;
}
WTerminal.terminalAddCommand("eval", evalRun, evalHelp);
};
//init
if (document.body) {
initTerminalEvalCommand();
} else {
window.addEventListener("load", initTerminalEvalCommand);
}
}

View File

@ -0,0 +1,42 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This adds the test command to the terminal.
* current test is to get local variables
*/
{
const help = function(term) {
term.printLn("Runs a test (nothing).");
}
const run = function(term) {
term.printLn("Feature test warning: Under construction, can have unexpected results, errors and crashes.");
// todo: add test ... like throw errors and stuff
// throw {name : "NotImplementedError", message : "too lazy to implement"};
// throw new Error("too lazy to implement", "some name perhaps?");
// class TerminalError extends Error{
// constructor(msg, name="TerminalError"){
// super(msg);
// this.name = name;
// }
// }
// throw new TerminalError("my message", "MyName");
let num = 1;
num.toPrecision(500);
};
const addExtention = function() {
if (WTerminal === undefined) { //is WTerminal not available?
console.error("AddExtention Error: WTerminal is missing!");
return;
}
WTerminal.terminalAddCommand("featuretest", run, help);
//add alias
WTerminal.terminalAddAlias("ft", "featuretest");
};
//init
if (document.body) {
addExtention();
} else {
window.addEventListener("load", addExtention);
}
}

96
src/termext/ext-passw.js Normal file
View File

@ -0,0 +1,96 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This adds a password generatiing- and testing function
*/
{
const generatePasswordHelp = function(term) {
term.printLn("Generates a new password and prints it out. But also copies it for you.");
}
const generatePassword = function(term, argLine) {
const UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz";
const NUMBERS = "1234567890";
const SPECIAL_CHARACTERS = "!@#$%^&*()_-+=,./<>\\|[]{}";
const ALL_CHARACTERS = UPPERCASE_LETTERS + LOWERCASE_LETTERS + NUMBERS + SPECIAL_CHARACTERS;
let passwd = "";
let length = 15;
for (let i = 0; i < length; i++) {
passwd += ALL_CHARACTERS.charAt(Math.floor(Math.random() * ALL_CHARACTERS.length));
}
const el = document.createElement('span');
el.innerText = passwd;
// el.style.fontSize = 'x-large';
el.style.paddingLeft = '1em';
term.printLn("Generated password(length=" + length + "):", el);
navigator.clipboard.writeText(passwd);
};
const isPasswordSafeHelp = function(term) {
term.printLn("Checks if a password is posted as breached by pwnedpasswords.com");
};
const isPasswordSafe = function(term, argLine) {
const sha1 = function(string) {
const buffer = new TextEncoder("utf-8").encode(string);
return crypto.subtle.digest("SHA-1", buffer).then(function(buffer) {
// Get the hex code
let hexCodes = [];
const padding = '00000000'
const view = new DataView(buffer);
for (let i = 0; i < view.byteLength; i += 4) {
// Using getUint32 reduces the number of iterations needed (we process 4 bytes each time)
const value = view.getUint32(i)
// toString(16) will give the hex representation of the number without padding
const stringValue = value.toString(16)
// We use concatenation and slice for padding
const paddedValue = (padding + stringValue).slice(-padding.length)
hexCodes.push(paddedValue);
}
// Join all the hex strings into one
return hexCodes.join("");
});
};
const password = argLine;
term.printLn("Checking password, please wait...");
sha1(password).then((hash) => {
fetch("https://api.pwnedpasswords.com/range/" + hash.substr(0, 5)).then((response) => response.text()).then((response) => {
const respLines = response.split('\n');
const hashSub = hash.slice(5).toUpperCase();
let isSafe = true;
// console.log('hash: ' + hashSub);
for (let i in respLines) {
// console.log(i+': '+respLines[i])
if (respLines[i].substring(0, hashSub.length) == hashSub) {
isSafe = false;
break;
}
}
if (isSafe) {
term.printLn("Password is safe.");
} else {
term.printLn("Password has been breached.");
}
}).catch((err) => {
outputSafe.innerText = "Could not check if password is safe: " + err;
});
});
};
const initTerminalPasswCommands = function() {
if (WTerminal === undefined) { //is WTerminal not available?
console.error("WTerminal is missing!");
return;
}
WTerminal.terminalAddCommand("generatepassword", generatePassword, generatePasswordHelp);
WTerminal.terminalAddCommand("ispasswordsafe", isPasswordSafe, isPasswordSafeHelp);
};
//init
if (document.body) {
initTerminalPasswCommands();
} else {
window.addEventListener("load", initTerminalPasswCommands);
}
}

164
src/termext/ext-popvar.js Normal file
View File

@ -0,0 +1,164 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This adds the test command to the terminal.
* current test is to get local variables
*/
{// this code block hides the variables below from other scripts.
const runPopVar = function(term, argLine) {
const PAUSE_SYMBOL = "&#9724;";
const INTERVAL_TIME = "1000"; // 1000 == 1second
class PopUpWindow {
static popupCounter = 0;
constructor(variableName, term) {
this.variableName = variableName;
this.printVar = term.printVar;
this._getObjType = term._getObjType;
this._printObject = term._printObject;
this.printLn = term.printLn;
this.options = {};
let o = this.options;
let to = term.options;
o.printToConsoleLog = false;
o.tpo_unknownObjectPrint = to.tpo_unknownObjectPrint;
o.tpo_objectPrefix = to.tpo_objectPrefix;
o.tpo_specialPrefix = to.tpo_specialPrefix;
o.tpo_maxDepth = to.tpo_maxDepth;
o.tpo_innerMaxLength = to.tpo_innerMaxLength;
this.createPopup();
this.printVariable();
this.intervalId = setInterval(() => this.printVariable(), INTERVAL_TIME);
}
createPopup() {
const t = 2 + PopUpWindow.popupCounter++;
let containerStyle = 'border: 1px solid black; z-index: 9990; position: absolute; background: #ffffffa0; border-radius: 2px; backdrop-filter: blur(3px); box-shadow: 3px 3px 3px #00000066;';
containerStyle += ` top: ${t}em; left: ${t}em;`;
this.container = WTerminal.createElement('div', { style: containerStyle, title: this.variableName });
const outputStyle = 'margin: 2px; font-family: Monospace, Incosolata, Courier; font-size: 12px; line-height: 1.05;';// overflow-y: scroll; max-height: ' + (window.innerHeight-80) +'px;';
this.outputEl = WTerminal.createElement('pre', { style: outputStyle });
this.btnPauseContinue = WTerminal.createElement('button', { title: "pause" });
this.btnPauseContinue.innerHTML = PAUSE_SYMBOL;
this.btnPauseContinue.addEventListener("click", () => this.onPausePlay());
let btnRefresh = WTerminal.createElement('button', { title: "refresh" });
btnRefresh.innerHTML = '&#8635;';
btnRefresh.addEventListener("click", () => this.printVariable());
let btnClose = WTerminal.createElement('button', { title: 'close' });
btnClose.addEventListener("click", () => this.closePopup());
btnClose.innerHTML = "&#10006;";
let headerDiv = WTerminal.createElement('div', { style: "border-bottom: 1px solid black; padding: 2px; background: #00000060" });
headerDiv.appendChild(btnRefresh);
headerDiv.appendChild(this.btnPauseContinue);
headerDiv.appendChild(document.createTextNode(" Popvar " + PopUpWindow.popupCounter));
let spanForClose = WTerminal.createElement('span', { style: "float: right;" }, btnClose);
headerDiv.appendChild(spanForClose);
this.container.appendChild(headerDiv);
this.container.appendChild(this.outputEl);
document.body.appendChild(this.container);
headerDiv.onmousedown = (e) => this.startDrag(e);
}
onPausePlay() {
if (this.intervalId === 0) {
this.intervalId = setInterval(() => this.printVariable(), INTERVAL_TIME);
this.printVariable();
this.btnPauseContinue.innerHTML = PAUSE_SYMBOL;
this.btnPauseContinue.title = "pause";
} else {
clearInterval(this.intervalId);
this.intervalId = 0;
this.btnPauseContinue.innerHTML = "&#9658;";
this.btnPauseContinue.title = "play";
}
}
printVariable() {
const oldOutput = this.outputEl;
const outputStyle = 'margin: 2px; font-family: Monospace, Incosolata, Courier; font-size: 12px; line-height: 1.05;';// overflow-y: scroll; max-height: ' + (window.innerHeight-80) +'px;';
this.outputEl = WTerminal.createElement('pre', { style: outputStyle });
this.printVar(WTerminal.getGlobalVariable(this.variableName), this.variableName);
oldOutput.replaceWith(this.outputEl);
}
closePopup() {
document.body.removeChild(this.container);
}
startDrag(e) {
if (e.button !== 0) return;
e.preventDefault();
this.pos3 = e.clientX;
this.pos4 = e.clientY;
document.onmouseup = () => this.endDrag();
document.onmousemove = (e) => this.dragPopup(e);
}
dragPopup(e) {
// e = e || window.event;
e.preventDefault();
this.pos1 = this.pos3 - e.clientX;
this.pos2 = this.pos4 - e.clientY;
this.pos3 = e.clientX;
this.pos4 = e.clientY;
this.container.style.top = (this.container.offsetTop - this.pos2) + "px";
this.container.style.left = (this.container.offsetLeft - this.pos1) + "px";
}
endDrag() {
document.onmouseup = null;
document.onmousemove = null;
}
};//class PopUpWindow
if (globalThis === undefined) {
term.printError("Popvar error: Missing globalThis");
} else if (argLine == '') {
term.printError("Popvar error: No name");
} else {
// return new PopUpWindow(argLine, term);
new PopUpWindow(argLine, term);
}
}// runPopvar
const initTerminalVariableCommands = function() {
const ext = {
popvar: {
run: runPopVar,
help: function(term) {
term.printLn("Creates a popup window with (refreshable) the given variable contents.");
term.printBold("Usage:");
term.printLn("popvar VARIABLE_NAME //shows VARIABLE_NAME contents in a popup");
term.printBold("Samples:");
term.printLn("popvar terminal //Shows the terminal variable contents in a popup");
}
},
};
const aliases = {
pop: "popvar",
pt: "popvar terminal",
};
if (WTerminal === undefined) { //is WTerminal not available?
console.error("WTerminal is missing!");
return;
}
//add commands in ext
for (let c of Object.keys(ext)) {
WTerminal.terminalAddCommand(c, ext[c].run, ext[c].help);
}
//add aliases
for (let c of Object.keys(aliases)) {
WTerminal.terminalAddAlias(c, aliases[c]);
}
};
//init
if (document.body) {
initTerminalVariableCommands();
} else {
window.addEventListener("load", initTerminalVariableCommands);
}
}

View File

@ -0,0 +1,40 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This adds the test command to the terminal.
* current test is to get local variables
*/
{// this code block hides the variables below from other scripts.
const initTerminalStressTestCommand = function() {
const help = function(term) {
term.printLn("Runs a stress test on the terminal.");
};
const run = function(term, argLine) {
const testArgLine = function(str) {
term.printVar(WTerminal.splitToArguments(str), '`' + argLine + '`.<b>splitToArguments()</b>');
};
if (argLine != '') testArgLine(argLine);
term.terminalCommand("? && alias && const && option && date && time && uptime && starttime && echo OK", true);
term.terminalCommand("? help && help alias && ? const && ? option && ? date && ? time && ? uptime", true);
term.terminalCommand("thisterminal && gg", true);
term.terminalCommand('gdb && result .2 && result .children.0 && result .innerHTML', true);
term.terminalCommand('eval 10000+4*(1+4*10) + Math.PI', true);
term.terminalCommand('dovar alert "finished stresstest"', true);
// term.terminalCommand('dovar document.getElementById ' + TERMINAL_OUTPUT_ID + ' && result .innerHTML', true);
return term.lastResult;
};
//add command
if (WTerminal === undefined) {
console.error("WTerminal is missing!");
return;
}
WTerminal.terminalAddCommand("stresstest", run, help);
};
//init
if (document.body) {
initTerminalStressTestCommand();
} else {
window.addEventListener("load", initTerminalStressTestCommand);
}
}

View File

@ -0,0 +1,45 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This adds the test command to the terminal.
* current test is to get local variables
*/
{
const addExtention = function() {
const help = function(term) {
term.printLn("Installs a logger at setTimeout and setInterval.");
}
const run = function(term) {
//capture setTimeout
const oldSetTimeout = setTimeout;
setTimeout = function() {
const e = new Error('Just for stack trace');
const result = oldSetTimeout.apply(this, arguments);
if (term) term.printLn(`New timeout ${result} registered.\n from: ${e.stack}`);
else console.log(`New timeout ${result} registered from: ${e.stack}`);
return result;
};
//capture setInterval
const oldSetInterval = setInterval;
setInterval = function() {
const e = new Error('Just for stack trace');
const result = oldSetInterval.apply(this, arguments);
if (term) term.printLn(`New interval ${result} registered.\n from: ${e.stack}`);
else console.log(`New interval ${result} registered from: ${e.stack}`);
return result;
}
term.printLn("activated");
}
if (WTerminal === undefined) { //is WTerminal not available?
console.error("WTerminal is missing!");
return;
}
WTerminal.terminalAddCommand("timerdebug", run, help);
};
//init
if (document.body) {
addExtention();
} else {
window.addEventListener("load", addExtention);
}
}

View File

@ -0,0 +1,88 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This allows the terminal to transfer from add-on to page level, by using the extemtopm command 'transfer'.
* THIS SCRIPT ONLY WORKS FOR A BROWSER-ADDON/EXTENTION!
*/
{// this code block hides the variables below from other scripts.
const TRANSFER_COMMAND_NAME = "transfer";
const TRANSFER_SCRIPT_ID = "terminal-transfer-script";
const initTerminalTransferCommand = function() {
const help = function(term) {
term.printLn("Transfers all the terminal functions and variables, from the addon-scope to the document/page-scope");
}
const run = function(term) {
term.printLn("Transfer warning: Expect difficulties debugging errors.");
term.print("When, after transfer, the new (transfered) terminal does not open check the console,");
term.printLn("if the error points to document.body.appendChild(scriptNode); then the generated script has a bug."); //generated script == tempScript
try {
//addon-terminal todo: try adding terminalPrint functions to the page!
let scriptNode = document.getElementById(TRANSFER_SCRIPT_ID);
if (scriptNode === null) {
scriptNode = document.createElement("script");
scriptNode.id = TRANSFER_SCRIPT_ID;
scriptNode.type = "text/javascript";
} else {
while (scriptNode.firstChild) {
scriptNode.removeChild(scriptNode.lastChild);
}
}
//create script
let tempScript = `{
console.log("loading ${TRANSFER_SCRIPT_ID}");
`;
tempScript += `
${WTerminal};
`;
tempScript += `
//Relaunch
const relaunchWTerminal = function(){
try{
let terminal = new WTerminal("dropdown", null, null);
terminal.terminalOpen();
terminal.printLn("This Terminal was transfered.");
terminal.commandListExtension = {`;
for (let el in term.commandListExtension) {
if (el == "transfer") continue;
tempScript += `${el}: { run: ${term.commandListExtension[el].run}, help: ${term.commandListExtension[el].help} },`
}
tempScript += `
};
terminal.aliasExtensionList = ${JSON.stringify(term.aliasExtensionList)};
terminal.printLn("Extentions transfered.");
terminal.printLn("Press '"+terminal.options.keyOpen + ((!terminal.options.keyOpenCtrl)?'" + CTRL':'"')+" to open the other terminal.");
} catch(e){
console.log("failed to transfer terminal");
console.error(e);
}
};
relaunchWTerminal();
}`;
/* tempScript = `console.log("ik werk wel!");`;*/
scriptNode.appendChild(document.createTextNode(tempScript));
// scriptNode.innerHTML = tempScript;
if (document.getElementById(TRANSFER_SCRIPT_ID) === null) {
document.body.appendChild(scriptNode);
}
term.printLn("Transfer done");
term.options.keyOpenCtrl = !term.options.keyOpenCtrl;
term.printLn(`Press '${term.options.keyOpen + ((!term.options.keyOpenCtrl) ? '" + CTRL' : '"')} to open the other terminal.`);
term.terminalClose();
} catch (e) {
term.printVar("Transfer error: ", e);
return e;
}
};
WTerminal.terminalAddCommand(TRANSFER_COMMAND_NAME, run, help);
};
if (document.body) {
initTerminalTransferCommand();
} else {
window.addEventListener("load", initTerminalTransferCommand);
}
}

View File

@ -0,0 +1,348 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: This adds the test command to the terminal.
* current test is to get local variables
*/
{// this code block hides the variables below from other scripts.
const initTerminalVariableCommands = function() {
const ext =
{
dovar: {
run: function(term, argLine) {
function _doFunction(gVar, functionKey, args) {
if (typeof gVar === "object" && typeof gVar[functionKey] === "function") {
// term.printVar(args, "do args");
return gVar[functionKey](...args);
} else {
term.printError("Do error: " + functionKey + " is not a function");
}
};
/* To run a function of a Class (like document.getElementById(..))
* We need the parent- /class-object from witch we call it from, to prevent errors (illegal acces)
* todo: ? --> use apply https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
*/
if (globalThis === undefined) {
term.printError("Do error: Missing globalThis");
} else if (argLine == '') {
term.printError("Do error: No name");
} else {
let args = WTerminal.splitToArguments(argLine);
const verbal = args.includes("-v");
if (verbal) {
args = args.filter(e => e !== '-v');
}
const gName = args.shift(); //full global function name
args.forEach(e => e = WTerminal.stringToValue(e));
let pName = gName.split('.'); //last object's parent name
let result;
if (pName.length == 1) {
result = _doFunction(globalThis, gName, args)
} else {
const lastName = pName.pop(); //last object name
result = _doFunction(WTerminal.getGlobalVariable(pName.join('.')), lastName, args);
}
if (verbal) term.printVar(result, gName + "(" + args.join(' ') + ")");
return result;
}
},
help: function(term) {
term.printLn("Runs given global function with arguments, and returns result.");
term.printBold("Usage:");
term.printLn("dovar FUNCTION_NAME [ArgumentS] //Runs function FUNCTION_NAME with optional ArgumentS");
term.printLn("dovar -v FUNCTION_NAME [ArgumentS] //Same as above but prints result too.");
term.printBold("Samples:");
term.printLn("dovar window.open https://developer.mozilla.org //Runs function window.open() with the url as argument");
term.printLn("dovar -v document.getElementById terminal-up //Runs function document.getElementById with terminal-up as argument")
term.printLn("dovar document.body.remove //Warning: removes all page content");
}
},
getvar: {
run: function(term, argLine) {
if (globalThis === undefined) {
term.printError("Getvar error: Missing globalThis");
} else if (argLine == '') {
term.printError("Getvar error: Missing argument: VARIABLE_NAME");
} else {
let result = {};
const args = WTerminal.splitToArguments(argLine);
for (const gName of args) {
result[gName] = WTerminal.getGlobalVariable(gName);
}
const keys = Object.keys(result);
if (keys.length == 1) {
return result[keys[0]];
} else {
return result;
}
}
},
help: function(term) {
term.printLn("Gets a global variable and returns it.");
term.printLn("The returned result will be found in terminal.lastResult")
term.printBold("Usage:");
term.printLn("getvar VARIABLE_NAME");
term.printBold("Samples:");
term.printLn("getvar terminal //Returns terminal");
term.printLn("getvar terminal.version //Returns terminal.version");
term.printLn("getvar var1 var2 var3 //Returns an object with var1 var2 and var3 keys and their global found value.");
term.printLn("getvar document //Returns global document object.");
term.printLn("getvar document.body.children //Returns a list of the children in the body.");
term.printLn("getvar document.head.children //Returns a list of the children in the head.");
}
},
globals: {
run: function(term) {
if (globalThis !== undefined) {
term.printVar(globalThis, "globalThis");
} else if (self !== undefined) {
term.printVar(self, "self");
} else if (global !== undefined) {
term.printVar(global, "global");
} else {
term.printError("Globals error: No globals!?");
}
if (document !== undefined && globalThis.document !== document) term.printVar(document, "document");
},
help: function(term) {
term.printLn("Prints all global variables.");
}
},
logvar: {
run: function(term, argLine) {
if (globalThis === undefined) {
term.printError("LogVar error: Missing globalThis");
} else if (argLine == '') {
term.printError("LogVar error: Missing argument: VARIABLE_NAME");
} else {
let result = {};
let args = WTerminal.splitToArguments(argLine);
const returnResult = args.includes("-r");
if (returnResult) {
args = args.filter(e => e !== '-r');
}
for (const gName of args) {
result[gName] = WTerminal.getGlobalVariable(gName);
}
if (Object.keys(result).length == 1) {
result = result[Object.keys(result)[0]];
}
// term.printVar(result);
console.log(result);
if (returnResult) return result;
}
},
help: function(term) {
term.printLn("Like printvar but Logs variable(s) to console.");
}
},
printvar: {
run: function(term, argLine) {
if (globalThis === undefined) {
term.printError("PrintVar error: Missing globalThis");
} else if (argLine == '') {
term.printError("PrintVar error: Missing argument: VARIABLE_NAME");
} else {
let result = {};
let args = WTerminal.splitToArguments(argLine);
const returnResult = args.includes("-r");
if (returnResult) {
args = args.filter(e => e !== '-r');
}
for (const gName of args) {
result[gName] = WTerminal.getGlobalVariable(gName);
}
const keys = Object.keys(result);
if (keys.length == 1) {
result = result[keys[0]];
term.printVar(result, keys[0]);
} else {
term.printVar(result);
}
if (returnResult) return result;
}
},
help: function(term) {
term.printLn("Prints value of global variable.");
term.printBold("Usage:");
term.printLn("printvar VARIABLE_NAME //Prints variable, returns nothing.");
term.printLn("printvar -r VARIABLE_NAME //Prints variable, returns variable.");
term.printBold("Samples:");
term.printLn("printvar terminal //Prints terminal");
term.printLn("printvar terminal.version //Prints terminal.version");
term.printLn("printvar -r terminal.lastResult //Prints terminal.lastResult and returns it.");
term.printLn("printvar document //Prints global document object.");
term.printLn("printvar document.body.children //Prints body elements.");
term.printLn("printvar document.head.children //Prints head elements.");
}
},
removevar: {
run: function(term, argLine) {
if (globalThis === undefined) {
term.printError("RemoveVar error: Missing globalThis");
} else if (argLine == '') {
term.printError("RemoveVar error: Missing arguments");
} else {
let args = WTerminal.splitToArguments(argLine);
const keys = Object.keys(globalThis);
const verbal = args.includes("-v");
if (verbal) {
args = args.filter(e => e !== '-v');
}
let count = 0;
if (args.includes("-a")) {
count = keys.length;
keys.forEach(key => {
if (verbal) term.printLn("removing: " + key)
delete globalThis[key];
});
term.printLn("Removed " + count + " keys.")
return;
} else if (args.includes("-n")) {
args = args.filter(e => e !== '-n');
keys.forEach(key => {
if (globalThis[key] === null || globalThis[key] === undefined) {
if (verbal) term.printLn("removing: " + key)
delete globalThis[key];
count++;
}
});
} else if (args.includes("-f")) {
args = args.filter(e => e !== '-f');
keys.forEach(key => {
if (globalThis[key] !== globalThis &&
!(typeof globalThis[key] === "function" && globalThis[key].toString().includes("[native code]"))) {
if (verbal) term.printLn("removing: " + key)
delete globalThis[key];
count++;
}
});
}
while (args.length > 0) {
const gName = args.shift(); //full global function name
const pName = gName.split('.'); //last object's parent name
const lastName = pName.pop(); //last object name
let obj = globalThis;
if (pName.length > 0) {
obj = WTerminal.getGlobalVariable(pName.join('.')); //parent object
}
if (obj !== undefined && obj[lastName] !== undefined) {
if (verbal) term.printLn("removing: " + gName)
delete obj[lastName];
count++;
} else {
if (obj === undefined) {
term.printError("Variable " + pName.join(".") + " = undefined")
} else {
term.printError("Variable " + gName + " = undefined");
}
}
}
term.printLn("Removed " + (count > 1 ? (count + " keys.") : (count === 0 ? "nothing" : "1 key")));
}
},
help: function(term) {
term.printLn("Removes global variables.");
term.printBold("Usage:");
term.printLn("removevar -v ... //Prints removing VARIABLE_NAME.");
term.printLn("removevar -f //Removes all global variables that are not a parent redefinition or native function.");
term.printLn("removevar -n //Removes all null or undefined global variables.");
term.printLn("removevar -a //Removes ALL global variables.");
term.printLn("removevar VARIABLE_NAMES //Removes the global variables provided.")
term.printBold("Samples:");
term.printLn("removevar terminal //Removes the global variable terminal.");
term.printLn("removevar var1 var2 //Removes the 2 global variables var1 and var2.");
term.printLn("removevar terminal.history //Removes history from terminal.");
term.printLn("removevar -n terminal.history //Removes history from terminal, and all null or undefined global variables.");
term.printLn("removevar -n -v //Prints variable names of all null or undefined global variables it removes.");
}
},
setvar: {
run: function(term, argLine) {
if (globalThis === undefined) {
term.printError("Setvar error: Missing globalThis");
} else {
if (argLine == '') {
term.printError("Setvar error: no name");
} else {
let args = WTerminal.splitToArguments(argLine);
for (const element of args) {
const keyValuePair = element.split("=");
if(keyValuePair.length < 2) throw new Error("missing arguements.");
const names = keyValuePair[0].split(".");
const value = WTerminal.stringToValue(keyValuePair[1]);
if (names.length == 1) {
globalThis[names[0]] = value;
term.printVar(globalThis[names[0]], keyValuePair[0]);
} else {
let obj = globalThis;
for (let i = 0; i < names.length - 1; i++) {
if (typeof obj[names[i]] === "object") obj = obj[names[i]];
else {
let rem = names.length - 1 - i;
names.splice(names.length - rem, rem);
let name = names.join('.');
term.printError(`Variable ${keyValuePair[0]} is not an object!`);
term.printVar(obj[names[i]], name);
return;
};
}
obj[names[names.length - 1]] = value;
return obj[names[names.length - 1]];
}
}
}
}
},
help: function(term) {
term.printBold("Usage:");
term.printLn("setvar NAME=VALUE //Sets VALUE to a global variable, NAME, and returns it.");
term.printBold("Samples:");
term.printLn("setvar terminal //Sets terminal to undefined");
term.printLn("setvar terminal.version=prehistoric //Sets terminal.version to string `prehistoric`");
term.printLn("setvar myNumber=8 //Sets myNumber to number 8");
term.printLn("setvar myNumber=(number)-8.1 //Sets myNumber to number -8.1");
term.printLn("setvar var1 var2 var3 //Creates 3 undefined variables");
term.printLn("setvar myName=\"Ward Truyen\" //Sets myName to string");
term.printLn('setvar obj={"1":"test","2":true} //Sets obj to JSON.stringified object');
term.printLn("setvar myFunc=(function)terminalPrintLn(\"Hello\");//Creates a function");
term.printLn("setvar myVar=(global)myFunc //Sets(binds) myVar to the contents of myFunc");
}
},
};
const aliases = {
run: "dovar",
pdo: "dovar -v ",
g: "getvar",
gg: "globals",
db: "printvar -r document.body.children",
dh: "printvar -r document.head.children",
gdb: "printvar -r document.body.children ",
gdh: "printvar -r document.head.children ",
result: "printvar -r terminal.lastResult",
error: "printvar terminal.lastError",
pv: "printvar -r ",
terminal: "printvar -r terminal",
// history: "printvar terminal.history",
};
//add commands in ext
if (WTerminal === undefined) {
console.error("WTerminal is missing!");
return;
}
for (let c of Object.keys(ext)) {
WTerminal.terminalAddCommand(c, ext[c].run, ext[c].help);
}
//add aliases
for (let c of Object.keys(aliases)) {
WTerminal.terminalAddAlias(c, aliases[c]);
}
};
//init
if (document.body) {
initTerminalVariableCommands();
} else {
window.addEventListener("load", initTerminalVariableCommands);
}
}

6
src/wterminal-addon.js Normal file
View File

@ -0,0 +1,6 @@
/* Author: Ward Truyen
* Version: 1.0.0
* About: this file is needed when the terminal is used as a browser-extention
*/
WTerminal.instalDropdownTerminal();

166
src/wterminal.css Normal file
View File

@ -0,0 +1,166 @@
/* Author: Ward Truyen
* Version: 1.1.0
*/
.wterminal-background {
z-index: 9995;
position: fixed;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
color: black;
line-height: normal;
visibility: hidden;
background: unset;
transition: all 0.2s ease-out 0s;
}
.wterminal-background .wterminal-container{
position: relative;
margin: 0px auto;
width: 70%;
border: 1px solid #888;
border-top: 0px solid grey;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 0px 4px 4px 4px;
background: linear-gradient(24deg, rgba(209,211,196,1) 20%, rgba(221,221,221,1) 50%, rgba(221,221,221,1) 70%, rgba(190,199,207,1) 90%);
box-shadow: 3px 3px 3px black;
text-align: left;
margin-top: -50%;
}
.wterminal-container pre {
color: unset;
border: 2px solid #a0a0a0d0;
min-height: 20em;
max-height: 20em;
margin: 0px 0px 2px;
padding: 2px 4px 6px 4px;
background-color: #F0F0F0;
overflow-y: scroll;
font-family: Monospace, Incosolata, Courier;
font-size: 12px;
line-height: 1.08;
width: unset;
}
.wterminal-output u{
/* adds a nice fat blue underline to titles */
text-decoration-color: #8cb4ff;
text-decoration-thickness: .15rem;
text-underline-offset: .1em;
}
.wterminal-input {
padding: 1px 2px;
margin: 0px;
background-color: #F0F0F0;
color: black;
border: 1px solid #ccc;
border-radius: 2px;
}
.wterminal-input:hover{
background-color: #DDD;
}
.wterminal-container input[type="submit"]:hover,
.wterminal-container button:hover{
background-color: #DDD;
cursor: pointer;
}
.wterminal-container input, .wterminal-container label{
line-height: unset;
display: unset;
/* width: unset; */
height: unset;
margin: 0;
margin-left: 2px;
}
.wterminal-container input[type="submit"],
.wterminal-container button{
width: unset;
height: unset;
margin: 0;
margin-left: 2px;
padding: 1px 4px;
color: black;
background-color: #eee;
border: 1px solid #aaa;
border-radius: 3px;
font-weight: normal;
}
.wterminal-background.wterminal-visible{
background-color: #0000008F;
backdrop-filter: blur(4px);
visibility: visible;
transition: background-color .1s ease-out 0s, backdrop-filter .1s ease-out 0s;
}
.wterminal-visible .wterminal-container{
margin-top: 0px;
transition: margin-top .2s ease-out 0s;
}
.wterminal-container form{
margin-left: 2px;
}
/* Width */
@media screen and (max-width: 480px) {
.wterminal-input{
width: 40%;
}
}
@media screen and (min-width: 480px) and (max-width: 720px) {
.wterminal-input{
width: 50%;
}
}
@media screen and (min-width: 720px) and (max-width: 1080px) {
.wterminal-input{
width: 60%;
}
}
@media screen and (min-width: 1080px) {
.wterminal-input{
width: 70%;
}
}
/* Height */
@media screen and (min-height: 512px) and (max-height: 1024px) {
.wterminal-background .wterminal-container .wterminal-output{
min-height: 32em;
max-height: 32em;
}
}
@media screen and (min-height: 1024px) and (max-height: 1536px) {
.wterminal-background .wterminal-container .wterminal-output{
min-height: 48em;
max-height: 48em;
}
}
@media screen and (min-height: 1536px) {
.wterminal-background .wterminal-container .wterminal-output{
min-height: 64em;
max-height: 64em;
}
}
.theme-dark .wterminal-background .wterminal-container{
color: #F0F0F0;
background: #303030;
}
.theme-dark .wterminal-container pre,
.theme-dark .wterminal-container button,
.theme-dark .wterminal-container input
{
background-color: black;
/* color: #F0F0F0; */
color: white;
}

1353
src/wterminal.js Normal file

File diff suppressed because it is too large Load Diff