function pgsh(ev) {
var self = this
/**
* Terminal UI/Shell Entry Point
*/
ev.on('cmd_entered', function(input) {
var args = input.trim().split(' ')
// Use the first argument as a command, or the value of `active`.
var command = self.active ? self.active : args.shift()
if (command in self.commands) {
self[command](args)
} else if (command) {
show(command + ' not found')
}
})
/**
* Key functions
*/
ev.on('mount', function() {
document.getElementById(ev.id).onkeydown = function(e) {
var code = e.keyCode || e.which
// Tab for file path completion.
if (code == 9) {
var input = ev.tags['command-line'].refs.command.value.trim().split(' ')
var command = input.shift()
if (['cat', 'cd', 'ls'].indexOf(command) != -1) {
var args = input.join(' ').trim()
var isFile = (command == 'cat') ? true : false;
var path = completePath(args, isFile)
if (path) { ev.trigger('cmd_set', command + ' ' + path) }
}
e.preventDefault()
}
}
})
/**
* Variables and Setup
*/
var getHome = function() { return self.su_active ? '/root' : '/home/pgs' }
this.cwd = getHome()
var getPrompt = function() {
if (self.su_active) {
return '' +
'root ' +
'' + self.cwd + ' ' +
'% '
}
return '' +
'pgs ' +
'' + self.cwd + ' ' +
'$ '
}
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 = pgfs
/**
* Commands
*/
this.commands = {
'about': { order: 1, help: 'Author Information' },
'cat': { order: 2, help: 'Con(cat)enate the contents of a file' },
'cd': { order: 2, help: 'Change folder/directory' },
'clear': { order: 2, help: 'Clears the screen' },
'exit': { order: 2, help: 'Leave the current context' },
'hello': { order: 2, help: 'Say hello to the computer\nUsage: hello [name]' },
'help': { order: 1, help: 'List commands or view information for one\nUsage: help [command]' },
'ls': { order: 2, help: 'List folder contents' },
'pwd': { order: 2, help: 'Show the current working directory' },
'search': { order: 2, help: 'Search the Web (with a Duck)\nUsage: search [query]' },
'su': { order: 2, help: 'Gain Phenomenal Cosmic Power' },
'version': { order: 1, help: 'Display shell information' }
}
this.about = function(args) { 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("cat: " + arg + ": no such file")
}
}
this.cd = function(args) {
var arg = args.join(' ').trim()
arg = arg ? arg : getHome()
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.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()
if (address.length == 0) {
show('Hello to you too')
} else if (address == 'pgsh') {
show('Hello human')
} else {
show('My name is not "' + address + '"')
}
}
this.help = function(args) {
command = args.join(' ').trim()
if (command in self.commands) {
show(self.commands[command].help)
} else if (command) {
show('No information for: ' + command)
} else {
var main = []
var other = []
for (var name in self.commands) {
if (self.commands[name].order == 1) {
main.push(name)
} else {
other.push(name)
}
}
show('\nAvailable commands:\n\n' +
'* ' + main.sort().join(' ') + '\n\n' +
'* ' + other.sort().join(' ') + '\n\n' +
'`help [command]` for more information.')
}
}
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 dstat = 'drwxr-xr-x '
var fstat = '-rw-r--r-- '
var items = []
for (var item in contents) {
var prefix = (typeof contents[item] === 'string') ? fstat : dstat
var suffix = (typeof contents[item] === 'string') ? '' : ''
items.push(prefix + item + suffix)
}
items.unshift(dstat + '..')
items.unshift('total ' + items.length)
show(items.join('\n'))
} else {
show("ls: " + args + ": no such directory")
}
}
this.pwd = function(args) { show(self.cwd) }
this.search = function(args) {
if (!args.join(' ').trim()) { return show(self.commands.search.help) }
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('\n\n' +
'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')
}
ev.trigger('prompt_set', getPrompt())
}
}
this.version = function(args) {
show('Parsley Gardens Shell (pgsh) 1.2.0\nBuilt with: ' +
'Riot ' +
'Skeleton ' +
'highlight.js')
}
/**
* 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.substring(0, 1) == '/' || 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, isFile) {
// Old way of defining default values, for Safari.
var isFile = (typeof isFile !== 'undefined') ? isFile : false;
var parts = path.trim().split('/')
var fname = isFile ? 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 (isFile && !(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] }
})
// Sort JS object. Not a great way, but more compatible.
if (typeof tree === 'object') {
var keys = []
for (key in tree) {
keys.push(key)
}
keys.sort()
var temp = {}
keys.forEach(function(key) {
temp[key] = tree[key]
})
tree = temp
}
return tree
}
// Complete a given file path string based on the current tree.
var completePath = function(input, isFile) {
// Extract file fragment and get actual path.
var slash = input.lastIndexOf('/') + 1
var frag = input.substring(slash)
var base = input.substring(0, slash)
var path = resolveAbsPath(base)
// Complete fragments.
if (pathExists(path)) {
var contents = getContents(path)
var found = ''
for (var name in contents) {
if (!isFile && typeof contents[name] === 'string') { continue }
if (!frag) { found = name; break }
if (name.indexOf(frag) == 0) { found = name; break }
}
var end = (typeof contents[found] === 'object') ? '/' : ''
return base + found + end
}
return ''
}
}