From c9b6b9a5ca082fe7c1b6f58d7713f785a9eb6a5c Mon Sep 17 00:00:00 2001 From: Martial Simon Date: Mon, 15 Sep 2025 01:08:27 +0200 Subject: add: graphs et rushs --- graphs/js/.gitignore | 54 ++++++ graphs/js/.prettierrc.cjs | 6 + graphs/js/accountConstraints/account.js | 42 +++++ graphs/js/accountConstraints/accountError.js | 23 +++ graphs/js/advancedCommunication/client.js | 26 +++ graphs/js/advancedCommunication/server.js | 88 +++++++++ graphs/js/armstrongNumber/armstrongNumber.js | 17 ++ graphs/js/coordinatesDisplay/coordinates.js | 123 +++++++++++++ graphs/js/coordinatesDisplay/index.html | 20 ++ graphs/js/coordinatesDisplay/resources/logo.svg | 63 +++++++ graphs/js/coordinatesDisplay/server.js | 92 ++++++++++ graphs/js/counter/counter.js | 12 ++ graphs/js/counter/index.html | 20 ++ graphs/js/counter/style.css | 31 ++++ .../js/deepCopyAndEquality/deepCopyAndEquality.js | 41 +++++ graphs/js/epiTinder/data.json | 118 ++++++++++++ graphs/js/epiTinder/epiTinder.js | 114 ++++++++++++ graphs/js/epiTinderDB/epiTinderDB.js | 204 +++++++++++++++++++++ graphs/js/epiTinderDB/provided.sql | 17 ++ graphs/js/eslint/fibo.js | 31 ++++ graphs/js/foodTruck/data.js | 70 +++++++ graphs/js/foodTruck/dirtyFoodTruck.js | 172 +++++++++++++++++ graphs/js/foodTruck/foodTruck.js | 124 +++++++++++++ graphs/js/foodTruck/index.html | 40 ++++ graphs/js/foodTruck/sleep.js | 11 ++ graphs/js/foodTruck/static/failedOnload.js | 9 + graphs/js/foodTruck/static/index.js | 78 ++++++++ graphs/js/foodTruck/static/style.css | 200 ++++++++++++++++++++ graphs/js/gallery/gallery.js | 54 ++++++ graphs/js/helloWorld/helloWorld.js | 7 + graphs/js/inspectAndFuse/inspectAndFuse.js | 77 ++++++++ graphs/js/intergalactic/destinations.js | 47 +++++ graphs/js/intergalactic/travelers.js | 40 ++++ graphs/js/jestBasic/fibo.js | 29 +++ graphs/js/jestBasic/fibo.test.js | 37 ++++ graphs/js/logMeIn/logMeIn.js | 59 ++++++ graphs/js/modularLogger/modularLogger.js | 76 ++++++++ graphs/js/myCompany/boss.js | 30 +++ graphs/js/myCompany/company.js | 77 ++++++++ graphs/js/myCompany/employee.js | 21 +++ graphs/js/notSoFast/articles.json | 74 ++++++++ graphs/js/notSoFast/notSoFast.js | 33 ++++ graphs/js/notSoFast/server.js | 147 +++++++++++++++ graphs/js/oidc/complete/epita/index.html | 35 ++++ graphs/js/oidc/complete/epita/index.js | 40 ++++ graphs/js/oidc/main.html | 27 +++ graphs/js/oidc/redirect.js | 17 ++ graphs/js/oidc/server.js | 33 ++++ graphs/js/oidc/style.css | 116 ++++++++++++ graphs/js/replace/replace.js | 8 + graphs/js/storageWars/index.html | 23 +++ graphs/js/storageWars/storageWars.js | 80 ++++++++ graphs/js/throttleDebounce/throttleDebounce.js | 48 +++++ graphs/js/todoList/index.html | 17 ++ graphs/js/todoList/style.css | 64 +++++++ graphs/js/todoList/todoList.js | 27 +++ 56 files changed, 3189 insertions(+) create mode 100644 graphs/js/.gitignore create mode 100644 graphs/js/.prettierrc.cjs create mode 100644 graphs/js/accountConstraints/account.js create mode 100644 graphs/js/accountConstraints/accountError.js create mode 100644 graphs/js/advancedCommunication/client.js create mode 100644 graphs/js/advancedCommunication/server.js create mode 100644 graphs/js/armstrongNumber/armstrongNumber.js create mode 100644 graphs/js/coordinatesDisplay/coordinates.js create mode 100644 graphs/js/coordinatesDisplay/index.html create mode 100755 graphs/js/coordinatesDisplay/resources/logo.svg create mode 100644 graphs/js/coordinatesDisplay/server.js create mode 100644 graphs/js/counter/counter.js create mode 100644 graphs/js/counter/index.html create mode 100644 graphs/js/counter/style.css create mode 100644 graphs/js/deepCopyAndEquality/deepCopyAndEquality.js create mode 100644 graphs/js/epiTinder/data.json create mode 100644 graphs/js/epiTinder/epiTinder.js create mode 100644 graphs/js/epiTinderDB/epiTinderDB.js create mode 100644 graphs/js/epiTinderDB/provided.sql create mode 100644 graphs/js/eslint/fibo.js create mode 100644 graphs/js/foodTruck/data.js create mode 100644 graphs/js/foodTruck/dirtyFoodTruck.js create mode 100644 graphs/js/foodTruck/foodTruck.js create mode 100644 graphs/js/foodTruck/index.html create mode 100644 graphs/js/foodTruck/sleep.js create mode 100644 graphs/js/foodTruck/static/failedOnload.js create mode 100644 graphs/js/foodTruck/static/index.js create mode 100644 graphs/js/foodTruck/static/style.css create mode 100644 graphs/js/gallery/gallery.js create mode 100644 graphs/js/helloWorld/helloWorld.js create mode 100644 graphs/js/inspectAndFuse/inspectAndFuse.js create mode 100644 graphs/js/intergalactic/destinations.js create mode 100644 graphs/js/intergalactic/travelers.js create mode 100644 graphs/js/jestBasic/fibo.js create mode 100644 graphs/js/jestBasic/fibo.test.js create mode 100644 graphs/js/logMeIn/logMeIn.js create mode 100644 graphs/js/modularLogger/modularLogger.js create mode 100644 graphs/js/myCompany/boss.js create mode 100644 graphs/js/myCompany/company.js create mode 100644 graphs/js/myCompany/employee.js create mode 100644 graphs/js/notSoFast/articles.json create mode 100644 graphs/js/notSoFast/notSoFast.js create mode 100644 graphs/js/notSoFast/server.js create mode 100644 graphs/js/oidc/complete/epita/index.html create mode 100644 graphs/js/oidc/complete/epita/index.js create mode 100644 graphs/js/oidc/main.html create mode 100644 graphs/js/oidc/redirect.js create mode 100644 graphs/js/oidc/server.js create mode 100644 graphs/js/oidc/style.css create mode 100644 graphs/js/replace/replace.js create mode 100644 graphs/js/storageWars/index.html create mode 100644 graphs/js/storageWars/storageWars.js create mode 100644 graphs/js/throttleDebounce/throttleDebounce.js create mode 100644 graphs/js/todoList/index.html create mode 100644 graphs/js/todoList/style.css create mode 100644 graphs/js/todoList/todoList.js (limited to 'graphs/js') diff --git a/graphs/js/.gitignore b/graphs/js/.gitignore new file mode 100644 index 0000000..afb8184 --- /dev/null +++ b/graphs/js/.gitignore @@ -0,0 +1,54 @@ +*.tar +main.js +package.json +node_modules/ +yarn.lock +package-lock.json + +# IDEs and editors +.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# System Files +.DS_Store +Thumbs.db + +# Lint and formatter +.prettierrc.js +eslint.config.mjs diff --git a/graphs/js/.prettierrc.cjs b/graphs/js/.prettierrc.cjs new file mode 100644 index 0000000..fcc77f6 --- /dev/null +++ b/graphs/js/.prettierrc.cjs @@ -0,0 +1,6 @@ +module.exports = { + tabWidth: 4, + useTabs: false, // Use spaces instead of defaults tabs + semi: true, // Force semilicons + printWidth: 80, // Max width +}; diff --git a/graphs/js/accountConstraints/account.js b/graphs/js/accountConstraints/account.js new file mode 100644 index 0000000..4a3ddbe --- /dev/null +++ b/graphs/js/accountConstraints/account.js @@ -0,0 +1,42 @@ +"use strict"; + +const { + InvalidUsernameError, + InvalidPasswordError, + InvalidYearOfBirthError, +} = require("./accountError"); + +class Account { + // FIXME DONE: A constructor was deleted here + constructor(username, password, yearOfBirth) { + const regUsername = /^[a-zA-Z0-9\-_]+$/; + const lower = /[a-z]/; + const upper = /[A-Z]/; + const digits = /[0-9]/; + + if (!regUsername.test(username)) { + throw new InvalidUsernameError("Invalid username."); + } + + this.username = username; + if ( + !password.match(lower) || + !password.match(upper) || + !password.match(digits) || + password.length < 8 || + password.length > 100 + ) { + throw new InvalidPasswordError("Invalid password."); + } + + this.password = password; + if (yearOfBirth <= 1800 || yearOfBirth > new Date().getFullYear()) { + throw new InvalidYearOfBirthError("Invalid birth year"); + } + + this.yearOfBirth = yearOfBirth; + } +} +module.exports = { + Account, +}; diff --git a/graphs/js/accountConstraints/accountError.js b/graphs/js/accountConstraints/accountError.js new file mode 100644 index 0000000..9b427e0 --- /dev/null +++ b/graphs/js/accountConstraints/accountError.js @@ -0,0 +1,23 @@ +class InvalidUsernameError extends Error { + name = "InvalidUsernameError"; + constructor(message) { + super("Invalid username: " + message); + } +} +class InvalidPasswordError extends Error { + name = "InvalidPasswordError"; + constructor(message) { + super("Invalid password: " + message); + } +} +class InvalidYearOfBirthError extends Error { + name = "InvalidYearOfBirthError"; + constructor(message) { + super("Invalid birth year: " + message); + } +} +module.exports = { + InvalidPasswordError, + InvalidUsernameError, + InvalidYearOfBirthError, +}; diff --git a/graphs/js/advancedCommunication/client.js b/graphs/js/advancedCommunication/client.js new file mode 100644 index 0000000..7c051a8 --- /dev/null +++ b/graphs/js/advancedCommunication/client.js @@ -0,0 +1,26 @@ +const WebSocket = require("ws"); + +function addClient(userName) { + const socket = new WebSocket("ws://localhost:8080?username=" + userName); + + socket.on("open", () => { + socket.send(userName + ": trying to establish connection"); + }); + socket.on("close", (code, reason) => { + console.log( + ": Connection has been closed: [" + + code + + "] " + + reason, + ); + }); + socket.on("message", (data) => { + console.log(": " + data); + }); + return socket; +} +module.exports = { + addClient, +}; diff --git a/graphs/js/advancedCommunication/server.js b/graphs/js/advancedCommunication/server.js new file mode 100644 index 0000000..e642396 --- /dev/null +++ b/graphs/js/advancedCommunication/server.js @@ -0,0 +1,88 @@ +const WebSocket = require("ws"); + +function startServer() { + const server = new WebSocket.Server({ port: 8080 }); + const connections = new Array(); + let maxID = 0; + + console.log("Websocket server is running on port 8080."); + server.on("connection", (socket, request) => { + const userName = request.url.substring(11); + + maxID++; + const current_connection = { + userName: userName, + id: maxID, + socket: socket, + }; + + socket.on("message", (data) => { + console.log("[" + current_connection.id + "] " + data); + }); + socket.on("close", (code, reason) => { + console.log( + "[" + + current_connection.id + + "] Disconnected: [" + + code + + "] " + + reason, + ); + if (code !== 1008) { + for (const conn of connections) { + if (conn.id !== current_connection.id) { + conn.socket.send(userName + " disconnected"); + } + } + + connections.splice(connections.indexOf(current_connection), 1); + } + }); + for (const conn of connections) { + if (conn.userName === userName) { + socket.close( + 1008, + 'Username: "' + userName + '" is already taken', + ); + return; + } + } + + socket.send("Welcome " + userName); + connections.push(current_connection); + if (connections.length === 1) { + socket.send(userName + ", you are the only player connected"); + } else { + socket.send(connections.length + " players are connected"); + } + + for (const conn of connections) { + if (conn.userName !== userName) { + conn.socket.send(userName + " connected"); + } + } + + if (connections.length < 4) { + for (const conn of connections) { + conn.socket.send( + "Waiting for " + + (4 - connections.length) + + " other players to start the game", + ); + } + } else { + for (const conn of connections) { + conn.socket.send( + "Match will start soon, disconnecting " + + conn.userName + + " from the lobby", + ); + conn.socket.close(1000, "Match is starting"); + } + } + }); + return server; +} +module.exports = { + startServer, +}; diff --git a/graphs/js/armstrongNumber/armstrongNumber.js b/graphs/js/armstrongNumber/armstrongNumber.js new file mode 100644 index 0000000..59da441 --- /dev/null +++ b/graphs/js/armstrongNumber/armstrongNumber.js @@ -0,0 +1,17 @@ +function armstrongNumber(number) { + if (number < 0 || number == null || number == undefined) { + return false; + } + + const ndigits = number.toString().length; + let sum = 0; + + for (let i = 0; i < ndigits; i++) { + sum = sum + number.toString()[i] ** ndigits; + } + + return sum === number; +} +module.exports = { + armstrongNumber, +}; diff --git a/graphs/js/coordinatesDisplay/coordinates.js b/graphs/js/coordinatesDisplay/coordinates.js new file mode 100644 index 0000000..845599f --- /dev/null +++ b/graphs/js/coordinatesDisplay/coordinates.js @@ -0,0 +1,123 @@ +/** + * Retrieves the width of the map. + * @returns {Promise} A promise that resolves when the width is retrieved. + */ +const getMapWidth = async () => { + const res = await fetch("http://localhost:2707/mapWidth"); + + return res.json(); +}; + +/** + * Fetches the bit at the specified index from the server. + * @param {number} i - The index of the bit to fetch. + * @returns {Promise} - A promise that resolves with the fetched bit. + */ +const getMapPiece = async (i) => { + const res = await fetch(`http://localhost:2707/piece/${i}`); + + return res.json(); +}; + +/** + * Creates a grid element with the specified map width. + * @param {number} mapWidth - The width of the map. + */ + +function createGrid(mapWidth) { + const div = document.createElement("div"); + + div.id = "asyncGrid"; + div.style.display = "grid"; + div.style.gridTemplateColumns = `repeat(${mapWidth}, 1fr)`; + div.style.gridTemplateRows = `repeat(${mapWidth}, 1fr)`; + div.style.width = "fit-content"; + div.style.height = "fit-content"; + document.body.appendChild(div); +} + +/** + * Displays an SVG element asynchronously at the specified coordinates. + * + * @param {string} svg - The SVG element to be displayed. + * @param {number} x - The x-coordinate of the grid cell where the SVG element will be displayed. + * @param {number} y - The y-coordinate of the grid cell where the SVG element will be displayed. + */ +async function displayPiece(svg, x, y) { + const svgElement = document.createElement("div"); + + svgElement.innerHTML = svg; + + const width = svgElement.lastChild.getAttribute("width"); + const height = svgElement.lastChild.getAttribute("height"); + + svgElement.style.width = `${width}px`; + svgElement.style.height = `${height}px`; + svgElement.style.gridColumn = x + 1; + svgElement.style.gridRow = y + 1; + svgElement.style.padding = 0; + svgElement.style.margin = 0; + document.getElementById("asyncGrid").appendChild(svgElement); +} + +async function displayMap() { + // FIXME ! + + const startTime = Date.now(); + const width = await getMapWidth(); + + createGrid(width); + const promises = new Array(); + + for (let i = 0; i < width * width; i++) { + promises.push( + getMapPiece(i).then(async (piece) => { + await displayPiece(piece.svg, piece.x, piece.y); + }), + ); + } + + await Promise.all(promises); + return Date.now() - startTime; + /*return new Promise((resolve, reject) => { + resolve(getMapWidth); + }) + .then((width) => { + createGrid(width); + let promises = new Array(); + for (let i = 0; i < width * width; i++) { + promises.push( + new Promise((resolve) => { + resolve(getMapPiece(i)); + }).then((piece) => { + displayPiece(piece.svg, piece.x, piece.y); + }), + ); + } + Promise.all(promises); + return Date.now() - startTime; + }) + .catch((error) => error);*/ + // don't forget you can use above functions to help you and also make your own +} + +// Your code goes here, the html page will load your script and launch it +// Catch errors if you push your testing code so the Moulinette doesn't fail +displayMap() + .then((time) => { + console.log("displayed in " + time + " ms."); + }) + .catch((error) => { + console.error(error); + }); + +// As always, we need to export the displayMap function for the tests. +// To avoid errors in browsers (where "module" is not defined), +// we wrap the exports inside a conditional check to ensure they +// are only assigned when the code is running in a Node.js environment. + +if (typeof window === "undefined") { + module.exports = { + displayMap, + }; +} diff --git a/graphs/js/coordinatesDisplay/index.html b/graphs/js/coordinatesDisplay/index.html new file mode 100644 index 0000000..d2bf830 --- /dev/null +++ b/graphs/js/coordinatesDisplay/index.html @@ -0,0 +1,20 @@ + + + + Async Display + + + + + + + diff --git a/graphs/js/coordinatesDisplay/resources/logo.svg b/graphs/js/coordinatesDisplay/resources/logo.svg new file mode 100755 index 0000000..14ac718 --- /dev/null +++ b/graphs/js/coordinatesDisplay/resources/logo.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Being a hero means fighting back + even when it seems impossible + + + + + \ No newline at end of file diff --git a/graphs/js/coordinatesDisplay/server.js b/graphs/js/coordinatesDisplay/server.js new file mode 100644 index 0000000..45b7fed --- /dev/null +++ b/graphs/js/coordinatesDisplay/server.js @@ -0,0 +1,92 @@ +// svg image server +const express = require("express"); +const cors = require("cors"); +const DOMParser = require("xmldom").DOMParser; +const XMLSerializer = require("xmldom").XMLSerializer; +const fs = require("fs"); + +const app = express(); + +app.use(cors()); + +app.use(express.static("public")); + +app.listen(2707, () => { + console.log("Server listening on port 2707 👂"); +}); + +const sidePiecesCount = 20; // size has to odd to make the puzzle solvable + +app.get("/mapWidth", (req, res) => { + res.json(sidePiecesCount); +}); + +const parser = new DOMParser(); + +const svgText = fs.readFileSync(__dirname + "/resources/logo.svg", "utf8"); +const svg = parser.parseFromString(svgText, "image/svg+xml"); + +const width = svg.documentElement.getAttribute("width"); +const height = svg.documentElement.getAttribute("height"); + +const gridSideSize = Math.max(width, height); + +const pieceSize = gridSideSize / sidePiecesCount; + +const piecesMap = new Array(sidePiecesCount * sidePiecesCount); + +for (let x = 0; x < sidePiecesCount; x++) { + for (let y = 0; y < sidePiecesCount; y++) { + const piece = svg.cloneNode(true); + + piece.documentElement.setAttribute( + "viewBox", + `${x * pieceSize} ${y * pieceSize} ${pieceSize} ${pieceSize}`, + ); // ATTENTION : this method is really slow, in real life cases you should really cut the image and not send it with information on where to crop it + piece.documentElement.setAttribute("width", `${pieceSize}`); + piece.documentElement.setAttribute("height", `${pieceSize}`); + const svgString = new XMLSerializer().serializeToString(piece); + + piecesMap[x + y * sidePiecesCount] = { + svg: svgString, + x: x, + y: y, + }; + } +} + +const shuffledCoordinates = Array.from( + Array(sidePiecesCount * sidePiecesCount).keys(), +); + +const swap = (a, i, j) => { + const tmp = a[i]; + + a[i] = a[j]; + a[j] = tmp; +}; + +const shuffle = (a) => { + let i = a.length - 1; + + while (i > 0) { + const j = Math.floor(Math.random() * i); + + swap(a, i, j); + i--; + } +}; + +shuffle(shuffledCoordinates); + +console.log("Coordinates shuffled ! 🥳"); +console.log("now serving on port 2707. 🫡"); + +for (let x = 0; x < shuffledCoordinates.length; x++) { + app.get(`/piece/${x}`, (req, res) => { + console.log(`piece ${x} requested 🤖`); + res.send(piecesMap[shuffledCoordinates[x]]); + }); +} + +exports.app = app; diff --git a/graphs/js/counter/counter.js b/graphs/js/counter/counter.js new file mode 100644 index 0000000..89198de --- /dev/null +++ b/graphs/js/counter/counter.js @@ -0,0 +1,12 @@ +const counter = document.getElementById("count"); +let counterVal = parseInt(counter.innerHTML); +const btnPlus = document.getElementById("plus"); +const btnMinus = document.getElementById("minus"); + +btnPlus.addEventListener("click", () => { + counter.innerHTML = ++counterVal; +}); + +btnMinus.addEventListener("click", () => { + counter.innerHTML = --counterVal; +}); diff --git a/graphs/js/counter/index.html b/graphs/js/counter/index.html new file mode 100644 index 0000000..d088aac --- /dev/null +++ b/graphs/js/counter/index.html @@ -0,0 +1,20 @@ + + + + + + + + + My Super Counter + + +
+
0
+
+ + +
+
+ + diff --git a/graphs/js/counter/style.css b/graphs/js/counter/style.css new file mode 100644 index 0000000..677acc2 --- /dev/null +++ b/graphs/js/counter/style.css @@ -0,0 +1,31 @@ +html, +body { + height: 100%; +} + +.container { + height: 100%; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; +} + +#count { + font-size: 20vh; +} + +.actions { + display: flex; + height: 10vh; +} + +.actions > button { + width: 10vw; + background-color: rgb(0, 132, 255); + font-size: 5vh; +} + +#plus { + margin-right: 20px; +} diff --git a/graphs/js/deepCopyAndEquality/deepCopyAndEquality.js b/graphs/js/deepCopyAndEquality/deepCopyAndEquality.js new file mode 100644 index 0000000..4a67b6b --- /dev/null +++ b/graphs/js/deepCopyAndEquality/deepCopyAndEquality.js @@ -0,0 +1,41 @@ +function deepCopy(x) { + if (x == null || x == undefined) { + return x; + } else if (x instanceof Date) { + return new Date(x.valueOf()); + } else if (x instanceof Array) { + const res = new Array(); + + x.forEach((element) => res.push(deepCopy(element))); + return res; + } else if (typeof x === "object") { + const res = {}; + + for (const prop in x) { + res[prop] = deepCopy(x[prop]); + } + + const sort = (o) => + Object.keys(o) + .sort() + .reduce((final, key) => { + final[key] = o[key]; + return final; + }, {}); + + return sort(res); + } else { + return x; + } +} +function deepEquality(a, b) { + if (typeof a !== typeof b) { + return false; + } + + return JSON.stringify(deepCopy(a)) === JSON.stringify(deepCopy(b)); +} +module.exports = { + deepCopy, + deepEquality, +}; diff --git a/graphs/js/epiTinder/data.json b/graphs/js/epiTinder/data.json new file mode 100644 index 0000000..cd325a5 --- /dev/null +++ b/graphs/js/epiTinder/data.json @@ -0,0 +1,118 @@ +{ + "users": [ + { + "id": 0, + "name": "Puff", + "age": 2, + "description": "I like everyone who does javascript (I'm the mascot of the workshop)" + }, + { + "id": 1, + "name": "Bubble", + "age": 1, + "description": "I'm the real javascript mascot but don't tell puff he'll cry again" + }, + { + "id": 2, + "name": "Maxime Buisson", + "age": 40, + "description": "I already have my other half but I want to make friends." + }, + { + "id": 3, + "name": "Dorian Penso", + "age": 25, + "description": "Gym enthusiast by day, code lover by night, seeking a partner to write code and lift weights together." + }, + { + "id": 4, + "name": "Pierre-Alexis Valbrun", + "age": 32, + "description": "I'm a full-stack developer, I can handle both your front-end and back-end." + }, + { + "id": 5, + "name": "Paul Genillon", + "age": 26, + "description": "It’s not humid.. it’s raining" + }, + { + "id": 6, + "name": "Botumrath Morchoisne", + "age": 24, + "description": "What am I doing here??" + }, + { + "id": 7, + "name": "Pierre Santamaria", + "age": 22, + "description": "I'll love you like I love JavaScript, I won't get anything you tell me but it'll be passionate." + }, + { + "id": 8, + "name": "Baptiste Schaudel", + "age": 9, + "description": "I am operational to manage my team, I will let you discover if I am too with love." + }, + { + "id": 9, + "name": "Maxim Payeur", + "age": 8, + "description": "I lost my e, can you help me find it." + }, + { + "id": 10, + "name": "Vinh-Toan Phan", + "age": 17, + "description": "I'm an excelent cooker, do you want to be my cookware." + }, + { + "id": 11, + "name": "Angelina Kuntz", + "age": 44, + "description": "You can call me Ada" + }, + { + "id": 12, + "name": "Ewan Lemonnier", + "age": 19, + "description": "Barely legal" + }, + { + "id": 13, + "name": "Anaïs Druelle", + "age": 21, + "description": "What's the only part of a vegetable you can't eat?" + }, + { + "id": 14, + "name": "Mathéo Romé", + "age": 22, + "description": "I can’t sing but I will charm you anyway" + }, + { + "id": 15, + "name": "Noé Bauvineau", + "age": 58, + "description": "Searching for someone who can appreciate the elegance of a well-designed API." + }, + { + "id": 16, + "name": "Vincent Thirouin", + "age": 39, + "description": "I may not be able to dance, but I can sure write some smooth code." + }, + { + "id": 17, + "name": "Thibault Viennot", + "age": 30, + "description": "Searching for someone who knows how to have fun while debugging" + }, + { + "id": 18, + "name": "Pauline Chautard", + "age": 22, + "description": "I'm like a compiler, I can take your high-level needs and turn them into low-level satisfaction." + } + ] +} \ No newline at end of file diff --git a/graphs/js/epiTinder/epiTinder.js b/graphs/js/epiTinder/epiTinder.js new file mode 100644 index 0000000..881a2c8 --- /dev/null +++ b/graphs/js/epiTinder/epiTinder.js @@ -0,0 +1,114 @@ +const fs = require("fs"); +const express = require("express"); + +function readUsersFromJSONFile(JSON_filename) { + /* + ** Return the list of users stored in the JSON file + ** JSON_filename: path to the JSON file + */ + const content = fs.readFileSync(JSON_filename, (err) => { + if (err) { + console.error(err); + return; + } + }); + + return JSON.parse(content).users; +} + +function writeUsersToJSONFile(JSON_filename, users) { + /* + ** Overwrite the given JSON_filename with the given + ** list of users. + ** JSON_filename: path to the JSON file + ** users : list of users objects + */ + const usersJSON = JSON.stringify({ users: users }); + + fs.writeFileSync(JSON_filename, usersJSON, (err) => { + if (err) { + console.error(err); + return; + } + }); +} +function epiTinderWebServer(host, port, filename) { + const app = express(); + + app.use(express.json()); + const users = readUsersFromJSONFile(filename); + let maxID = 0; + + if (users.length > 0) { + maxID = Math.max(...users.map((e) => e.id)) + 1; + } + + app.get("/", (req, res) => { + res.status(200).send({ message: "Hello World!" }); + }); + app.get("/users", (req, res) => { + res.status(200).send(users); + }); + app.post("/users", (req, res) => { + const new_user = { + id: maxID++, + name: req.body.name, + age: req.body.age, + description: req.body.description, + }; + + users.push(new_user); + writeUsersToJSONFile(filename, users); + res.status(201).send(new_user); + }); + app.get("/users/:id", (req, res) => { + const u = users.find((e) => e.id == req.params.id); + + if (u == undefined) { + res.status(404).send({ + message: `No user with id: ${req.params.id} found`, + }); + } else { + res.status(200).send(u); + } + }); + app.put("/users/:id", (req, res) => { + const u = users.findIndex((e) => e.id == req.params.id); + + if (u == -1) { + res.status(404).send({ + message: `No user with id: ${req.params.id} found`, + }); + } else { + users[u].name = req.body.name; + users[u].age = req.body.age; + users[u].description = req.body.description; + writeUsersToJSONFile(filename, users); + res.status(201).send(users[u]); + } + }); + app.delete("/users/:id", (req, res) => { + const u = users.findIndex((e) => e.id == req.params.id); + + if (u == -1) { + res.status(404).send({ + message: `No user with id: ${req.params.id} found`, + }); + } else { + const usr = users[u]; + + users.splice(u, 1); + writeUsersToJSONFile(filename, users); + res.status(200).send(usr); + } + }); + app.get("*", (req, res) => { + res.status(404).send({ message: "Not found" }); + }); + return app.listen(port, () => { + console.log("Server running at http://" + host + ":" + port + "/"); + }); +} +module.exports = { + epiTinderWebServer, +}; diff --git a/graphs/js/epiTinderDB/epiTinderDB.js b/graphs/js/epiTinderDB/epiTinderDB.js new file mode 100644 index 0000000..d9f35a8 --- /dev/null +++ b/graphs/js/epiTinderDB/epiTinderDB.js @@ -0,0 +1,204 @@ +const { Sequelize, Op, DataTypes } = require("sequelize"); + +let seq = null; +let userModel = null; + +async function connectToDB() { + if (!seq) { + seq = new Sequelize( + "postgres", + process.env.USERNAME, + process.env.PASSWORD, + { + port: 5432, + dialect: "postgres", + }, + ); + } + + if (!userModel) { + userModel = seq.define( + "epitinder_users", + { + name: { + type: DataTypes.STRING, + }, + age: { + type: DataTypes.INTEGER, + }, + description: { + type: DataTypes.STRING, + }, + }, + { + // Other model options go here + timestamps: false, + }, + ); + } + + return seq; +} + +async function getAllUsers() { + return userModel.findAll({ + attributes: ["id", "name", "age", "description"], + raw: true, + }); +} + +async function getUser(id) { + return userModel + .findOne({ + attributes: ["id", "name", "age", "description"], + where: { id: id }, + raw: true, + }) + .catch(() => { + return null; + }); +} + +async function addUser(newUser) { + if ( + newUser["id"] || + !newUser["name"] || + !newUser["age"] || + !newUser["description"] + ) { + return null; + } + + return userModel + .create({ + name: newUser.name, + age: newUser.age, + description: newUser.description, + }) + .then((usr) => usr.dataValues) + .catch(() => null); +} +async function updateUser(user) { + if ( + !user["id"] || + !user["name"] || + !user["age"] || + !user["description"] || + Object.keys(user).length > 4 + ) { + return null; + } + + const res = await userModel + .findOne({ + where: { id: user.id }, + raw: true, + }) + .catch(() => null); + + if (!res) { + return null; + } + + await userModel + .update( + { + name: user.name, + age: user.age, + description: user.description, + }, + { + where: { id: user.id }, + raw: true, + }, + ) + .catch(() => null); + return user; +} + +async function deleteUser(id) { + let res = await userModel + .findOne({ + where: { id: id }, + raw: true, + }) + .catch(() => null); + + if (!res) { + return null; + } + + res = { + id: res.id, + name: res.name, + age: res.age, + description: res.description, + }; + await userModel.destroy({ where: { id: id } }); + return res; +} + +async function getAllUsersName() { + return userModel + .findAll({ + attributes: ["name"], + raw: true, + }) + .catch(() => { + return null; + }); +} + +async function getAllYoungAdults() { + return userModel + .findAll({ + where: { + age: { + [Op.between]: [18, 29], + }, + }, + raw: true, + }) + .catch(() => { + return null; + }); +} + +module.exports = { + getAllUsers, + getAllUsersName, + getAllYoungAdults, + getUser, + addUser, + deleteUser, + updateUser, + connectToDB, +}; + +async function main() { + await connectToDB(); + //console.log(await getAllUsers()); + //console.log(await getAllUsersName()); + //console.log(await getAllYoungAdults()); + //console.log(await getUser(5)); + /*console.log( + await addUser({ + name: "Martial Simon", + age: 19, + description: "Sex is like pointers, I like it raw", + }), + );*/ + console.log( + await updateUser({ + id: 56, + name: "Mickael Razzouk", + age: 21, + sex_appeal: 100, + description: "Le goat", + }), + ); + //console.log(await getUser(5)); + //console.log(await deleteUser(23)); +} + +main(); diff --git a/graphs/js/epiTinderDB/provided.sql b/graphs/js/epiTinderDB/provided.sql new file mode 100644 index 0000000..81d65da --- /dev/null +++ b/graphs/js/epiTinderDB/provided.sql @@ -0,0 +1,17 @@ +CREATE TABLE epitinder_users (id SERIAL PRIMARY KEY, name VARCHAR(255), age INT, description VARCHAR(255)); + +INSERT INTO epitinder_users (name, age, description) VALUES ('Puff',2, 'I like everyone who does javascript (I m the mascot of the workshop)'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Bubble',1, 'I m the real javascript mascot but don t tell puff he ll cry again'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Maxime Buisson',40, 'I already have my other half but I want to make friends.'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Dorian Penso',25, 'Gym enthusiast by day, code lover by night, seeking a partner to write code and lift weights together.'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Pierre-Alexis Valbrun',32, 'I m a full-stack developer, I can handle both your front-end and back-end.'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Paul Genillon',21, 'It is not compiling since it is a wooclap.'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Botumrath Morchoisne',24, 'What am I doing here ??'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Pierre Santamaria',22, 'I will love you like I love JavaScript, I won t get anything you tell me but it will be passionate.'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Baptiste Schaudel',9, 'I am operational to manage my team, I will let you discover if I am too with love.'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Maxim Payeur',8, 'What am I doing here ??'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Vinh-Toan Phan',17, 'I am an excelent cooker, do you want to be my cookware.'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Angelina Kuntz',44, 'You can call me Ada'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Ewan Lemonnier',19, 'Barely legal'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Anaïs Druelle',21, 'What is the only part of a vegetable you can t eat?'); +INSERT INTO epitinder_users (name, age, description) VALUES ('Mathéo Romé',22, 'I can’t sing but I will charm you anyway'); \ No newline at end of file diff --git a/graphs/js/eslint/fibo.js b/graphs/js/eslint/fibo.js new file mode 100644 index 0000000..9b466e7 --- /dev/null +++ b/graphs/js/eslint/fibo.js @@ -0,0 +1,31 @@ +function fibo(n) { + // I'm a comment + if (typeof n != "number" || isNaN(n)) { + return -1; + } + + if (n < 0) { + return -1; + } + + if (n === 0) { + return 0; + } + + let a = 1; + + let b = 1; + let result = 1; + + for (let i = 2; i < n; i++) { + result = a + b; + b = a; + a = result; + } + + return result; +} + +module.exports = { + fibo, +}; diff --git a/graphs/js/foodTruck/data.js b/graphs/js/foodTruck/data.js new file mode 100644 index 0000000..dab9982 --- /dev/null +++ b/graphs/js/foodTruck/data.js @@ -0,0 +1,70 @@ +"use strict"; + +const stocks = { + mozzarella: 4, + parmesan: 5, + cheddar: 6, + "goat cheese": 7, + meat: 3, + chicken: 4, + salmon: 7, + tuna: 8, + bacon: 4, + mushrooms: 4, + tomato: 5, + "tomato sauce": 5, + "sour cream": 5, + fries: 20, + eggs: 10, + buns: 20, + lettuce: 5, + oregano: 8, +}; + +const recipes = { + pizza: { + Margarita: { + cheese: { + mozzarella: 4, + }, + sauce: "tomato sauce", + toppings: { oregano: 2 }, + }, + "4 cheese": { + cheese: { + mozzarella: 2, + parmesan: 2, + cheddar: 2, + "goat cheese": 2, + }, + toppings: { oregano: 2 }, + sauce: "tomato sauce", + }, + "Creamy 4 cheese": { + cheese: { + mozzarella: 2, + parmesan: 2, + cheddar: 2, + "goat cheese": 2, + }, + toppings: { oregano: 2 }, + sauce: "sour cream", + }, + }, + burger: { + Whoopty: { cheddar: 2, meat: 2, lettuce: 2, tomato: 2, buns: 2 }, + McChicken: { cheddar: 2, chicken: 1, lettuce: 2, buns: 2 }, + }, +}; + +if (typeof window === "undefined") { + module.exports = { + stocks, + recipes, + }; +} else { + if (!globalThis.stocks && !globalThis.recipes) { + globalThis.stocks = stocks; + globalThis.recipes = recipes; + } +} diff --git a/graphs/js/foodTruck/dirtyFoodTruck.js b/graphs/js/foodTruck/dirtyFoodTruck.js new file mode 100644 index 0000000..e0043e1 --- /dev/null +++ b/graphs/js/foodTruck/dirtyFoodTruck.js @@ -0,0 +1,172 @@ +if (typeof window === "undefined") { + if (!globalThis.stocks && !globalThis.recipes) { + const { stocks, recipes } = require("./data"); + + globalThis.stocks = stocks; + globalThis.recipes = recipes; + } +} + +function delivering(recipeName) { + setTimeout(() => { + console.log(`Delivering ${recipeName}`); + }, 2000); +} + +function end_of_order(recipeName) { + console.log("All ingredients have been prepared"); + setTimeout(() => { + console.log(`Cooking ${recipeName}`); + }, 2000); + setTimeout(() => { + delivering(recipeName); + }, 5000); +} + +function check_and_update_ingredient(recipeName, ingredient, quantity) { + if (globalThis.stocks[ingredient] < quantity) { + console.log( + `Not enough ingredients for ${recipeName} because there is no more ${ingredient}`, + ); + return false; + } + + globalThis.stocks[ingredient] -= quantity; + return true; +} + +function check_and_update_ingredients(recipeName, ingredients) { + for (const ingredient in ingredients) { + const quantity = ingredients[ingredient]; + + if (!check_and_update_ingredient(recipeName, ingredient, quantity)) { + return false; + } + } + + return true; +} + +function prepare_pizza(recipeName) { + let i = 0; + const pizza = globalThis.recipes.pizza[recipeName]; + const timeouts = []; + + timeouts.push( + setTimeout(() => { + console.log(`Preparing ${pizza.sauce}`); + if (!check_and_update_ingredient(recipeName, pizza.sauce, 1)) { + for (const t of timeouts) { + clearTimeout(t); + } + + return; + } + }, i), + ); + + timeouts.push( + setTimeout(() => { + i = 1000; + console.log(`Preparing ${Object.keys(pizza.toppings).join(", ")}`); + if (!check_and_update_ingredients(recipeName, pizza.toppings)) { + for (const t of timeouts) { + clearTimeout(t); + } + + return; + } + + setTimeout(() => { + console.log( + `Preparing ${Object.keys(pizza.cheese).join(", ")}`, + ); + if (!check_and_update_ingredients(recipeName, pizza.cheese)) { + for (const t of timeouts) { + clearTimeout(t); + } + + return; + } + }, i); + }, i + 1000), + ); + i += 3000; + timeouts.push( + setTimeout(() => { + end_of_order(recipeName); + }, i), + ); +} + +function prepare_burger(recipeName) { + const burger = globalThis.recipes.burger[recipeName]; + let i = 0; + const timeouts = []; + + for (const ingredient in burger) { + const timeout = setTimeout(() => { + console.log(`Preparing ${ingredient}`); + if ( + !check_and_update_ingredient( + recipeName, + ingredient, + burger[ingredient], + ) + ) { + for (const t of timeouts) { + clearTimeout(t); + } + + return; + } + }, i * 1000); + + timeouts.push(timeout); + i++; + } + + const lastTimeout = setTimeout(() => { + end_of_order(recipeName); + }, i * 1000); + + timeouts.push(lastTimeout); +} + +function order(recipeName) { + let i = 0; + + console.log(`Ordering ${recipeName}`); + + if ( + !(recipeName in globalThis.recipes.pizza) && + !(recipeName in globalThis.recipes.burger) + ) { + console.log(`Recipe ${recipeName} does not exist`); + return; + } + + setTimeout(() => { + i = 1000; + setTimeout(() => { + console.log(`Production has started for ${recipeName}`); + }, i); + }, i + 1000); + i += 3000; + setTimeout(() => { + if (recipeName in globalThis.recipes.pizza) { + prepare_pizza(recipeName); + } + }, i); + setTimeout(() => { + if (recipeName in globalThis.recipes.burger) { + prepare_burger(recipeName); + } + }, i); +} + +if (typeof window === "undefined") { + module.exports = { + order, + }; +} diff --git a/graphs/js/foodTruck/foodTruck.js b/graphs/js/foodTruck/foodTruck.js new file mode 100644 index 0000000..afe14d4 --- /dev/null +++ b/graphs/js/foodTruck/foodTruck.js @@ -0,0 +1,124 @@ +if (typeof window === "undefined") { + if ( + !globalThis.stocks && + !globalThis.recipes && + !globalThis.globalThis.sleep + ) { + const { stocks, recipes } = require("./data"); + const { sleep } = require("./sleep"); + + globalThis.stocks = stocks; + globalThis.recipes = recipes; + globalThis.globalThis.sleep = sleep; + } +} + +if (typeof window === "undefined") { + module.exports = { + order, + }; +} + +/* + * + * Write your code below these lines + * + */ +async function end_of_order(recipeName, wait) { + if (wait) { + await globalThis.sleep(1); + } + + console.log("All ingredients have been prepared"); + await globalThis.sleep(2); + console.log(`Cooking ${recipeName}`); + await globalThis.sleep(5); + console.log(`Delivering ${recipeName}`); + return null; +} +function check_and_update_ingredient(recipeName, ingredient, quantity) { + if (globalThis.stocks[ingredient] < quantity) { + console.log( + `Not enough ingredients for ${recipeName} because there is no more ${ingredient}`, + ); + return false; + } + + globalThis.stocks[ingredient] -= quantity; + return true; +} + +function check_and_update_ingredients(recipeName, ingredients) { + for (const ingredient in ingredients) { + const quantity = ingredients[ingredient]; + + if (!check_and_update_ingredient(recipeName, ingredient, quantity)) { + return false; + } + } + + return true; +} + +async function order(recipeName) { + console.log(`Ordering ${recipeName}`); + + if ( + !(recipeName in globalThis.recipes.pizza) && + !(recipeName in globalThis.recipes.burger) + ) { + console.log(`Recipe ${recipeName} does not exist`); + return; + } + + await globalThis.sleep(2); + console.log(`Production has started for ${recipeName}`); + await globalThis.sleep(1); + if (recipeName in globalThis.recipes.burger) { + const burger = globalThis.recipes.burger[recipeName]; + + for (const ingredient in burger) { + console.log(`Preparing ${ingredient}`); + if ( + !check_and_update_ingredient( + recipeName, + ingredient, + burger[ingredient], + ) + ) { + return; + } + + await globalThis.sleep(1); + } + + return await end_of_order(recipeName, false); + } else if (recipeName in globalThis.recipes.pizza) { + const pizza = globalThis.recipes.pizza[recipeName]; + + console.log(`Preparing ${pizza.sauce}`); + if (!check_and_update_ingredient(recipeName, pizza.sauce, 1)) { + return; + } + + await globalThis.sleep(1); + console.log(`Preparing ${Object.keys(pizza.toppings).join(", ")}`); + if (!check_and_update_ingredients(recipeName, pizza.toppings)) { + return; + } + + await globalThis.sleep(1); + console.log(`Preparing ${Object.keys(pizza.cheese).join(", ")}`); + if (!check_and_update_ingredients(recipeName, pizza.cheese)) { + return; + } + + return await end_of_order(recipeName, true); + } +} + +if (typeof window === "undefined") { + module.exports = { + order, + }; +} diff --git a/graphs/js/foodTruck/index.html b/graphs/js/foodTruck/index.html new file mode 100644 index 0000000..0bbc954 --- /dev/null +++ b/graphs/js/foodTruck/index.html @@ -0,0 +1,40 @@ + + + + + + + Foodtruck + + + + + + +
+

FoodTruck

+

Choose your order !

+ +
+ + +
+
+ +
+
+ +
+

"Being a hero means fighting back even when it seems impossible."

+
+ + + + + + + + + \ No newline at end of file diff --git a/graphs/js/foodTruck/sleep.js b/graphs/js/foodTruck/sleep.js new file mode 100644 index 0000000..e4244d9 --- /dev/null +++ b/graphs/js/foodTruck/sleep.js @@ -0,0 +1,11 @@ +function sleep(s) { + return new Promise((resolve) => setTimeout(resolve, s * 1000)); +} + +if (typeof window === "undefined") { + module.exports = { + sleep, + }; +} else { + globalThis.sleep = sleep; +} diff --git a/graphs/js/foodTruck/static/failedOnload.js b/graphs/js/foodTruck/static/failedOnload.js new file mode 100644 index 0000000..cf47073 --- /dev/null +++ b/graphs/js/foodTruck/static/failedOnload.js @@ -0,0 +1,9 @@ +function scriptFailedLoad(src) { + const container = document.getElementById("order"); + const errorDiv = document.createElement("div"); + + errorDiv.id = "errorscript"; + errorDiv.innerHTML = ` +

Script '${src.split("/").pop()}' does not exist.

`; + container.appendChild(errorDiv); +} diff --git a/graphs/js/foodTruck/static/index.js b/graphs/js/foodTruck/static/index.js new file mode 100644 index 0000000..6d84098 --- /dev/null +++ b/graphs/js/foodTruck/static/index.js @@ -0,0 +1,78 @@ +function addFood(name) { + const container = document.getElementById("order"); + const foodDiv = document.createElement("div"); + + foodDiv.classList.add("food"); + foodDiv.innerHTML = ` +

${name}

+ `; + container.appendChild(foodDiv); +} + +function logToDisplay(message, type = "info") { + const displayElement = document.getElementById("display"); + const logMessage = document.createElement("div"); + + logMessage.textContent = message; + logMessage.classList.add("log-message", type); + displayElement.appendChild(logMessage); + + displayElement.scrollTop = displayElement.scrollHeight; + setTimeout(() => { + logMessage.remove(); + }, 5000); +} + +console.log = logToDisplay; + +function loadMenu() { + if (document.getElementById("errorscript") === null) { + Object.keys(recipes).forEach((category) => { + Object.keys(recipes[category]).forEach((recipeName) => { + addFood(recipeName); + }); + }); + } +} + +var isDirtyFoodtruck = false; +var pageTitle = document.getElementById("page-title"); +var switchButton = document.getElementById("switch-button"); + +function loadScript(isDirty) { + var scriptContainer = document.body; + var currentScripts = [...scriptContainer.querySelectorAll("script")].filter( + (script) => { + var name_script = script.src.split("/").pop(); + + return /(dirty)?foodtruck\.js/i.test(name_script); + }, + ); + + // currentScripts should return only one value + scriptToLoad = isDirty ? "dirtyFoodTruck.js" : "foodTruck.js"; + document.body.removeChild(currentScripts[0]); + + const script = document.createElement("script"); + + script.src = scriptToLoad; + + script.onerror = () => scriptFailedLoad(scriptToLoad); + script.onload = () => loadMenu(); + document.body.appendChild(script); +} + +switchButton.addEventListener("click", () => { + isDirtyFoodtruck = !isDirtyFoodtruck; + pageTitle.textContent = isDirtyFoodtruck ? "Dirty Foodtruck" : "Foodtruck"; + switchButton.querySelector("a").textContent = isDirtyFoodtruck + ? "Switch to Foodtruck" + : "Switch to Dirty Foodtruck"; + var orderDiv = document.getElementById("order"); + + orderDiv.innerHTML = ""; + + loadScript(isDirtyFoodtruck); +}); + +loadMenu(); diff --git a/graphs/js/foodTruck/static/style.css b/graphs/js/foodTruck/static/style.css new file mode 100644 index 0000000..c094d98 --- /dev/null +++ b/graphs/js/foodTruck/static/style.css @@ -0,0 +1,200 @@ +:root { + --main-color: #21344a; + --background-color: #333; + --own-white: #FDF0E7; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: Arial, sans-serif; + +} + +body { + background-color: var(--background-color); + color: #333; + font-size: 16px; + line-height: 1.5; + display: flex; + flex-direction: column; + min-height: 100vh; + margin: 0; +} + +header { + background-color: var(--main-color); + color: var(--own-white); + text-align: center; + flex-shrink: 0; + padding: 20px 0; + position: relative; +} + +header h1 { + font-size: 50px; + margin-bottom: 5px; +} + +header p { + font-size: 20px; +} + +header button { + position: absolute; + right: 20px; + top: 50%; + transform: translateY(-50%); + background-color: var(--own-white); + color: var(--main-color); + padding: 10px 15px; + font-size: 1rem; + font-weight: bold; + border-radius: 10px; + border-style: solid; + border-color: #000; + border-width: 1px; + cursor: pointer; + transition: background-color 0.3s, color 0.3s; + text-decoration: none; +} + +header button:hover { + background-color: #f5f5f5; + color: var(--main-color); +} + +header button a { + text-decoration: none; + color: inherit; +} + +#order { + flex-shrink: 0; + margin: 0; + + display: flex; + flex-wrap: wrap; + gap: 20px; + padding: 30px; + margin-top: 20px; + justify-content: space-between; +} + +.food { + background-color: var(--own-white); + padding: 15px; + border-radius: 8px; + text-align: center; + flex: 1 1 calc(25% - 20px); + box-sizing: border-box; +} + +.food p { + font-size: 20px; + margin-bottom: 10px; +} + +.food button { + background-color: var(--main-color); + color: white; + border: none; + padding: 10px; + font-size: 16px; + border-radius: 5px; + cursor: pointer; + width: 100%; + transition: background-color 0.3s; +} + +.food button:hover { + background-color: #972615; +} + +#display { + flex-grow: 1; + overflow-y: auto; + background-color: var(--background-color); + color: white; + padding: 10px; + font-family: monospace; + white-space: pre-wrap; + box-sizing: border-box; + height: 0; + max-height: calc(100vh - 200px); + overflow-y: scroll; +} + +.log-message { + padding: 5px; + border-radius: 5px; + animation: fadeOut 5s forwards; + overflow-y: hidden; + justify-content: center; + text-align: center; + align-items: center; +} + +.log-message.info { + color: var(--own-white); + font-size: 20px; +} + +.log-message.error { + color: #f44336; +} + +.log-message.warn { + color: #ffc107; +} + +@keyframes fadeOut { + 0% { + opacity: 1; + } + + 80% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +#errorscript { + color: red; + font-size: 30px; + font-weight: bold; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.8); + box-sizing: border-box; + border-radius: 15px; + padding: 20px; +} + +footer img { + size: 15px; +} + +footer { + flex-shrink: 0; + background-color: var(--main-color); + color: white; + text-align: center; + padding: 20px; + display: flex; + justify-content: center; + align-items: center; + gap: 20px; +} + +footer p { + font-size: 20px; +} \ No newline at end of file diff --git a/graphs/js/gallery/gallery.js b/graphs/js/gallery/gallery.js new file mode 100644 index 0000000..8446846 --- /dev/null +++ b/graphs/js/gallery/gallery.js @@ -0,0 +1,54 @@ +const fs = require("fs"); +const path = require("path"); + +function extract(directoryPath) { + const mail = new RegExp( + "([a-z0-9_.+-]+)@([a-z0-9.-]+)\\.(([a-zA-Z0-9]|\\.){2,})", + "g", + ); + + try { + let emails = []; + const dirs = fs.readdirSync(directoryPath, { withFileTypes: true }); + + for (const dir of dirs) { + if (dir.isFile()) { + const data = fs.readFileSync( + path.join(directoryPath, dir.name), + "utf8", + ); + const matches = data.match(mail); + + if (matches) { + emails = [...emails, ...matches]; + } + } else if (dir.isDirectory()) { + emails = [ + ...emails, + ...extract(path.join(directoryPath, dir.name)), + ]; + } + } + + return emails; + } catch { + throw new Error("The directory does not exist"); + } +} + +/* + +fzhfeklzjfh@dakljhlzekj.f +fkzjefh@aaa0.fr +dejkzd@djkelh.fr +djhezklf0587dzedez@gmail................ +xavier.login@epita.fr +xavier.login@epita.fR + +*/ + +module.exports = { + extract, +}; + +console.log(extract("gallery")); diff --git a/graphs/js/helloWorld/helloWorld.js b/graphs/js/helloWorld/helloWorld.js new file mode 100644 index 0000000..3564ffc --- /dev/null +++ b/graphs/js/helloWorld/helloWorld.js @@ -0,0 +1,7 @@ +function helloWorld() +{ + console.log("Hello World!") +} +module.exports = { + helloWorld, +}; \ No newline at end of file diff --git a/graphs/js/inspectAndFuse/inspectAndFuse.js b/graphs/js/inspectAndFuse/inspectAndFuse.js new file mode 100644 index 0000000..2072bb1 --- /dev/null +++ b/graphs/js/inspectAndFuse/inspectAndFuse.js @@ -0,0 +1,77 @@ +function getNumberFields(inputObject) { + if (inputObject == null) { + return new Array(); + } + + const res = new Array(); + + for (const prop in inputObject) { + if (typeof inputObject[prop] === "number") { + res.push(prop); + } + } + + return res; +} +function incrementCounters(inputObject) { + const reg = /counter/i; + const fields = getNumberFields(inputObject); + + if (fields == null) { + return; + } + + fields.forEach((f) => { + if (f.match(reg)) { + inputObject[f]++; + } + }); +} +function deleteUppercaseProperties(inputObject) { + if (inputObject == null) { + return; + } + + const reg = /[a-z]/; + + for (const prop in inputObject) { + if (!prop.match(reg)) { + delete inputObject[prop]; + } else if (inputObject[prop] instanceof Object) { + deleteUppercaseProperties(inputObject[prop]); + } + } +} +function fusion(...objs) { + const res = {}; + + for (const obj of objs) { + for (const prop in obj) { + if (!Object.hasOwn(res, prop)) { + res[prop] = obj[prop]; + } else { + if ( + typeof res[prop] != typeof obj[prop] || + typeof res[prop] === "boolean" + ) { + res[prop] = obj[prop]; + } else if (obj[prop] instanceof Array) { + res[prop] = [...res[prop], ...obj[prop]]; + } else if (typeof prop === "object") { + res[prop] = fusion(res[prop], obj[prop]); + } else { + res[prop] += obj[prop]; + } + } + } + } + + return res; +} + +module.exports = { + fusion, + incrementCounters, + deleteUppercaseProperties, + getNumberFields, +}; diff --git a/graphs/js/intergalactic/destinations.js b/graphs/js/intergalactic/destinations.js new file mode 100644 index 0000000..c184458 --- /dev/null +++ b/graphs/js/intergalactic/destinations.js @@ -0,0 +1,47 @@ +function displayDestinations(destinations) { + if ( + !(destinations instanceof Map) || + destinations == null || + destinations == undefined || + destinations.size === 0 + ) { + console.log("No destination is available."); + } else { + destinations.forEach((v, k) => { + console.log(k + ": " + v); + }); + } +} +function addDestination(destinations, name, cost) { + if ( + typeof name != "string" || + typeof cost != "number" || + cost < 0 || + destinations.has(name) + ) { + return false; + } + + destinations.set(name, cost); + return true; +} +function removeDestination(destinations, name) { + if (typeof name != "string" || !destinations.has(name)) { + return false; + } else { + destinations.delete(name); + return true; + } +} +function getDestinationsInOrder(destinations) { + const res = new Array(); + + destinations.forEach((v, k) => res.push([v, k])); + return res.sort((vkp1, vkp2) => vkp1[0] - vkp2[0]).map((vkp) => vkp[1]); +} +module.exports = { + displayDestinations, + addDestination, + removeDestination, + getDestinationsInOrder, +}; diff --git a/graphs/js/intergalactic/travelers.js b/graphs/js/intergalactic/travelers.js new file mode 100644 index 0000000..b806883 --- /dev/null +++ b/graphs/js/intergalactic/travelers.js @@ -0,0 +1,40 @@ +function addTraveler(travelers, firstname, lastname) { + const vip = /(.*[jJ].*[sS].*)|(.*[sS].*[jJ].*)/; + const fullname = firstname + " " + lastname; + + if (travelers.length >= 8) { + console.log(fullname); + if (vip.test(fullname)) { + const last_i = travelers.findLastIndex( + (element) => !vip.test(element), + ); + + if (last_i === -1) { + return false; + } + + travelers.splice(last_i, 1); + } else { + return false; + } + } + + travelers.push(fullname); + return true; +} +function deleteTraveler(travelers, firstname, lastname) { + const fullname = firstname + " " + lastname; + const i = travelers.indexOf(fullname); + + if (i === -1) { + return false; + } + + travelers.splice(i, 1); + return true; +} + +module.exports = { + addTraveler, + deleteTraveler, +}; diff --git a/graphs/js/jestBasic/fibo.js b/graphs/js/jestBasic/fibo.js new file mode 100644 index 0000000..0a19955 --- /dev/null +++ b/graphs/js/jestBasic/fibo.js @@ -0,0 +1,29 @@ +function fibo(n) { + if (typeof n != "number" || isNaN(n)) { + return -1; + } + + if (n < 0) { + return -1; + } + + if (n === 0) { + return 0; + } + + let a = 1; + let b = 1; + let result = 1; + + for (let i = 2; i < n; i++) { + result = a + b; + b = a; + a = result; + } + + return result; +} + +module.exports = { + fibo, +}; diff --git a/graphs/js/jestBasic/fibo.test.js b/graphs/js/jestBasic/fibo.test.js new file mode 100644 index 0000000..6a97365 --- /dev/null +++ b/graphs/js/jestBasic/fibo.test.js @@ -0,0 +1,37 @@ +const { fibo } = require("./fibo"); + +describe("basic value", () => { + test("fibo of 0", () => { + expect(fibo(0)).toBe(0); + }); + test("fibo of 1", () => { + expect(fibo(1)).toBe(1); + }); + test("fibo of 2", () => { + expect(fibo(2)).toBe(1); + }); + test("fibo of 4", () => { + expect(fibo(4)).toBe(3); + }); + test("fibo of 19", () => { + expect(fibo(19)).toBe(4181); + }); +}); + +describe("errors", () => { + test("fibo of -1", () => { + expect(fibo(-1)).toBe(-1); + }); + test("fibo of null", () => { + expect(fibo(null)).toBe(-1); + }); + test("fibo of undefined", () => { + expect(fibo(undefined)).toBe(-1); + }); + test("fibo of a string", () => { + expect(fibo("uwu adrien")).toBe(-1); + }); + test("fibo of NaN", () => { + expect(fibo(NaN)).toBe(-1); + }); +}); diff --git a/graphs/js/logMeIn/logMeIn.js b/graphs/js/logMeIn/logMeIn.js new file mode 100644 index 0000000..2591071 --- /dev/null +++ b/graphs/js/logMeIn/logMeIn.js @@ -0,0 +1,59 @@ +const express = require("express"); +const jsonwebtoken = require("jsonwebtoken"); + +function logMeIn(host, port) { + const secretKey = process.env.JWT_SECRET_KEY; + const app = express(); + + app.use(express.json()); + + app.get("/", (req, res) => { + res.status(200).send({ message: "Hello World!" }); + }); + app.post("/login", (req, res) => { + const login = req.body.username; + const passwd = req.body.password; + + if (login !== "xavier.login" || passwd != "1234") { + res.status(401).send({ error: "Invalid username or password" }); + } else { + const jwt = jsonwebtoken.sign(req.body, secretKey); + + res.status(200).send({ jwt: jwt }); + } + }); + app.get("/secret", (req, res) => { + if (req.headers == null || req.headers == undefined) { + res.status(401).send({ error: "Unauthorized" }); + return; + } + + try { + const decoded = jsonwebtoken.verify( + req.headers.authorization.split(" ")[1], + secretKey, + ); + + if ( + decoded.username !== "xavier.login" || + decoded.password !== "1234" + ) { + res.status(401).send({ error: "Unauthorized" }); + } + + res.status(200).send({ message: "Access granted" }); + } catch { + res.status(401).send({ error: "Unauthorized" }); + } + }); + + return app.listen(port, () => { + console.log("Server running at http://" + host + ":" + port + "/"); + }); +} + +module.exports = { + logMeIn, +}; + +//logMeIn("127.0.0.1", 3000); diff --git a/graphs/js/modularLogger/modularLogger.js b/graphs/js/modularLogger/modularLogger.js new file mode 100644 index 0000000..b22b23c --- /dev/null +++ b/graphs/js/modularLogger/modularLogger.js @@ -0,0 +1,76 @@ +function makeLog(date, nameLogLevel, message) { + return ( + "[" + + date.toLocaleString("fr-FR", { timeStyle: "medium" }) + + "][" + + nameLogLevel + + "] - " + + message + ); +} +function loggerFactory() { + const logs = []; + + return (level = 1000) => { + if (level === 1000) { + return (date, nameLogLevel, message) => { + if ( + !["DEBUG", "INFO", "WARN", "ERROR"].includes(nameLogLevel) + ) { + console.log( + nameLogLevel + + " is an invalid log level. Please use one of the following: DEBUG, INFO, WARN, ERROR.", + ); + } else { + logs.push({ + date: date, + nameLogLevel: nameLogLevel, + message: message, + }); + } + }; + } else if (typeof level !== "number" || level < 0) { + console.log("Bad argument."); + } else { + if (level > 3) { + level = 3; + } + + for (const log of logs) { + const print = makeLog( + log["date"], + log["nameLogLevel"], + log["message"], + ); + + switch (log["nameLogLevel"]) { + case "ERROR": + console.log(print); + break; + case "WARN": + if (level > 0) { + console.log(print); + } + + break; + case "INFO": + if (level > 1) { + console.log(print); + } + + break; + case "DEBUG": + if (level > 2) { + console.log(print); + } + + break; + } + } + } + }; +} + +module.exports = { + loggerFactory, +}; diff --git a/graphs/js/myCompany/boss.js b/graphs/js/myCompany/boss.js new file mode 100644 index 0000000..eadf09c --- /dev/null +++ b/graphs/js/myCompany/boss.js @@ -0,0 +1,30 @@ +const { Employee } = require("./employee"); + +class Boss extends Employee { + constructor(name, accreditationLevel) { + super(name); + this.accreditationLevel = accreditationLevel; + } + getAccreditation() { + return this.accreditationLevel; + } + fire(target) { + if (!(target instanceof Employee)) { + console.log("I cannot fire that!"); + return false; + } else if ( + !(target instanceof Boss) || + target.getAccreditation() < this.accreditationLevel + ) { + console.log(target.getName() + " you are fired!"); + return true; + } else { + console.log("I cannot fire someone superior to me!"); + return false; + } + } +} + +module.exports = { + Boss, +}; diff --git a/graphs/js/myCompany/company.js b/graphs/js/myCompany/company.js new file mode 100644 index 0000000..fd800e7 --- /dev/null +++ b/graphs/js/myCompany/company.js @@ -0,0 +1,77 @@ +const { Boss } = require("./boss"); +const { Employee } = require("./employee"); + +class Company { + constructor(name) { + this.name = name; + this.employees = new Array(); + } + getName() { + return this.name; + } + getEmployees() { + return this.employees; + } + getNumberOfEmployees() { + return this.employees.filter((e) => e instanceof Employee).length; + } + getNumberOfBosses() { + return this.employees.filter((e) => e instanceof Boss).length; + } + addEmployee(target) { + if (!(target instanceof Employee)) { + return; + } + + this.employees.push(target); + } + promoteEmployee(targetIndex) { + if (this.employees[targetIndex] instanceof Boss) { + this.employees[targetIndex].accreditationLevel++; + console.log( + "Boss " + + this.employees[targetIndex].getName() + + " is promoted, his level of accreditation is now " + + this.employees[targetIndex].getAccreditation(), + ); + } else { + this.employees.splice( + targetIndex, + 1, + new Boss(this.employees[targetIndex].getName(), 1), + ); + console.log( + "Employee " + + this.employees[targetIndex].getName() + + " is promoted to boss post", + ); + } + } + fireEmployee(bossIndex, targetIndex) { + if (this.employees[bossIndex].fire(this.employees[targetIndex])) { + if (this.employees[targetIndex] instanceof Boss) { + console.log( + "Boss " + + this.employees[targetIndex].getName() + + " is no longer in " + + this.name + + " company", + ); + } else { + console.log( + "Employee " + + this.employees[targetIndex].getName() + + " is no longer in " + + this.name + + " company", + ); + } + + this.employees.splice(targetIndex, 1); + } + } +} + +module.exports = { + Company, +}; diff --git a/graphs/js/myCompany/employee.js b/graphs/js/myCompany/employee.js new file mode 100644 index 0000000..5489dd7 --- /dev/null +++ b/graphs/js/myCompany/employee.js @@ -0,0 +1,21 @@ +class Employee { + constructor(name) { + this.name = name; + } + getName() { + return this.name; + } + fire(target) { + if (target instanceof Employee) { + console.log("I am an employee, I cannot fire someone!"); + } else { + console.log("I cannot fire that!"); + } + + return false; + } +} + +module.exports = { + Employee, +}; diff --git a/graphs/js/notSoFast/articles.json b/graphs/js/notSoFast/articles.json new file mode 100644 index 0000000..5ffb5fb --- /dev/null +++ b/graphs/js/notSoFast/articles.json @@ -0,0 +1,74 @@ +[ + { + "id":0, + "name": "Cheeseburger", + "description": "Beef patty, melted cheese, ketchup, mustard, pickles", + "price": 4.99, + "customer_note": 4.5, + "stocks": 20 + }, + { + "id":1, + "name": "Fries", + "description": "Crispy, golden-brown potato sticks", + "price": 2.49, + "customer_note": 4.0, + "stocks": 50 + }, + { + "id":2, + "name": "Hot Dog", + "description": "Grilled all-beef frankfurter, topped with ketchup, mustard, and relish", + "price": 3.99, + "customer_note": 3.5, + "stocks": 15 + }, + { + "id":3, + "name": "Chicken Nuggets", + "description": "Crispy breaded chicken bites, perfect for dipping", + "price": 5.99, + "customer_note": 4.2, + "stocks": 30 + }, + { + "id":4, + "name": "Milkshake", + "description": "Thick and creamy vanilla milkshake, made with real ice cream", + "price": 3.49, + "customer_note": 4.8, + "stocks": 10 + }, + { + "id":5, + "name": "BBQ Bacon Burger", + "description": "Beef patty, bacon, cheddar cheese, onion rings, BBQ sauce", + "price": 7.99, + "customer_note": 4.3, + "stocks": 5 + }, + { + "id":6, + "name": "Onion Rings", + "description": "Crispy fried onion rings, perfect as a side", + "price": 2.99, + "customer_note": 4.0, + "stocks": 40 + }, + { + "id":7, + "name": "Chicken Sandwich", + "description": "Grilled chicken breast, lettuce, tomato, mayo", + "price": 6.49, + "customer_note": 3.8, + "stocks": 25 + }, + { + "id":8, + "name": "Spicy Chicken Nuggets", + "description": "Crispy breaded chicken bites with a spicy kick", + "price": 6.99, + "customer_note": 4.5, + "stocks": 15 + } +] \ No newline at end of file diff --git a/graphs/js/notSoFast/notSoFast.js b/graphs/js/notSoFast/notSoFast.js new file mode 100644 index 0000000..113c8f6 --- /dev/null +++ b/graphs/js/notSoFast/notSoFast.js @@ -0,0 +1,33 @@ +const axios = require("axios"); + +async function notSoFast(host, port) { + let nbArticles = await axios.get(`http://${host}:${port}/articles`); + + nbArticles = nbArticles.data.message; + + const articles = []; + + if (0 + nbArticles === 0) { + return articles; + } + + let res = await axios.get(`http://${host}:${port}/articles/${0}`); + + articles.push(res.data); + for (let i = 1; i < 0 + nbArticles; i++) { + const delay = res.headers["x-ratelimit-reset"] * 1000 - Date.now(); + + if (res.headers["x-ratelimit-remaining"] == 0) { + await new Promise((oof) => setTimeout(oof, delay + 26)); + } + + res = await axios.get(`http://${host}:${port}/articles/${i}`); + articles.push(res.data); + } + + return articles; +} + +module.exports = { + notSoFast, +}; diff --git a/graphs/js/notSoFast/server.js b/graphs/js/notSoFast/server.js new file mode 100644 index 0000000..916c7d8 --- /dev/null +++ b/graphs/js/notSoFast/server.js @@ -0,0 +1,147 @@ +const _0x56061b = _0x4a1f; + +(function (_0x3d4f7d, _0x384467) { + const _0x2bc30d = _0x4a1f, + _0x2a57c0 = _0x3d4f7d(); + + while ([]) { + try { + const _0x509389 = + parseInt(_0x2bc30d(0xf9)) / 0x1 + + (-parseInt(_0x2bc30d(0xf8)) / 0x2) * + (parseInt(_0x2bc30d(0xf3)) / 0x3) + + parseInt(_0x2bc30d(0x100)) / 0x4 + + -parseInt(_0x2bc30d(0xef)) / 0x5 + + parseInt(_0x2bc30d(0xeb)) / 0x6 + + parseInt(_0x2bc30d(0xee)) / 0x7 + + (-parseInt(_0x2bc30d(0xe3)) / 0x8) * + (-parseInt(_0x2bc30d(0xf4)) / 0x9); + + if (_0x509389 === _0x384467) { + break; + } else { + _0x2a57c0["push"](_0x2a57c0["shift"]()); + } + } catch (_0x4f4837) { + _0x2a57c0["push"](_0x2a57c0["shift"]()); + } + } +})(_0x5ba1, 0x37cc5); +const express = require(_0x56061b(0xfa)), + app = express(), + path = require("path"); +const articles_data = require( + path[_0x56061b(0xfd)](__dirname, _0x56061b(0xf7)), + ), + rateLimiter = { + max: 0x5, + windowMs: 0x3e8, + reset: Date[_0x56061b(0xe7)]() - 0x7d0, + remaining: 0x5, + }; + +function _0x4a1f(_0xa5c9f4, _0x34d88a) { + const _0x5ba126 = _0x5ba1(); + + return ( + (_0x4a1f = function (_0x4a1f4f, _0x57de0b) { + _0x4a1f4f = _0x4a1f4f - 0xe1; + const _0x91c04c = _0x5ba126[_0x4a1f4f]; + + return _0x91c04c; + }), + _0x4a1f(_0xa5c9f4, _0x34d88a) + ); +} +function _0x5ba1() { + const _0x15e55c = [ + "remaining", + "/articles", + "X-RateLimit-Reset", + "now", + "X-RateLimit-Remaining", + "\x20has\x20been\x20found", + "end", + "1185066KdUuVR", + "listen", + "send", + "1849449xJgCmq", + "1269280gTNiiN", + "get", + "reset", + "/articles/:id([0-9]+)", + "111uXMghV", + "72891SGOhSD", + "Too\x20many\x20requests", + "Server\x20running\x20at\x20http://localhost:", + "./articles.json", + "11996YaeIzr", + "64830rqNiDl", + "express", + "status", + "find", + "resolve", + "No\x20article\x20with\x20id\x20", + "max", + "678736fMeEWy", + "X-RateLimit-Limit", + "params", + "log", + "set", + "8YaqgyJ", + ]; + + _0x5ba1 = function () { + return _0x15e55c; + }; + return _0x5ba1(); +} +app[_0x56061b(0xf0)](_0x56061b(0xe5), (_0x53adb0, _0x2f6c36) => { + const _0x2e7227 = _0x56061b; + + _0x2f6c36[_0x2e7227(0xfb)](0xc8)[_0x2e7227(0xed)]({ + message: articles_data["length"], + }); +}), + app["get"](_0x56061b(0xf2), (_0x58952c, _0x1c1b41) => { + const _0x1800af = _0x56061b; + + rateLimiter[_0x1800af(0xf1)] < Date[_0x1800af(0xe7)]() && + ((rateLimiter["remaining"] = rateLimiter["max"]), + (rateLimiter["reset"] = Date["now"]() + rateLimiter["windowMs"])); + if (rateLimiter[_0x1800af(0xe4)] == 0x0) { + _0x1c1b41[_0x1800af(0xfb)](0x1ad)["send"](_0x1800af(0xf5)); + return; + } else { + rateLimiter[_0x1800af(0xe4)]--; + } + + const _0xca1069 = rateLimiter[_0x1800af(0xff)], + _0x5c15a1 = rateLimiter[_0x1800af(0xe4)], + _0x5d6b61 = parseFloat(rateLimiter[_0x1800af(0xf1)] / 0x3e8); + + _0x1c1b41[_0x1800af(0xe2)](_0x1800af(0x101), _0xca1069), + _0x1c1b41[_0x1800af(0xe2)](_0x1800af(0xe8), _0x5c15a1), + _0x1c1b41[_0x1800af(0xe2)](_0x1800af(0xe6), _0x5d6b61); + const _0x413373 = articles_data[_0x1800af(0xfc)]( + (_0x4868bf) => + _0x4868bf["id"] === parseInt(_0x58952c[_0x1800af(0x102)]["id"]), + ); + + if (_0x413373) { + _0x1c1b41["status"](0xc8)["send"](_0x413373); + return; + } else { + _0x1c1b41["writeHead"]( + 0x194, + _0x1800af(0xfe) + + _0x58952c[_0x1800af(0x102)]["id"] + + _0x1800af(0xe9), + ); + } + + _0x1c1b41[_0x1800af(0xea)](); + }); +const server = app[_0x56061b(0xec)](0xbb8, () => {}); + +console[_0x56061b(0xe1)](_0x56061b(0xf6) + 0xbb8 + "/"); diff --git a/graphs/js/oidc/complete/epita/index.html b/graphs/js/oidc/complete/epita/index.html new file mode 100644 index 0000000..2706a33 --- /dev/null +++ b/graphs/js/oidc/complete/epita/index.html @@ -0,0 +1,35 @@ + + + + + + + + + +
+
+ user +
+

Puff Puff

+

Marseille

+

2059

+ +
+
+

infos

+
    +
+
+
+
+ + +
+
+ + diff --git a/graphs/js/oidc/complete/epita/index.js b/graphs/js/oidc/complete/epita/index.js new file mode 100644 index 0000000..513197c --- /dev/null +++ b/graphs/js/oidc/complete/epita/index.js @@ -0,0 +1,40 @@ +window.END_SESSION_URL = "https://cri.epita.fr/end-session"; +const reqInfosBtn = document.getElementById("RequestBtn"); +const params = new URLSearchParams(window.location.search); +const code = params.get("code"); + +let form = new FormData(); + +form.append("client_id", "assistants-atelier-js"); +form.append("redirect_uri", "http://localhost:8080/complete/epita/"); +form.append("grant_type", "authorization_code"); +form.append("code", code); +const tokenEndpoint = "http://localhost:8080/auth-api"; +reqInfosBtn.addEventListener("click", async () => { + let response = await fetch(tokenEndpoint, { + method: "POST", + body: form, + }); + const responsePretty = await response.json(); + const token = responsePretty.id_token; + const content = token.split(".")[1]; + const b64 = content.replace(/-/g, "+").replace(/_/g, "/"); + const payload = JSON.parse(window.atob(b64)); + + document.getElementById("name").innerHTML = payload.name; + document.getElementById("campus").innerHTML = payload.zoneinfo; + document.getElementById("grad-year").innerHTML = payload.graduation_years; + document + .getElementById("image") + .setAttribute("src", payload.picture_square); + const ul = document.getElementById("list"); + for (const group of payload.groups) { + const item = document.createElement("li"); + item.innerHTML = group.slug + " " + group.name; + ul.appendChild(item); + } +}); + +document + .getElementById("EndBtn") + .addEventListener("click", () => window.location.replace(END_SESSION_URL)); diff --git a/graphs/js/oidc/main.html b/graphs/js/oidc/main.html new file mode 100644 index 0000000..698eb6d --- /dev/null +++ b/graphs/js/oidc/main.html @@ -0,0 +1,27 @@ + + + + + + + + + + +
+
+ user +
+

User Application

+

app

+

Small application to display user informations
Using ForgeID for Auth

+ +
+ +
+
+ + + diff --git a/graphs/js/oidc/redirect.js b/graphs/js/oidc/redirect.js new file mode 100644 index 0000000..d063df3 --- /dev/null +++ b/graphs/js/oidc/redirect.js @@ -0,0 +1,17 @@ +const redirectBtn = document.getElementById("redirectBtn"); + +const authQueryParams = { + client_id: "assistants-atelier-js", + scope: "epita profile picture", + redirect_uri: "http://localhost:8080/complete/epita/", + response_type: "code", +}; + +const authEndpoint = "https://cri.epita.fr/authorize"; +window.LOGIN_URL = new URL( + `?client_id=${authQueryParams.client_id}&scope=${authQueryParams.scope}&redirect_uri=${authQueryParams.redirect_uri}&response_type=${authQueryParams.response_type}`, + authEndpoint, +); +redirectBtn.addEventListener("click", () => { + window.location.replace(window.LOGIN_URL); +}); diff --git a/graphs/js/oidc/server.js b/graphs/js/oidc/server.js new file mode 100644 index 0000000..401b48f --- /dev/null +++ b/graphs/js/oidc/server.js @@ -0,0 +1,33 @@ +const express = require("express"); +const { createProxyMiddleware } = require("http-proxy-middleware"); + +const app = express(); + +/** + * The requests sent to our local server running on http://localhost:8080 + * will pass by the reverse proxy and be sent to a specified path. + * + * In our case, + * /auth-api -> https://cri.epita.fr/token + **/ + +const path = `https://cri.epita.fr/token`; +const proxyAuth = createProxyMiddleware("/auth-api", { + target: path, + changeOrigin: true, + pathRewrite: { + "^/auth-api": "", + }, +}); + +app.get("/", (req, res) => { + res.sendFile("main.html", { root: "./" }); +}); + +app.use(proxyAuth, express.static("./")); + +const port = 8080; + +app.listen(port, () => { + console.log(`Server is running at http://localhost:${port}`); +}); diff --git a/graphs/js/oidc/style.css b/graphs/js/oidc/style.css new file mode 100644 index 0000000..0433246 --- /dev/null +++ b/graphs/js/oidc/style.css @@ -0,0 +1,116 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat'); + +* { + box-sizing: border-box; +} + +body { + background-color: whitesmoke; + font-family: Montserrat, sans-serif; + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; +} + +h1 { + margin: 10px 0; + color: white; + word-break: break-all; +} + +h3 { + margin: 5px 0; + text-transform: uppercase; + color: white; + word-break: break-all; +} + +p { + font-size: 16px; + line-height: 21px; + color: white; + word-break: break-all; +} + +.card-container { + background: linear-gradient(180deg, rgba(8,4,78,1) 0%, rgba(9,9,121,1) 51%, rgba(0,155,255,1) 100%); + box-shadow: 0px 20px 40px -10px rgba(0,0,0,0.75); + padding-top: 30px; + width: 600px; + height: auto; + max-width: 100%; + text-align: center; + display: flex; + flex-direction: column; + justify-content: center; + border-radius: 25px; +} + +.img-container { + display: flex; + width: 100%; + align-items: center; + justify-content: center; + margin-bottom: 6em; +} + +.round { + border: 3px solid whitesmoke; + border-radius: 50%; + padding: 2px; +} + +.buttons { + margin-top: 2em; +} + +button { + margin-bottom: 10px; +} + +button.primary { + background-color: whitesmoke; + border: 1px solid black; + border-radius: 3px; + color: #231E39; + font-family: Montserrat, sans-serif; + font-weight: 500; + padding: 10px 25px; +} + +button.primary.ghost { + background-color: transparent; + color: black; +} + +.info-container { + display: flex; + justify-content: center; +} + +.infos { + background-color: #0a0a8a; + border-radius: 10px; + text-align: left; + padding: 15px; + margin-top: 30px; + width: 80%; + margin-bottom: 15px; +} + +.infos ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +.infos ul li { + border: 2px solid whitesmoke; + border-radius: 5px; + color: white; + display: inline-block; + font-size: 14px; + margin: 0 7px 7px 0; + padding: 7px; +} \ No newline at end of file diff --git a/graphs/js/replace/replace.js b/graphs/js/replace/replace.js new file mode 100644 index 0000000..24b79e4 --- /dev/null +++ b/graphs/js/replace/replace.js @@ -0,0 +1,8 @@ +function replace(str) { + const captureDateRegex = new RegExp("(\\d{2})/(\\d{2})/(\\d{4})", "g"); + + return str.replaceAll(captureDateRegex, "$3-$1-$2"); +} +module.exports = { + replace, +}; diff --git a/graphs/js/storageWars/index.html b/graphs/js/storageWars/index.html new file mode 100644 index 0000000..49fad63 --- /dev/null +++ b/graphs/js/storageWars/index.html @@ -0,0 +1,23 @@ + + + + + + + + Storage Wars + + +
+ + +
+ +
+ + diff --git a/graphs/js/storageWars/storageWars.js b/graphs/js/storageWars/storageWars.js new file mode 100644 index 0000000..ed90536 --- /dev/null +++ b/graphs/js/storageWars/storageWars.js @@ -0,0 +1,80 @@ +/* FIXME */ +const userInfo = document.getElementById("userInfo"); +const userName = document.getElementById("name"); +const userEmail = document.getElementById("email"); +const userAge = document.getElementById("age"); +const errorBox = document.getElementById("error"); +const jwtField = document.getElementById("inputJWT"); + +localStorage.clear(); + +function displayError() { + errorBox.innerHTML = "Invalid token"; + if (localStorage.getItem("token")) { + localStorage.removeItem("token"); + } + + userInfo.style.display = "none"; +} + +function decodeToken() { + if (localStorage.getItem("token") != null) { + // parse the token + try { + const token = localStorage.getItem("token"); + const content = token.split(".")[1]; + const b64 = content.replace(/-/g, "+").replace(/_/g, "/"); + const payload = decodeURIComponent(window.atob(b64)); + + return JSON.parse(payload); + } catch { + displayError(); + } + } else { + return null; + } +} + +function addToken() { + /* FIXME */ + localStorage.setItem("token", jwtField.value); + display(); +} + +function display() { + errorBox.innerHTML = ""; + if (localStorage.getItem("token") === "") { + displayError(); + return; + } + + // validity check + const dec = decodeToken(); + + if (dec == null || Date.now() < dec.iat * 1000) { + displayError(); + return; + } + + userInfo.style.display = "inherit"; + if (dec["name"] != undefined) { + userName.innerHTML = dec["name"]; + } else { + userName.innerHTML = "No name"; + } + + if (dec["email"] != undefined) { + userEmail.innerHTML = dec["email"]; + } else { + userEmail.innerHTML = "No email"; + } + + if (dec["age"] != undefined) { + userAge.innerHTML = dec["age"]; + } else { + userAge.innerHTML = "No age"; + } +} + +window.addToken = addToken; +display(); diff --git a/graphs/js/throttleDebounce/throttleDebounce.js b/graphs/js/throttleDebounce/throttleDebounce.js new file mode 100644 index 0000000..f6e15f9 --- /dev/null +++ b/graphs/js/throttleDebounce/throttleDebounce.js @@ -0,0 +1,48 @@ +function debounce(func, n) { + let timer = null; + + return (...args) => { + clearTimeout(timer); + timer = setTimeout(func, n, ...args); + }; +} +function throttle(func, n) { + let throttling = false; + let last_args = null; + let last_call = Date.now(); + let timeout = null; + + return (...args) => { + if (!throttling) { + last_call = Date.now(); + if (last_args) { + func.apply(this, last_args); + last_args = null; + } else { + func(...args); + } + + throttling = true; + if (timeout) { + clearTimeout(timeout); + } + + timeout = setTimeout( + () => { + throttling = false; + + if (last_args) { + func.apply(this, last_args); + } + }, + n - (Date.now() - last_call), + ); + } else { + last_args = args; + } + }; +} +module.exports = { + debounce, + throttle, +}; diff --git a/graphs/js/todoList/index.html b/graphs/js/todoList/index.html new file mode 100644 index 0000000..116cd2c --- /dev/null +++ b/graphs/js/todoList/index.html @@ -0,0 +1,17 @@ + + + + + My To-do List + + + + +

My To-do List

+
+ + +
+
    + + diff --git a/graphs/js/todoList/style.css b/graphs/js/todoList/style.css new file mode 100644 index 0000000..ebdd8d7 --- /dev/null +++ b/graphs/js/todoList/style.css @@ -0,0 +1,64 @@ +body { + background-color: #f2f2f2; + font-family: Arial, sans-serif; +} + +h1 { + text-align: center; + margin-top: 20px; +} + +div { + display: flex; + justify-content: center; + width: 70%; + margin-right: auto; + margin-left: auto; +} + +input[type="text"] { + padding: 10px; + border: none; + border-radius: 5px; + margin-right: 10px; + width: 70%; +} + +#todoList { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 30px; + list-style-type: decimal; +} + +.todoItem { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + margin-bottom: 10px; + width: 80%; + background-color: #fff; + border-radius: 5px; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3); +} + +.todoText { + flex-grow: 1; + margin-right: 10px; + padding-left: 10px; +} + +button { + padding: 5px 10px; + background-color: #f44336; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +button:hover { + background-color: #e53935; +} diff --git a/graphs/js/todoList/todoList.js b/graphs/js/todoList/todoList.js new file mode 100644 index 0000000..34b2edf --- /dev/null +++ b/graphs/js/todoList/todoList.js @@ -0,0 +1,27 @@ +const input = document.getElementById("textBox"); +const addBtn = document.getElementById("addButton"); +const list = document.getElementById("todoList"); + +addBtn.addEventListener("click", () => { + if (input.value === "") { + return; + } + + const todo = document.createElement("li"); + + todo.classList.add("todoItem"); + const label = document.createElement("span"); + + label.innerHTML = input.value; + input.value = ""; + label.classList.add("todoText"); + todo.appendChild(label); + const del = document.createElement("button"); + + del.innerHTML = "Delete"; + del.addEventListener("click", () => { + list.removeChild(todo); + }); + todo.appendChild(del); + list.appendChild(todo); +}); -- cgit v1.2.3