Переглянути джерело

[add] responsive, mobile keyboard

isundil 8 роки тому
батько
коміт
b8093accca
15 змінених файлів з 253 додано та 109 видалено
  1. 10 3
      css/grid.less
  2. 6 0
      css/main.less
  3. 49 2
      css/scoreboard.less
  4. 10 29
      js/Grid.js
  5. 5 1
      js/Makefile
  6. 23 0
      js/Player.js
  7. 12 3
      js/polling.js
  8. 20 4
      js/resources.js
  9. 35 21
      js/uiGrid.js
  10. 33 3
      js/uiScoreboard.js
  11. 6 3
      js/workflow.js
  12. 0 0
      public/crosswords.min.css
  13. 15 23
      public/crosswords.min.js
  14. 18 10
      public/game.html
  15. 11 7
      src/Grid.js

+ 10 - 3
css/grid.less

@@ -5,6 +5,7 @@
 
 .crossword-line {
     display: block;
+    white-space: nowrap;
     height: @cell-size;
 
     .cell {
@@ -13,8 +14,10 @@
         height: 100%;
         border-left: 1px solid @border-color;
         border-top: 1px solid @border-color;
+        border-bottom: 1px solid @border-color;
         overflow: hidden;
         text-align: center;
+        background-color: white;
 
         &:last-child {
             border-right: 1px solid @border-color;
@@ -113,9 +116,13 @@
             background: #b0c2ff;
         }
     }
+}
 
-    &:last-child .cell {
-        border-bottom: 1px solid @border-color;
-    }
+.input-hidden {
+    position: absolute;
+    display: inline-block;
+    height: 0;
+    width: 0;
+    z-index: -100;
 }
 

+ 6 - 0
css/main.less

@@ -7,9 +7,15 @@
 @arrow-color: darkblue;
 @disabled-color: #263340;
 
+@bullet-size: 0.75em;
+
 html,body {
     font-family: Lato, sans-serif;
     margin: 0;
     padding: 0;
 }
 
+.hidden {
+    display: none;
+}
+

+ 49 - 2
css/scoreboard.less

@@ -1,10 +1,57 @@
 
+h1 {
+    font-size: 1.5em;
+}
+
+h2 {
+    font-size: 1.12em;
+}
+
 .scoreboard {
-    position: fixed;
+    position: absolute;
     top: 0;
     bottom: 0;
     left: 0;
     width: 250px;
-    padding: 1em 0;
+    border-right: 1px solid @border-color;
+    z-index: -1;
+
+    header {
+        text-align: center;
+    }
+}
+
+.scoreboard-players {
+    display: flex;
+    flex-direction: column;
+    padding: 0;
+
+    & > li {
+        display: flex;
+        padding: 10px;
+
+        &.player-self .player-name {
+            font-style: italic;
+        }
+
+        &::before {
+            display: inline-block;
+            height: 0.75em;
+            width: 0.75em;
+            margin-top: (1em - @bullet-size) /2;
+            margin-right: 5px;
+            border-radius: 0.75em;
+            content: " ";
+            background: currentColor;
+        }
+
+        .player-name {
+            flex: 1;
+        }
+
+        .player-score {
+            margin-left: auto;
+        }
+    }
 }
 

+ 10 - 29
js/grid.js → js/Grid.js

@@ -54,42 +54,20 @@ Cell.prototype.update = function(data, players) {
     return 0;
 };
 
-/** @constructor */
-function Player(data) {
-    /** @const @type {string} */
-    this.id = data["name"];
-    /** @const @type {string} */
-    this.idEncoded = encodeURIComponent(data["name"]);
-    /** @type {number} */
-    this.score = data["score"];
-
-    var pos = data["name"].indexOf('|');
-    /** @const @type {string} */
-    this.name = data["name"].substr(0, pos);
-
-    /** @const @type {string} */
-    this.color = '#' +data["name"].substr(pos +1);
-
-    console.log(this);
-}
-
-Player.prototype.update = function(data) {
-    this.score = data["score"];
-    return data["v"];
-};
-
 /**
  * @constructor
 **/
-function Grid(data) {
-    /** @type {string} */
+function Grid(data, pseudo) {
+    /** @const @type {string} */
     this.title = data["title"] || "";
-    /** @type {number} */
+    /** @const @type {number} */
     this.difficulty = data["difficulty"];
-    /** @type {number} */
+    /** @const @type {number} */
     this.width = data["w"];
-    /** @type {number} */
+    /** @const @type {number} */
     this.height = data["h"];
+    /** @const @type {number} */
+    this.startTime = data["startTime"] || 0;
 
     /** @type {Object.<string, Player>} */
     this.players = {};
@@ -97,6 +75,9 @@ function Grid(data) {
     /** @type {Player|null} */
     this.playerSelf = null;
 
+    /** @const @type {string} */
+    this.playerSelfId = pseudo;
+
     this.words = [];
     this.grid = [];
     for (var i =0; i < this.width; i++) {

+ 5 - 1
js/Makefile

@@ -2,7 +2,11 @@
 SRC=		\
 			resources.js		\
 			ui.js				\
-			grid.js				\
+			uiScoreboard.js		\
+			uiGrid.js			\
+			ui.js				\
+			Grid.js				\
+			Player.js				\
 			polling.js			\
 			workflow.js
 

+ 23 - 0
js/Player.js

@@ -0,0 +1,23 @@
+
+/** @constructor */
+function Player(data) {
+    /** @const @type {string} */
+    this.id = data["name"];
+    /** @const @type {string} */
+    this.idEncoded = encodeURIComponent(data["name"]);
+    /** @type {number} */
+    this.score = data["score"];
+
+    var pos = data["name"].indexOf('|');
+    /** @const @type {string} */
+    this.name = data["name"].substr(0, pos);
+
+    /** @const @type {string} */
+    this.color = '#' +data["name"].substr(pos +1);
+}
+
+Player.prototype.update = function(data) {
+    this.score = data["score"];
+    return data["v"];
+};
+

+ 12 - 3
js/polling.js

@@ -28,9 +28,18 @@ function initPolling() {
         if (pseudo) {
             doGet("/api/poll?grid=" +GRID_PUBLIC_ID +"&v=" +KNOWN_VERSION, function(status, resp) {
                 if (resp) {
-                    GRID = new Grid(resp);
-                    updateOnPollResult(resp);
-                    GRID.playerSelf = GRID.players[pseudo];
+                    GRID = new Grid(resp, pseudo);
+                    if (resp["players"]) {
+                        GRID.updatePlayers(resp["players"]);
+                        if (!GRID.playerSelf)
+                            GRID.playerSelf = GRID.players[GRID.playerSelfId];
+                        onPlayersUpdated();
+                    }
+                    if (resp["grid"]) {
+                        GRID.update(resp["grid"]);
+                        uiCreateGrid();
+                    }
+                    KNOWN_VERSION = Math.max(KNOWN_VERSION, resp["v"] || 0);
                     scheduleNextPoll();
                 } // TODO else cannot init party
             });

+ 20 - 4
js/resources.js

@@ -1,9 +1,18 @@
 
 var R = {
-	id: {
-		scoreboard: "scoreboard"
-		,grid: "grid"
-	}
+    id: {
+        scoreboard: {
+            container: "scoreboard"
+            ,header: {
+                title: "gridTitle"
+                ,difficulty: "gridDifficulty"
+                ,time: "gridTime"
+            }
+            ,players: "scoreboardPlayers"
+        }
+        ,grid: "grid"
+        ,input: "input"
+    }
     ,klass: {
         line: "crossword-line"
         ,cell: {
@@ -26,6 +35,13 @@ var R = {
             ,selected: "cell-selected"
             ,currentInput: "cell-input"
         }
+        ,scoreboard: {
+            player: {
+                name: "player-name"
+                ,score: "player-score"
+                ,isMe: "player-self"
+            }
+        }
     }
 };
 

+ 35 - 21
js/uiGrid.js

@@ -30,6 +30,8 @@ function uiCreateCell(cellData, x, y) {
 function uiCreateGrid() {
     var frag = document.createDocumentFragment();
 
+    dGet(R.id.scoreboard.header.title).textContent = GRID.title;
+    dGet(R.id.scoreboard.header.difficulty).textContent = GRID.difficulty;
     for (var i =0; i < GRID.height; i++) {
         var line = dCreate("div");
 
@@ -86,6 +88,7 @@ function onGridUpdated() {
             if (i.data.letter) {
                 i.dom.textContent = i.data.letter;
                 if (i.data.found) {
+                    var className = i.dom.className;
                     i.dom.classList.add(R.klass.cell.found);
                     i.dom.classList.remove(R.klass.cell.wrong);
                     i.dom.style.color = i.data.found.color;
@@ -97,6 +100,13 @@ function onGridUpdated() {
     });
 }
 
+function moveInput(cell) {
+    var input = dGet(R.id.input);
+    input.style.top = cell.dom.offsetTop +"px";
+    input.style.left = cell.dom.offsetLeft +"px";
+    input.focus();
+}
+
 function gridClickDelegate(e) {
     var target = e.target;
     while (target && (target.dataset && !target.dataset.x))
@@ -105,29 +115,33 @@ function gridClickDelegate(e) {
         var clickedCell = UI_CELLS[parseInt(target.dataset.x, 10) +parseInt(target.dataset.y, 10) *GRID.width];
         if (clickedCell.data.isBlack) {
             unselect();
-        } else if (clickedCell.data.definitions) {
-            var first = true;
-            unselect();
-            if (clickedCell.data.definitions[target.dataset.definition]) {
-                clickedCell.data.definitions[target.dataset.definition].word.forEach(function(coordinates) {
-                    select(coordinates[0], coordinates[1]);
-                    if (first && !UI_CELLS[coordinates[0] +coordinates[1] *GRID.width].data.found) {
-                        CURRENTINPUT = UI_CELLS[coordinates[0] +coordinates[1] *GRID.width];
-                        CURRENTINPUT.dom.classList.add(R.klass.cell.currentInput);
-                    }
-                    first = false;
-                });
-            }
         } else {
-            var words = GRID.getWord(clickedCell.x, clickedCell.y);
-            unselect();
-            words.forEach(function (word) {
-                word.forEach(function (coordinates) {
-                    select(coordinates[0], coordinates[1]);
+            if (clickedCell.data.definitions) {
+                var first = true;
+                unselect();
+                if (clickedCell.data.definitions[target.dataset.definition]) {
+                    clickedCell.data.definitions[target.dataset.definition].word.forEach(function(coordinates) {
+                        select(coordinates[0], coordinates[1]);
+                        if (first && !UI_CELLS[coordinates[0] +coordinates[1] *GRID.width].data.found) {
+                            CURRENTINPUT = UI_CELLS[coordinates[0] +coordinates[1] *GRID.width];
+                            CURRENTINPUT.dom.classList.add(R.klass.cell.currentInput);
+                        }
+                        first = false;
+                    });
+                }
+            } else {
+                var words = GRID.getWord(clickedCell.x, clickedCell.y);
+                unselect();
+                words.forEach(function (word) {
+                    word.forEach(function (coordinates) {
+                        select(coordinates[0], coordinates[1]);
+                    });
                 });
-            });
-            target.classList.add(R.klass.cell.currentInput);
-            CURRENTINPUT = clickedCell;
+                target.classList.add(R.klass.cell.currentInput);
+                CURRENTINPUT = clickedCell;
+            }
+            // Display mobile / tablet keyboard
+            moveInput(CURRENTINPUT);
         }
     }
 }

+ 33 - 3
js/uiScoreboard.js

@@ -4,27 +4,57 @@ var UI_PLAYERS = {};
 function uiCreatePlayer(player) {
     var dom = dCreate("li")
         ,playerName = dCreate("span");
+
+    playerName.className = R.klass.scoreboard.player.name;
     playerName.textContent = player.name;
     dom.appendChild(playerName);
     dom.score = dCreate("span");
+    dom.score.className = R.klass.scoreboard.player.score;
     dom.appendChild(dom.score);
     dom.style.color = player.color;
     return dom;
 }
 
 function onPlayersUpdated() {
-    var container;
+    var container
+        ,playerIds = [];
 
     for (var i in GRID.players) {
         var uiPlayer = UI_PLAYERS[i];
+
         if (!uiPlayer) {
             uiPlayer = UI_PLAYERS[i] = uiCreatePlayer(GRID.players[i]);
+            if (i === GRID.playerSelf.id)
+                uiPlayer.classList.add(R.klass.scoreboard.player.isMe);
             if (!container)
-                container = dGet(R.id.scoreboard);
+                container = dGet(R.id.scoreboard.players);
             container.appendChild(uiPlayer);
         }
         uiPlayer.score.textContent = GRID.players[i].score;
+        playerIds.push(i);
+    }
+    playerIds.sort(function(a, b) {
+        return GRID.players[b].score - GRID.players[a].score;
+    });
+    for (var i =0, nbPlayers = playerIds.length; i <nbPlayers; i++) {
+        UI_PLAYERS[playerIds[i]].style.order = i;
     }
-    //TODO resort
 }
 
+setInterval(function() {
+    if (GRID) {
+        var ellapsed = (Date.now() -GRID.startTime) / 1000;
+
+        var seconds = Math.floor(ellapsed % 60);
+        ellapsed = (ellapsed - seconds) / 60;
+        var minutes = Math.floor(ellapsed % 60);
+        ellapsed = Math.floor((ellapsed - minutes) / 60);
+
+        var text = ellapsed ? (ellapsed +":") : ''; //hours
+        text += (minutes < 10 ? '0' : '') +minutes +":";
+        text += (seconds < 10 ? '0' : '') +seconds;
+
+        dGet(R.id.scoreboard.header.time).textContent = text;
+    }
+}, 1000);
+

+ 6 - 3
js/workflow.js

@@ -43,12 +43,13 @@ function keyPressHandler(cell, key) {
 function updateOnPollResult(resp) {
     if (resp["players"]) {
         GRID.updatePlayers(resp["players"]);
+        if (!GRID.playerSelf)
+            GRID.playerSelf = GRID.players[GRID.playerSelfId];
         onPlayersUpdated();
-        console.log("players updated");
     }
     if (resp["grid"]) {
         GRID.update(resp["grid"]);
-        uiCreateGrid();
+        onGridUpdated();
     }
     KNOWN_VERSION = Math.max(KNOWN_VERSION, resp["v"] || 0);
 }
@@ -89,9 +90,11 @@ document.addEventListener('DOMContentLoaded', function() {
                     onGridUpdated();
                 }
             } else if (e.key.toUpperCase() == "DELETE") {
+                CURRENTINPUT.dom.classList.remove(R.klass.cell.wrong);
                 CURRENTINPUT.data.letter = null;
                 onGridUpdated();
-            } else if (e.key.toUpperCase() == "BACKSPACE") {
+            } else if (e.key.toUpperCase() == "BACKSPACE") { // TODO catch
+                CURRENTINPUT.dom.classList.remove(R.klass.cell.wrong);
                 CURRENTINPUT.data.letter = null;
                 //TODO go back
                 onGridUpdated();

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
public/crosswords.min.css


+ 15 - 23
public/crosswords.min.js

@@ -1,23 +1,15 @@
-var R={id:{scoreboard:"scoreboard",grid:"grid"},klass:{line:"crossword-line",cell:{item:"cell",black:"cell-disabled",definition:"cell-definition",letter:"cell-letter",definitions:{item:"definition",rightVertical:"definition-right-vt",rightHorizontal:"definition-right-hz",bottomVertical:"definition-bottom-vt",bottomHorizontal:"definition-bottom-hz"},found:"cell-letter-correct",pending:"cell-letter-pending",wrong:"cell-letter-wrong",selected:"cell-selected",currentInput:"cell-input"}}};var UI_CELLS=[],UI_PLAYERS={},playerDomList;function dCreate(domName){return document.createElement(domName)}function dGet(id){return document.getElementById(id)}
-function uiCreateCell(cellData,x,y){var cell=dCreate("div");cell.className=R.klass.cell.item;if(cellData.isBlack)cell.classList.add(R.klass.cell.black);else if(cellData.definitions!==null){cell.classList.add(R.klass.cell.definition);var cellContent=dCreate("span");for(var i=0,nbDefinitions=cellData.definitions.length;i<nbDefinitions;i++){var definition=cellData.definitions[i],domDefinition=dCreate("span");domDefinition.dataset.x=x;domDefinition.dataset.y=y;domDefinition.dataset.definition=i;domDefinition.className=
-R.klass.cell.definitions.item;domDefinition.innerHTML=definition.text.join("<br/>");cellContent.appendChild(domDefinition)}cell.appendChild(cellContent)}else cell.classList.add(R.klass.cell.letter);return cell}function uiCreatePlayer(player){var dom=dCreate("li"),playerName=dCreate("span");playerName.textContent=player.name;dom.appendChild(playerName);dom.score=dCreate("span");dom.appendChild(dom.score);dom.style.color=player.color;return dom}
-function uiCreateGrid(){var frag=document.createDocumentFragment();for(var i=0;i<GRID.height;i++){var line=dCreate("div");line.className=R.klass.line;frag.appendChild(line);for(var j=0;j<GRID.width;j++){var cell=uiCreateCell(GRID.grid[j][i],j,i);cell.dataset.x=j;cell.dataset.y=i;line.appendChild(cell);UI_CELLS.push({x:j,y:i,dom:cell,data:GRID.grid[j][i]})}}for(var i=0;i<GRID.height;i++)for(var j=0;j<GRID.width;j++){var cell=UI_CELLS[i*GRID.width+j];if(cell.data.definitions)cell.data.definitions.forEach(function(d){switch(d.direction){case Definition.RIGHT_VERTICAL:UI_CELLS[i*
-GRID.width+j+1].dom.classList.add(R.klass.cell.definitions.rightVertical);break;case Definition.RIGHT_HORIZONTAL:UI_CELLS[i*GRID.width+j+1].dom.classList.add(R.klass.cell.definitions.rightHorizontal);break;case Definition.BOTTOM_VERTICAL:UI_CELLS[(i+1)*GRID.width+j].dom.classList.add(R.klass.cell.definitions.bottomVertical);break;case Definition.BOTTOM_HORIZONTAL:UI_CELLS[(i+1)*GRID.width+j].dom.classList.add(R.klass.cell.definitions.bottomHorizontal);break}})}onGridUpdated();var gridContainer=dGet(R.id.grid);
-gridContainer.textContent="";gridContainer.appendChild(frag)}function onGridUpdated(){UI_CELLS.forEach(function(i){if(!i.data.definitions&&!i.data.isBlack)if(i.data.letter){i.dom.textContent=i.data.letter;if(i.data.found){i.dom.classList.add(R.klass.cell.found);i.dom.classList.remove(R.klass.cell.wrong);i.dom.style.color=i.data.found.color}}else i.dom.textContent=""})}
-function onPlayersUpdated(){var container;for(var i in GRID.players){var uiPlayer=UI_PLAYERS[i];if(!uiPlayer){uiPlayer=UI_PLAYERS[i]=uiCreatePlayer(GRID.players[i]);if(!container)container=dGet(R.id.scoreboard);container.appendChild(uiPlayer)}uiPlayer.score.textContent=GRID.players[i].score}}
-function gridClickDelegate(e){var target=e.target;while(target&&(target.dataset&&!target.dataset.x))target=target.parentElement;if(target&&target.dataset&&target.dataset.x&&target.dataset.y){var clickedCell=UI_CELLS[parseInt(target.dataset.x,10)+parseInt(target.dataset.y,10)*GRID.width];if(clickedCell.data.isBlack)unselect();else if(clickedCell.data.definitions){var first=true;unselect();if(clickedCell.data.definitions[target.dataset.definition])clickedCell.data.definitions[target.dataset.definition].word.forEach(function(coordinates){select(coordinates[0],
-coordinates[1]);if(first&&!UI_CELLS[coordinates[0]+coordinates[1]*GRID.width].data.found){CURRENTINPUT=UI_CELLS[coordinates[0]+coordinates[1]*GRID.width];CURRENTINPUT.dom.classList.add(R.klass.cell.currentInput)}first=false})}else{var words=GRID.getWord(clickedCell.x,clickedCell.y);unselect();words.forEach(function(word){word.forEach(function(coordinates){select(coordinates[0],coordinates[1])})});target.classList.add(R.klass.cell.currentInput);CURRENTINPUT=clickedCell}}};function Definition(data){this.text=data["text"];this.direction=data["pos"];this.word=null}Definition.RIGHT_HORIZONTAL=1;Definition.RIGHT_VERTICAL=2;Definition.BOTTOM_HORIZONTAL=3;Definition.BOTTOM_VERTICAL=4;function Cell(){this.isBlack=false;this.definitions=null;this.found=null;this.letter=null}
-Cell.prototype.update=function(data,players){if(data["type"]===null)this.isBlack=true;else if(data["definitions"]!==undefined){this.definitions=[];data["definitions"].forEach(function(definition){this.definitions.push(new Definition(definition))}.bind(this))}else if(data["letter"]){this.letter=data["letter"];this.found=players[data["found"]];return data["v"]}return 0};
-function Player(data){this.id=data["name"];this.idEncoded=encodeURIComponent(data["name"]);this.score=data["score"];var pos=data["name"].indexOf("|");this.name=data["name"].substr(0,pos);this.color="#"+data["name"].substr(pos+1);console.log(this)}Player.prototype.update=function(data){this.score=data["score"];return data["v"]};
-function Grid(data){this.title=data["title"]||"";this.difficulty=data["difficulty"];this.width=data["w"];this.height=data["h"];this.players={};this.playerSelf=null;this.words=[];this.grid=[];for(var i=0;i<this.width;i++){this.grid[i]=[];for(var j=0;j<this.height;j++)this.grid[i][j]=new Cell}}
-Grid.prototype.computeWord=function(x,y,dx,dy){if(!this.grid[x+dx]||!this.grid[x+dx][y+dy]||this.grid[x+dx][y+dy].definitions||this.grid[x+dx][y+dy].isBlack)return[[x,y]];var word=this.computeWord(x+dx,y+dy,dx,dy);word.unshift([x,y]);return word};Grid.prototype.getWord=function(x,y){var words=[];this.words.forEach(function(word){for(var i=0,nbLetters=word.length;i<nbLetters;i++)if(word[i][0]==x&&word[i][1]==y){words.push(word);break}});return words};
-Grid.prototype.updatePlayers=function(playerData){var maxVersion=0;playerData.forEach(function(player){var localPlayer=this.players[player["name"]];if(!localPlayer)localPlayer=this.players[player["name"]]=new Player(player);maxVersion=Math.max(maxVersion,localPlayer.update(player))}.bind(this));return maxVersion};
-Grid.prototype.update=function(data){var maxVersion=null,topologyUpdated=false;data.forEach(function(cellData){var updateResult=this.grid[cellData["x"]][cellData["y"]].update(cellData,this.players);maxVersion=Math.max(maxVersion||0,updateResult);if(updateResult===0)topologyUpdated=true}.bind(this));if(topologyUpdated){var words=[];for(var i=0;i<this.width;i++)for(var j=0;j<this.height;j++)if(this.grid[i][j].definitions)this.grid[i][j].definitions.forEach(function(definition){var word;switch(definition.direction){case Definition.RIGHT_VERTICAL:word=
-this.computeWord(i+1,j,0,1);break;case Definition.RIGHT_HORIZONTAL:word=this.computeWord(i+1,j,1,0);break;case Definition.BOTTOM_VERTICAL:word=this.computeWord(i,j+1,0,1);break;case Definition.BOTTOM_HORIZONTAL:word=this.computeWord(i,j+1,1,0);break}words.push(word);definition.word=word}.bind(this));this.words=words}return maxVersion};var POLL_INTERVAL=5E3;function doGet(url,callback){var xhr=new XMLHttpRequest;xhr.onreadystatechange=function(e){if(xhr.readyState===4){var resp=null;if(xhr.status===200){resp=xhr.response;try{resp=JSON.parse((resp))}catch(e){resp=null}}callback(xhr.status,resp)}};xhr.open("GET",url,true);xhr.send(null)}
-function initPolling(){lazyGetPseudonyme(function(pseudo){if(pseudo)doGet("/api/poll?grid="+GRID_PUBLIC_ID+"&v="+KNOWN_VERSION,function(status,resp){if(resp){GRID=new Grid(resp);updateOnPollResult(resp);GRID.playerSelf=GRID.players[pseudo];scheduleNextPoll()}})})}
-function pollNow(){if(pollNow.polling!==true){if(pollNow.pollSchedule){clearTimeout(pollNow.pollSchedule);pollNow.pollSchedule=0}pollNow.polling=true;doGet("/api/poll?grid="+GRID_PUBLIC_ID+"&v="+KNOWN_VERSION,function(status,resp){pollNow.polling=false;if(resp)updateOnPollResult(resp);scheduleNextPoll()})}}function scheduleNextPoll(){if(!pollNow.pollSchedule)pollNow.pollSchedule=setInterval(pollNow,POLL_INTERVAL)};var GRID_PUBLIC_ID,KNOWN_VERSION=0,GRID,SELECTED=[],CURRENTINPUT=null;function lazyGetPseudonyme(cb){var pseudo=window["sessionStorage"].getItem("pseudonyme_"+GRID_PUBLIC_ID);if(pseudo)cb(pseudo);else doGet("/api/register?grid="+GRID_PUBLIC_ID,function(status,pseudo){if(status&&pseudo){window["sessionStorage"].setItem("pseudonyme_"+GRID_PUBLIC_ID,pseudo);cb(pseudo)}else cb(null)})}
-function keyPressHandler(cell,key){cell.dom.classList.add(R.klass.cell.pending);doGet("/api/put?grid="+GRID_PUBLIC_ID+"&key="+key+"&x="+cell.x+"&y="+cell.y+"&me="+GRID.playerSelf.idEncoded,function(status,resp){cell.dom.classList.remove(R.klass.cell.pending);if(status===403&&!cell.data.found)cell.dom.classList.add(R.klass.cell.wrong);else if(status===204)pollNow();else pollNow()})}
-function updateOnPollResult(resp){if(resp["players"]){GRID.updatePlayers(resp["players"]);onPlayersUpdated();console.log("players updated")}if(resp["grid"]){GRID.update(resp["grid"]);uiCreateGrid()}KNOWN_VERSION=Math.max(KNOWN_VERSION,resp["v"]||0)}function unselect(){SELECTED.forEach(function(cell){cell.dom.classList.remove(R.klass.cell.selected)});if(CURRENTINPUT){CURRENTINPUT.dom.classList.remove(R.klass.cell.currentInput);CURRENTINPUT=null}SELECTED=[]}
-function select(x,y){var cell=UI_CELLS[x+y*GRID.width];SELECTED.push(cell);cell.dom.classList.add(R.klass.cell.selected)}
-document.addEventListener("DOMContentLoaded",function(){GRID_PUBLIC_ID=document.location.hash.substr(1);if(GRID_PUBLIC_ID==""){document.location.href="/";return}document.addEventListener("click",gridClickDelegate);document.addEventListener("keypress",function(e){if(CURRENTINPUT&&!CURRENTINPUT.data.found)if(e.key.length===1){var key=e.key.charAt(0).toUpperCase();if(key.match(/[A-Z]/)){CURRENTINPUT.data.letter=key;keyPressHandler(CURRENTINPUT,key);onGridUpdated()}}else if(e.key.toUpperCase()=="DELETE"){CURRENTINPUT.data.letter=
-null;onGridUpdated()}else if(e.key.toUpperCase()=="BACKSPACE"){CURRENTINPUT.data.letter=null;onGridUpdated()}console.log(e)});initPolling()});
+var e={};function h(a){var b=document.createElement("li"),c=document.createElement("span");c.className="player-name";c.textContent=a.name;b.appendChild(c);b.i=document.createElement("span");b.i.className="player-score";b.appendChild(b.i);b.style.color=a.color;return b}
+function k(){var a,b=[],c;for(c in l.g){var d=e[c];d||(d=e[c]=h(l.g[c]),c===l.j.id&&d.classList.add("player-self"),a||(a=document.getElementById("scoreboardPlayers")),a.appendChild(d));d.i.textContent=l.g[c].i;b.push(c)}b.sort(function(a,b){return l.g[b].i-l.g[a].i});c=0;for(a=b.length;c<a;c++)e[b[c]].style.order=c}
+setInterval(function(){if(l){var a=(Date.now()-l.D)/1E3,b=Math.floor(a%60),a=(a-b)/60,c=Math.floor(a%60),a=Math.floor((a-c)/60),a=(a?a+":":"")+((10>c?"0":"")+c+":")+((10>b?"0":"")+b);document.getElementById("gridTime").textContent=a}},1E3);var m=[];function p(a,b,c){var d=document.createElement("div");d.className="cell";if(a.o)d.classList.add("cell-disabled");else if(null!==a.f){d.classList.add("cell-definition");for(var f=document.createElement("span"),g=0,G=a.f.length;g<G;g++){var H=a.f[g],n=document.createElement("span");n.dataset.x=b;n.dataset.y=c;n.dataset.definition=g;n.className="definition";n.innerHTML=H.text.join("<br/>");f.appendChild(n)}d.appendChild(f)}else d.classList.add("cell-letter");return d}
+function q(){var a=document.createDocumentFragment();document.getElementById("gridTitle").textContent=l.F;document.getElementById("gridDifficulty").textContent=l.G;for(var b=0;b<l.s;b++){var c=document.createElement("div");c.className="crossword-line";a.appendChild(c);for(var d=0;d<l.b;d++){var f=p(l.c[d][b],d,b);f.dataset.x=d;f.dataset.y=b;c.appendChild(f);m.push({x:d,y:b,a:f,data:l.c[d][b]})}}for(b=0;b<l.s;b++)for(d=0;d<l.b;d++)f=m[b*l.b+d],f.data.f&&f.data.f.forEach(function(a){switch(a.direction){case 2:m[b*
+l.b+d+1].a.classList.add("definition-right-vt");break;case 1:m[b*l.b+d+1].a.classList.add("definition-right-hz");break;case 4:m[(b+1)*l.b+d].a.classList.add("definition-bottom-vt");break;case 3:m[(b+1)*l.b+d].a.classList.add("definition-bottom-hz")}});r();c=document.getElementById("grid");c.textContent="";c.appendChild(a)}
+function r(){m.forEach(function(a){a.data.f||a.data.o||(a.data.m?(a.a.textContent=a.data.m,a.data.l&&(a.a.classList.add("cell-letter-correct"),a.a.classList.remove("cell-letter-wrong"),a.a.style.color=a.data.l.color)):a.a.textContent="")})}function t(){var a=u,b=document.getElementById("input");b.style.top=a.a.offsetTop+"px";b.style.left=a.a.offsetLeft+"px";b.focus()}
+function v(a){for(a=a.target;a&&a.dataset&&!a.dataset.x;)a=a.parentElement;if(a&&a.dataset&&a.dataset.x&&a.dataset.y){var b=m[parseInt(a.dataset.x,10)+parseInt(a.dataset.y,10)*l.b];if(b.data.o)w();else{if(b.data.f){var c=!0;w();b.data.f[a.dataset.definition]&&b.data.f[a.dataset.definition].B.forEach(function(a){x(a[0],a[1]);c&&!m[a[0]+a[1]*l.b].data.l&&(u=m[a[0]+a[1]*l.b],u.a.classList.add("cell-input"));c=!1})}else{var d=y(b.x,b.y);w();d.forEach(function(a){a.forEach(function(a){x(a[0],a[1])})});
+a.classList.add("cell-input");u=b}t()}}};function z(a){this.text=a.text;this.direction=a.pos;this.B=null}function A(){this.o=!1;this.m=this.l=this.f=null}A.prototype.update=function(a,b){if(null===a.type)this.o=!0;else if(void 0!==a.definitions)this.f=[],a.definitions.forEach(function(a){this.f.push(new z(a))}.bind(this));else if(a.letter)return this.m=a.letter,this.l=b[a.found],a.v;return 0};
+function B(a,b){this.F=a.title||"";this.G=a.difficulty;this.b=a.w;this.s=a.h;this.D=a.startTime||0;this.g={};this.j=null;this.u=b;this.A=[];this.c=[];for(var c=0;c<this.b;c++){this.c[c]=[];for(var d=0;d<this.s;d++)this.c[c][d]=new A}}function C(a,b,c,d,f){if(!a.c[b+d]||!a.c[b+d][c+f]||a.c[b+d][c+f].f||a.c[b+d][c+f].o)return[[b,c]];a=C(a,b+d,c+f,d,f);a.unshift([b,c]);return a}
+function y(a,b){var c=[];l.A.forEach(function(d){for(var f=0,g=d.length;f<g;f++)if(d[f][0]==a&&d[f][1]==b){c.push(d);break}});return c}function D(a){var b=0;a.forEach(function(a){var c=this.g[a.name];c||(c=this.g[a.name]=new E(a));b=Math.max(b,c.update(a))}.bind(l))}
+B.prototype.update=function(a){var b=null,c=!1;a.forEach(function(a){a=this.c[a.x][a.y].update(a,this.g);b=Math.max(b||0,a);0===a&&(c=!0)}.bind(this));if(c){for(var d=[],f=0;f<this.b;f++)for(var g=0;g<this.s;g++)this.c[f][g].f&&this.c[f][g].f.forEach(function(a){var b;switch(a.direction){case 2:b=C(this,f+1,g,0,1);break;case 1:b=C(this,f+1,g,1,0);break;case 4:b=C(this,f,g+1,0,1);break;case 3:b=C(this,f,g+1,1,0)}d.push(b);a.B=b}.bind(this));this.A=d}return b};function E(a){this.id=a.name;this.C=encodeURIComponent(a.name);this.i=a.score;var b=a.name.indexOf("|");this.name=a.name.substr(0,b);this.color="#"+a.name.substr(b+1)}E.prototype.update=function(a){this.i=a.score;return a.v};function F(a,b){var c=new XMLHttpRequest;c.onreadystatechange=function(){if(4===c.readyState){var a=null;if(200===c.status){a=c.response;try{a=JSON.parse(a)}catch(f){a=null}}b(c.status,a)}};c.open("GET",a,!0);c.send(null)}function I(){J(function(a){a&&F("/api/poll?grid="+K+"&v="+L,function(b,c){c&&(l=new B(c,a),c.players&&(D(c.players),l.j||(l.j=l.g[l.u]),k()),c.grid&&(l.update(c.grid),q()),L=Math.max(L,c.v||0),M())})})}
+function N(){!0!==N.c&&(N.b&&(clearTimeout(N.b),N.b=0),N.c=!0,F("/api/poll?grid="+K+"&v="+L,function(a,b){N.c=!1;b&&(b.players&&(D(b.players),l.j||(l.j=l.g[l.u]),k()),b.grid&&(l.update(b.grid),r()),L=Math.max(L,b.v||0));M()}))}function M(){N.b||(N.b=setInterval(N,5E3))};var K,L=0,l,O=[],u=null;function J(a){var b=window.sessionStorage.getItem("pseudonyme_"+K);b?a(b):F("/api/register?grid="+K,function(b,d){b&&d?(window.sessionStorage.setItem("pseudonyme_"+K,d),a(d)):a(null)})}function P(a){var b=u;b.a.classList.add("cell-letter-pending");F("/api/put?grid="+K+"&key="+a+"&x="+b.x+"&y="+b.y+"&me="+l.j.C,function(a){b.a.classList.remove("cell-letter-pending");403!==a||b.data.l?N():b.a.classList.add("cell-letter-wrong")})}
+function w(){O.forEach(function(a){a.a.classList.remove("cell-selected")});u&&(u.a.classList.remove("cell-input"),u=null);O=[]}function x(a,b){var c=m[a+b*l.b];O.push(c);c.a.classList.add("cell-selected")}
+document.addEventListener("DOMContentLoaded",function(){K=document.location.hash.substr(1);""==K?document.location.href="/":(document.addEventListener("click",v),document.addEventListener("keypress",function(a){if(u&&!u.data.l)if(1===a.key.length){var b=a.key.charAt(0).toUpperCase();b.match(/[A-Z]/)&&(u.data.m=b,P(b),r())}else"DELETE"==a.key.toUpperCase()?(u.a.classList.remove("cell-letter-wrong"),u.data.m=null,r()):"BACKSPACE"==a.key.toUpperCase()&&(u.a.classList.remove("cell-letter-wrong"),u.data.m=
+null,r());console.log(a)}),I())});

+ 18 - 10
public/game.html

@@ -1,13 +1,21 @@
 <!DOCTYPE html5>
 <html>
-	<head>
-		<title>Crosswords</title>
-		<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet"/>
-		<link href="crosswords.min.css" rel="stylesheet"/>
-	</head>
-	<body>
-		<ul id="scoreboard" class="scoreboard"></ul>
-		<div id="grid" class="crossword"></div>
-		<script src="crosswords.min.js"></script>
-	</body>
+    <head>
+        <title>Crosswords</title>
+        <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet"/>
+        <link href="crosswords.min.css" rel="stylesheet"/>
+    </head>
+    <body>
+        <div id="scoreboard" class="scoreboard">
+            <header>
+                <h1 id="gridTitle"></h1>
+                <h2 id="gridDifficulty"></h2>
+                <div id="gridTime">00:00</div>
+            </header>
+            <ul id="scoreboardPlayers" class="scoreboard-players"></ul>
+        </div>
+        <div id="grid" class="crossword"></div>
+        <script src="crosswords.min.js"></script>
+        <input id="input" type="text" class="input-hidden" />
+    </body>
 </html>

+ 11 - 7
src/Grid.js

@@ -175,10 +175,10 @@ function Grid(publicId, data) {
     this.minVersion = 0;
     /** @type {Object.<string, Player>} playerId -> score */
     this.players = {};
-
-    this.colors = shuffleArray([ "007b00", "00407b", "42007b", "7b0059", "7b0000", "00747b", "007b39", "7b6300", "ff5f5f", "5c7cff" ]);
 };
 
+Grid.colors = shuffleArray([ "007b00", "00407b", "42007b", "7b0059", "7b0000", "00747b", "007b39", "7b6300", "ff5f5f", "5c7cff" ]);
+
 Grid.prototype.getCell = function(x, y) {
     for (var i =0, nbCells = this.grid.length; i < nbCells; i++) {
         var cell = this.grid[i];
@@ -202,12 +202,15 @@ function shuffleArray(arr) {
 }
 
 Grid.prototype.generateNewPlayerId = function() {
+    var players = {};
+
+    for (var playerId in this.players)
+        players[playerId.substr(0, playerId.indexOf('|'))] = true;
+
     var playerName = (function() {
         var dessertType = shuffleArray([ "muffin", "cookie", "cake", "marshmallow", "pizza", "pie", "mousse" ])
-            ,ingredient = shuffleArray([ "blueberry", "chocolate", "cherry", "apple", "pear", "caramell" ])
-            ,players = {};
-        for (var playerId in this.players)
-            players[playerId.substr(0, playerId.indexOf('|'))] = true;
+            ,ingredient = shuffleArray([ "blueberry", "chocolate", "cherry", "apple", "pear", "caramell" ]);
+
         for (var i =0, iMax = dessertType.length; i < iMax; i++)
             for (var j =0, jMax = ingredient.length; j < jMax; j++) {
                 var id = ingredient[i] +' ' +dessertType[j];
@@ -221,7 +224,7 @@ Grid.prototype.generateNewPlayerId = function() {
             i++;
         return baseId +i;
     })();
-    return playerName +'|' +this.colors[Object.keys(this.players).length % this.colors.length];
+    return playerName +'|' +Grid.colors[Object.keys(this.players).length % Grid.colors.length];
 };
 
 Grid.prototype.registerNewPlayer = function(t) {
@@ -239,6 +242,7 @@ Grid.prototype.toStatic = function(v) {
         ret.difficulty = this.difficulty;
         ret.w = this.width;
         ret.h = this.height;
+        ret.startTime = this.minVersion;
     }
     var grid = [];
     this.grid.forEach((cell) => {

Деякі файли не було показано, через те що забагато файлів було змінено