/** * @typedef Command * @property {string} command * @property {string} responseType * @property {string?} value * @property {string[]?} headers * @property {string[]?} rows */ /** * @type {Command[]} commands */ import commands from "./resources/commands.json"; import { getCV, pif, rmRf, setDarkMode } from "./custom-comands"; import { dragElement } from "./draggable"; // Tableau contenant les commandes (utile pour la complétion des commandes) let commandsList = []; commands.forEach((c) => { commandsList.push(c.command); }); // Commandes qui nécessitent un traitement JS const customCommands = ["clear", "dark", "light", "get cv"]; commandsList = commandsList.concat(customCommands); // Commandes 'easter eggs' non disponibles à l'autocomplétion const hiddenCommands = ["pif", "rm -rf /"]; // Ajout de la possibilité de déplacer la fenêtre pour les PC if (window.innerWidth > 1024) { dragElement(document.querySelector(".terminal")); } // Tableau contenant l'historique des commandes const commandsHistory = []; let historyMode = false; let historyIndex = -1; const terminalBody = document.querySelector(".terminal__body"); // Ajout de la ligne par défaut addNewLine(); // Easter egg de décembre, ajout de flocons de neige const now = new Date(); if (now.getMonth() === 11) { let htmlFlakes = ""; for (let i = 0; i < 6; i++) { htmlFlakes += `
help
pour afficher la liste des commandes disponibles.`;
} else {
if (commandObj.responseType === "list" && Array.isArray(commandObj.value)) {
html = "${commandObj.value.join("\n")}`; } } return html; } /** * Ajoute une nouvelle ligne input de commande et désactive la précédente. * @param {string|null} previousUid uid de la ligne précédente. */ function addNewLine(previousUid = null) { const uid = Math.random().toString(36).replace("0.", ""); // terminal__line const terminalLineEl = document.createElement("div"); terminalLineEl.classList.add("terminal__line"); // terminal__response const terminalResponseEl = document.createElement("div"); terminalResponseEl.classList.add("terminal__response"); terminalResponseEl.id = `response-${uid}`; // input text const inputEl = document.createElement("input"); inputEl.type = "text"; inputEl.id = `input-${uid}`; inputEl.autocapitalize = "off"; inputEl.dataset.uid = uid; inputEl.dataset.active = "1"; // Utile pour le focus inputEl.addEventListener("keydown", onCommandInput); terminalLineEl.appendChild(inputEl); if (previousUid) { const previousInputEl = document.getElementById(previousUid); if (previousInputEl) { previousInputEl.setAttribute("disabled", "true"); previousInputEl.removeEventListener("keydown", onCommandInput); delete previousInputEl.dataset.active; } } document.getElementById("terminal").appendChild(terminalLineEl); document.getElementById("terminal").appendChild(terminalResponseEl); inputEl.focus(); // Ajoute le focus dès la création du champs } /** * Gère le keydown sur l'input de la commande. * @param e */ function onCommandInput(e) { const commandValue = e.target.value.trim().toLowerCase(); if (e.keyCode === 13) { // ENTER if (commandValue !== "") { historyMode = false; const idResponse = `response-${e.target.dataset.uid}`; const responseEl = document.getElementById(idResponse); let html; if ( hiddenCommands.includes(commandValue) || customCommands.includes(commandValue) ) { html = handleCustomCommands(commandValue); } else { html = getDomForCommand(commandValue); } if (responseEl) { responseEl.innerHTML = html; commandsHistory.push(commandValue); addNewLine(e.target.id); } } } else if (e.keyCode === 9) { // TAB e.preventDefault(); if (commandValue === "") { this.value = "help"; } else { const matchingCommand = commandsList.find((c) => c.startsWith(commandValue) ); if (matchingCommand) { this.value = matchingCommand; } } historyMode = false; } else if (e.keyCode === 38 || e.keyCode === 40) { // UP / DOWN // Gestion de l'historique if (commandsHistory.length > 0) { if (historyMode === false) { historyIndex = commandsHistory.length - 1; } else { if (e.keyCode === 38 && historyIndex !== 0) { // UP historyIndex--; } else if ( e.keyCode === 40 && historyIndex !== commandsHistory.length - 1 ) { historyIndex++; } } this.value = commandsHistory[historyIndex]; } historyMode = true; } } /** * Permet de gérer les commandes cachées (non proposées dans l'autocomplétion) * @param {string} command * @returns {string|void} Html à afficher dans la réponse de la commande */ function handleCustomCommands(command) { switch (command) { case "pif": pif(); return "C'est la fête !"; case "light": if (document.body.classList.length === 0) return "Vous êtes déjà en mode clair"; setDarkMode(false); return "Vous êtes maintenant en mode clair."; case "dark": if (document.body.classList.length === 1) return "Vous êtes déjà en mode sombre"; setDarkMode(true); return "Vous êtes maintenant en mode sombre."; case "get cv": getCV(); return "Le CV va être téléchargé."; case "rm -rf /": rmRf(); return "w4dhIHZhIFDDiVRFUiAh"; case "clear": terminalBody.innerHTML = ``; return; } } /** * Convert HTML to DOM object * @param html * @returns {DocumentFragment} */ function stringToDom(html) { return document.createRange().createContextualFragment(html); } // ------------------------------------------------------------------------------------ // EVENT LISTENNER // ------------------------------------------------------------------------------------ // Ajout du focus sur l'input même si on clique sur le body (pour garder le curseur) document.body.addEventListener("click", function (e) { if (e.target.tagName !== "INPUT") { const activeInput = document.querySelector("input[data-active]"); activeInput.focus(); } }); document.querySelector(".fake-close").addEventListener("click", function (e) { const terminalEl = document.querySelector(".terminal"); terminalEl.parentElement.removeChild(terminalEl); });