add solution word to client

This commit is contained in:
Jonas Weinz 2021-06-24 12:10:59 +02:00
parent aca27cdae2
commit c0f5f3f785
11 changed files with 698258 additions and 640 deletions

View File

@ -1,579 +1,27 @@
import { html, css, LitElement, unsafeCSS } from 'https://unpkg.com/lit-element/lit-element.js?module'; import { html, css, LitElement, unsafeCSS } from 'https://unpkg.com/lit-element/lit-element.js?module';
import './infoBox.js' import './infoBox.js'
import {GridElement, GridLetter, GridBox, gridType} from './gridBoxes.js'
const gridType = { import './solutionBox.js'
EMPTY: 0,
HINT: 1,
LETTER: 2
};
export class GridElement {
constructor(x, y, grid, type) {
this.x = x;
this.y = y;
this.type = type
this.grid = grid;
this.gridLetter = null;
this.hintBox = null;
this.emptyBox = null;
}
setGridLetter(gridLetter) {
this.gridLetter = gridLetter;
}
getGridLetter() {
return this.gridLetter;
}
setHintBox(hintBox) {
this.hintBox = hintBox;
}
getHintBox() {
return this.hintBox;
}
setEmptyBox(emptyBox) {
this.emptyBox = emptyBox;
}
getEmptyBox() {
return this.emptyBox;
}
getGridType() {
return this.type;
}
}
export class GridBox extends LitElement {
static get styles() {
return css`
input[type='text'] {
width: 1.5em;
height: 1.5em;
text-align: center;
border-style: solid;
border-color: black;
border-width: 0.2 em;
background-color: black;
outline: none;
color: transparent;
text-shadow: 0 0 1 black;
font-size: 2em;
user-select: none;
text-transform: uppercase;
}
`;
}
static get properties() {
return {
x: { type: Number },
y: { type: Number },
grid_id: { type: String }
}
}
constructor() {
super();
this.x = 0;
this.y = 0;
this.gridElement = null;
this.grid_id = null;
this.crosswordGrid = null;
this.isRevealed = false;
}
update(props) {
super.update();
if (props.has("grid_id")) {
this.updateGridElement();
}
}
updateGridElement() {
if (this.grid_id != null) {
this.crosswordGrid = document.getElementById(this.grid_id);
this.gridElement = this.crosswordGrid.getGridElement(this.x, this.y);
this.gridElement.setEmptyBox(this);
}
}
render() {
return html`<input type="text" id="${this.x}-${this.y}" maxlength="1" value="" disabled></input>`;
}
}
customElements.define('grid-box', GridBox);
export class GridLetter extends GridBox {
static get styles() {
return css`
.letter {
display: grid;
}
input[type='text'] {
grid-column: 1;
grid-row: 1;
width: 1.5em;
height: 1.5em;
text-align: center;
border-style: solid;
border-width: 0.2 em;
border-color: black;
outline: none;
color: black;
font-size: 2em;
user-select: none;
text-transform: capitalize;
}
input[type='text']:focus {
grid-column: 1;
grid-row: 1;
background-color: LightYellow;
border-color: Yellow;
}
input[type='text']:read-only {
grid-column: 1;
grid-row: 1;
background-color: LightGreen;
}
input[type='text']:selection {
grid-column: 1;
grid-row: 1;
background: transparent;
}
.circle {
margin: auto;
text-align: left;
vertical-align: bottom;
bottom: 0;
position: relative;
z-index: 0;
pointer-events: none;
grid-column: 1;
grid-row: 1;
border: 0.1em solid lightGray;
color: black;
height: 80%;
width: 80%;
border-radius:50%;
-moz-border-radius:50%;
-webkit-border-radius:50%;
background: transparent;
}
`;
}
static get properties() {
return {
letter: { type: String },
x: { type: Number },
y: { type: Number },
grid_id: { type: String }
}
}
constructor() {
super();
this._value = '';
this.is_focused = false;
this.firstKeydownOver = true;
this.solutionIndex = -1;
this.lastFocusedX = -1;
this.lastFocusedY = -1;
this.updateGridElement();
}
set value(value) {
const oldValue = this.value;
console.log("update value to: " + value);
this._value = value;
this.requestUpdate('value', oldValue);
}
get value() {
return this._value;
}
onInput(e) {
if (!this.is_focused) {
e.handled = true;
return;
}
//if (this.key_delay){
// var el = this.shadowRoot.getElementById(`${this.x}-${this.y}`);
// el.value = this.value;
// el.select();
// e.handled = true;
// return;
//}
var oldVal = this.value;
this.value = e.target.value
if (this.revealed) {
this.value = oldVal;
this.crosswordGrid.focusNextCell(this.x, this.y);
return
}
if (this.value.length > 1) {
this.value = str.slice(0);
e.target.value = this.value
e.handled = true;
}
if (!e.handled && this.value === " ") {
this.value = "";
e.target.value = "";
e.handled = true;
}
if (!e.handled && this.value.length === 0) {
this.crosswordGrid.focusPrevCell(this.x, this.y, this.lastFocusedX, this.lastFocusedY);
e.handled = true;
}
else if (!e.handled && this.value.length === 1) {
this.crosswordGrid.focusNextCell(this.x, this.y);
e.handled = true;
}
else {
this.focus();
}
if (oldVal != this.value) {
this.crosswordGrid.sendMessage({
'type': 'update',
'x': this.x,
'y': this.y,
'letter': this.value
})
}
if (!e.handled) {
e.target.select();
e.handled = true;
}
}
onKeyup(e) {
if (!this.firstKeydownOver) {
e.handled = true;
return;
}
var key = e.key;
if (this.value.length == 0 || this.revealed) {
if (key === 'Backspace' || key === 'Delete') {
this.crosswordGrid.focusPrevCell(this.x, this.y, this.lastFocusedX, this.lastFocus);
e.handled = true;
return
}
else if (this.revealed) {
this.crosswordGrid.focusNextCell(this.x, this.y);
e.handled = true;
return
}
}
}
onKeydown(e) {
this.firstKeydownOver = true;
if (!this.is_focused) {
e.handled = true;
return;
}
var key = e.key;
console.log(key);
// check arrow keys
if (key === "ArrowLeft") {
// left
this.crosswordGrid.focusPrevCellHorizontal(this.x, this.y);
e.handled = true;
return;
}
else if (key === "ArrowUp") {
// up
this.crosswordGrid.focusPrevCellVertical(this.x, this.y);
e.handled = true;
return;
}
else if (key === "ArrowRight") {
//right
this.crosswordGrid.focusNextCellHorizontal(this.x, this.y);
e.handled = true;
return;
}
else if (key === "ArrowDown") {
//down
this.crosswordGrid.focusNextCellVertical(this.x, this.y);
e.handled = true;
return;
}
e.target.select();
}
onFocus(e) {
e.target.select();
this.is_focused = true;
this.crosswordGrid.updateHints(this.x, this.y);
}
focus(lastX = -1, lastY = -1) {
if (lastX > 0 && lastY > 0) {
this.lastFocusedX = lastX;
this.lastFocusedY = lastY;
}
var element = this.shadowRoot.getElementById(`${this.x}-${this.y}`);
this.firstKeydownOver = false;
element.focus();
}
blur() {
var element = this.shadowRoot.getElementById(`${this.x}-${this.y}`);
element.blur();
this.is_focused = false;
}
updateGridElement() {
super.updateGridElement()
if (this.grid_id != null) {
this.gridElement.setGridLetter(this);
this.solutionIndex = this.crosswordGrid.getSolutionIndex(this.x, this.y)
}
}
updateLetter(letter, revealed) {
this.value = letter;
if (revealed) {
console.log("rec")
this.shadowRoot.getElementById(`${this.x}-${this.y}`).readOnly = true;
this.blur();
this.revealed = true;
}
}
render() {
this.updateGridElement();
var solutionOverlay = html``;
if (this.solutionIndex >= 0) {
solutionOverlay = html`
<div class="circle">${this.solutionIndex}</div>
`;
}
return html`
<div class="letter">
<input type="text" id="${this.x}-${this.y}" value="${this.value}" @input=${this.onInput} @focus=${this.onFocus} @keydown=${this.onKeydown} @keyup=${this.onKeyup} autocomplete="off"></input>
${solutionOverlay}
</div>
`;
}
}
customElements.define('grid-letter', GridLetter);
export class HintBox extends GridBox {
static get styles() {
return css`
.hint {
display: grid;
}
.triangle-down {
position: relative;
top: 3em;
left: 1em;
z-index: 10;
grid-column: 1;
grid-row: 1;
width: 0;
height: 0;
border-left: 0.75em solid transparent;
border-right: 0.75em solid transparent;
border-top: 1.3em solid #555;
}
.triangle-right {
position: relative;
top: 1em;
left: 3em;
z-index: 10;
grid-column: 1;
grid-row: 1;
width: 0;
height: 0;
border-top: 0.75em solid transparent;
border-bottom: 0.75em solid transparent;
border-left: 1.3em solid #555;
}
input[type='text']{
grid-column: 1;
grid-row: 1;
width: 1.5em;
height: 1.5em;
text-align: center;
border-style: solid;
border-color: black;
border-width: 0.2 em;
background-color: #555;
outline: none;
color: white;
text-shadow: 0 0 1 black;
font-size: 2em;
user-select: none;
text-transform: uppercase;
}
`;
}
static get properties() {
return {
x: { type: Number },
y: { type: Number },
grid_id: { type: String },
horizontal_hint: { type: String },
vertical_hint: { type: String },
hint_id: { type: String }
}
}
constructor() {
super();
this.vertical_hint = "";
this.horizontal_hint = "";
this.hint_id = 0;
}
getVerticalHint() {
return this.vertical_hint;
}
getHorizontalHint() {
return this.horizontal_hint;
}
hasVerticalHint() {
return this.vertical_hint.length > 0;
}
hasHorizontalHint() {
return this.horizontal_hint.length > 0;
}
updateGridElement() {
super.updateGridElement()
if (this.grid_id != null) {
this.gridElement.setHintBox(this);
}
}
render() {
this.updateGridElement();
var verticalArrow = html``;
var horizontalArrow = html``;
if (this.hasVerticalHint()) {
verticalArrow = html`<div class="triangle-down"></div>`;
}
if (this.hasHorizontalHint()) {
horizontalArrow = html`<div class="triangle-right"></div>`;
}
return html`
<div class="hint">
<input type="text" id="${this.x}-${this.y}" value="${this.hint_id}" disabled></input>
${verticalArrow}
${horizontalArrow}
</div>
`;
}
}
customElements.define('hint-box', HintBox);
export class CrosswordGrid extends LitElement { export class CrosswordGrid extends LitElement {
static get styles() { static get styles() {
return css` return css`
body { body {
background-color: black; background-color: black;
text-align: center;
} }
table { table {
position: relative; position: relative;
top: 5em; top: 7em;
border-spacing: 0px; border-spacing: 0px;
border-style: solid; border-style: solid;
border-color: black; border-color: black;
border-width: 1em; border-width: 1em;
text-align: center;
margin-left: auto;
margin-right: auto;
} }
td { td {
@ -588,7 +36,7 @@ export class CrosswordGrid extends LitElement {
width: { type: Number }, width: { type: Number },
height: { type: Number }, height: { type: Number },
infobox_id: { type: String } infobox_id: { type: String }
} };
} }
constructor() { constructor() {
@ -607,7 +55,17 @@ export class CrosswordGrid extends LitElement {
this.serverConnection = null; this.serverConnection = null;
this.solution_locations = null; this.solution_locations = [];
this.solutionBox = null;
this.gridCount = 0;
}
setSolutionBox(solBox) {
this.solutionBox = solBox;
if (self.solution_locations)
solBox.length = self.solution_locations.length;
} }
getSolutionIndex(x, y) { getSolutionIndex(x, y) {
@ -626,9 +84,15 @@ export class CrosswordGrid extends LitElement {
this.grid = []; this.grid = [];
const width = json_grid.w; const width = json_grid.w;
const height = json_grid.h; const height = json_grid.h;
this.gridCount += 1;
this.solution_locations = json_grid.solution this.solution_locations = json_grid.solution
if (this.solutionBox)
this.solutionBox.length = this.solution_locations.length;
this.solutionBox.requestUpdate();
console.log("update box");
var y; var y;
var x; var x;
@ -659,6 +123,10 @@ export class CrosswordGrid extends LitElement {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.requestUpdate();
} }
focusNextCellHorizontal(x, y) { focusNextCellHorizontal(x, y) {
@ -813,11 +281,11 @@ export class CrosswordGrid extends LitElement {
renderCell(el) { renderCell(el) {
switch (el.type) { switch (el.type) {
case gridType.LETTER: { case gridType.LETTER: {
return html`<grid-letter grid_id='${this.id}' x='${el.x}' y='${el.y}' ></grid-letter>`; return html`<grid-letter grid_id='${this.id}' gridCount='${this.gridCount}' x='${el.x}' y='${el.y}' ></grid-letter>`;
} }
case gridType.EMPTY: { case gridType.EMPTY: {
return html`<grid-box grid_id='${this.id}' x='${el.x}' y='${el.y}' ></grid-box>`; return html`<grid-box grid_id='${this.id}' gridCount='${this.gridCount}' x='${el.x}' y='${el.y}' ></grid-box>`;
} }
case gridType.HINT: { case gridType.HINT: {
@ -834,7 +302,7 @@ export class CrosswordGrid extends LitElement {
this.num_hints += 1; this.num_hints += 1;
return html` return html`
<hint-box grid_id='${this.id}' x='${el.x}' y='${el.y}' horizontal_hint='${horizontal_hint}' vertical_hint='${vertical_hint}' hint_id=${this.num_hints}></hint-box> <hint-box grid_id='${this.id}' gridCount='${this.gridCount}' x='${el.x}' y='${el.y}' horizontal_hint='${horizontal_hint}' vertical_hint='${vertical_hint}' hint_id=${this.num_hints}></hint-box>
`; `;
} }
@ -847,6 +315,13 @@ export class CrosswordGrid extends LitElement {
console.error("received bad message"); console.error("received bad message");
} }
cell.getGridLetter().updateLetter(letter, revealed) cell.getGridLetter().updateLetter(letter, revealed)
if (cell.getGridLetter().solutionIndex >= 0) {
this.setSolutionLetter(
cell.getGridLetter().solutionIndex,
letter,
revealed
);
}
} }
registerServerConnection(connection) { registerServerConnection(connection) {
@ -860,13 +335,32 @@ export class CrosswordGrid extends LitElement {
this.serverConnection.sendMessage(msg) this.serverConnection.sendMessage(msg)
} }
setSolutionLetter(solutionNumber, letter, revealed = false){
this.solutionBox.setLetter(solutionNumber, letter, revealed);
const x = this.solution_locations[solutionNumber][1];
const y = this.solution_locations[solutionNumber][0];
console.log("update solution letter");
this.grid[y][x].getGridLetter().value = letter;
}
render() { render() {
this.num_hints = 0; this.num_hints = 0;
console.log("refreshing grid"); console.log("refreshing grid");
var solution_length = 0;
if (this.solution_locations){
solution_length = this.solution_locations.length;
}
return html` return html`
<table> <table>
${this.grid.map((row) => html`<tr>${row.map((el) => html`<td>${this.renderCell(el)}</td>`)}</tr>`)} ${this.grid.map((row) => html`<tr>${row.map((el) => html`<td>${this.renderCell(el)}</td>`)}</tr>`)}
</table> </table>
<solution-box id="solution-box", grid_id="${this.id}", length="${solution_length}" ></solution-box>
`; `;
} }
} }

569
js_client/gridBoxes.js Normal file
View File

@ -0,0 +1,569 @@
import { html, css, LitElement, unsafeCSS } from 'https://unpkg.com/lit-element/lit-element.js?module';
import './infoBox.js'
export const gridType = {
EMPTY: 0,
HINT: 1,
LETTER: 2
};
export class GridElement {
constructor(x, y, grid, type) {
this.x = x;
this.y = y;
this.type = type
this.grid = grid;
this.gridLetter = null;
this.hintBox = null;
this.emptyBox = null;
}
setGridLetter(gridLetter) {
this.gridLetter = gridLetter;
}
getGridLetter() {
return this.gridLetter;
}
setHintBox(hintBox) {
this.hintBox = hintBox;
}
getHintBox() {
return this.hintBox;
}
setEmptyBox(emptyBox) {
this.emptyBox = emptyBox;
}
getEmptyBox() {
return this.emptyBox;
}
getGridType() {
return this.type;
}
}
export class GridBox extends LitElement {
static get styles() {
return css`
input[type='text'] {
width: 1.5em;
height: 1.5em;
text-align: center;
border-style: solid;
border-color: black;
border-width: 0.2 em;
background-color: black;
outline: none;
color: transparent;
text-shadow: 0 0 1 black;
font-size: 2em;
user-select: none;
text-transform: uppercase;
}
`;
}
static get properties() {
return {
x: { type: Number },
y: { type: Number },
gridCount: {type: Number},
grid_id: { type: String }
}
}
constructor() {
super();
this.x = 0;
this.y = 0;
this.gridElement = null;
this.grid_id = null;
this.crosswordGrid = null;
this.isRevealed = false;
this.gridCount = 0;
}
update(props) {
super.update(props);
if (props.has("grid_id") || props.has("gridCount")) {
this.updateGridElement();
}
}
updateGridElement() {
if (this.grid_id != null) {
this.crosswordGrid = document.getElementById(this.grid_id);
this.gridElement = this.crosswordGrid.getGridElement(this.x, this.y);
this.gridElement.setEmptyBox(this);
}
}
render() {
return html`<input type="text" id="${this.x}-${this.y}" maxlength="1" value="" disabled></input>`;
}
}
customElements.define('grid-box', GridBox);
export class GridLetter extends GridBox {
static get styles() {
return css`
.letter {
display: grid;
}
input[type='text'] {
grid-column: 1;
grid-row: 1;
width: 1.5em;
height: 1.5em;
text-align: center;
border-style: solid;
border-width: 0.2 em;
border-color: black;
outline: none;
color: black;
font-size: 2em;
user-select: none;
text-transform: capitalize;
}
input[type='text']:focus {
grid-column: 1;
grid-row: 1;
background-color: LightYellow;
border-color: Yellow;
}
input[type='text']:read-only {
grid-column: 1;
grid-row: 1;
background-color: LightGreen;
}
input[type='text']:selection {
grid-column: 1;
grid-row: 1;
background: transparent;
}
.circle {
margin: auto;
text-align: left;
vertical-align: bottom;
bottom: 0;
position: relative;
z-index: 0;
pointer-events: none;
grid-column: 1;
grid-row: 1;
border: 0.1em solid grey;
color: black;
height: 80%;
width: 80%;
border-radius:50%;
-moz-border-radius:50%;
-webkit-border-radius:50%;
background: transparent;
}
`;
}
static get properties() {
return {
letter: { type: String },
x: { type: Number },
y: { type: Number },
gridCount: {type: Number},
grid_id: { type: String }
}
}
constructor() {
super();
this._value = '';
this.is_focused = false;
this.firstKeydownOver = true;
this.solutionIndex = -1;
this.lastFocusedX = -1;
this.lastFocusedY = -1;
this.updateGridElement();
}
set value(value) {
const oldValue = this.value;
console.log("update value to: " + value);
this._value = value;
this.requestUpdate('value', oldValue);
}
get value() {
return this._value;
}
onInput(e) {
if (!this.is_focused) {
e.handled = true;
return;
}
//if (this.key_delay){
// var el = this.shadowRoot.getElementById(`${this.x}-${this.y}`);
// el.value = this.value;
// el.select();
// e.handled = true;
// return;
//}
var oldVal = this.value;
this.value = e.target.value
if (this.revealed) {
this.value = oldVal;
this.crosswordGrid.focusNextCell(this.x, this.y);
return
}
if (this.value.length > 1) {
this.value = this.value[0];
e.target.value = this.value
e.handled = true;
}
if (!e.handled && this.value === " ") {
this.value = "";
e.target.value = "";
e.handled = true;
}
if (!e.handled && this.value.length === 0) {
this.crosswordGrid.focusPrevCell(this.x, this.y, this.lastFocusedX, this.lastFocusedY);
e.handled = true;
}
else if (!e.handled && this.value.length === 1) {
this.crosswordGrid.focusNextCell(this.x, this.y);
e.handled = true;
}
else {
this.focus();
}
if (oldVal != this.value) {
if (this.solutionIndex >= 0) {
this.crosswordGrid.setSolutionLetter(this.solutionIndex, this.value);
}
this.crosswordGrid.sendMessage({
'type': 'update',
'x': this.x,
'y': this.y,
'letter': this.value
})
}
if (!e.handled) {
e.target.select();
e.handled = true;
}
}
onKeyup(e) {
if (!this.firstKeydownOver) {
e.handled = true;
return;
}
var key = e.key;
if (this.value.length == 0 || this.revealed) {
if (key === 'Backspace' || key === 'Delete') {
this.crosswordGrid.focusPrevCell(this.x, this.y, this.lastFocusedX, this.lastFocus);
e.handled = true;
return
}
else if (this.revealed) {
this.crosswordGrid.focusNextCell(this.x, this.y);
e.handled = true;
return
}
}
}
onKeydown(e) {
this.firstKeydownOver = true;
if (!this.is_focused) {
e.handled = true;
return;
}
var key = e.key;
console.log(key);
// check arrow keys
if (key === "ArrowLeft") {
// left
this.crosswordGrid.focusPrevCellHorizontal(this.x, this.y);
e.handled = true;
return;
}
else if (key === "ArrowUp") {
// up
this.crosswordGrid.focusPrevCellVertical(this.x, this.y);
e.handled = true;
return;
}
else if (key === "ArrowRight") {
//right
this.crosswordGrid.focusNextCellHorizontal(this.x, this.y);
e.handled = true;
return;
}
else if (key === "ArrowDown") {
//down
this.crosswordGrid.focusNextCellVertical(this.x, this.y);
e.handled = true;
return;
}
e.target.select();
}
onFocus(e) {
e.target.select();
this.is_focused = true;
this.crosswordGrid.updateHints(this.x, this.y);
}
focus(lastX = -1, lastY = -1) {
if (lastX > 0 && lastY > 0) {
this.lastFocusedX = lastX;
this.lastFocusedY = lastY;
}
var element = this.shadowRoot.getElementById(`${this.x}-${this.y}`);
this.firstKeydownOver = false;
element.focus();
}
blur() {
var element = this.shadowRoot.getElementById(`${this.x}-${this.y}`);
element.blur();
this.is_focused = false;
}
updateGridElement() {
super.updateGridElement()
if (this.grid_id != null) {
this.gridElement.setGridLetter(this);
this.solutionIndex = this.crosswordGrid.getSolutionIndex(this.x, this.y)
}
}
updateLetter(letter, revealed) {
this.value = letter;
if (revealed) {
console.log("rec")
this.shadowRoot.getElementById(`${this.x}-${this.y}`).readOnly = true;
this.blur();
this.revealed = true;
}
}
render() {
this.updateGridElement();
var solutionOverlay = html``;
if (this.solutionIndex >= 0) {
solutionOverlay = html`
<div class="circle">${this.solutionIndex + 1}</div>
`;
}
return html`
<div class="letter">
<input type="text" id="${this.x}-${this.y}" value="${this.value}" @input=${this.onInput} @focus=${this.onFocus} @keydown=${this.onKeydown} @keyup=${this.onKeyup} autocomplete="off"></input>
${solutionOverlay}
</div>
`;
}
}
customElements.define('grid-letter', GridLetter);
export class HintBox extends GridBox {
static get styles() {
return css`
.hint {
display: grid;
}
.triangle-down {
position: relative;
top: 3em;
left: 1em;
z-index: 10;
grid-column: 1;
grid-row: 1;
width: 0;
height: 0;
border-left: 0.75em solid transparent;
border-right: 0.75em solid transparent;
border-top: 1.3em solid #555;
}
.triangle-right {
position: relative;
top: 1em;
left: 3em;
z-index: 10;
grid-column: 1;
grid-row: 1;
width: 0;
height: 0;
border-top: 0.75em solid transparent;
border-bottom: 0.75em solid transparent;
border-left: 1.3em solid #555;
}
input[type='text']{
grid-column: 1;
grid-row: 1;
width: 1.5em;
height: 1.5em;
text-align: center;
border-style: solid;
border-color: black;
border-width: 0.2 em;
background-color: #555;
outline: none;
color: white;
text-shadow: 0 0 1 black;
font-size: 2em;
user-select: none;
text-transform: uppercase;
}
`;
}
static get properties() {
return {
x: { type: Number },
y: { type: Number },
grid_id: { type: String },
horizontal_hint: { type: String },
vertical_hint: { type: String },
gridCount: {type: Number},
hint_id: { type: String }
}
}
constructor() {
super();
this.vertical_hint = "";
this.horizontal_hint = "";
this.hint_id = 0;
}
getVerticalHint() {
return this.vertical_hint;
}
getHorizontalHint() {
return this.horizontal_hint;
}
hasVerticalHint() {
return this.vertical_hint.length > 0;
}
hasHorizontalHint() {
return this.horizontal_hint.length > 0;
}
updateGridElement() {
super.updateGridElement()
if (this.grid_id != null) {
this.gridElement.setHintBox(this);
}
}
render() {
this.updateGridElement();
var verticalArrow = html``;
var horizontalArrow = html``;
if (this.hasVerticalHint()) {
verticalArrow = html`<div class="triangle-down"></div>`;
}
if (this.hasHorizontalHint()) {
horizontalArrow = html`<div class="triangle-right"></div>`;
}
return html`
<div class="hint">
<input type="text" id="${this.x}-${this.y}" value="${this.hint_id}" disabled></input>
${verticalArrow}
${horizontalArrow}
</div>
`;
}
}
customElements.define('hint-box', HintBox);

View File

@ -8,7 +8,7 @@ function copyToClipboard(text) {
input.select(); input.select();
document.execCommand('copy'); document.execCommand('copy');
input.parentNode.removeChild(input); input.parentNode.removeChild(input);
alert("copied url to clipboard"); alert("copied url to clipboard, you can share this link to work collaboratively on this puzzle in real time");
} }
export class InfoBox extends LitElement { export class InfoBox extends LitElement {
@ -34,7 +34,7 @@ export class InfoBox extends LitElement {
top: 0em; top: 0em;
left: 0em; left: 0em;
right: 0em; right: 0em;
height: 6em; min-height: 8em;
border-style: solid; border-style: solid;
border-color: white; border-color: white;
@ -55,7 +55,7 @@ export class InfoBox extends LitElement {
border-width: 0.2em; border-width: 0.2em;
border-style: solid; border-style: solid;
min-width: 10em; min-width: 10em;
min-height: 2.5em; min-height: 3.5em;
box-shadow: none; box-shadow: none;
outline: none; outline: none;
margin: 0.1em 0.1em; margin: 0.1em 0.1em;

View File

@ -1,3 +1,3 @@
import '/grid.js' import './grid.js'
import '/serverConnection.js' import './serverConnection.js'

View File

@ -1,6 +1,6 @@
import { html, css, LitElement } from 'https://unpkg.com/lit-element/lit-element.js?module'; import { html, css, LitElement } from 'https://unpkg.com/lit-element/lit-element.js?module';
import { WebsocketConnection } from '/websocket.js'; import { WebsocketConnection } from './websocket.js';
import { getCookie, setCookie } from '/cookie.js'; import { getCookie, setCookie } from './cookie.js';
export class ServerConnection extends WebsocketConnection { export class ServerConnection extends WebsocketConnection {
static get styles() { static get styles() {

View File

@ -1,90 +1,183 @@
import { html, css, LitElement, unsafeCSS } from 'https://unpkg.com/lit-element/lit-element.js?module'; import { html, css, LitElement, unsafeCSS } from 'https://unpkg.com/lit-element/lit-element.js?module';
import { GridLetter } from './gridBoxes.js';
export class SolutionBox extends LitElement { export class SolutionLetter extends LitElement {
static get styles() { static get styles() {
return css` return GridLetter.styles;
.letter {
display: grid;
}
input[type='text'] {
grid-column: 1;
grid-row: 1;
width: 1.5em;
height: 1.5em;
text-align: center;
border-style: solid;
border-width: 0.2 em;
border-color: black;
outline: none;
color: black;
font-size: 2em;
user-select: none;
text-transform: capitalize;
}
input[type='text']:read-only {
grid-column: 1;
grid-row: 1;
background-color: LightGreen;
}
input[type='text']:selection {
grid-column: 1;
grid-row: 1;
background: transparent;
}
.circle {
margin: auto;
text-align: left;
vertical-align: bottom;
bottom: 0;
position: relative;
z-index: 0;
pointer-events: none;
grid-column: 1;
grid-row: 1;
border: 0.1em solid lightGray;
color: black;
height: 80%;
width: 80%;
border-radius:50%;
-moz-border-radius:50%;
-webkit-border-radius:50%;
background: transparent;
}
`;
} }
static get properties() { static get properties() {
return { return {
length: { type: Number } solutionIndex: { type: Number },
grid_id: { type: String }
};
}
constructor() {
super();
this._letter = "";
this.solutionIndex = 0;
this.grid_id = "";
this.grid = null;
this.revealed = false;
}
update(props) {
if (props.has("grid_id")) {
this.grid = document.getElementById(this.grid_id);
}
super.update(props);
}
reveal(letter) {
var el = this.shadowRoot.getElementById("solution" + this.solutionIndex);
this.letter = letter;
el.readOnly = true;
this.revealed = true;
}
set letter(letter) {
this._letter = letter
var e = this.shadowRoot.getElementById("solution" + this.solutionIndex);
if (e.value != this.letter)
e.value = this.letter
}
get letter(){
return this._letter;
}
onFocus(e) {
e.target.select();
}
onInput(e) {
if (this.revealed) {
e.handled = true;
return;
}
var val = e.target.value;
var el = this.shadowRoot.getElementById("solution" + this.solutionIndex);
if (val.length > 1) {
val = val[0];
}
if (e.target.value != this.letter){
this.letter = val;
this.grid.setSolutionLetter(this.solutionIndex, this.letter);
}
e.target.select();
e.handled = true;
}
render() {
console.log("render solution letter");
return html`
<div class="letter">
<input type="text" id="solution${this.solutionIndex}" value="${this.letter}" @input=${this.onInput} @focus=${this.onFocus} autocomplete="off"></input>
<div class="circle">${this.solutionIndex + 1}</div>
</div>
`;
}
}
customElements.define('solution-letter', SolutionLetter);
export class SolutionBox extends LitElement {
static get styles() {
return css`
body {
background-color: black;
}
.codewordlabel {
z-index: 5;
position: relative;
top: 5em;
font-size: 1.5em;
color: white;
text-color: white;
}
table {
position: relative;
top: 7em;
border-spacing: 0px;
border-style: solid;
border-color: black;
border-width: 1em;
}
td {
padding: 0px;
}
`;
}
static get properties() {
return {
length: { type: Number },
grid_id: { type: String }
}; };
} }
constructor() { constructor() {
super(); super();
this.length = 0; this.length = 0;
this.values = []; this.values = []
this.grid_id = "";
this.grid = null;
} }
update(props) { update(props) {
if (props.has("length")) { console.log("update colution, grid is " + this.grid_id);
if (this.grid_id.length > 0) {
this.grid = document.getElementById(this.grid_id);
this.grid.setSolutionBox(this);
console.log("updated grid parent");
this.values = [];
var i; var i;
for (i = 0; i < this.length; i++) { for (i = 0; i < this.length; i++) {
this.values.push(""); this.values.push("");
} }
this.requestUpdate();
console.log("update length");
}
super.update(props);
}
setLetter(solutionIndex, letter, revealed = false) {
var solutionBox = this.shadowRoot.getElementById("solutionLetter" + solutionIndex)
solutionBox.letter = letter;
if (revealed) {
solutionBox.reveal(letter);
} }
super.update()
} }
render() { render() {
console.log("rerender box");
return html` return html`
<div class="codewordlabel">Codeword</div>
<table><tr>
${this.values.map((el, i) => html`<td><solution-letter id="solutionLetter${i}" grid_id=${this.grid_id} letter="" solutionIndex=${i} ></solution-letter></td>`)}
</tr></table>
`; `;
} }
} }
customElements.define('solution-box', SolutionBox);

View File

@ -25,6 +25,7 @@ export class WebsocketConnection extends LitElement {
onclose(event){ onclose(event){
console.log("websocket closed"); console.log("websocket closed");
setTimeout(() => this.connect(), 1000);
} }
onerror(event){ onerror(event){

View File

@ -1,6 +1,8 @@
import logging import logging
import uuid import uuid
import datetime as dt
from . import json_websockets from . import json_websockets
from . import session from . import session
@ -9,6 +11,21 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
sessions = {} sessions = {}
last_cleanup = None
def clean_sessions():
now = dt.datetime.utcnow()
if CrosswordConnection.last_cleanup is None or (now - CrosswordConnection.last_cleanup).total_seconds() > 3600:
CrosswordConnection.last_cleanup = now
sessions_to_close = []
for key, session in CrosswordConnection.sessions.items():
if session.is_expired():
sessions_to_close.append(key)
for key in sessions_to_close:
del(CrosswordConnection.sessions[key])
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -83,6 +100,9 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
await self.send_crossword(sessionId, lang=lang) await self.send_crossword(sessionId, lang=lang)
# clean up old session
CrosswordConnection.clean_sessions()
async def send_error(self, msg: str): async def send_error(self, msg: str):
await self.send({ await self.send({
'type': 'error', 'type': 'error',

View File

@ -185,7 +185,7 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density=0.5):
# check if there is space before and after # check if there is space before and after
if y - 1 >= 0 and grid[y - 1, x] != " ": if y - 1 >= 0 and grid[y - 1, x] != " ":
return False return False
if y + n < grid.shape[0] - 1 and grid[y+n, x] != " ": if y + n < grid.shape[0] and grid[y+n, x] != " ":
return False return False
if grid.shape[0] - n < y or y < 0: if grid.shape[0] - n < y or y < 0:
@ -214,7 +214,7 @@ def create_word_grid(w: int, h: int, lang_code: str = "en", target_density=0.5):
# check if there is space before and after # check if there is space before and after
if x - 1 >= 0 and grid[y, x - 1] != " ": if x - 1 >= 0 and grid[y, x - 1] != " ":
return False return False
if x + n < grid.shape[1] - 1 and grid[y, x + n] != " ": if x + n < grid.shape[1] and grid[y, x + n] != " ":
return False return False
if grid.shape[1] - n < x or x < 0: if grid.shape[1] - n < x or x < 0:

View File

@ -4,10 +4,12 @@ from . import crossword
class Session(object): class Session(object):
def __init__(self) -> None: def __init__(self, days_to_expire:int = 2) -> None:
self.crossword = None self.crossword = None
self.datetime_created = dt.datetime.utcnow() self.datetime_created = dt.datetime.utcnow()
self.connected_sockets = set() self.connected_sockets = set()
self.last_touched = self.datetime_created
self.days_to_expire = 2
def cleanup(self): def cleanup(self):
sockets_to_remove = [] sockets_to_remove = []
@ -46,3 +48,17 @@ class Session(object):
self.create_crossword(lang = lang) self.create_crossword(lang = lang)
return self.crossword return self.crossword
def touch(self):
self.last_touched = dt.datetime.utcnow()
def is_expired(self):
self.cleanup()
if len(self.connected_sockets) > 0:
return False
now = dt.datetime.utcnow()
if (now - self.last_touched).days > self.days_to_expire:
return True
return False

697425
server/steam@cake: Normal file

File diff suppressed because it is too large Load Diff