aberdeen 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aberdeen.d.ts +573 -0
- package/dist/aberdeen.js +1756 -0
- package/dist/aberdeen.js.map +1 -0
- package/dist/prediction.d.ts +29 -0
- package/dist/prediction.js +110 -0
- package/dist/prediction.js.map +1 -0
- package/dist/route.d.ts +16 -0
- package/dist/route.js +119 -0
- package/dist/route.js.map +1 -0
- package/dist/transitions.d.ts +18 -0
- package/dist/transitions.js +67 -0
- package/dist/transitions.js.map +1 -0
- package/package.json +5 -2
- package/.github/workflows/deploy.yml +0 -43
- package/.vscode/launch.json +0 -23
- package/examples/input/index.html +0 -8
- package/examples/input/input.css +0 -56
- package/examples/input/input.js +0 -66
- package/examples/list/index.html +0 -7
- package/examples/list/list.js +0 -47
- package/examples/router/index.html +0 -8
- package/examples/router/page-home.js +0 -12
- package/examples/router/page-list.js +0 -35
- package/examples/router/page-settings.js +0 -6
- package/examples/router/router.js +0 -76
- package/examples/router/style.css +0 -88
- package/examples/tic-tac-toe/index.html +0 -8
- package/examples/tic-tac-toe/tic-tac-toe.css +0 -50
- package/examples/tic-tac-toe/tic-tac-toe.js +0 -90
- package/tests/_fakedom.js +0 -255
- package/tests/_init.js +0 -81
- package/tests/array.js +0 -109
- package/tests/binding.js +0 -106
- package/tests/browsers.js +0 -22
- package/tests/clean.js +0 -26
- package/tests/count.js +0 -105
- package/tests/create.js +0 -92
- package/tests/destroy.js +0 -270
- package/tests/dom.js +0 -219
- package/tests/errors.js +0 -114
- package/tests/immediate.js +0 -87
- package/tests/map.js +0 -76
- package/tests/objmap.js +0 -40
- package/tests/onEach.js +0 -392
- package/tests/prediction.js +0 -97
- package/tests/props.js +0 -49
- package/tests/schedule.js +0 -44
- package/tests/scope.js +0 -277
- package/tests/sort.js +0 -105
- package/tests/store.js +0 -254
- package/tsconfig.json +0 -67
package/examples/input/input.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import {node, Store, mount, text, observe} from '../../dist/aberdeen.js'
|
|
2
|
-
import {grow, shrink} from '../../dist/transitions.js'
|
|
3
|
-
|
|
4
|
-
const store = new Store({
|
|
5
|
-
name: 'John Doe',
|
|
6
|
-
age: 23,
|
|
7
|
-
gender: 'w',
|
|
8
|
-
active: false,
|
|
9
|
-
vehicle: 'car',
|
|
10
|
-
bio: 'John was born on an unknown date at an unknown location.\n\nHis main claim to fame is showing up out of nowhere.',
|
|
11
|
-
color: '#00fa9a',
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
mount(document.body, () => {
|
|
15
|
-
node('h2', () => {
|
|
16
|
-
text((store.get('name') || "Nobody") + "'s biography")
|
|
17
|
-
})
|
|
18
|
-
node('input', store.ref('name'))
|
|
19
|
-
node('input', {type: 'number', placeholder: 'Age'}, store.ref('age'))
|
|
20
|
-
node('label', () => {
|
|
21
|
-
node('input', {type: 'checkbox'}, store.ref('active'))
|
|
22
|
-
text('Active member')
|
|
23
|
-
})
|
|
24
|
-
observe(() => {
|
|
25
|
-
if (store.get('active')) node('input', {type: 'number', placeholder: 'Member id', create: grow, destroy: shrink}, store.ref('member_id'))
|
|
26
|
-
else store.delete('member_id')
|
|
27
|
-
})
|
|
28
|
-
node('select', () => {
|
|
29
|
-
node('option', {value: "m"}, "Man")
|
|
30
|
-
node('option', {value: "w"}, "Woman")
|
|
31
|
-
node('option', {value: "o"}, "Other..")
|
|
32
|
-
}, store.ref('gender'))
|
|
33
|
-
|
|
34
|
-
observe(() => {
|
|
35
|
-
if (store.get('gender')==='o') node('input', {placeholder: 'Specify gender', create: grow, destroy: shrink}, store.ref('gender_other'))
|
|
36
|
-
else store.delete('gender_other')
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
node('fieldset', () => {
|
|
40
|
-
const vehicles = {plane: 'Plane', car: 'Car', bike: 'Bicycle', none: 'None'}
|
|
41
|
-
for(let id in vehicles) {
|
|
42
|
-
node('label', () => {
|
|
43
|
-
node('input', {type: 'radio', name: 'vehicle', value: id}, store.ref('vehicle'))
|
|
44
|
-
text(vehicles[id])
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
node('textarea', {placeholder: "Biography"}, store.ref('bio'))
|
|
50
|
-
|
|
51
|
-
node('label', () => {
|
|
52
|
-
node('input', {type: 'color'}, store.ref('color'))
|
|
53
|
-
text('Favorite color')
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
node('input', {type: 'range', min: 50, max: 230}, store.ref('height'))
|
|
57
|
-
|
|
58
|
-
node('input', {type: 'date'}, store.ref('first_day'))
|
|
59
|
-
|
|
60
|
-
node('pre', () => {
|
|
61
|
-
text(JSON.stringify(store.get(), undefined, 4))
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
package/examples/list/index.html
DELETED
package/examples/list/list.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import {node, mount, Store, text} from '../../dist/aberdeen.js';
|
|
2
|
-
|
|
3
|
-
const items = new Store([])
|
|
4
|
-
const orderIndex = new Store(0)
|
|
5
|
-
const search = new Store("")
|
|
6
|
-
|
|
7
|
-
const makeWord = () => Math.random().toString(36).substring(2, 12).replace(/[0-9]+/g, '').replace(/^\w/, c => c.toUpperCase());
|
|
8
|
-
|
|
9
|
-
const COLUMN_NAMES= ["First name", "Last name", "Age", "Gender", "City"]
|
|
10
|
-
|
|
11
|
-
const addItems = (count) => {
|
|
12
|
-
for(let i=0; i<count; i++) {
|
|
13
|
-
items.push([
|
|
14
|
-
makeWord(),
|
|
15
|
-
makeWord(),
|
|
16
|
-
0 | (Math.random() * 99),
|
|
17
|
-
Math.random()<0.5 ? 'male' : 'female',
|
|
18
|
-
makeWord()
|
|
19
|
-
])
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
addItems(100)
|
|
24
|
-
|
|
25
|
-
mount(document.body, () => {
|
|
26
|
-
node('button', 'Add 10', {click: () => addItems(10)})
|
|
27
|
-
node('button', 'Add 100', {click: () => addItems(100)})
|
|
28
|
-
node('button', 'Add 1000', {click: () => addItems(1000)})
|
|
29
|
-
node('input', {placeholder: 'Search first name', autofocus: true}, search)
|
|
30
|
-
node('table.game', () => {
|
|
31
|
-
node('tr', () => {
|
|
32
|
-
for(let i=0; i<COLUMN_NAMES.length; i++) {
|
|
33
|
-
node('th', COLUMN_NAMES[i], {click: () => orderIndex.set(i)})
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
items.onEach(item => {
|
|
37
|
-
node('tr', () => {
|
|
38
|
-
item.onEach(field => {
|
|
39
|
-
node('td', () => {
|
|
40
|
-
text(field.get())
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
node('td', '⌫', {click: () => item.set()})
|
|
44
|
-
})
|
|
45
|
-
}, item => item.get(0).toLowerCase().startsWith(search.get().toLowerCase()) ? item.get(orderIndex.get()) : undefined)
|
|
46
|
-
})
|
|
47
|
-
})
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// import {node, mount, Store, text, router} from 'https://cdn.jsdelivr.net/npm/aberdeen/+esm';
|
|
2
|
-
import {node} from '../../dist/aberdeen.js';
|
|
3
|
-
|
|
4
|
-
export default function() {
|
|
5
|
-
node('h3', 'Welcome home!!')
|
|
6
|
-
node('ul', () => {
|
|
7
|
-
node('li', "Use the tabs to navigate between dynamically loaded pages. They will be pushed to the browser history.")
|
|
8
|
-
node('li', "The List tab demos the use of subpages and a live updating search query parameter.")
|
|
9
|
-
node('li', "Click 'Modal!' to open a modal. It can be closed using browser back. Closing it by clicking the background drop will also remove it from the stack.")
|
|
10
|
-
node('li', "Click 'LOGO' to go home. The stack will be unwound (going back) until the last time this page was visited or the first page in our session.")
|
|
11
|
-
})
|
|
12
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// import {node, mount, Store, text, router} from 'https://cdn.jsdelivr.net/npm/aberdeen/+esm';
|
|
2
|
-
import {node, prop} from '../../dist/aberdeen.js';
|
|
3
|
-
import {route} from "../../dist/route.js";
|
|
4
|
-
|
|
5
|
-
const words = ['butterfly', 'orchestra', 'whisper', 'mountain', 'zebra', 'chocolate', 'umbrella', 'lighthouse', 'rainbow', 'dragon', 'bicycle', 'galaxy', 'penguin', 'tornado', 'waterfall', 'cinnamon', 'compass', 'firefly', 'carousel', 'telescope'];
|
|
6
|
-
|
|
7
|
-
export default function() {
|
|
8
|
-
node('input', route.makeRef('search', 'filter'), {placeholder: 'Filter'})
|
|
9
|
-
|
|
10
|
-
node('.columns', () => {
|
|
11
|
-
|
|
12
|
-
// The list of words
|
|
13
|
-
node('nav.vertical', () => {
|
|
14
|
-
for(let word of words) {
|
|
15
|
-
node('button', word, {click: () => route.set('p', 1, word)}, () => {
|
|
16
|
-
prop('class', {active: route.get('p', 1) === word})
|
|
17
|
-
prop('style', {display: word.indexOf(route.get('search', 'filter') || '') >= 0 ? '' : 'none' })
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
// The detail view for the selected word
|
|
23
|
-
node('section', () => {
|
|
24
|
-
const word = route.get('p', 1)
|
|
25
|
-
if (!word) return
|
|
26
|
-
if (words.indexOf(word) < 0) {
|
|
27
|
-
// Word specified in URL is not in our list. Go back to list without selection.
|
|
28
|
-
route.merge({mode: 'back', p: {1: undefined}})
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
node('h2', word)
|
|
32
|
-
node('p', `This word has ${word.length} letters.`)
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// import {node, mount, Store, text, router} from 'https://cdn.jsdelivr.net/npm/aberdeen/+esm';
|
|
2
|
-
import {node, mount, Store, text, observe, prop} from '../../dist/aberdeen.js';
|
|
3
|
-
import {route} from "../../dist/route.js";
|
|
4
|
-
import {grow, shrink} from "../../dist/transitions.js"
|
|
5
|
-
|
|
6
|
-
// This is not something you'd normally do: when opening this example from a file
|
|
7
|
-
// (instead of serving it from a well-configured backend), fake the initial path
|
|
8
|
-
// as if it were `/`.
|
|
9
|
-
if (route.get('p').indexOf('router') >= 0) route.set({path: '/'})
|
|
10
|
-
|
|
11
|
-
// Load modules on-demand
|
|
12
|
-
const modules = new Store({})
|
|
13
|
-
function loadModule(name) {
|
|
14
|
-
const module = modules.get(name)
|
|
15
|
-
if (module==null) backgroundLoadModule(name)
|
|
16
|
-
return module
|
|
17
|
-
}
|
|
18
|
-
async function backgroundLoadModule(name) {
|
|
19
|
-
let module
|
|
20
|
-
try {
|
|
21
|
-
module = await import(`./page-${name||'home'}.js`)
|
|
22
|
-
} catch(e) {
|
|
23
|
-
console.error(e)
|
|
24
|
-
module = false
|
|
25
|
-
}
|
|
26
|
-
modules.set(name, module)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Are main page component
|
|
30
|
-
function drawTemplate() {
|
|
31
|
-
node('header', () => {
|
|
32
|
-
node('nav', () => {
|
|
33
|
-
node('button.no-line.logo', 'LOGO', {click: () => route.set({path: '/'})})
|
|
34
|
-
// Draw the top navigation bar
|
|
35
|
-
const menu = {"": 'Home', settings: 'Settings', list: 'List'}
|
|
36
|
-
for(const [id, label] of Object.entries(menu)) {
|
|
37
|
-
node('button', label, {click: () => route.set({p: [id]})}, () => {
|
|
38
|
-
prop('class', {active: route.get('p', 0) === id})
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
node('div', {style: {flex: 1}})
|
|
42
|
-
node('button.no-line', 'Modal!', {click: () => route.set('state', 'modal', 'home')})
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
node('main', () => {
|
|
47
|
-
let module = loadModule(route.get('p', 0))
|
|
48
|
-
if (module) module.default()
|
|
49
|
-
else if (module===false) node('p', 'No such page!')
|
|
50
|
-
else node('p', 'Loading...')
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
node("footer", () => {
|
|
54
|
-
route.dump()
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
observe(() => {
|
|
58
|
-
let modal = route.get('state', 'modal');
|
|
59
|
-
if (!modal) return
|
|
60
|
-
node('.modal-bg', () => {
|
|
61
|
-
node('.modal', () => {
|
|
62
|
-
let module = loadModule(modal);
|
|
63
|
-
if (module) module.default()
|
|
64
|
-
else if (module===false) node('p', 'No such modal!')
|
|
65
|
-
else node('p', 'Loading...')
|
|
66
|
-
})
|
|
67
|
-
}, {
|
|
68
|
-
click: function(e) {if (e.target===this) route.merge({mode: 'back', state: {modal: undefined}})},
|
|
69
|
-
create: grow,
|
|
70
|
-
destroy: shrink,
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
mount(document.body, drawTemplate)
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
body, html {
|
|
2
|
-
padding: 0;
|
|
3
|
-
margin: 0;
|
|
4
|
-
}
|
|
5
|
-
* {
|
|
6
|
-
box-sizing: border-box;
|
|
7
|
-
}
|
|
8
|
-
main {
|
|
9
|
-
padding: 20px;
|
|
10
|
-
}
|
|
11
|
-
.columns {
|
|
12
|
-
display: flex;
|
|
13
|
-
gap: 20px;
|
|
14
|
-
}
|
|
15
|
-
nav {
|
|
16
|
-
display: flex;
|
|
17
|
-
gap: 10px;
|
|
18
|
-
}
|
|
19
|
-
nav.vertical {
|
|
20
|
-
flex-direction: column;
|
|
21
|
-
gap: 5px;
|
|
22
|
-
}
|
|
23
|
-
button {
|
|
24
|
-
margin-top: 10px;
|
|
25
|
-
border: 0;
|
|
26
|
-
background-color: transparent;
|
|
27
|
-
border-bottom: 2px solid #ccc;
|
|
28
|
-
cursor: pointer;
|
|
29
|
-
}
|
|
30
|
-
button.no-line {
|
|
31
|
-
border: 0;
|
|
32
|
-
}
|
|
33
|
-
nav.vertical button {
|
|
34
|
-
border-right: 2px solid #ccc;
|
|
35
|
-
border-bottom: 0;
|
|
36
|
-
}
|
|
37
|
-
button.active {
|
|
38
|
-
border-color: #2150ac !important;
|
|
39
|
-
}
|
|
40
|
-
header {
|
|
41
|
-
display: fixed;
|
|
42
|
-
top: 0;
|
|
43
|
-
left: 0;
|
|
44
|
-
right: 0;
|
|
45
|
-
padding: 10px 20px;
|
|
46
|
-
box-shadow: 0 0 10px black;
|
|
47
|
-
}
|
|
48
|
-
.modal-bg {
|
|
49
|
-
position: fixed;
|
|
50
|
-
top: 0;
|
|
51
|
-
left: 0;
|
|
52
|
-
width: 100%;
|
|
53
|
-
height: 100%;
|
|
54
|
-
background-color: rgba(0, 0, 0, 0.5);
|
|
55
|
-
display: flex;
|
|
56
|
-
justify-content: center;
|
|
57
|
-
align-items: center;
|
|
58
|
-
z-index: 1000;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.modal {
|
|
62
|
-
background-color: white;
|
|
63
|
-
padding: 20px;
|
|
64
|
-
border-radius: 8px;
|
|
65
|
-
max-width: 500px;
|
|
66
|
-
width: 90%;
|
|
67
|
-
position: relative;
|
|
68
|
-
z-index: 1001;
|
|
69
|
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.logo {
|
|
73
|
-
font-weight: bold;
|
|
74
|
-
text-shadow: 0 0 4px #2150ac;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
footer {
|
|
78
|
-
color: #888;
|
|
79
|
-
display: fixed;
|
|
80
|
-
left: 0;
|
|
81
|
-
bottom: 0;
|
|
82
|
-
right: 0;
|
|
83
|
-
padding: 10px 20px;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
footer ul {
|
|
87
|
-
margin: 0;
|
|
88
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
body {
|
|
2
|
-
font: 14px "Century Gothic", Futura, sans-serif;
|
|
3
|
-
margin: 20px;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
ol, ul {
|
|
7
|
-
padding-left: 30px;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.board-row:after {
|
|
11
|
-
clear: both;
|
|
12
|
-
content: "";
|
|
13
|
-
display: table;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.status {
|
|
17
|
-
margin-bottom: 10px;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.square {
|
|
21
|
-
background: #fff;
|
|
22
|
-
border: 1px solid #999;
|
|
23
|
-
float: left;
|
|
24
|
-
font-size: 24px;
|
|
25
|
-
font-weight: bold;
|
|
26
|
-
line-height: 34px;
|
|
27
|
-
height: 34px;
|
|
28
|
-
margin-right: -1px;
|
|
29
|
-
margin-top: -1px;
|
|
30
|
-
padding: 0;
|
|
31
|
-
text-align: center;
|
|
32
|
-
width: 34px;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.square:focus {
|
|
36
|
-
outline: none;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.kbd-navigation .square:focus {
|
|
40
|
-
background: #ddd;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.game {
|
|
44
|
-
display: flex;
|
|
45
|
-
flex-direction: row;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.game-info {
|
|
49
|
-
margin-left: 20px;
|
|
50
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import {node, prop, mount, Store, text} from '../../dist/aberdeen.js';
|
|
2
|
-
|
|
3
|
-
const store = new Store({
|
|
4
|
-
squares: [],
|
|
5
|
-
turn: 'X',
|
|
6
|
-
history: [{}],
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
const drawSquare = (position) => {
|
|
10
|
-
node('button.square', () => {
|
|
11
|
-
let value = store.get('squares', position)
|
|
12
|
-
if (value) text(value)
|
|
13
|
-
else prop('click', () => fillSquare(position))
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const drawBoard = () => {
|
|
18
|
-
for(let y=0; y<3; y++) {
|
|
19
|
-
node('div.board-row', () => {
|
|
20
|
-
for(let x=0; x<3; x++) {
|
|
21
|
-
drawSquare(y*3 + x)
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const drawInfo = () => {
|
|
28
|
-
node('div', () => {
|
|
29
|
-
let winner = calculateWinner(store.get('squares'))
|
|
30
|
-
if (winner) {
|
|
31
|
-
text(`Winner: ${winner}`)
|
|
32
|
-
} else {
|
|
33
|
-
text(`Next player: ${store.get('turn')}`)
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
node('ol', () => {
|
|
37
|
-
store.onEach('history', item => {
|
|
38
|
-
node('li', () => {
|
|
39
|
-
node('button', () => {
|
|
40
|
-
text(item.index() ? `Go to move ${item.index()}` : `Go to game start`)
|
|
41
|
-
prop('click', () => {
|
|
42
|
-
store.set('historyPos', item.index())
|
|
43
|
-
store.set('squares', item.get())
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const fillSquare = (position) => {
|
|
52
|
-
// If there's already a winner, don't allow a new square to be filled
|
|
53
|
-
if (calculateWinner(store.get('squares'))) return
|
|
54
|
-
|
|
55
|
-
// Fill the square
|
|
56
|
-
store.set('squares', position, store.get('turn'))
|
|
57
|
-
|
|
58
|
-
// Next player's turn
|
|
59
|
-
store.set('turn', store.get('turn')==='X' ? 'O' : 'X')
|
|
60
|
-
|
|
61
|
-
if (store.get('historyPos') != null) {
|
|
62
|
-
// Truncate everything after history pos
|
|
63
|
-
store.set('history', store.get('history').slice(0,store.get('historyPos')+1))
|
|
64
|
-
// Stop 'time traveling'
|
|
65
|
-
store.delete('historyPos')
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Append the current squares-state to the history array
|
|
69
|
-
store.push('history', store.get('squares'))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const calculateWinner = (squares) => {
|
|
73
|
-
const lines = [
|
|
74
|
-
[0, 1, 2], [3, 4, 5], [6, 7, 8], // horizontal
|
|
75
|
-
[0, 3, 6], [1, 4, 7], [2, 5, 8], // vertical
|
|
76
|
-
[0, 4, 8], [2, 4, 6] // diagonal
|
|
77
|
-
];
|
|
78
|
-
for (const [a, b, c] of lines) {
|
|
79
|
-
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
|
|
80
|
-
return squares[a];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
mount(document.body, () => {
|
|
86
|
-
node('div.game', () => {
|
|
87
|
-
node('div.game-board', drawBoard)
|
|
88
|
-
node('div.game-info', drawInfo)
|
|
89
|
-
})
|
|
90
|
-
})
|