Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dwmkerr/crosswords-js

Tiny, lightweight crossword control for the web.
https://github.com/dwmkerr/crosswords-js

crosswords javascript noframeworks web

Last synced: 21 days ago
JSON representation

Tiny, lightweight crossword control for the web.

Awesome Lists containing this project

README

        

# crosswords-js

[![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-)

[![Release Please][3]][4]
[![NPM Package Version][5]][6]
[![codecov][7]][8]

> **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:

- Lightweight
- Fast
- Simple
- Framework-free

Inspired by the excellent free online crosswords on [The Guardian Crosswords][18].

Demo: [dwmkerr.github.io/crosswords-js/][9]

CrosswordsJS Screenshot

## Index

- [Documentation](#documentation)
- [Quickstart](#quickstart)
- [Application Programming Interface (API)](#application-programming-interface-api)
- [Styling](#styling)
- [Sample applications](#sample-applications)
- [Contributor guide](#contributor-guide)
- [Setting up your dev environment](#setting-up-your-dev-environment)
- [1a. Linux, MacOS](#1a-linux-macos)
- [1b. Windows](#1b-windows)
- [1c. Manual setup](#1c-manual-setup)
- [2. Once you've started the development server](#2-once-youve-started-the-development-server)
- [Maintaining your dev environment](#maintaining-your-dev-environment)
- [Quality assurance](#quality-assurance)
- [Manual checks](#manual-checks)
- [Building the _dev environment_ assets for production](#building-the-dev-environment-assets-for-production)
- [Keyboard functionality](#keyboard-functionality)
- [Crossword definition tips](#crossword-definition-tips)
- [1. How do I create a clue which spans multiple parts of a crossword? (multi-segment clue)](#1-how-do-i-create-a-clue-which-spans-multiple-parts-of-a-crossword-multi-segment-clue)
- [2. How do I style the clue content with **bold**, _italic_ and _**bold-italic**_ words?](#2-how-do-i-style-the-clue-content-with-bold-italic-and-bold-italic-words)
- [3. How do I handle different grid sizes?](#3-how-do-i-handle-different-grid-sizes)
- [Design overview](#design-overview)
- [Design goals](#design-goals)
- [Build workflows](#build-workflows)
- [Pull-request workflow](#pull-request-workflow)
- [Main workflow](#main-workflow)
- [Adding contributors](#adding-contributors)
- [Managing releases](#managing-releases)
- [Contributors](#contributors)
- [TODO](#todo)

## Documentation

The project documentation is written in [Markdown][27] and is located in the repository at [`./docs`][61].

- [Documentation index][41]

## Quickstart

1. Install the package:

```bash
npm install crosswords-js
```

2. Include the minified JavaScript package source and CSS in your webpage:

```html


```

3. To create a crossword, locate or edit a [**CrosswordSource**][29], which can be `import`ed from a simple JSON file to create a [**CrosswordDefinition**][52]:

```json
{
"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][21], [there][22] or [everywhere][36].

Further on, the _CrosswordDefinition_ needs to be compiled into a [**CrosswordModel**][37]. Compiling validates the the _CrosswordDefinition_, making sure that there are no incongruities in the structure, for example:

- overlapping clues
- clues which don't fit in the grid bounds
- ...and so on.

4. In your JavaScript code, load the **crosswords-js** package and a _CrosswordDefinition_:

```js
import { compileCrossword, newCrosswordController } from 'crosswords-js';
import crosswordDefinition from 'node_modules/crosswords-js/data/ftimes_17095.json';
```

5. Now get the [DOM][20] 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:
>
> ```html
> ...
>


> ...
>

> ```
>
> We locate the element via the webpage [DOM][20]:
>
> ```js
> const gridParent = document.getElementById('crossword-grid-placeholder');
> const cluesParent = document.getElementById('crossword-clues-placeholder');
> ```

6. And pass the `crosswordDefinition`, `gridParent` and `viewParent` elements into the [**Controller**][38] constructor:

```js
let controller = newCrosswordController(
crosswordDefinition,
gridParent,
cluesParent,
);
```

This binds the crossword **gridView** anf **cluesView** into the webpage [DOM][20].

## Application Programming Interface (API)

You can use the `controller` to programmatically manipulate the **gridView** - the crossword grid [DOM][20] element.

1. Invoke the [**user event handlers**][39]

- Call the _user event handler_ methods of the `controller` directly in code
```js
// 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_.

```html


Clue


Test
Clean
Reveal
Reset
MoSet

```

```js
// 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"]);
```

2. You can also provide your own handlers to listen to [**controller events**][40].

For further information on these topics, consult the [module API][30] documentation.

For examples, refer to the [development server code][31].

## Styling

The library ships with some simple default styles out of the box, but aims to be easily customisable. See [`crossword-styling.md`][35] for details.

## Sample applications

The _development server_ is a pure [Node.js][32] application of the the **crosswords-js** package. It exercises nearly all the available functionality. The code is found in the [dev][33] directory of this repository.

```bash
# Open the development server on http://localhost:5173
npm start
```

## Contributor guide

### Setting up your dev environment

> We **strongly** recommend you follow the popular ["triangular" workflow][63], 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.

#### 1a. Linux, MacOS

Check out the code and open the repository root directory...

```bash
git clone https://github.com/dwmkerr/crosswords-js.git &&
cd crosswords-js
```

then...

```bash
# From the repository root, bootstrap the package and all tools
bin/bootstrap-posix-ish.sh
# Open the development server
npm start
```

#### 1b. Windows

If you are running a [modern version of Windows][58], you can add a Linux distro to your computer using [WSL][57] and then follow the Linux instructions [above][59].

#### 1c. Manual setup

If the script above failed or doesn't suit your environment...

1. Ensure you are using Node LTS. We recommend using [Node Version Manager][10] to make it easier to keep up to date:

```bash
# 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
```

2. Check out the code...

```bash
git clone https://github.com/dwmkerr/crosswords-js.git
```

3. Open the repository root directory, install the packages, and start the development server...

```bash
cd crosswords-js
# Fetch all dependent packages
npm install
# Start the development server
npm start
```

#### 2. Once you've started the development server

- The development server webpage will be visible at [http://localhost:5173/][11]
- _The webpage will dynamically refresh whenever you save your source edits_
- View the development webpage HTML: [dev/index.html][23]
- View the development webpage JavaScript: [dev/index.js][31]
- View the development webpage styles via the [**less**][24] source: [dev/index.less][25]
- _Less files are dynamically compiled to CSS by [ViteJS][28] for the development server_.

### Maintaining your dev environment

If you have installed **Node Version Manager (nvm)** following the [recommended procedure][49], you can keep up with the latest versions of nvm, npm, node LTS, and the latest package versions for this module by regularly running:

```bash
# Update the tools and packages used in this environment
npm run update
```

### Quality assurance

> You can automate the manual checks [in the section below][62] on each commit to your local git repository.
>
> ```bash
> npm run qa:install
> ```
>
> If you ever need to bypass the automated checks, stage your changes then run:
>
> ```bash
> git commit --no-verify
> ```

#### Manual checks

1. We use [MochaJS][26] for unit testing. The test source code is located in the repository at [`./test`][42]. Run the tests with:

```bash
npm test
```

2. Linting is provided by [ESLint][43], which is also configured to use [Prettier][44] for code formatting:

```bash
# Lint the code.
npm run lint
# Lint and fix the code.
npm run lint:fix
```

3. Documentation and HTML can be checked for standard conformance using [Prettier][44]:

```bash
# Check html and docs for correctness.
npm run prettier
# Check and fix html and docs for correctness.
npm run prettier:fix
```

4. Spelling can be checked using [CSpell][45]:

```bash
# 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
```

5. Ensure you build and stage the _production_ assets

```bash
# Build and stage the production assets
npm run build && git add dist/
```

6. 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:

```bash
git config --local commit.template ./.git-commit-template.txt
```

### Building the _dev environment_ assets for production

The `dev` environment **production assets** are built by [ViteJS][28] at [`dev/dist`][46]. The `dist` folder is created when the assets are built.

```bash
# 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/`][47]

```bash
# Build the assets and preview locally at http://locahost:4173
npm run dev:preview
```

## Keyboard functionality

_You can also find these keyboard shortcuts in the [documentation][48]_

These are the default shortcuts:

- **Left**/**Right**/**Up**/**Down**: Move (if possible) to the cell in the direction specified.
- **Space**: Move to the next cell in the focused clue, if one exists.
- **Shift+Space**: Move to the previous cell in the focused clue, if one exists.
- **Delete**: Delete the current cell.
- **Backspace**: Delete the current cell, and move to the previous cell in the focused clue, if one exists.
- **Tab**: Move to the first cell of the next clue, 'wrapping' to the first clue in the opposite direction.
- **Shift+Tab**: Move to the last cell of the previous clue, 'wrapping' to the last clue in the opposite direction.
- **A**-**Z**: Enter the character. Not locale aware!
- **Enter**: At a clue intersection, switch between across and down.

You can **override** the default shortcuts by create your own `eventBinding` sets. This is described in an [API use case][54].

## Crossword definition tips

### 1. How do I create a clue which spans multiple parts of a crossword? ([multi-segment clue][51])

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:

```json
{
"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_][50] (which would be `(9,3,5)` in a linear clue) has separated. However, the crossword [_GridView_][53] 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: - I have used this crossword for testing (but not included the definition in the codebase as I don't have permissions to distribute it).

### 2. How do I style the clue content with **bold**, _italic_ and _**bold-italic**_ words?

We support a subset of [Markdown][27].

- Style a word or phrase by book-ending the text with _matching_ tags described below, for example: `**bold** text`. These Markdown tags are converted to CSS styles in the **cluesView**, or anywhere else clues are displayed.
- You can style just a part of a word by embedding the tags _within_ the word, for example: `partial*italic*s`
- You can even embed a style within a style, for example: `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. |

### 3. How do I handle different grid sizes?

We determine the [GridView][55] dimensions dynamically whenever a [**CrosswordSource**][56] is loaded.

## Design overview

The design of this project follows the [Model-view-controller (MVC) design pattern][19]. The naming of files and code artifacts follow from this pattern.

## Design goals

This project is currently a work in progress. The overall design goals are:

1. This should be _agnostic_ to the type of crossword. It shouldn't depend on any proprietary formats or structures used by specific publications.
2. This should be _accessible_, and show how to make interactive content which is inclusive and supports modern accessibility patterns.
3. This project should be _simple to use_, without requiring a lot of third party dependencies or knowledge.

## Build workflows

There are two workflows that run for the project:

### Pull-request workflow

Whenever a pull request is raised, the [Pull Request Workflow][12] is run. This will:

- Install dependencies
- Lint
- Run Tests
- Upload Coverage

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][13] will open a Release Pull Request. When this request is merged, the [Main Workflow][14] is run.

### Main workflow

When a [Release Please][15] pull request is merged to main, the [Main Workflow][16] is run. This will:

- Install dependencies
- Lint
- Run Tests
- Upload Coverage
- Deploy to NPM if the `NPM_TOKEN` secret is set
- Upload the new release to the [GitHub Pages site][9]

Each 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.

## Adding contributors

To add contributors, use a comment like the below in anNode.jsy pull request:

```
@all-contributors please add @ for docs, code, tests
```

More detailed documentation is available at:

[allcontributors.org/docs/en/bot/usage][17]

## Managing releases

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:

```bash
# 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}"
```

## Contributors



Dave Kerr
Dave Kerr

📖 💻 ⚠️
Paul Spain
Paul Spain

📖 💻 ⚠️
Misha Kaletsky
Misha Kaletsky

💻 📖 👀

## TODO

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:

- [x] bug: backspace moves backwards, I think that deleting the letter is a better action for this (with left/up/ key to move backwards)
- [x] bug: [Demo site][9] is not tracking latest version
- [ ] bug: [Demo site][9] seems to have loading issues
- [x] feat(docs): improve the demo site image (its an old one at the moment!)
- [x] feat: show how we can check answers or highlight incorrect entries (see issue #9)
- [x] feat(samples): allow us to switch between 2-3 crosswords on the sample
- [x] feat(samples): cursor initially on the first clue
- [x] feat(dom): support a keyboard scheme or configurable keybindings so that keys for navigating / editing the crossword can be specified in config (allowing for schemes such as 'the guardian' or 'the age')
- [x] fix: the border on word separators slightly offsets the rendering of the grid
- [x] fix: the border on word separators in 'down' clues. Only partially extends across cell-width. (See "14 down" clue in "Financial Times 17,095" test crossword)
- [ ] feat(accessibility): get screenreader requirements
- [x] refactor: Simplify the static site by removing Angular and Bootstrap, keeping everything as lean and clean as possible. Later, replace with a React sample? OR have multiple samples, one for each common framework?
- [x] refactor: finish refactoring
- [x] feat: support clues which span non-contiguous ranges (such as large clues with go both across and down).
- [x] feat: simplify the crossword model by using `a` or `d` for `across` or `down` in the clue text (meaning we don't have to have two arrays of clues)
- [x] feat: allow italics with underscores, or bold with stars (i.e. very basic markdown)...
- [x] feat: clicking the first letter of a clue which is part of another clue should allow for a toggle between directions
- [x] todo: document the clue structure
- [ ] refactor: re-theme site to a clean black and white serif style, more like a newspaper
- [x] build: enforce linting (current it is allowed to fail)

[1]: https://img.shields.io/badge/all_co#1a-linuxntributors-2-orange.svg?style=flat-square
[2]: #contributors-
[3]: https://github.com/dwmkerr/crosswords-js/actions/workflows/main.yml/badge.svg
[4]: https://github.com/dwmkerr/crosswords-js/actions/workflows/main.yml
[5]: https://img.shields.io/npm/v/crosswords-js
[6]: https://www.npmjs.com/package/crosswords-js
[7]: https://codecov.io/gh/dwmkerr/crosswords-js/branch/main/graph/badge.svg
[8]: https://codecov.io/gh/dwmkerr/crosswords-js
[9]: https://dwmkerr.github.io/crosswords-js/
[10]: https://github.com/nvm-sh/nvm
[11]: http://localhost:5173/
[12]: ./.github/workflows/pull-request.yaml
[13]: https://github.com/google-github-actions/release-please-action
[14]: #release-pipeline
[15]: https://github.com/google-github-actions/release-please-action
[16]: ./.github/workflows/main.yml
[17]: https://allcontributors.org/docs/en/bot/usage
[18]: https://www.theguardian.com/crosswords
[19]: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
[20]: https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model
[21]: data/alberich_4.json
[22]: data/ftimes_17095.json
[23]: dev/index.html
[24]: https://lesscss.org/functions/
[25]: dev/index.less
[26]: https://mochajs.org/
[27]: https://www.markdownguide.org/
[28]: https://vitejs.dev/
[29]: docs/crossword-domain.md#crossword-source
[30]: docs/module-api.md
[31]: dev/index.js
[32]: https://nodejs.org/
[33]: dev/
[34]: sample/
[35]: docs/crossword-styling.md
[36]: data/ftimes_17095.json
[37]: docs/crossword-data-structures.md#crosswordmodel
[38]: docs/module-api.md#overview
[39]: docs/module-api.md#user-event-handlers
[40]: docs/module-api.md#published-events
[41]: docs/README.md
[42]: test/
[43]: https://eslint.org/
[44]: https://prettier.io/
[45]: https://cspell.org/
[46]: dev/dist/
[47]: http://localhost:4173/
[48]: docs/keyboard-shortcuts.md
[49]: https://github.com/nvm-sh/nvm#installing-and-updating
[50]: docs/crossword-domain.md#clue-segment
[51]: docs/crossword-domain.md#multi-segment-clue
[52]: docs/crossword-domain.md#crossword-definition
[53]: docs/module-api.md#views
[54]: docs/module-api.md#3-changing-keyboard-shortcuts
[55]: docs/module-api.md#views
[56]: docs/crossword-domain.md#crossword-source
[57]: https://learn.microsoft.com/en-us/windows/wsl/install
[58]: https://learn.microsoft.com/en-us/windows/wsl/install#prerequisites
[59]: #1a-linux
[61]: docs/
[62]: #manual-checks
[63]: https://gist.github.com/pvspain/0438b9b6733650d3e0f52f0d82ec993f