add solution word to client
This commit is contained in:
parent
aca27cdae2
commit
c0f5f3f785
@ -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
569
js_client/gridBoxes.js
Normal 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);
|
@ -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;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import '/grid.js'
|
import './grid.js'
|
||||||
import '/serverConnection.js'
|
import './serverConnection.js'
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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);
|
||||||
|
@ -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){
|
||||||
|
@ -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,12 +11,27 @@ 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)
|
||||||
|
|
||||||
self._session = None
|
self._session = None
|
||||||
|
|
||||||
async def send_crossword(self, sessionId: str, lang:str = "en"):
|
async def send_crossword(self, sessionId: str, lang: str = "en"):
|
||||||
if sessionId not in CrosswordConnection.sessions:
|
if sessionId not in CrosswordConnection.sessions:
|
||||||
await self.send_error(msg="unknown session")
|
await self.send_error(msg="unknown session")
|
||||||
return
|
return
|
||||||
@ -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',
|
||||||
@ -103,7 +123,7 @@ class CrosswordConnection(json_websockets.JsonWebsocketConnection):
|
|||||||
sessionId = message['sessionId']
|
sessionId = message['sessionId']
|
||||||
if "lang" in message:
|
if "lang" in message:
|
||||||
lang = message['lang']
|
lang = message['lang']
|
||||||
await self.register(sessionId=sessionId, lang = lang)
|
await self.register(sessionId=sessionId, lang=lang)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._session is None:
|
if self._session is None:
|
||||||
|
@ -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:
|
||||||
|
@ -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
697425
server/steam@cake:
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user