|
|
@@ -1,11 +1,13 @@
|
|
|
function pgsh(ev) {
|
|
|
var self = this
|
|
|
|
|
|
+ /**
|
|
|
+ * Terminal UI/Shell Entry Point
|
|
|
+ */
|
|
|
ev.on('cmd_entered', function(input) {
|
|
|
- // First word is the command, all others are arguments.
|
|
|
- var parts = input.trim().split(' ')
|
|
|
- var command = self.active ? self.active : parts.splice(0, 1).toString()
|
|
|
- var args = parts
|
|
|
+ var args = input.trim().split(' ')
|
|
|
+ // Use the first argument as a command, or the value of `active`.
|
|
|
+ var command = self.active ? self.active : args.splice(0, 1).toString()
|
|
|
if (command in self.commands) {
|
|
|
self[command](args)
|
|
|
} else if (command) {
|
|
|
@@ -13,49 +15,121 @@ function pgsh(ev) {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- var show = function(text) { ev.trigger('disp_add', text) }
|
|
|
- var set = function(text) { ev.trigger('disp_set', text) }
|
|
|
-
|
|
|
- this.prompt = '<span style="color:blueviolet">pgs </span>' +
|
|
|
- '<span style="color:green">$ </span>'
|
|
|
- this.prompt_su = '<span style="color:tomato">root </span>' +
|
|
|
- '<span style="color:red">% </span>'
|
|
|
- this.welcome = 'Linux parsleygardens.net 3.4.5-6-7-i286 #8 PGS Vimputer ' +
|
|
|
+ /**
|
|
|
+ * Variables and Setup
|
|
|
+ */
|
|
|
+ this.cwd = '/home/pgs'
|
|
|
+ var getPrompt = function() {
|
|
|
+ if (self.su_active) {
|
|
|
+ return '<span style="color:purple">root </span>' +
|
|
|
+ '<span style="color:tomato">' + self.cwd + ' </span>' +
|
|
|
+ '<span style="color:red">% </span>'
|
|
|
+ }
|
|
|
+ return '<span style="color:blueviolet">pgs </span>' +
|
|
|
+ '<span style="color:sienna">' + self.cwd + ' </span>' +
|
|
|
+ '<span style="color:green">$ </span>'
|
|
|
+ }
|
|
|
+ this.prompt = getPrompt()
|
|
|
+ this.welcome = 'Linux parsleygardens.net 3.4.5-6-7-i286 #8 PGS Vimputer' +
|
|
|
'3.4.56-7 i286\n\n' +
|
|
|
'~ Welcome to Parsley Gardens! ~\n' +
|
|
|
'"He maketh me to lie down in green pa..."\n\n' +
|
|
|
'Type `help` for list of commands\n\n'
|
|
|
+ this.tree = {
|
|
|
+ 'bin': {},
|
|
|
+ 'etc': {},
|
|
|
+ 'home': {
|
|
|
+ 'pgs': {
|
|
|
+ 'about.md':
|
|
|
+ '# Parsley Gardens\n\nSite created by ** Weiyi Lou ** ' +
|
|
|
+ new Date().getFullYear(),
|
|
|
+ 'git.txt':
|
|
|
+ 'Code Repository at <a target="_blank"' +
|
|
|
+ 'href="https://code.parsleygardens.net/explore/projects">' +
|
|
|
+ 'code.parsleygardens.net</a>',
|
|
|
+ 'magic.txt': 'slightlymagic.com.au'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 'mnt': {},
|
|
|
+ 'root': {
|
|
|
+ 'bin': {
|
|
|
+ 'destroy.sh':
|
|
|
+ "#!/usr/bin/env bash\n" +
|
|
|
+ "set -euo pipefail\n" +
|
|
|
+ "IFS=$'\\n\\t'\n\n" +
|
|
|
+ "rm -rf /"
|
|
|
+ },
|
|
|
+ 'everyday_lost_item_locations_global.sqlite': '',
|
|
|
+ 'government-secrets.txt': ''
|
|
|
+ },
|
|
|
+ 'usr': {
|
|
|
+ 'local': {
|
|
|
+ 'bin': {
|
|
|
+ 'nothing.txt': 'Really nothing'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 'var': {
|
|
|
+ 'cache': {},
|
|
|
+ 'log': {},
|
|
|
+ 'run': {}
|
|
|
+ }
|
|
|
+ }
|
|
|
this.commands = {
|
|
|
- 'about': 'Author Information',
|
|
|
+ 'about': 'Author information',
|
|
|
+ 'cat': 'Con(cat)enate the contents of a file',
|
|
|
+ 'cd': 'Change folder/directory',
|
|
|
'clear': 'Clears the screen',
|
|
|
- 'version': 'Shell Information',
|
|
|
- 'git': 'Link to Code Repository',
|
|
|
- 'hello': 'Say hello to the computer\nUsage: hello [name]',
|
|
|
- 'magic': 'Link to Artist',
|
|
|
- 'su': 'Gain Phenomenal Cosmic Power',
|
|
|
'exit': 'Leave the current context',
|
|
|
+ 'hello': 'Say hello to the computer\nUsage: hello [name]',
|
|
|
+ 'help': 'List commands or view information for one\nUsage: help [command]',
|
|
|
+ 'ls': 'List folder contents',
|
|
|
+ 'pwd': 'Show the current working directory',
|
|
|
+ 'question': 'Displays a question',
|
|
|
'search': 'Search the Web (with a Duck)\nUsage: search [query]',
|
|
|
- 'questions': 'Answer some questions!',
|
|
|
- 'help': 'List commands or view information for one\nUsage: help [command]'
|
|
|
+ 'su': 'Gain Phenomenal Cosmic Power',
|
|
|
+ 'version': 'Display shell information'
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * The Commands
|
|
|
+ * Commands
|
|
|
*/
|
|
|
this.about = function(args) {
|
|
|
- show('Site by Weiyi Lou ' + new Date().getFullYear() + '\n')
|
|
|
+ self.cat(['/home/pgs/about.md'])
|
|
|
+ }
|
|
|
+ this.cat = function(args) {
|
|
|
+ var arg = args.join(' ').trim()
|
|
|
+ var path = resolveAbsPath(arg)
|
|
|
+ if (pathExists(path, true)) {
|
|
|
+ var content = getContents(path)
|
|
|
+ if (path.search('.md|.sh') != -1) {
|
|
|
+ content = hljs.highlightAuto(content).value
|
|
|
+ }
|
|
|
+ show('\n' + content + '\n\n')
|
|
|
+ } else {
|
|
|
+ show("cd: " + arg + ": no such file")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.cd = function(args) {
|
|
|
+ var arg = args.join(' ').trim()
|
|
|
+ var path = resolveAbsPath(arg)
|
|
|
+ if (pathExists(path)) {
|
|
|
+ self.cwd = path
|
|
|
+ ev.trigger('prompt_set', getPrompt())
|
|
|
+ } else {
|
|
|
+ show("cd: " + arg + ": no such directory")
|
|
|
+ }
|
|
|
}
|
|
|
this.clear = function(args) {
|
|
|
ev.trigger('disp_clear')
|
|
|
}
|
|
|
- this.version = function(args) {
|
|
|
- show('Parsley Gardens Shell (pgsh) 1.0.0 Built with ' +
|
|
|
- '<a target="_blank" href="http://riotjs.com/">Riot</a>')
|
|
|
- }
|
|
|
- this.git = function(args) {
|
|
|
- show('Code Repository at <a target="_blank" ' +
|
|
|
- 'href="https://code.parsleygardens.net/explore/projects">' +
|
|
|
- 'code.parsleygardens.net</a>')
|
|
|
+ this.exit = function(args) {
|
|
|
+ if (self.su_active) {
|
|
|
+ self.su_active = false
|
|
|
+ ev.trigger('prompt_set', getPrompt())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ show('Close browser window to exit')
|
|
|
}
|
|
|
this.hello = function(args) {
|
|
|
address = args.join(' ').trim()
|
|
|
@@ -67,55 +141,40 @@ function pgsh(ev) {
|
|
|
show('My name is not "' + address + '"')
|
|
|
}
|
|
|
}
|
|
|
- this.magic = function(args) {
|
|
|
- show('Animation and Illustration at <a target="_blank" ' +
|
|
|
- 'href="http://slightlymagic.com.au">slightlymagic.com.au</a>')
|
|
|
- }
|
|
|
- this.su = function(args) {
|
|
|
- if (self.su_active !== true) {
|
|
|
- self.su_active = true
|
|
|
- if (!self.su_warned) {
|
|
|
- self.su_warned = true
|
|
|
- show('We trust you have received the usual lecture from the ' +
|
|
|
- 'local System Administrator.\n' +
|
|
|
- 'It usually boils down to these three things:\n\n' +
|
|
|
- ' #1) Respect the privacy of others.\n' +
|
|
|
- ' #2) Think before you type.\n' +
|
|
|
- ' #3) With great power comes great\n\n')
|
|
|
+ this.help = function(args) {
|
|
|
+ command = args.join(' ').trim()
|
|
|
+ if (command in self.commands) {
|
|
|
+ show(self.commands[command])
|
|
|
+ } else if (command) {
|
|
|
+ show('No information for: ' + command)
|
|
|
+ } else {
|
|
|
+ var commands = [];
|
|
|
+ for (var name in self.commands) {
|
|
|
+ commands.push(name)
|
|
|
}
|
|
|
- ev.trigger('prompt_set', self.prompt_su)
|
|
|
+ show('Available commands:\n\n' + commands.sort().join(' ') +
|
|
|
+ '\n\n`help [command]` for more information.')
|
|
|
}
|
|
|
}
|
|
|
- this.exit = function(args) {
|
|
|
- if (self.su_active) {
|
|
|
- self.su_active = false
|
|
|
- ev.trigger('prompt_set', self.prompt)
|
|
|
- return
|
|
|
+ this.ls = function(args) {
|
|
|
+ var arg = args.join(' ').trim()
|
|
|
+ var folder = arg || self.cwd
|
|
|
+ var path = resolveAbsPath(folder)
|
|
|
+ if (pathExists(path)) {
|
|
|
+ var contents = getContents(path)
|
|
|
+ var output = ''
|
|
|
+ for (var item in contents) {
|
|
|
+ output += item + "\n"
|
|
|
+ }
|
|
|
+ show(output)
|
|
|
+ } else {
|
|
|
+ show("ls: " + args + ": no such directory")
|
|
|
}
|
|
|
- show('Close browser window to exit')
|
|
|
}
|
|
|
- this.search = function(args) {
|
|
|
- if (!args.join(' ').trim()) { return show(self.commands.search) }
|
|
|
- ev.trigger('prompt_hide')
|
|
|
- show('Searching for "' + args.join(' ') + '" in new window...')
|
|
|
- setTimeout(function() {
|
|
|
- window.open('https://duckduckgo.com/?q=' + args.join('+'), '_blank')
|
|
|
- ev.trigger('prompt_show')
|
|
|
- }, 1000)
|
|
|
+ this.pwd = function(args) {
|
|
|
+ show(self.cwd)
|
|
|
}
|
|
|
- this.questions = function(args) {
|
|
|
- if (!self.active) {
|
|
|
- self.active = 'questions'
|
|
|
- ev.trigger('context_swap', 'questions')
|
|
|
- set('Welcome to the questions. Do you want to continue?')
|
|
|
- return
|
|
|
- }
|
|
|
- if (args == 'exit') {
|
|
|
- self.active = ''
|
|
|
- ev.trigger('context_swap')
|
|
|
- show('Thanks for answering questions!')
|
|
|
- return
|
|
|
- }
|
|
|
+ this.question = function(args) {
|
|
|
var questions = [
|
|
|
'Isn\'t <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ"' +
|
|
|
' target="_blank">this song</a> the best?',
|
|
|
@@ -125,28 +184,88 @@ function pgsh(ev) {
|
|
|
"Don't you mean prism?",
|
|
|
'Butts twelve by pies?',
|
|
|
'I say there, Monstrosity. Do you know the times?',
|
|
|
- '...is essay-writing an exercise in masking ignorance?',
|
|
|
'What is the name of the spaces between the teeth of a comb?',
|
|
|
'Why are you dressed up like Ship\'s Captain?'
|
|
|
]
|
|
|
- set('You answered with "' + args + '"!\nNext question!\n' +
|
|
|
- '<span style="color:#555">(Type "exit" to end)</span>\n\n')
|
|
|
var rand = Math.floor(Math.random() * questions.length);
|
|
|
show(questions[rand])
|
|
|
}
|
|
|
- this.help = function(args) {
|
|
|
- command = args.join(' ').trim()
|
|
|
- if (command in self.commands) {
|
|
|
- show(self.commands[command])
|
|
|
- } else if (command) {
|
|
|
- show('No information for: ' + command)
|
|
|
- } else {
|
|
|
- var commands = [];
|
|
|
- for(var name in self.commands) {
|
|
|
- commands.push(name)
|
|
|
+ this.search = function(args) {
|
|
|
+ if (!args.join(' ').trim()) { return show(self.commands.search) }
|
|
|
+ ev.trigger('prompt_hide')
|
|
|
+ show('Searching for "' + args.join(' ') + '" in new window...')
|
|
|
+ setTimeout(function() {
|
|
|
+ window.open('https://duckduckgo.com/?q=' + args.join('+'), '_blank')
|
|
|
+ ev.trigger('prompt_show')
|
|
|
+ }, 1000)
|
|
|
+ }
|
|
|
+ this.su = function(args) {
|
|
|
+ if (self.su_active !== true) {
|
|
|
+ self.su_active = true
|
|
|
+ if (!self.su_warned) {
|
|
|
+ self.su_warned = true
|
|
|
+ show('We trust you have received the usual lecture from the ' +
|
|
|
+ 'local System Administrator.\n' +
|
|
|
+ 'It usually boils down to these three things:\n\n' +
|
|
|
+ ' #1) Respect the privacy of others.\n' +
|
|
|
+ ' #2) Think before you type.\n' +
|
|
|
+ ' #3) With great power comes great\n\n')
|
|
|
}
|
|
|
- show('Available commands:\n' + commands.sort().join(' ') +
|
|
|
- '\n\n`help [command]` for more information.')
|
|
|
+ ev.trigger('prompt_set', getPrompt())
|
|
|
}
|
|
|
}
|
|
|
+ this.version = function(args) {
|
|
|
+ show('Parsley Gardens Shell (pgsh) 1.0.0\nBuilt with ' +
|
|
|
+ '<a target="_blank" href="http://riotjs.com/">Riot</a>')
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper Functions
|
|
|
+ */
|
|
|
+ var show = function(text) { ev.trigger('disp_add', text) }
|
|
|
+ var set = function(text) { ev.trigger('disp_set', text) }
|
|
|
+
|
|
|
+ // Calculate a destination's path with respect to the current folder.
|
|
|
+ var resolveAbsPath = function(dest) {
|
|
|
+ var parts = dest.trim().split('/')
|
|
|
+ var absolute = dest.startsWith('/') || self.cwd == '/'
|
|
|
+ var path = absolute ? [''] : self.cwd.split('/')
|
|
|
+ parts.forEach(function(part) {
|
|
|
+ if (part == '..' && path.length > 1) { path.pop() }
|
|
|
+ if (part != '..' && part) { path.push(part) }
|
|
|
+ })
|
|
|
+ return path.length > 1 ? path.join('/') : '/'
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if a path exists in the tree
|
|
|
+ var pathExists = function(path, file = false) {
|
|
|
+ var parts = path.trim().split('/')
|
|
|
+ var fname = file ? parts.pop() : ''
|
|
|
+ var tree = self.tree
|
|
|
+ for (var i = 0; i < parts.length; i++) {
|
|
|
+ var part = parts[i]
|
|
|
+ if (!part) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if (!(tree.hasOwnProperty(part) && typeof tree[part] === 'object')) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ tree = tree[part]
|
|
|
+ }
|
|
|
+ if (file && !(tree.hasOwnProperty(fname) && typeof tree[fname] === 'string')) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Return the contents of a given path.
|
|
|
+ var getContents = function(path) {
|
|
|
+ var parts = path.trim().split('/')
|
|
|
+ var tree = self.tree
|
|
|
+ parts.forEach(function(part) {
|
|
|
+ if (part) { tree = tree[part] }
|
|
|
+ })
|
|
|
+ return tree
|
|
|
+ }
|
|
|
+
|
|
|
}
|