let gridVisible = false; let inputValid = true; // Generates each grid component given the data below const generateGridComponent = (gridClass, width, height) => { const gridComponent = document.createElement('div'); gridComponent.classList.add(gridClass); gridComponent.classList.add('grid-component'); if (gridVisible) { gridComponent.classList.add('show-grid'); } gridComponent.style = `width: ${width}px; height: ${height}px`; return gridComponent } // Determines if a given column index of a given height is between two pieces // of land. Returns true if so, otherwise false. const isBetweenLand = (elements, index, height) => { let foundLeft = false; let foundRight = false; for (let i = 0; i < elements.length; i++) { if (i < index && elements[i] >= height) { foundLeft = true; } else if (i > index && elements[i] >= height) { foundRight = true; } } return foundLeft && foundRight; }; // Generates the grid representing the problem const generateGrid = (elements, maxHeight) => { const container = document.getElementById('water-container'); while (container.firstChild) { container.removeChild(container.firstChild); } const childWidth = container.offsetWidth / elements.length; const childHeight = container.offsetHeight / maxHeight; for (let i = 0; i < elements.length; i++) { // Fill container with grid of elements const element = elements[i]; const column = document.createElement('div'); for (let j = maxHeight - 1; j >= 0; j--) { const componentClass = j > element ? (isBetweenLand(elements, i, j) ? 'water-component' : 'air-component') : 'land-component'; const component = generateGridComponent(componentClass, childWidth, childHeight); // Show actual array elements at the bottom of each column, only do this // if there is space. if (j == 0 && childWidth >= 30) { component.textContent = element; } column.appendChild(component); } container.appendChild(column) } }; // Get a function that will return smooth random results upon being called // sourced from http://bl.ocks.org/telic/9376360 function SmoothRandom(factor, start) { var last = (start !== undefined) ? start : Math.random(); var halfEnvelope = (1 / factor) / 2; return function () { // clamp output range to [0,1] as Math.random() var max = Math.min(1, last + halfEnvelope); var min = Math.max(0, last - halfEnvelope); // return a number within halfRange of the last returned value return last = Math.random() * (max - min) + min; }; } // Generates the array used in the problem const generateArray = () => { const nElements = document.getElementById('water-elements').value; const maxHeight = nElements; const elements = []; const nextNumber = SmoothRandom(nElements / 20); for (let i = 0; i < nElements; i++) { elements[i] = Math.floor(nextNumber() * maxHeight); } const copyButton = document.getElementById('water-copy-button'); copyButton.onclick = () => { navigator.clipboard.writeText(elements.join(' ')); copyButton.textContent = 'Copied!'; window.setTimeout(() => { copyButton.textContent = 'Copy Array to Clipboard' }, 2000); } generateGrid(elements, maxHeight); }; // Binds the show/hide grid button to determine when to show the interactive // with a visible grid. const bindGridButton = () => { const showGridButton = document.getElementById('show-grid-button') showGridButton.onclick = () => { gridVisible = !gridVisible; showGridButton.textContent = gridVisible ? 'Hide Grid' : 'Show Grid'; for (const gridComponent of document.getElementsByClassName('grid-component')) { if (gridVisible) { gridComponent.classList.add('show-grid'); } else { gridComponent.classList.remove('show-grid'); } } } }; // Binds the array input button to keep track of whether the input is valid. const bindArrayInput = () => { // Code to be run when any change to the value occurs. const onValueUpdate = () => { const value = arrayInputButton.value; if (value && value >= 1 && value <= 100) { inputValid = true; } else { inputValid = false; } if (inputValid) { arrayInputButton.classList.remove('invalid') } else { arrayInputButton.classList.add('invalid') } }; const arrayInputButton = document.getElementById('water-elements'); arrayInputButton.onchange = onValueUpdate; arrayInputButton.onkeyup = onValueUpdate; }; // Binds the randomise land button to track when to re-render the interactive const bindRandomiseLandButton = () => { const randomiseWaterButton = document.getElementById('randomise-land-button'); randomiseWaterButton.onclick = () => { if (inputValid) { generateArray(); } } }; generateArray(); bindGridButton(); bindArrayInput(); bindRandomiseLandButton();