IMPORTANT: This is work in progress! The API may change dramatically as I work out what is most suitable.
Tiny, lightweight crossword control for the web. crosswords-js is:
Inspired by the excellent free online crosswords on The Guardian Crosswords.
Demo: dwmkerr.github.io/crosswords-js/
The project documentation is written in Markdown and is located in the repository at ./docs
.
Install the package:
npm install crosswords-js
Include the minified JavaScript package source and CSS in your webpage:
<link
href="node_modules/crosswords-js/dist/crosswords.css"
rel="stylesheet"
/>
<script src="node_modules/crosswords-js/dist/crosswords.js"></script>
To create a crossword, locate or edit a CrosswordSource, which can be import
ed from a simple JSON file to create a CrosswordDefinition:
{
"width": 15,
"height": 15,
"acrossClues": [
{
"x": 1,
"y": 1,
"clue": "1. Conspicuous influence exerted by active troops (8,5)"
},
...
],
"downClues": [
{
"x": 3,
"y": 1,
"clue": "1. A coy sort of miss pointlessly promoting lawlessness (9)"
},
...
]
}
Complete CrosswordSource file examples can be found here, there or everywhere.
Further on, the CrosswordDefinition needs to be compiled into a CrosswordModel. Compiling validates the the CrosswordDefinition, making sure that there are no incongruities in the structure, for example:
In your JavaScript code, load the crosswords-js package and a CrosswordDefinition:
import { compileCrossword, newCrosswordController } from 'crosswords-js';
import crosswordDefinition from 'node_modules/crosswords-js/data/ftimes_17095.json';
Now get the DOM elements which will be the parents for the crossword grid and clues blocks:
For example, if we have placeholder
div
elements somewhere in our webpage:... <div id="crossword-grid-placeholder" /> ... <div id="crossword-clues-placeholder" />We locate the element via the webpage DOM:
const gridParent = document.getElementById('crossword-grid-placeholder'); const cluesParent = document.getElementById('crossword-clues-placeholder');
And pass the crosswordDefinition
, gridParent
and viewParent
elements into the Controller constructor:
let controller = newCrosswordController(
crosswordDefinition,
gridParent,
cluesParent,
);
This binds the crossword gridView anf cluesView into the webpage DOM.
You can use the controller
to programmatically manipulate the gridView - the crossword grid DOM element.
Call the user event handler methods of the controller
directly in code
// Check the current clue answer against the solution.
controller.testCurrentClue();
Bind the user event handler methods via id
or class
attributes on DOM elements in your HTML markup, such as buttons.
<div id="clue-buttons">
<p>Clue</p>
<button id="test-clue">Test</button>
<button id="clean-clue">Clean</button>
<button id="reveal-clue">Reveal</button>
<button class="reset-clue">Reset</button>
<button class="reset-clue">MoSet</button>
</div>
// Bind one element with id "test-clue"
controller.bindEventHandlerToId("test-clue", "click", document);
// Using default arguments for
// eventName ("click") and dom (document)
controller.bindEventHandlerToId("reveal-clue");
// Bind event handler to multiple elements with class "reset-clue"
// default arguments are available as before
controller.bindEventHandlerToClass("reset-clue", "click", document);
});
// Bind ALL the user event handlers, using defaults
controller.bindEventHandlersToIds();
// Bind the user event handlers to ALL elements with
// the given class(es), passing an array of one or more class names
controller.bindEventHandlersToClass(["reset-clue"]);
For further information on these topics, consult the module API documentation.
For examples, refer to the development server code.
The library ships with some simple default styles out of the box, but aims to be easily customisable. See crossword-styling.md
for details.
The development server is a pure Node.js application of the the crosswords-js package. It exercises nearly all the available functionality. The code is found in the dev directory of this repository.
# Open the development server on http://localhost:5173
npm start
We strongly recommend you follow the popular "triangular" workflow, as recommended by GitHub, when working on this project. It aids collaboration by:
- producing simple, linear commit sequences for pull-requests, and
- easily incorporating changes in the upstream repo.
Check out the code and open the repository root directory...
git clone https://github.com/dwmkerr/crosswords-js.git &&
cd crosswords-js
then...
# From the repository root, bootstrap the package and all tools
bin/bootstrap-posix-ish.sh
# Open the development server
npm start
If you are running a modern version of Windows, you can add a Linux distro to your computer using WSL and then follow the Linux instructions above.
If the script above failed or doesn't suit your environment...
# Install/update node to latest long-term-support (LTS) version, and install/update npm to latest version.
nvm install --lts --latest-npm
nvm use --lts
git clone https://github.com/dwmkerr/crosswords-js.git
cd crosswords-js
# Fetch all dependent packages
npm install
# Start the development server
npm start
If you have installed Node Version Manager (nvm) following the recommended procedure, you can keep up with the latest versions of nvm, npm, node LTS, and the latest package versions for this module by regularly running:
# Update the tools and packages used in this environment
npm run update
You can automate the manual checks in the section below on each commit to your local git repository.
npm run qa:installIf you ever need to bypass the automated checks, stage your changes then run:
git commit --no-verify
We use MochaJS for unit testing. The test source code is located in the repository at ./test
. Run the tests with:
npm test
Linting is provided by ESLint, which is also configured to use Prettier for code formatting:
# Lint the code.
npm run lint
# Lint and fix the code.
npm run lint:fix
Documentation and HTML can be checked for standard conformance using Prettier:
# Check html and docs for correctness.
npm run prettier
# Check and fix html and docs for correctness.
npm run prettier:fix
Spelling can be checked using CSpell:
# Check _staged_ files for spelling.
npm run spell
# Check new and changed files for spelling.
npm run spell:changed
# Check all files for spelling.
npm run spell:all
Ensure you build and stage the production assets
# Build and stage the production assets
npm run build && git add dist/
Please install our git commit template. This enables project commit guidelines to be prefixed to the standard git commit message. From the root directory of your repository:
git config --local commit.template ./.git-commit-template.txt
The dev
environment production assets are built by ViteJS at dev/dist
. The dist
folder is created when the assets are built.
# Build the assets under dev/dist
npm run dev:build
You can preview the production assets by running the following command and opening a browser on http://localhost:4173/
# Build the assets and preview locally at http://locahost:4173
npm run dev:preview
You can also find these keyboard shortcuts in the documentation
These are the default shortcuts:
You can override the default shortcuts by create your own eventBinding
sets. This is described in an API use case.
This is a little fiddly. I have tried to ensure the syntax is as close to what a reader would see in a printed crossword to make this as clear as possible. Here is an example:
{
"downClues": [{
"x": 6, "y": 1
"clue": "4,21. The king of 7, this general axed threat strategically (9)"
}],
"acrossClues": [{
"x": 1, "y": 11,
"clue": "21. See 4 (3,5)"
}]
}
Note that the LengthText (which would be (9,3,5)
in a linear clue) has separated. However, the crossword GridView will render the full LengthText for the first (head) clue segment (and nothing for the tail segments).
An example of a crossword with many multi-segment clues is at: https://www.theguardian.com/crosswords/cryptic/28038 - I have used this crossword for testing (but not included the definition in the codebase as I don't have permissions to distribute it).
We support a subset of Markdown.
**bold** text
. These Markdown tags are converted to CSS styles in the cluesView, or anywhere else clues are displayed.partial*italic*s
a _comp**lic**ated_ example
Style | Markdown tag | Example | Associated CSS class |
---|---|---|---|
italic |
_ or *
|
Some _italic_ text. |
.cw-italic { font-style: italic; } |
bold |
__ or **
|
Some **bold** text. |
.cw-bold { font-weight: bold; } |
bold-italic |
___ or ***
|
Some ___bold, italic___ text. |
The classes above are combined. |
We determine the GridView dimensions dynamically whenever a CrosswordSource is loaded.
The design of this project follows the Model-view-controller (MVC) design pattern. The naming of files and code artifacts follow from this pattern.
This project is currently a work in progress. The overall design goals are:
There are two workflows that run for the project:
Whenever a pull request is raised, the Pull Request Workflow is run. This will:
Each stage is run on all recent Node versions, except for the upload coverage stage which only runs for the Node.js LTS version. When a pull request is merged to the main
branch, if the changes trigger a new release, then Release Please will open a Release Pull Request. When this request is merged, the Main Workflow is run.
When a Release Please pull request is merged to main, the Main Workflow is run. This will:
NPM_TOKEN
secret is setEach stage is run on all recent Node versions, except for the upload coverage stage which only runs for the Node.js LTS version.
️ Note that the NPM Publish step sets the package to public - don't forget to change this if you have a private module.
To add contributors, use a comment like the below in anNode.jsy pull request:
@all-contributors please add @<username> for docs, code, tests
More detailed documentation is available at:
allcontributors.org/docs/en/bot/usage
When changes to main
are made, the Release Please stage of the workflow will work out whether a new release should be generated (by checking if there are user facing changes) and also what the new version number should be (by checking the log of conventional commits). Once this is done, if a release is required, a new pull request is opened that will create the release.
Force a specific release version with this command:
# Specify your version. We use Semantic Versioning (https://semver.org/)
version="0.1.0"
git commit --allow-empty -m "chore: release ${version}" -m "Release-As: ${version}"
Dave Kerr |
Paul Spain |
Misha Kaletsky ? |
This is a scattergun list of things to work on, once a good chunk of these have been done the larger bits can be moved to GitHub Issues:
a
or d
for across
or down
in the clue text (meaning we don't have to have two arrays of clues)