Skip to content

Commit

Permalink
Simplified script.js and added backspace and click functionality
Browse files Browse the repository at this point in the history
`checkIfCrosswordIsComplete` uses the `every` method to easily check if a crossword is complete.

The grid now responds to the users click and always ensures that only a single cell can be highlighted a different colour to show input focus (before, the old input focus did not revert its colour).

`onDefinitionsListItemClick` now sets the user's input focus to the beginning of the word that was clicked.

Regarding `index.html`, some changes were made to make it more readable and clean.

`babel.cfg` was made to specify what files to extract translatable strings from when using pybabel.
  • Loading branch information
tomasvana10 committed Jan 29, 2024
1 parent ca448dd commit ff158cb
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 191 deletions.
4 changes: 2 additions & 2 deletions locales/base.pot
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Translations template for crossword_puzzle.
# Copyright (C) 2024 Tomas Vana
# This file is distributed under the same license as the PROJECT project.
# This file is distributed under the same license as the crossword_puzzle project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-01-11 23:12+1100\n"
"POT-Creation-Date: 2024-01-28 14:35+1100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down
2 changes: 2 additions & 0 deletions src/babel.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[python: src/**.py]
[jinja2: **/templates/**.html]
4 changes: 2 additions & 2 deletions src/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ theme = dark-blue
language = en

[misc]
launches = 0
cword_browser_opened = 0
launches = 4
cword_browser_opened = 1
webapp_port = 5000

4 changes: 2 additions & 2 deletions src/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ class CrosswordDirections:
'''Constants representing words going across or down. Used extensively in conditional statements
and functions in `cword_gen.py`.
'''
ACROSS = "a"
DOWN = "d"
ACROSS = "ACROSS"
DOWN = "DOWN"


class CrosswordStyle:
Expand Down
9 changes: 5 additions & 4 deletions src/cword_webapp/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ def run(*args, **data):
def main():
return render_template(
"index.html",
empty=args[1],
cword_data=data["cword_data"],
empty=data["empty"],
name=data["name"],
word_count=data["word_count"],
failed_insertions=data["failed_insertions"],
Expand All @@ -20,11 +21,11 @@ def main():
definitions_d=data["definitions_d"],
)

app.run(debug=False, port=int(args[0]))
app.run(debug=False, port=int(data["port"]))

def init_webapp(*args, **data):
def init_webapp(**data):
global server
server = Process(target=run, args=args, kwargs=data)
server = Process(target=run, kwargs=data)
server.start()

def terminate_app():
Expand Down
160 changes: 73 additions & 87 deletions src/cword_webapp/static/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,130 +6,116 @@ information to the user.
let grid, dimensions, empty, definitions_a, definitions_d; // Jinja2 template variables
let direction = 'ACROSS',
cellCoords = null,
currentCell = null;
currentCell = null,
futureCoords = null,
mode = null;

document.addEventListener("DOMContentLoaded", function() { // On page load
const FOCUSED_CELL_COLOUR = "#a7d8ff";
const UNFOCUSED_CELL_COLOUR = "whitesmoke"; // for now


document.addEventListener("DOMContentLoaded", () => { // On page load
const body = document.querySelector("body");

/// Retrieve Jinja2 template variables
grid = body.getAttribute("data-grid");
dimensions = body.getAttribute("data-dimensions");
grid = eval(body.getAttribute("data-grid")); /* Convert Python array to JS array */
dimensions = parseInt(body.getAttribute("data-dimensions"));
empty = body.getAttribute("data-empty");
definitions_a = body.getAttribute("data-definitions_a");
definitions_d = body.getAttribute("data-definitions_d");
});

document.addEventListener("keydown", function(event) { // Detect user input
if (cellCoords === null) { // User hasn't selected a cell
return;
};

if (checkIfCrosswordIsComplete()) { // User has completed crossword
alert("You completed the crossword!");
};

let inputCellElement = getInputCellElementFromRowAndColumnData(cellCoords)
currentCell = inputCellElement
document.addEventListener("keydown", (event) => { // Detect user input
if (cellCoords === null) return; // User hasn't selected a cell

let inputValue = event.key; // Retrieve the character the user typed
if (!(inputValue.length == 1 && inputValue.match(/[a-z]/i))) return; // If the input is not a letter (eg. ALT)
inputCellElement.innerText = inputValue; // Update the grid accordingly
let inputValue = event.key;
mode = (inputValue == "Backspace" || inputValue == "Delete") ? "del" : "enter"
if (mode != "del") {
if (!(inputValue.length == 1 && inputValue.match(/\p{L}/u))) return; // Input isn't a language char

cellCoords = shiftInputFocus();
highlightInputFocus(getInputCellElementFromRowAndColumnData(cellCoords));
currentCell = getInputCellElement(cellCoords);
currentCell.childNodes[0].nodeValue = inputValue; // Using nodes prevents any `num_label` elements from being deleted
} else {
currentCell = getInputCellElement(cellCoords);
currentCell.childNodes[0].nodeValue = "";
};

if (checkIfCrosswordIsComplete()) { alert("You completed the crossword!") };

changeCellFocus(currentCell, focus=false);
cellCoords = shiftCellCoords(cellCoords, direction, mode);
currentCell = getInputCellElement(cellCoords);
changeCellFocus(currentCell, focus=true);
});

function shiftInputFocus() {
let futureCoords = direction == 'DOWN'
? [cellCoords[0] + 1, cellCoords[1]]
: [cellCoords[0], cellCoords[1] + 1];
let futureCell = getInputCellElementFromRowAndColumnData(futureCoords);
function shiftCellCoords(coords, dir, mode) {
if (mode == "enter") {
futureCoords = dir == "DOWN"
? [coords[0] + 1, coords[1]]
: [coords[0], coords[1] + 1]
} else if (mode == "del") {
futureCoords = dir == "DOWN"
? [coords[0] - 1, coords[1]]
: [coords[0], coords[1] - 1]
};
let futureCell = getInputCellElement(futureCoords);

return futureCell !== null && futureCell.classList.contains('non_empty_cell')
? futureCoords
: cellCoords
? futureCoords /* If the cell at the future coords has the `non_empty_cell` class */
: coords; /* Keep the coords at the current cell */
};

function onDefinitionsListItemClick(label, dir) { // Click on a word's definitions -> set input to start of that word
// Search for the number label element by using definition list item's num_label data
let numLabelElement = document.querySelector(`[data-num_label="${label}"]`);
// Get the parent of the number label element, which is the cell
let cellOfNumLabelElement = numLabelElement.parentElement;

cellCoords = updateCellCoords(cellOfNumLabelElement);
function onDefinitionsListItemClick(num_label, dir) { // Click on a word's definitions -> set input to start of that word
if (currentCell !== null) { changeCellFocus(currentCell, focus=false) };

// Retrieve the new cell element from the parent of the number label element
currentCell = document.querySelector(`[data-num_label="${num_label}"]`).parentElement;
updateCellCoords(currentCell);
changeCellFocus(currentCell, focus=true);
direction = dir;
};

highlightInputFocus(cellOfNumLabelElement);
function onCellClick(cell) {
if (currentCell !== null) { changeCellFocus(currentCell, focus=false) };

direction = dir
currentCell = cell
updateCellCoords(cell);
changeCellFocus(cell, focus=true);
direction = shiftCellCoords(cellCoords, 'ACROSS', mode="enter") == cellCoords ? 'DOWN' : 'ACROSS';
};

function updateCellCoords(cell) {
return [parseInt(cell.getAttribute("data-row")), parseInt(cell.getAttribute("data-column"))]
cellCoords = [parseInt(cell.getAttribute("data-row")), parseInt(cell.getAttribute("data-column"))];
};

function highlightInputFocus(newCell) {
//.style.backgroundColor = "white";
newCell.style.backgroundColor = "#a7d8ff";
function changeCellFocus(cell, focus) {
cell.style.backgroundColor = focus ? FOCUSED_CELL_COLOUR : UNFOCUSED_CELL_COLOUR;
};

function getInputCellElementFromRowAndColumnData(cellCoords) {
return cellCoords ? document.querySelector(`[data-row="${cellCoords[0]}"][data-column="${cellCoords[1]}"]`) : null;
}
function getInputCellElement(cellCoords) {
return cellCoords
? document.querySelector(`[data-row="${cellCoords[0]}"][data-column="${cellCoords[1]}"]`)
: true;
};

function checkIfCrosswordIsComplete() {
// Compare all table cells with the grid, and if they are identical, tell the user they have
// completed the crossword.
let webAppGrid = getWebAppGrid(); // Get current web app grid
console.log(webAppGrid)
console.log(grid)

for (let row = 0; row < grid.length; row++) {
for (let column = 0; column < grid.length; column++) {
if (webAppGrid[row][column] == empty) { // Empty table cell, don't do anything
continue;
};
if (webAppGrid[row][column] != grid[row][column]) { // Grid element does not match the
// web app grid element. They have
// not completed the crossword.
return false;
};
};
};
return true; // All checks were true, the user has completed the crossword.
// Compare all table cells with the grid, and if they are identical, return true
let webAppGrid = getWebAppGrid();
return webAppGrid.every((row, i) => row.every((cell, j) => cell == grid[i][j]));
};

function getWebAppGrid() {
let nonEmptyCells = document.querySelectorAll(".non_empty_cell");
let webAppGrid = Array(parseInt(dimensions)).fill(Array(parseInt(dimensions)).fill(empty));
console.log('webAppGrid', webAppGrid)
console.log('nonEmptyCells', nonEmptyCells)
// Create an empty replica of the crossword grid, then update it according to the web app grid.
let nonEmptyCellElements = document.querySelectorAll(".non_empty_cell");
let webAppGrid = Array.from({length: dimensions}, () => Array(dimensions).fill(empty));

nonEmptyCells.forEach((cell) => { // Update webAppGrid with `data-value` properties from the grid's cells
nonEmptyCellElements.forEach((cell) => {
let row = parseInt(cell.getAttribute("data-row"));
let column = parseInt(cell.getAttribute("data-column"));
let value = cell.getAttribute("data-value");

console.log(row, column, value)
let value = cell?.childNodes[0]?.nodeValue.toUpperCase() ?? empty
webAppGrid[row][column] = value;
});

return webAppGrid;
};

// TO DO

function onCellClick(cell) {
let row = cell.getAttribute("data-row");
let column = cell.getAttribute("data-column");

determineWhereToSetInputFocusOnCellClick(cell, row, column);
};

function determineWhereToSetInputFocusOnCellClick(cell, row, column) {
// logic

// update cellInputFocus to the row, column that was determined
let inputCellElement = getInputCellElementFromRowAndColumnData(crossOriginIsolated)

highlightInputFocus(inputCellElement);
};
Loading

0 comments on commit ff158cb

Please sign in to comment.