diff --git a/dom/Dockerfile b/dom/Dockerfile index 6a184c09..02fca84c 100644 --- a/dom/Dockerfile +++ b/dom/Dockerfile @@ -1,8 +1,10 @@ FROM buildkite/puppeteer:latest ENV GIT_TERMINAL_PROMPT=0 -# RUN apk add --no-cache git +RUN apt-get update +RUN apt-get install -y git WORKDIR /app -COPY ./puppeteer . +COPY ./dom . COPY ./subjects ./subjects +RUN ls -la ENTRYPOINT ["/bin/sh", "/app/entrypoint.sh"] diff --git a/dom/build-brick-and-break.js b/dom/build-brick-and-break.js deleted file mode 100644 index dff5778b..00000000 --- a/dom/build-brick-and-break.js +++ /dev/null @@ -1,47 +0,0 @@ -const body = document.querySelector('body') - -const create = (tag) => { - const element = document.createElement(tag) - return element -} - -export const build = (amount = 54) => { - let count = 1 - const intervalID = setInterval(() => { - const brick = create('div') - brick.title = 'brick' - brick.id = `brick-${count}` - if (count % 3 === 2) { - brick.dataset.foundation = true - } - brick.append(count) - body.append(brick) - - if (count === amount) { - window.clearInterval(intervalID) - return - } - - count++ - }, 100) -} - -export const repair = (...ids) => { - ids.forEach((id) => { - const toRepair = document.getElementById(id) - if (toRepair) { - toRepair.dataset.repaired = toRepair.hasAttribute('data-foundation') - ? 'in progress' - : true - } - }) -} - -export const destroy = () => { - const bricks = [...document.querySelectorAll('[title="brick"]')] - const toRemove = bricks[bricks.length - 1] - - if (toRemove) { - toRemove.remove() - } -} diff --git a/dom/entrypoint.sh b/dom/entrypoint.sh index e8319369..1de5a880 100644 --- a/dom/entrypoint.sh +++ b/dom/entrypoint.sh @@ -18,6 +18,5 @@ else cat > "$first_file" fi -cd - -node --unhandled-rejections=strict /app/test.js "${EXERCISE}" +cd /app +node --no-warnings --unhandled-rejections=strict test.js "${EXERCISE}" diff --git a/dom/fifty-shades-of-cold.js b/dom/fifty-shades-of-cold.js deleted file mode 100644 index 6fc38b46..00000000 --- a/dom/fifty-shades-of-cold.js +++ /dev/null @@ -1,43 +0,0 @@ -import { colors } from './data.js' - -export const generateClasses = () => { - document.head.append( - Object.assign(document.createElement('style'), { - type: 'text/css', - id: 'colors', - innerHTML: colors - .map((color) => `.${color} { background: ${color}; }`) - .join('\n'), - }), - ) -} - -const body = document.querySelector('body') - -const cold = ['aqua', 'blue', 'turquoise', 'green', 'purple', 'cyan', 'navy'] - -export const generateColdShades = () => { - const shades = colors.filter((color) => { - for (const c of cold) { - if (color.includes(c)) { - return true - } - } - }) - - shades.forEach((c) => { - const shade = document.createElement('div') - shade.className = c - shade.textContent = c - body.append(shade) - }) -} - -export const choseShade = (shade) => { - const all = [...document.querySelectorAll('div')] - all.forEach((a) => { - if (!a.classList.contains(shade)) { - a.classList.replace(a.className, shade) - } - }) -} diff --git a/dom/get-them-all.js b/dom/get-them-all.js deleted file mode 100644 index 11372b59..00000000 --- a/dom/get-them-all.js +++ /dev/null @@ -1,25 +0,0 @@ -export const getArchitects = () => { - const architects = [...document.getElementsByTagName('a')] - const others = [...document.getElementsByTagName('span')] - return [architects, others] -} - -export const getClassical = () => { - const classicals = [...document.getElementsByClassName('classical')] - const others = [...document.querySelectorAll('a:not(.classical)')] - return [classicals, others] -} - -export const getActive = () => { - const active = [...document.querySelectorAll('.classical.active')] - const others = [...document.querySelectorAll('.classical:not(.active)')] - return [active, others] -} - -export const getBonannoPisano = () => { - const bonanno = document.getElementById('BonannoPisano') - const others = [ - ...document.querySelectorAll('a.classical.active:not(#BonannoPisano)'), - ] - return [bonanno, others] -} diff --git a/dom/gossip-grid.js b/dom/gossip-grid.js deleted file mode 100644 index f4cd675b..00000000 --- a/dom/gossip-grid.js +++ /dev/null @@ -1,103 +0,0 @@ -import { gossips } from './data.js' - -const body = document.querySelector('body') - -const ranges = document.createElement('div') -ranges.className = 'ranges' -body.append(ranges) - -const inputs = [ - { props: ['width'], min: 200, max: 800, value: 250 }, - { props: ['fontSize', 'lineHeight'], min: 20, max: 40, value: 25 }, - { props: ['background'], min: 20, max: 75, value: 60 }, -] - -export const grid = () => { - inputs.forEach((input) => createInput(input)) - createAddGossip() - gossips.forEach((g) => createGossip(g)) -} - -const createGossip = (g, isNew = false) => { - const gossip = document.createElement('div') - const addGossip = document.getElementById('add-gossip') - const { fontSize, lineHeight, width, background } = addGossip.style - gossip.className = 'gossip' - gossip.textContent = g - if (isNew) { - gossip.style.fontSize = fontSize - gossip.style.lineHeight = lineHeight - gossip.style.width = width - gossip.style.background = background - gossip.classList.add('fade-in') - body.insertBefore(gossip, addGossip.nextElementSibling) - } else { - body.append(gossip) - } -} - -const createAddGossip = () => { - const addGossip = document.createElement('form') - addGossip.className = 'gossip' - addGossip.id = 'add-gossip' - addGossip.onsubmit = () => false - - const newInput = document.createElement('textarea') - newInput.autofocus = true - newInput.placeholder = 'Got a gossip to share ?' - newInput.addEventListener('keyup', (e) => addNewGossip(newInput, e)) - - const button = document.createElement('button') - button.className = 'button' - button.textContent = 'Share gossip!' - button.addEventListener('click', (e) => addNewGossip(newInput)) - - addGossip.append(newInput, button) - body.append(addGossip) -} - -const addNewGossip = (input, event) => { - const noValue = !input.value.trim() - const notEnterKey = event && event.keyCode !== 13 - if (notEnterKey || noValue) return - createGossip(input.value, true) - gossips.push(input.value) - input.value = '' - input.focus() -} - -const createInput = ({ props, min, max, value }) => { - const range = document.createElement('div') - range.className = 'range' - - const input = document.createElement('input') - input.type = 'range' - input.min = min - input.max = max - input.value = value - input.addEventListener('input', (e) => customize(e, ...props)) - - const propLabel = document.createElement('label') - propLabel.textContent = props[0] - - const valueLabel = document.createElement('span') - valueLabel.textContent = value - - range.append(propLabel, input, valueLabel) - ranges.append(range) -} - -const customize = ({ target }, ...props) => { - for (const card of [...document.querySelectorAll('.gossip')]) { - for (const prop of props) { - const updatedValue = - (prop === 'lineHeight' && `${Number(target.value) * 1.5}px`) || - (prop === 'background' && `hsl(280, 50%, ${target.value}%)`) || - `${target.value}px` - card.style[prop] = updatedValue - } - } - - const valueLabel = target.nextElementSibling - valueLabel.textContent = target.value -} diff --git a/dom/harder-bigger-bolder-stronger.js b/dom/harder-bigger-bolder-stronger.js deleted file mode 100644 index cd2d40e8..00000000 --- a/dom/harder-bigger-bolder-stronger.js +++ /dev/null @@ -1,14 +0,0 @@ -const body = document.querySelector('body') - -const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - -export const generateLetters = () => { - body.append(...[...Array(120).keys()].map((c) => { - const shape = document.createElement('div') - - shape.textContent = alphabet[Math.floor(Math.random() * alphabet.length)] - shape.style.fontSize = `${c + 11}px` - shape.style.fontWeight = [300,400,600][Math.floor(c / 40)] - return shape - })) -} diff --git a/dom/keycodes-symphony.js b/dom/keycodes-symphony.js deleted file mode 100644 index c67b299a..00000000 --- a/dom/keycodes-symphony.js +++ /dev/null @@ -1,37 +0,0 @@ -const body = document.querySelector('body') - -export const compose = () => { - document.addEventListener('keydown', (e) => handleKey(e)) - setTimeout( - () => document.removeEventListener('keydown', (e) => handleKey(e)), - 500, - ) -} - -const handleKey = (e) => { - const notes = [...document.querySelectorAll('.note')] - - if (e.key === 'Backspace') { - const last = notes[notes.length - 1] - last && last.remove() - return - } - - if (e.key === 'Escape') { - if (notes.length) { - notes.forEach((note) => note.remove()) - } - return - } - - createNote(e) -} - -const createNote = ({ key }) => { - const number = key.charCodeAt(0) * 2 - 150 - const note = document.createElement('div') - note.className = 'note' - note.textContent = key - note.style.background = `hsl(270, ${number}%, ${number}%)` - body.append(note) -} diff --git a/dom/mouse-trap.js b/dom/mouse-trap.js deleted file mode 100644 index c888a3d8..00000000 --- a/dom/mouse-trap.js +++ /dev/null @@ -1,67 +0,0 @@ -const body = document.querySelector('body') - -const box = document.createElement('div') -box.className = 'box' -body.append(box) - -const { top, bottom, left, right } = box.getBoundingClientRect() - -const diameter = 50 -const radius = diameter / 2 - -const insideX = (clientX) => clientX > left + radius && clientX < right - radius -const insideY = (clientY) => clientY > top + radius && clientY < bottom - radius - -let isInside = false - -export const createCircle = () => { - document.addEventListener('click', (e) => create(e)) - setTimeout(() => document.removeEventListener('click', create), 500) -} - -const create = ({ clientX, clientY }) => { - const circle = document.createElement('div') - circle.className = 'circle' - body.append(circle) - circle.style.top = `${clientY - radius}px` - circle.style.left = `${clientX - radius}px` - circle.style.background = 'white' - const hasEntered = insideX(clientX) && insideY(clientY) - if (hasEntered) { - circle.style.background = 'var(--purple)' - } - isInside = false -} - -export const moveCircle = () => { - document.addEventListener('mousemove', (e) => move(e)) - setTimeout(() => document.removeEventListener('mousemove', move), 500) -} - -const move = (e) => { - const circles = [...document.getElementsByClassName('circle')] - const circle = circles[circles.length - 1] - if (!circle) return - position(e, circle) -} - -const position = ({ clientX, clientY }, circle) => { - const hasEntered = insideX(clientX) && insideY(clientY) - - if (hasEntered) { - isInside = true - circle.style.background = 'var(--purple)' - } - - if (isInside) { - if (insideY(clientY)) { - circle.style.top = `${clientY - radius}px` - } - if (insideX(clientX)) { - circle.style.left = `${clientX - radius}px` - } - } else { - circle.style.top = `${clientY - radius}px` - circle.style.left = `${clientX - radius}px` - } -} diff --git a/dom/package.json b/dom/package.json index 7cdfde1a..3dbc1ca5 100644 --- a/dom/package.json +++ b/dom/package.json @@ -1,9 +1,3 @@ { - "type": "module", - "scripts": { - "test": "node --unhandled-rejections=strict test" - }, - "dependencies": { - "puppeteer-core": "^3.3.0" - } + "type": "module" } diff --git a/dom/pick-and-click.js b/dom/pick-and-click.js deleted file mode 100644 index d3e1d8ec..00000000 --- a/dom/pick-and-click.js +++ /dev/null @@ -1,107 +0,0 @@ -const body = document.querySelector('body') - -const hslValue = document.createElement('div') -hslValue.className = 'hsl' -hslValue.textContent = 'hsl(0, 50%, 0%)' - -const hueValue = document.createElement('div') -hueValue.className = 'text hue' -hueValue.textContent = 'hue' - -const luminosityValue = document.createElement('div') -luminosityValue.className = 'text luminosity' -luminosityValue.textContent = 'luminosity' - -const origin = document.createElement('div') -origin.className = 'text origin' - -const picked = document.createElement('div') -picked.className = 'text picked' -picked.textContent = 'Color successfully picked!' - -const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') -svg.setAttributeNS( - 'http://www.w3.org/2000/xmlns/', - 'xmlns:xlink', - 'http://www.w3.org/1999/xlink', -) -svg.setAttribute('width', window.innerWidth) -svg.setAttribute('height', window.innerHeight) -svg.setAttribute('viewBox', `0 0 ${window.innerWidth} ${window.innerHeight}`) - -const axisX = document.createElementNS('http://www.w3.org/2000/svg', 'line') -axisX.setAttribute('y1', window.innerHeight) -axisX.setAttribute('y2', 0) -axisX.id = 'axisX' -svg.append(axisX) - -const axisY = document.createElementNS('http://www.w3.org/2000/svg', 'line') -axisY.setAttribute('x1', window.innerWidth) -axisY.setAttribute('x2', 0) -axisY.id = 'axisY' -svg.append(axisY) - -body.append(hslValue, hueValue, luminosityValue, origin, picked, svg) - -export const pick = () => { - document.addEventListener('mousemove', (e) => set(e)) - setTimeout( - () => document.removeEventListener('mousemove', (e) => set(e)), - 500, - ) - - body.addEventListener('click', click) - setTimeout(() => document.removeEventListener('click', click), 500) - - body.addEventListener('copy', copy) - setTimeout(() => document.removeEventListener('copy', copy), 500) -} - -const click = (e) => { - document.execCommand('copy') - const wave = document.createElement('div') - wave.className = 'click-wave' - wave.style.top = `${e.clientY - 10}px` - wave.style.left = `${e.clientX - 10}px` - body.append(wave) - setTimeout(() => wave.remove(), 150) -} - -const copy = (event) => { - event.preventDefault() - if (event.clipboardData) { - event.clipboardData.setData('text/plain', hslValue.textContent) - picked.classList.add('fade-in') - setTimeout(() => picked.classList.remove('fade-in'), 1000) - } -} - -const calc = (number, max) => - Math.round(Math.min(max, Math.max(0, max * number))) - -const set = ({ clientX, clientY }) => { - const { innerWidth, innerHeight } = window - const padding = 100 - const mouseX = clientX - padding - const mouseY = clientY - padding - - const hue = calc(mouseX / (innerWidth - padding * 2), 360) - const luminosity = calc(mouseY / (innerHeight - padding * 2), 100) - const color = `hsl(${hue}, 50%, ${luminosity}%)` - - axisX.setAttribute('x1', clientX) - axisX.setAttribute('x2', clientX) - axisY.setAttribute('y1', clientY) - axisY.setAttribute('y2', clientY) - - axisX.setAttribute('stroke', color) - axisY.setAttribute('stroke', color) - - body.style.color = color - body.style.background = color - origin.style.background = color - hslValue.textContent = color - - hueValue.textContent = `hue\n${hue}` - luminosityValue.textContent = `${luminosity}\nluminosity` -} diff --git a/dom/pimp-my-style.js b/dom/pimp-my-style.js deleted file mode 100644 index fac3e1e0..00000000 --- a/dom/pimp-my-style.js +++ /dev/null @@ -1,30 +0,0 @@ -import { styles } from './data.js' - -let count = 0 -let increment = 1 - -export const pimp = () => { - const button = document.querySelector('.button') - - const ceiling = count === styles.length - 1 - const floor = !count - - const increasing = increment > 0 - - if (increasing || (floor && !increment)) { - button.classList.add(styles[count]) - } else { - button.classList.remove(styles[count]) - } - - if (ceiling) { - increment = increment ? 0 : -1 - } - if (floor) { - increment = increment < 0 ? 0 : 1 - } - - button.classList.toggle('unpimp', increment < 0 || (!increment && ceiling)) - - count += increment -} diff --git a/dom/test.js b/dom/test.js index 1a42cc6c..88e095f6 100644 --- a/dom/test.js +++ b/dom/test.js @@ -2,14 +2,20 @@ import http from 'http' import fs from 'fs' import path from 'path' import { deepStrictEqual } from 'assert' -import puppeteer from 'puppeteer-core' +import puppeteer from 'puppeteer' const exercise = process.argv[2] if (!exercise) throw Error(`usage: node test EXERCISE_NAME`) const PORT = 9898 const config = { - headless: false, - executablePath: process.env.CHROME_PATH || '/usr/bin/google-chrome', + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + + // This will write shared memory files into /tmp instead of /dev/shm, + // because Docker’s default for /dev/shm is 64MB + '--disable-dev-shm-usage', + ], } const mediaTypes = { @@ -25,7 +31,7 @@ const server = http .createServer(({ url, method }, response) => { console.log(method + ' ' + url) const filepath = url.endsWith(`${exercise}/${exercise}.js`) - ? path.join('.', url.slice(exercise.length + 1)) + ? path.join('/jail/student', url.slice(exercise.length + 1)) : path.join('./subjects', url) const ext = path.extname(filepath) response.setHeader('Content-Type', mediaTypes[ext.slice(1)] || 'text/plain') diff --git a/dom/where-do-we-go.js b/dom/where-do-we-go.js deleted file mode 100644 index b772e0d2..00000000 --- a/dom/where-do-we-go.js +++ /dev/null @@ -1,72 +0,0 @@ -import { places } from './data.js' - -const body = document.querySelector('body') - -export const explore = () => { - createSections() - - const location = document.createElement('div') - location.className = 'location' - setLocation(location) - - const direction = document.createElement('div') - direction.className = 'direction' - - body.append(location, direction) - - document.addEventListener('wheel', (event) => - setLocation(location, direction, event), - ) - setTimeout(() => - document.removeEventListener('wheel', (event) => - setLocation(location, direction, event), - ), - ) -} - -const createSections = () => { - const sorted = places.sort( - (a, b) => getDegree(b.coordinates) - getDegree(a.coordinates), - ) - - sorted.map(({ name, color }) => { - const nameDashCase = name - .toLowerCase() - .split(',')[0] - .split(' ') - .join('-') - - const url = `https://raw.githubusercontent.com/MarieMalarme/dom-js/master/assets/images/${nameDashCase}.jpg` - - const section = document.createElement('section') - section.style.background = `center / cover url(${url})` - body.append(section) - }) -} - -const getDegree = (coordinates) => { - const north = coordinates.includes('N') - const degree = coordinates.split("'")[0].replace('°', '.') - return north ? degree : -degree -} - -const setLocation = (location, direction, event) => { - const { name, coordinates, color } = getLocation() - location.textContent = `${name}\n${coordinates}` - location.style.color = color - location.onclick = () => { - window.open(`https://www.google.com/maps/place/${coordinates}`, '_blank') - } - - if (!event) return - const scrollUp = event.deltaY < 0 - direction.innerHTML = `
${scrollUp ? 'N' : 'S'}
` -} - -const getLocation = () => { - const { innerHeight, scrollY } = window - const index = Math.ceil((scrollY - innerHeight / 2) / innerHeight) - return places[index] -}