Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove jquery & modernize code #234

Draft
wants to merge 32 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
14d663c
initial work to remove jquery & modernize code
zackschuster Sep 27, 2022
1f1f744
lib: move asserts to test file
zackschuster Sep 28, 2022
68c8ca2
build: update tooling & process
zackschuster Sep 28, 2022
81d0404
deps: eliminate jquery
zackschuster Sep 28, 2022
fe786bf
chore: switch to eslint
zackschuster Sep 28, 2022
7060b06
templates: link to local compilations
zackschuster Sep 29, 2022
d10e208
build: migrate website compilation logic to gulpfile
zackschuster Sep 29, 2022
5a22310
lib: inline predicate logic
zackschuster Sep 29, 2022
91ee4f7
chore: fix Chessboard -> new Chessboard
zackschuster Sep 29, 2022
100ab34
build: go golfing
zackschuster Sep 29, 2022
7fa106a
lib: replace deepCopy with builtins
zackschuster Sep 29, 2022
7fcef8e
lib: greatly reduce piece element creation work
zackschuster Oct 3, 2022
d7d191b
lib: directly create elements & remove interpolateTemplate
zackschuster Oct 3, 2022
d7da36d
lib: fade out pieces when clearing
zackschuster Oct 3, 2022
ccf55c2
style: center spare pieces using flexbox
zackschuster Oct 3, 2022
99f83c8
examples: format code examples to take less space
zackschuster Oct 3, 2022
d38cd64
test: remove dead code
zackschuster Oct 4, 2022
ff6089f
lib: add isTouchDevice to exports
zackschuster Oct 4, 2022
d70c795
examples: fix board flip invocation
zackschuster Oct 4, 2022
b030f0b
examples: convert navlist to use details elements
zackschuster Oct 4, 2022
b84d360
lib: ship wikimedia piece svgs as built-in data urls
zackschuster Oct 4, 2022
95af686
website: update manifest link for developing locally
zackschuster Oct 4, 2022
b001b9e
lib: add jsdoc for chessboard config
zackschuster Oct 5, 2022
0efd731
templates: drop unused google font
zackschuster Oct 5, 2022
32ea685
lib: partially restore piece dragging
zackschuster Oct 5, 2022
17aa5fe
lib: switch to html5 drag-and-drop api
zackschuster Oct 6, 2022
a297361
lib: manage spare pieces with map
zackschuster Oct 6, 2022
028483f
lib: unify piece handling
zackschuster Oct 6, 2022
0a7c8e8
lib: make config handling more readable
zackschuster Oct 6, 2022
29cf0b6
lib: cleanup
zackschuster Oct 6, 2022
b0e00cb
lib: big bang cleanup
zackschuster Oct 6, 2022
d3cecf6
lib: fix animation speed & position statefulness
zackschuster Oct 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

dist/
node_modules/
website/examples/

website/*.html
website/css/chessboard.css
website/js/chessboard.js
website/examples/
.DS_Store

266 changes: 266 additions & 0 deletions Gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
import { writeFile, readFile, rm, mkdir } from 'node:fs/promises';

import gulp from 'gulp';
import kidif from 'kidif';
import mustache from 'mustache';
import sass from 'sass';
import { rollup } from 'rollup';
import { terser } from 'rollup-plugin-terser';

const { version } = JSON.parse(await readFile('./package.json', 'utf-8'));
const banner = `/** @preserve
* chessboard.js v${version}
* https://github.com/oakmac/chessboardjs/
*
* Copyright (c) 2019, Chris Oakman
* Released under the MIT license
* https://github.com/oakmac/chessboardjs/blob/master/LICENSE.md
*/`;

export const build = gulp.series(resetDistFolder, gulp.parallel(buildJs, buildCss), buildWebsite);
export const watch = () => gulp.watch(['data', 'examples', 'lib', 'templates', 'website/css', 'website/js'], { ignoreInitial: false }, build);
export default build;

async function resetDistFolder() {
await rm('./dist', { recursive: true, force: true });
await mkdir('./dist');
}

async function buildJs() {
const bundle = await rollup({ input: './lib/chessboard.js' });

const format = /** @type {import('rollup').ModuleFormat} */('iife');
const output = { banner, format, name: 'ChessboardJS', outro: `Object.assign(window, exports);` };

return Promise.all([
bundle.write({ file: `./dist/chessboard-${version}.js`, ...output }),
bundle.write({ file: `./dist/chessboard-${version}.min.js`, plugins: [terser()], ...output }),
]);
}

async function buildCss() {
return Promise.all([
writeFile(`./dist/chessboard-${version}.css`, banner + '\n' + sass.compile('lib/chessboard.css').css),
writeFile(`./dist/chessboard-${version}.min.css`, banner + '\n' + sass.compile('lib/chessboard.css', { style: 'compressed' }).css),
]);
}

async function buildWebsite() {
const chessboardJsScript = `<script src="../dist/chessboard-${version}.js"></script>`;
const docs = JSON.parse(await readFile('./data/docs.json', 'utf-8'));
const examples = /** @type {{ id: number, name: string, description: string, group: string, html: string, js: string, chessboardJsScript: string, includeChessJS?: boolean }[]} */(kidif('examples/*.example'));
const groups = ['Basic Usage', 'Config', 'Methods', 'Events', 'Integration'];
const examplesByGroup = /** @type {Record<string, typeof examples>} */({
[groups.at(0) ?? '']: [],
[groups.at(1) ?? '']: [],
[groups.at(2) ?? '']: [],
[groups.at(3) ?? '']: [],
[groups.at(4) ?? '']: [],
});

for (const example of examples) {
example.id = Number(example.id);
example.chessboardJsScript = chessboardJsScript;

if (!example.id) continue;
else if (example.id >= 5000) {
example.includeChessJS = true;
example.group = groups.at(4) ?? '';
}
else if (example.id >= 4000) example.group = groups.at(3) ?? '';
else if (example.id >= 3000) example.group = groups.at(2) ?? '';
else if (example.id >= 2000) example.group = groups.at(1) ?? '';
else if (example.id >= 1000) example.group = groups.at(0) ?? '';

examplesByGroup[example.group].push(example);
}

const encoding = 'utf-8';
const [
headTemplate,
docsTemplate,
downloadTemplate,
examplesTemplate,
homepageTemplate,
singleExampleTemplate,
licensePageTemplate,
headerTemplate,
footerTemplate,
] = await Promise.all([
readFile('templates/_head.mustache', encoding),
readFile('templates/docs.mustache', encoding),
readFile('templates/download.mustache', encoding),
readFile('templates/examples.mustache', encoding),
readFile('templates/homepage.mustache', encoding),
readFile('templates/single-example.mustache', encoding),
readFile('templates/license.mustache', encoding),
readFile('templates/_header.mustache', encoding),
readFile('templates/_footer.mustache', encoding),
]);

await rm('./website/examples', { recursive: true, force: true });
await mkdir('./website/examples');

await Promise.all([
writeFile('website/index.html', mustache.render(homepageTemplate, {
chessboardJsScript,
example2: `
const board2 = new Chessboard('board2', {
draggable: true,
dropOffBoard: 'trash',
sparePieces: true
})
document.getElementById('startBtn')
.onclick = () => board2.start()
document.getElementById('clearBtn')
.onclick = () => board2.clear()
`.trim().replace(/^\s{8}/mg, ''),
footer: footerTemplate,
head: mustache.render(headTemplate, { pageTitle: 'Homepage', version }),
version
})),

writeFile('website/examples.html', mustache.render(examplesTemplate, {
chessboardJsScript,
examplesJavaScript: buildExamplesJS(),
footer: footerTemplate,
head: mustache.render(headTemplate, { pageTitle: 'Examples', version }),
header: mustache.render(headerTemplate, { examplesActive: true, version }),
nav: groups.reduce((html, group, i) => {
const groupNum = i + 1;
html += `<details id="groupHeader-${groupNum}"><summary>${group}</summary><ul id="groupContainer-${groupNum}">`;

for (const example of examplesByGroup[group]) {
html += `<li id="exampleLink-${example.id}">${example.name}</id>`;
}

return (html += '</ul></details>');
}, ''),
version
})),

writeFile('website/docs.html', mustache.render(docsTemplate, {
configTableRows: docs.config.reduce(function (html, prop) {
if (typeof prop === 'string') return html;

html += `<tr id="config:${prop.name}">`; // table row
html += `<td><p><a href="docs.html#config:${prop.name}"><code class="js plain">${prop.name}</code></a></p><p class=property-type-7ae66>${buildTypeHTML(prop.type)}</p></td>`; // property and type
html += `<td class="center"><p>${prop.default || '<small>n/a</small>'}</p></td>`; // default
html += `<td>${buildDescriptionHTML(prop.desc)}</td>`; // description
html += `<td>${buildExamplesCellHTML(prop.examples)}</td>`; // examples

return html + '</tr>';
}, ''),
errorRows: docs.errors.reduce((html, error) => {
if (typeof error === 'string') return html;

html += `<tr id="errors:${error.id}">`; // table row
html += `<td class="center"><p><a href="docs.html#errors:${error.id}">${error.id}</a></p></td>`; // id
html += `<td><p>${error.desc}</p></td>`; // desc

// more information
if (error.fix) {
html += `<td>${Array.isArray(error.fix) ? error.fix.map(p => `<p>${p}</p>`).join('') : `<p>${error.fix}</p>`}</td>`;
} else {
html += '<td><small>n/a</small></td>';
}

return html + '</tr>';
}, ''),
methodTableRows: docs.methods.reduce((html, method) => {
if (typeof method === 'string') return html;

const nameNoParens = method.name.replace(/\(.+$/, '');

html += method.noId ? '<tr>' : `<tr id="methods:${nameNoParens}">`; // table row
html += `<td><p><a href="docs.html#methods:${nameNoParens}"><code class="js plain">${method.name}</code></a></p></td>`; // name
html += Array.isArray(method.args) ? `<td>${method.args.map((arg) => '<p>' + arg[1] + '</p>').join('')}</td>` : '<td><small>none</small></td>'; // args
html += `<td>${buildDescriptionHTML(method.desc)}</td>`; // description
html += `<td>${buildExamplesCellHTML(method.examples)}</td>`; // examples

return html + '</tr>';
}, ''),
footer: footerTemplate,
head: mustache.render(headTemplate, { pageTitle: 'Documentation', version }),
header: mustache.render(headerTemplate, { docsActive: true, version }),
version,
})),

writeFile('website/download.html', mustache.render(downloadTemplate, {
footer: footerTemplate,
head: mustache.render(headTemplate, { pageTitle: 'Download', version }),
header: mustache.render(headerTemplate, { downloadActive: true, version }),
version
})),

writeFile('website/license.html', mustache.render(licensePageTemplate, { version })),

Promise.all(examples.map(example => {
return writeFile(`website/examples/${example.id}.html`, mustache.render(singleExampleTemplate, { version, ...example }));
})),
]);

// -----------------------------------------------------------------------------
// HTML
// -----------------------------------------------------------------------------

function buildExamplesJS() {
let txt = 'window.CHESSBOARD_EXAMPLES = {}\n\n';

examples.forEach(function (ex) {
txt += 'CHESSBOARD_EXAMPLES["' + ex.id + '"] = {\n' +
' description: ' + JSON.stringify(ex.description) + ',\n' +
' html: ' + JSON.stringify(ex.html) + ',\n' +
' name: ' + JSON.stringify(ex.name) + ',\n' +
' jsStr: ' + JSON.stringify(ex.js) + ',\n' +
' jsFn: function () {\n' + ex.js + '\n }\n' +
'};\n\n';
});

return txt;
}

function buildTypeHTML(type) {
if (!Array.isArray(type)) {
type = [type];
}

let html = '';
for (var i = 0; i < type.length; i++) {
if (i !== 0) {
html += ' <small>or</small><br />';
}
html += type[i];
}

return html;
}

function buildDescriptionHTML(desc) {
if (!Array.isArray(desc)) {
desc = [desc];
}

let html = '';
desc.forEach(function (d) {
html += '<p>' + d + '</p>';
});

return html;
}

function buildExamplesCellHTML(examplesIds) {
if (!Array.isArray(examplesIds)) {
examplesIds = [examplesIds];
}

let html = '';
examplesIds.forEach(function (exampleId) {
const example = examples.find(x => x.id === exampleId);
if (!example) return;
html += '<p><a href="examples.html#' + exampleId + '">' + example.name + '</a></p>';
});

return html;
}
}
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# chessboard.js

chessboard.js is a JavaScript chessboard component. It depends on [jQuery] v3.4.1 (or higher).
chessboard.js is a _zero-dependency_ JavaScript chessboard component.

Please see [chessboardjs.com] for documentation and examples.

Expand Down Expand Up @@ -53,7 +53,6 @@ npm run website

[MIT License](LICENSE.md)

[jQuery]:https://jquery.com/
[chessboardjs.com]:https://chessboardjs.com
[chess.js]:https://github.com/jhlywa/chess.js
[Example 5000]:https://chessboardjs.com/examples#5000
Expand Down
13 changes: 4 additions & 9 deletions data/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -426,18 +426,13 @@
"errors":[
{
"id":1001,
"desc":"The first argument to <code class='js plain'>Chessboard()</code> cannot be an empty string.",
"fix":"The first argument to the <code class='js plain'>Chessboard()</code> constructor should be the id of a DOM element or a reference to a single DOM element."
"desc":"The first argument to <code class='js plain'>new Chessboard()</code> cannot be an empty string.",
"fix":"The first argument to the <code class='js plain'>new Chessboard()</code> constructor should be the id of a DOM element or a reference to a single DOM element."
},
{
"id":1003,
"desc":"The first argument to <code class='js plain'>Chessboard()</code> must be an ID or a single DOM node.",
"fix":"The first argument to the <code class='js plain'>Chessboard()</code> constructor should be the id of a DOM element or a reference to a single DOM element."
},
{
"id":1005,
"desc":"Unable to find a valid version of jQuery. Please include jQuery 1.8.3 or higher on the page.",
"fix":"Chessboard requires <a href='http://jquery.com/'>jQuery</a> version 1.8.3 or higher."
"desc":"The first argument to <code class='js plain'>new Chessboard()</code> must be an ID or a single DOM node.",
"fix":"The first argument to the <code class='js plain'>new Chessboard()</code> constructor should be the id of a DOM element or a reference to a single DOM element."
},
{
"id":2826,
Expand Down
10 changes: 5 additions & 5 deletions examples/1000-empty-board.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
1000

===== Name
Empty Board
Empty Board

===== Description
Chessboard.js initializes to an empty board with no second argument.

===== HTML
<div id="myBoard" style="width: 400px"></div>

===== JS
var board = Chessboard('myBoard')
var board = new Chessboard('myBoard')
12 changes: 6 additions & 6 deletions examples/1001-start-position.example
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
===== id
1001

===== Name
Start Position
===== Name
Start Position

===== Description
Pass <code class="js string">'start'</code> as the second argument to initialize
the board to the start position.

===== HTML
<div id="myBoard" style="width: 400px"></div>

===== JS
var board = Chessboard('myBoard', 'start')
var board = new Chessboard('myBoard', 'start')
12 changes: 6 additions & 6 deletions examples/1002-fen.example
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
===== id
1002

===== Name
FEN String
===== Name
FEN String

===== Description
Pass a <a href="docs.html#fen_string">FEN String</a> as the second argument to
initialize the board to a specific position.

===== HTML
<div id="myBoard" style="width: 400px"></div>

===== JS
var ruyLopez = 'r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R'
var board = Chessboard('myBoard', ruyLopez)
var board = new Chessboard('myBoard', ruyLopez)
Loading