window.cli = { // Element references _input: null, _parent: null, _prompt: null, _output: null, // CLI State _history: [], _index: 0, pwd: '/', hostname: 'virtual', env: {}, exec: {}, filesystem: {}, user: 'root', version: '0.3.0', _buildPrompt: () => `${window.cli.user}@${window.cli.hostname}:${window.cli.pwd}${window.cli.user == 'root' ? '#' : '$'}`, build: (elementId) => { window.cli._parent = document.querySelector(elementId); if(!window.cli._parent) throw new Error(`Could not create konsole, element "${elementId}" does not exist`); window.cli._parent.innerHTML = `
`; window.cli._input = document.querySelector(elementId + ' .cli-stdin-input'); window.cli._prompt = document.querySelector(elementId + ' .cli-stdin-prompt'); window.cli._output = document.querySelector(elementId + ' .cli-stdout'); window.cli._input.addEventListener('keyup', (e) => { if(e.key == "Enter") { window.cli.disable(); if(!!window.cli._input.value) { window.cli._history.push(window.cli._input.value); window.cli._index = window.cli._history.length; window.cli.stdIn(window.cli._input.value) } window.cli._input.value = ''; window.cli.enable(); } else if(e.key == 'Up' || e.key == 'ArrowUp') { if(window.cli._index > 0) window.cli._index--; window.cli._input.value = window.cli._index == window.cli._history.length ? '' : window.cli._history[window.cli._index]; setTimeout(() => { const end = window.cli._input.value.length; window.cli._input.setSelectionRange(end, end); window.cli._input.focus(); }, 1) } else if(e.key == 'Down' || e.key == 'ArrowDown') { if(window.cli._index < window.cli._history.length) window.cli._index++; window.cli._input.value = window.cli._index == window.cli._history.length ? '' : window.cli._history[window.cli._index]; setTimeout(() => { const end = window.cli._input.value.length; window.cli._input.setSelectionRange(end, end); window.cli._input.focus(); }, 1) } }); setTimeout(() => window.cli.stdOut(window.cli.exec['banner'].run()), 1); }, disable: () => { window.cli._input.disabled = true; window.cli._prompt.style.visibility = 'hidden'; }, enable: () => { window.cli._input.disabled = false; window.cli._input.focus(); window.cli._prompt.style.visibility = 'visible'; }, path: (path=window.cli.pwd) => { let p = path[0] == '/'? path : (window.cli.pwd + (window.cli.pwd.endsWith('/') ? '' : '/') + path.replace('./', '')) .replaceAll('//', '/'); const parts = p.split('/').filter(p => !!p); for(let i = 0; i < parts.length; i++) { if(parts[i] == '..') { i--; parts.splice(i, 2); i--; } } return '/' + (parts.length ? parts.join('/') : ''); }, fs: (path, set) => { return window.cli.path(path).split('/').filter(p => !!p).reduce((t, p, i, arr) => { if(!t?.hasOwnProperty(p)) { if(set == undefined) return undefined; t[p] = {}; } if(set !== undefined && i == arr.length - 1) { if(set == null) delete t[p]; else t[p] = set; } return t[p]; }, window.cli.filesystem); }, stdErr: (text) => { const p = document.createElement('p'); p.classList.add('cli-stdout-line'); p.classList.add('cli-stdout-error'); p.innerText = text; window.cli._output.appendChild(p); }, stdIn:(command, suppress=false) => { (Array.isArray(command) ? command.join(' ') : command).split(';').filter(c => !!c).forEach(c => { const parts = c.match(/(?:[^\s"]+|"[^"]*")+/g); if(!parts) return; const exec = window.cli.exec[parts[0]]; if(!exec?.run) { if(!suppress) window.cli.stdErr(`${window.cli._buildPrompt()} ${command}\n${parts[0]}: command not found`); } else { try { const args = parts.slice(1).map(a => (a[0] == '"' || a[0] == "'") ? a.slice(1, -1) : a); const out = exec.run(args); if(!suppress) window.cli.stdOut(`${window.cli._buildPrompt()} ${command}${out != null ? '\n' + (out || '\n') : ''}`); } catch(err) { console.error(err); if(!suppress) { window.cli.stdErr(`${window.cli._buildPrompt()} ${command}\n${err.message || `${parts[0]}: exited with a non-zero status`}`); } } } }); }, stdOut: (text='', html=false) => { const p = document.createElement('p'); p.classList.add('cli-stdout-line'); p[html ? 'innerHTML' : 'innerText'] = text; window.cli._output.appendChild(p); }, type: (text, speed=150) => { let counter = 0; return new Promise(res => { let typing = setInterval(() => { if(counter < text.length) { window.cli._input.value += text[counter]; } else { clearInterval(typing); setTimeout(() => { window.cli.stdIn(text); window.cli._input.value = ''; res(); }, 750); } counter++; }, speed); }); } };