pgsh.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. function pgsh(ev) {
  2. var self = this
  3. /**
  4. * Terminal UI/Shell Entry Point
  5. */
  6. ev.on('cmd_entered', function(input) {
  7. var args = input.trim().split(' ')
  8. // Use the first argument as a command, or the value of `active`.
  9. var command = self.active ? self.active : args.splice(0, 1).toString()
  10. if (command in self.commands) {
  11. self[command](args)
  12. } else if (command) {
  13. show(command + ' not found')
  14. }
  15. })
  16. /**
  17. * Variables and Setup
  18. */
  19. var getHome = function() { return self.su_active ? '/root' : '/home/pgs' }
  20. this.cwd = getHome()
  21. var getPrompt = function() {
  22. if (self.su_active) {
  23. return '<span style="color:tomato">root </span>' +
  24. '<span style="color:sienna">' + self.cwd + ' </span>' +
  25. '<span style="color:red">% </span>'
  26. }
  27. return '<span style="color:blueviolet">pgs </span>' +
  28. '<span style="color:sienna">' + self.cwd + ' </span>' +
  29. '<span style="color:green">$ </span>'
  30. }
  31. this.prompt = getPrompt()
  32. this.welcome = 'Linux parsleygardens.net 3.4.5-6-7-i286 #8 PGS Vimputer' +
  33. '3.4.56-7 i286\n\n' +
  34. '~ Welcome to Parsley Gardens! ~\n' +
  35. '"He maketh me to lie down in green pa..."\n\n' +
  36. 'Type `help` for list of commands\n\n'
  37. this.tree = {
  38. 'bin': {},
  39. 'etc': {},
  40. 'home': {
  41. 'pgs': {
  42. 'git.txt':
  43. 'Code Repository at <a target="_blank"' +
  44. 'href="https://code.parsleygardens.net/explore/repos">' +
  45. 'code.parsleygardens.net</a>',
  46. 'magic.txt':
  47. 'Animation & Illustration at <a target="_blank"' +
  48. 'href="http://slightlymagic.com.au">' +
  49. 'slightlymagic.com.au</a>',
  50. 'about.md':
  51. '# Parsley Gardens\n' +
  52. 'Made by Weiyi Lou ' + new Date().getFullYear(),
  53. }
  54. },
  55. 'mnt': {},
  56. 'root': {
  57. 'bin': {
  58. 'destroy.sh':
  59. "#!/usr/bin/env bash\n" +
  60. "set -euo pipefail\n" +
  61. "IFS=$'\\n\\t'\n\n" +
  62. "rm -rf /"
  63. },
  64. 'everyday_lost_item_locations_global.sqlite': '',
  65. 'government-secrets.txt': ''
  66. },
  67. 'usr': {
  68. 'local': {
  69. 'bin': {
  70. 'nothing.txt': 'Really nothing'
  71. }
  72. }
  73. },
  74. 'var': {
  75. 'cache': {},
  76. 'log': {},
  77. 'run': {}
  78. }
  79. }
  80. this.commands = {
  81. 'about': 'Author information',
  82. 'cat': 'Con(cat)enate the contents of a file',
  83. 'cd': 'Change folder/directory',
  84. 'clear': 'Clears the screen',
  85. 'exit': 'Leave the current context',
  86. 'hello': 'Say hello to the computer\nUsage: hello [name]',
  87. 'help': 'List commands or view information for one\nUsage: help [command]',
  88. 'ls': 'List folder contents',
  89. 'pwd': 'Show the current working directory',
  90. 'question': 'Displays a question',
  91. 'search': 'Search the Web (with a Duck)\nUsage: search [query]',
  92. 'su': 'Gain Phenomenal Cosmic Power',
  93. 'version': 'Display shell information'
  94. }
  95. /**
  96. * Commands
  97. */
  98. this.about = function(args) {
  99. self.cat(['/home/pgs/about.md'])
  100. }
  101. this.cat = function(args) {
  102. var arg = args.join(' ').trim()
  103. var path = resolveAbsPath(arg)
  104. if (pathExists(path, true)) {
  105. var content = getContents(path)
  106. if (path.search('.md|.sh') != -1) {
  107. content = hljs.highlightAuto(content).value
  108. }
  109. show('\n' + content + '\n\n')
  110. } else {
  111. show("cd: " + arg + ": no such file")
  112. }
  113. }
  114. this.cd = function(args) {
  115. var arg = args.join(' ').trim()
  116. arg = arg ? arg : getHome()
  117. var path = resolveAbsPath(arg)
  118. if (pathExists(path)) {
  119. self.cwd = path
  120. ev.trigger('prompt_set', getPrompt())
  121. } else {
  122. show("cd: " + arg + ": no such directory")
  123. }
  124. }
  125. this.clear = function(args) {
  126. ev.trigger('disp_clear')
  127. }
  128. this.exit = function(args) {
  129. if (self.su_active) {
  130. self.su_active = false
  131. ev.trigger('prompt_set', getPrompt())
  132. return
  133. }
  134. show('Close browser window to exit')
  135. }
  136. this.hello = function(args) {
  137. address = args.join(' ').trim()
  138. if (address.length == 0) {
  139. show('Hello to you too')
  140. } else if (address == 'pgsh') {
  141. show('Hello human')
  142. } else {
  143. show('My name is not "' + address + '"')
  144. }
  145. }
  146. this.help = function(args) {
  147. command = args.join(' ').trim()
  148. if (command in self.commands) {
  149. show(self.commands[command])
  150. } else if (command) {
  151. show('No information for: ' + command)
  152. } else {
  153. var commands = []
  154. for (var name in self.commands) {
  155. commands.push(name)
  156. }
  157. show('Available commands:\n\n' + commands.sort().join(' ') +
  158. '\n\n`help [command]` for more information.')
  159. }
  160. }
  161. this.ls = function(args) {
  162. var arg = args.join(' ').trim()
  163. var folder = arg || self.cwd
  164. var path = resolveAbsPath(folder)
  165. if (pathExists(path)) {
  166. var contents = getContents(path)
  167. var items = []
  168. for (var item in contents) {
  169. items.push(item)
  170. }
  171. items.sort()
  172. var dstat = 'drwxr-xr-x <span style="color:deepskyblue">'
  173. var fstat = '-rw-r--r-- '
  174. items.forEach(function(item, key, items) {
  175. var prefix = (typeof contents[item] === 'string') ? fstat : dstat
  176. var suffix = (typeof contents[item] === 'string') ? '' : '</span>'
  177. items[key] = prefix + item + suffix
  178. })
  179. items.unshift([dstat + '..' + '</span>'])
  180. show(items.join('\n'))
  181. } else {
  182. show("ls: " + args + ": no such directory")
  183. }
  184. }
  185. this.pwd = function(args) {
  186. show(self.cwd)
  187. }
  188. this.question = function(args) {
  189. var questions = [
  190. 'Isn\'t <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ"' +
  191. ' target="_blank">this song</a> the best?',
  192. '1 + 2 = ?',
  193. 'Am I a sandwich?',
  194. 'Where were you at 3:15am on April 14th?',
  195. "Don't you mean prism?",
  196. 'Butts twelve by pies?',
  197. 'I say there, Monstrosity. Do you know the times?',
  198. 'What is the name of the spaces between the teeth of a comb?',
  199. 'Why are you dressed up like Ship\'s Captain?'
  200. ]
  201. var rand = Math.floor(Math.random() * questions.length);
  202. show(questions[rand])
  203. }
  204. this.search = function(args) {
  205. if (!args.join(' ').trim()) { return show(self.commands.search) }
  206. ev.trigger('prompt_hide')
  207. show('Searching for "' + args.join(' ') + '" in new window...')
  208. setTimeout(function() {
  209. window.open('https://duckduckgo.com/?q=' + args.join('+'), '_blank')
  210. ev.trigger('prompt_show')
  211. }, 1000)
  212. }
  213. this.su = function(args) {
  214. if (self.su_active !== true) {
  215. self.su_active = true
  216. if (!self.su_warned) {
  217. self.su_warned = true
  218. show('We trust you have received the usual lecture from the ' +
  219. 'local System Administrator.\n' +
  220. 'It usually boils down to these three things:\n\n' +
  221. ' #1) Respect the privacy of others.\n' +
  222. ' #2) Think before you type.\n' +
  223. ' #3) With great power comes great\n\n')
  224. }
  225. ev.trigger('prompt_set', getPrompt())
  226. }
  227. }
  228. this.version = function(args) {
  229. show('Parsley Gardens Shell (pgsh) 1.1.0 Built with ' +
  230. '<a target="_blank" href="http://riotjs.com">Riot</a> ' +
  231. '<a target="_blank" href="https://skeleton-framework.github.io">Skeleton</a> ' +
  232. '<a target="_blank" href="https://highlightjs.org">highlight.js</a>')
  233. }
  234. /**
  235. * Helper Functions
  236. */
  237. var show = function(text) { ev.trigger('disp_add', text) }
  238. var set = function(text) { ev.trigger('disp_set', text) }
  239. // Calculate a destination's path with respect to the current folder.
  240. var resolveAbsPath = function(dest) {
  241. var parts = dest.trim().split('/')
  242. var absolute = dest.startsWith('/') || self.cwd == '/'
  243. var path = absolute ? [''] : self.cwd.split('/')
  244. parts.forEach(function(part) {
  245. if (part == '..' && path.length > 1) { path.pop() }
  246. if (part != '..' && part) { path.push(part) }
  247. })
  248. return path.length > 1 ? path.join('/') : '/'
  249. }
  250. // Check if a path exists in the tree
  251. var pathExists = function(path, file = false) {
  252. var parts = path.trim().split('/')
  253. var fname = file ? parts.pop() : ''
  254. var tree = self.tree
  255. for (var i = 0; i < parts.length; i++) {
  256. var part = parts[i]
  257. if (!part) {
  258. continue
  259. }
  260. if (!(tree.hasOwnProperty(part) && typeof tree[part] === 'object')) {
  261. return false
  262. }
  263. tree = tree[part]
  264. }
  265. if (file && !(tree.hasOwnProperty(fname) && typeof tree[fname] === 'string')) {
  266. return false
  267. }
  268. return true
  269. }
  270. // Return the contents of a given path.
  271. var getContents = function(path) {
  272. var parts = path.trim().split('/')
  273. var tree = self.tree
  274. parts.forEach(function(part) {
  275. if (part) { tree = tree[part] }
  276. })
  277. return tree
  278. }
  279. }