├── README.md
├── css
└── terminal.css
├── index.php
├── js
├── defaultcommands.js
└── terminal.js
├── lastlogin.txt
├── phputil
├── gettermfiles.php
└── lastlogin.php
├── systeminfo.txt
└── terminalfiles
├── about.txt
└── welcome.txt
/README.md:
--------------------------------------------------------------------------------
1 | ## Web Terminal
2 |
3 | This is a basic ubuntu based web terminal that serves as an interactive portfolio of mine.
4 | You are welcome to use it, I just ask for credit.
5 |
6 | If you would like a live version, visit my portfolio: https://yasfu.net/
7 |
8 | It features a command system, as well as an "auto typer" that automatically types out and enters commands from code.
9 | This is useful for visitors that are not familiar with CLIs and broadens the amount of people who can navigate it.
10 | To increase this further, files listed with "ls" and other places can be clicked on to automatically execute commands like cat
11 |
12 | ## How-To setup
13 |
14 | If you'd like to configure it for your own needs, any file in the terminalfiles folder are automatically available for the terminal to use.
15 | By default the commands `cat welcome.txt` and `ls` are automatically ran when the terminal starts. You can change welcome.txt to change the welcome message or change the commands themselves in terminal.js, line 27:
16 |
17 | ```js
18 | autowriteQueue.push("Command here with arguments");
19 | ```
20 |
21 | Pushing anything to the queue will type out the command while locking user input as soon as input is ready.
22 |
23 |
24 | If you would like to add custom commands you may add it to any script included after terminal.js. I recommend making a new file and add any custom commands to that instead of defaultcommands.js to make any git merges a little easier.
25 | You can look at any command in defaultcommands.js for more examples or reference the example below:
26 |
27 | ```js
28 | function myCommand(args) {
29 | terminalPrint("Message here");
30 | cmdDone(); // Required after finishing function. Useful for any asynchronous functions. (ie. AJAX)
31 | }
32 |
33 | function ready() {
34 | addCmd("mycommand", myCommand);
35 | }
36 |
37 | $(document).ready(ready);
38 | ```
39 |
40 | the first argument passed to any command function is a list of arguments given by the user. It is seperated by spaces but ignores quotes
41 | For example: `mycommand there are "some arguments" 'to pass'` would yield `there, are, some arguments, to pass`.
42 |
43 | ### Useful functions
44 |
45 | `terminalPrint` is a simple print function. The first argument is the message to print, and the second optional argument is a boolean that, if true, adds a line break to the end. By default this value is true.
46 |
47 | `cmdDone` resets the user's input, and must be called after a command execute is finished. This is needed to support Asynchronous functions like AJAX.
48 |
49 | `toggleInput` disables or enables user input. `true` will block input and `false` will enable it. No argument will toggle it based on the current locked state.
50 |
51 | `addCmd` adds the command to the terminal. The first argument is a string and is the keyword for the user to execute the command. The second is a function that is called with a table of arguments the user passed with the command
52 |
53 |
54 | ## Contributing
55 |
56 | You're welcome to submit pull requests if you want to, just try and keep code style consistent with the rest of the file.
57 | Try to submit bug fixes instead of major feature changes. If you feel like a feature change may make a good contribution to the main software then you are welcome to do so, however there is no guarantee that it will be merged. Any new commands must be 'general' commands and can be widely applicable to be merged with the main branch. Something like `tail` or an existing linux/unix command.
58 | I have been using camelCase for the most part, and K&R style for bracket positioning.
59 |
--------------------------------------------------------------------------------
/css/terminal.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #181a1b;
3 | }
4 |
5 | #terminal {
6 | font-family: "Ubuntu Mono";
7 | font-size: 16px;
8 | color: white;
9 | word-break: break-word;
10 | }
11 |
12 | #terminal a {
13 | color: #3391ff;
14 | }
15 |
16 | #terminal .prefix .userdomain {
17 | color: rgb(127, 255, 127);
18 | }
19 |
20 | #terminal .prefix .tilde {
21 | color: blue;
22 | }
23 |
24 | #terminal .lsdir {
25 | color: aqua;
26 | }
27 |
28 | #terminal .lsfile, #terminal .helpcmd {
29 | color: white;
30 | }
31 |
32 | #terminal #terminput {
33 | border: none;
34 | background-image: none;
35 | background-color: transparent;
36 | -webkit-box-shadow: none;
37 | -moz-box-shadow: none;
38 | box-shadow: none;
39 | font-family: "Ubuntu Mono";
40 | font-size: 16px;
41 | width: 85%;
42 |
43 | /*
44 | Removes blinker, in a hacky way, but whatever.
45 | */
46 |
47 | color: transparent;
48 | -webkit-text-fill-color: white;
49 | -webkit-text-stroke-width: 0px;
50 | -webkit-text-stroke-color: black;
51 | }
52 |
53 | #terminal #terminput:focus {
54 | outline: none;
55 | }
56 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ethan Jones - Portfolio & about me
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/js/defaultcommands.js:
--------------------------------------------------------------------------------
1 | function cat(args) {
2 | let path = window.location.pathname + "terminalfiles/";
3 |
4 | if (args[0] == null) {
5 | cmdDone();
6 | return;
7 | }
8 |
9 | path = path + args[0];
10 | $.ajax(path)
11 | .done(function(data) {
12 | terminalPrint(data);
13 | })
14 | .fail(function() {
15 | terminalPrint(args[0] + " does not exist or there was an error trying to access it.");
16 | })
17 | .always(function() {
18 | cmdDone();
19 | });
20 | }
21 |
22 | function ls(args) {
23 | $.ajax("phputil/gettermfiles.php")
24 | .done(function(data) {
25 | let ar = JSON.parse(data);
26 |
27 | for (var v in ar) {
28 | let str = ar[v];
29 |
30 | if ((args.length < 1 || args[0] != "-a") && str.match(/^(\.+)/gm)) {
31 | continue;
32 | }
33 |
34 | var isDirectory = ar[v].match(/\.\w+/g) == null;
35 | if (isDirectory) {
36 | str = `` + str + ``;
37 | } else {
38 | str = `` + str + ``
39 | }
40 |
41 | terminalPrint(str + " ", false);
42 | }
43 |
44 | terminalPrint("");
45 | })
46 | .fail(function() {
47 | })
48 | .always(function() {
49 | cmdDone();
50 | });
51 | }
52 |
53 | function cd(args) {
54 | var resp = "bash: cd: {dir} Permission denied";
55 | var dir = "/:";
56 |
57 | if (args.length > 0) {
58 | var isDirectory = args[0].match(/\.\w+/g) == null;
59 | if (!isDirectory) {
60 | resp = "bash: cd: " + args[0] + ": Not a directory";
61 | } else {
62 | dir = args[0] + ":";
63 | }
64 | }
65 |
66 | resp = resp.replace("{dir}", dir);
67 | terminalPrint(resp);
68 |
69 | cmdDone();
70 | }
71 |
72 | function help() {
73 | let i = 1;
74 | for (var c in cmds) {
75 | var notlast = (i != Object.keys(cmds).length);
76 | var str = `` + c + ``
77 |
78 | terminalPrint(str, !notlast);
79 |
80 | if (notlast) {
81 | terminalPrint(", ", false);
82 | }
83 |
84 | i++;
85 | }
86 |
87 | cmdDone();
88 | }
89 |
90 | function clickCmd(cmd) {
91 | autowriteQueue.push(cmd);
92 | }
93 |
94 | function ready() {
95 | addCmd("cat", cat);
96 | addCmd("ls", ls);
97 | addCmd("cd", cd);
98 | addCmd("help", help);
99 | }
100 |
101 | $(document).ready(ready);
102 |
--------------------------------------------------------------------------------
/js/terminal.js:
--------------------------------------------------------------------------------
1 | let cmds = [];
2 | let printTemplate = `visitor@yasfu.net:~$ `;
3 | let inputBoxTemplate = ``;
4 | let textStampTemplate = `{TEXT} `;
5 | let printTxtTemplate = `{TEXT}`;
6 | let term;
7 | let inputBox;
8 |
9 | let autowrite;
10 | let isAutoWriting = false;
11 | let autowriteQueue = [];
12 |
13 | function ready() {
14 | term = $("#terminal");
15 |
16 | $.ajax("phputil/lastlogin.php").done(function(lastLogin) {
17 | $.ajax("systeminfo.txt").done(function(data) {
18 | data = data.replace("{USERAGENT}", navigator.userAgent);
19 | data = data.replace("{LASTLOGIN}", lastLogin);
20 |
21 | terminalPrint(data);
22 | }).always(function() {
23 | setTimeout(function() {
24 | addInputPrefix();
25 |
26 | setInterval(autoWrite, 80);
27 | autowriteQueue.push("cat welcome.txt");
28 | autowriteQueue.push("ls");
29 | }, 200);
30 | });
31 | });
32 | }
33 |
34 | function autoWrite() {
35 | if (inputBox == null) {
36 | return;
37 | }
38 |
39 | if (autowrite == null || autowrite == "") {
40 | if (isAutoWriting) {
41 | actEnter();
42 | isAutoWriting = false;
43 | }
44 |
45 | if (autowriteQueue.length < 1) {
46 | toggleInput(false);
47 | return;
48 | }
49 |
50 | if (inputBox == null) {
51 | return;
52 | }
53 |
54 | autowrite = autowriteQueue[0];
55 | autowriteQueue.shift();
56 | inputBox.val("");
57 | isAutoWriting = true;
58 | toggleInput(true);
59 | }
60 |
61 | let oVal = inputBox.val();
62 | inputBox.val(oVal + autowrite.charAt(0));
63 | autowrite = autowrite.slice(1);
64 | }
65 |
66 | function appendToTerm(obj) {
67 | obj.appendTo(term);
68 | }
69 |
70 | function toggleInput(tog) {
71 | if (inputBox == null) {
72 | return;
73 | }
74 |
75 | if (tog == null) {
76 | tog = !inputBox.prop("disabled");
77 | }
78 |
79 | inputBox.prop("disabled", tog);
80 | }
81 |
82 | function addInputPrefix() {
83 | let temp = $(printTemplate);
84 | appendToTerm(temp);
85 | resetInputBox(false);
86 | }
87 |
88 | function actEnter() {
89 | let v = inputBox.val();
90 | stampInputBox();
91 | callCmd(v);
92 | }
93 |
94 | function cmdDone() {
95 | addInputPrefix();
96 | resetInputBox();
97 | }
98 |
99 | function onInputKeypress(ev) {
100 | if (ev.which != "13") {
101 | return;
102 | }
103 |
104 | actEnter();
105 | }
106 |
107 | function stampInputBox() {
108 | let stampWMsg = inputBox.val();
109 | stampWMsg = textStampTemplate.replace("{TEXT}", stampWMsg);
110 |
111 | let textStamp = $(stampWMsg);
112 | textStamp.insertBefore(inputBox);
113 |
114 | inputBox.remove();
115 | inputBox = null;
116 | }
117 |
118 | function resetInputBox(shouldStamp) {
119 | if (shouldStamp && inputBox != null) {
120 | stampInputBox();
121 | }
122 |
123 | if (inputBox != null) {
124 | inputBox.remove();
125 | }
126 |
127 | inputBox = $(inputBoxTemplate);
128 |
129 | var prefW = $(".prefix").width();
130 | var winW = $(window).width();
131 | var prop = 100 * (prefW / winW);
132 | var nProp = 95 - prop;
133 |
134 | inputBox.width(nProp + "%");
135 |
136 | appendToTerm(inputBox);
137 |
138 | inputBox.on("keydown", onInputKeypress);
139 | inputBox.on("focusout", function() { inputBox.focus(); });
140 | inputBox.focus();
141 | }
142 |
143 | function terminalPrint(print, bre) {
144 | if (bre == null) {
145 | bre = true;
146 | }
147 |
148 | let printWMsg = printTxtTemplate.replace("{TEXT}", print);
149 |
150 | let text = $(printWMsg);
151 | appendToTerm(text);
152 |
153 |
154 | if (bre) {
155 | appendToTerm($(" "));
156 | }
157 | }
158 |
159 | function addCmd(cmd, func) {
160 | cmds[cmd] = func;
161 | }
162 |
163 | function callCmd(str) {
164 | let args = str.match(/(?:[^\s"]+|"[^"]*")+/g);
165 |
166 | if (args == null || args.length < 1) {
167 | cmdDone();
168 | return;
169 | }
170 |
171 | let cmd = args[0];
172 |
173 | args.shift();
174 |
175 | if (cmds[cmd] == null) {
176 | terminalPrint(cmd + ": command not found");
177 | cmdDone();
178 | return;
179 | }
180 |
181 | let call = cmds[cmd];
182 |
183 | try {
184 | call(args);
185 | } catch (error) {
186 | terminalPrint("Error processing command '" + cmd + "' with error: " + error);
187 | console.log("Error processing command '" + cmd + "' with error: " + error);
188 | cmdDone();
189 | }
190 | }
191 |
192 | $(document).ready(ready);
193 |
--------------------------------------------------------------------------------
/lastlogin.txt:
--------------------------------------------------------------------------------
1 | Thu Nov 21 19:24:08 2019
--------------------------------------------------------------------------------
/phputil/gettermfiles.php:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/phputil/lastlogin.php:
--------------------------------------------------------------------------------
1 | format('D M d H:i:s Y');
7 |
8 | $fp = fopen('../lastlogin.txt', 'w');
9 | fwrite($fp, $s);
10 | fclose($fp);
11 |
12 | die($loginTime);
13 |
14 | ?>
15 |
--------------------------------------------------------------------------------
/systeminfo.txt:
--------------------------------------------------------------------------------
1 | Welcome to yasfu.net interactive web terminal
2 |
3 | * Documentation: https://github.com/littlebigbug/portfolio-web-terminal
4 | * Repository: https://github.com/littlebigbug/portfolio-web-terminal
5 | * User Agent: {USERAGENT}
6 | * Support: ethan@yasfu.net
7 |
8 | Despite being a traditional-looking terminal, you may use your mouse to navigate links on the page.
9 | This is still a normal website!
10 |
11 | 67 packages can be updated.
12 | 11 updates are security updates.
13 |
14 | Last Login: {LASTLOGIN}
15 |
--------------------------------------------------------------------------------
/terminalfiles/about.txt:
--------------------------------------------------------------------------------
1 | My name is Ethan Jones, I'm a software developer student from Colorado, USA.
2 | I was born in the US but my family is from both US and the UK
3 | I love programming, music, and my wonderful girlfriend.
4 | I make and play video games in my spare time and go to Warren Tech High School to study Game Development.
5 |
--------------------------------------------------------------------------------
/terminalfiles/welcome.txt:
--------------------------------------------------------------------------------
1 | Welcome!
2 | My name is Ethan Jones and I am a Software Developer and Systems Admin.
3 | For more about me, check 'about.txt'
4 |
5 | My portfolio is based on an ubuntu terminal and I wrote it from scratch with javascript.
6 | If you are familiar with this sort of terminal, you are welcome to use the commands, otherwise you can just click those links.
7 | For a list of commands, type 'help'
8 |
9 | If you want to mess with this terminal yourself, You can grab a copy of the source code from the github repository.
10 |
--------------------------------------------------------------------------------