From b5fb8e693dc82037eec2617a7dc49d838a9a8441 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 18 Dec 2018 16:50:43 +0100
Subject: [PATCH] Some fixes, problems logic now functional

---
 TODO                                          |  2 --
 package-lock.json                             | 32 +++++++++++--------
 public/javascripts/components/game.js         | 26 +++++++++++----
 .../javascripts/components/problemSummary.js  | 23 +++++++------
 public/javascripts/components/problems.js     | 19 +++++++----
 public/stylesheets/variant.sass               | 12 ++++++-
 routes/all.js                                 |  2 +-
 views/index.pug                               |  2 +-
 8 files changed, 79 insertions(+), 39 deletions(-)

diff --git a/TODO b/TODO
index 3684a842..4b311b04 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,2 @@
-Set problems style (maybe will fix strange flashing bug?)
 global lang cookie, + display (remember in each variant what is shown...)
 translations (how ? switch on index page only, then find ideas...)
-Improve style for various screen sizes
diff --git a/package-lock.json b/package-lock.json
index 783e4095..cf4aad00 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1968,7 +1968,8 @@
         "code-point-at": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
@@ -1978,7 +1979,8 @@
         "console-control-strings": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
@@ -2095,7 +2097,8 @@
         "inherits": {
           "version": "2.0.3",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
@@ -2107,6 +2110,7 @@
           "version": "1.0.0",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
@@ -2232,7 +2236,8 @@
         "number-is-nan": {
           "version": "1.0.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
@@ -2365,6 +2370,7 @@
           "version": "1.0.2",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -2813,9 +2819,9 @@
       },
       "dependencies": {
         "readable-stream": {
-          "version": "3.0.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz",
-          "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==",
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.0.tgz",
+          "integrity": "sha512-vpydAvIJvPODZNagCPuHG87O9JNPtvFEtjHHRVwNVsVVRBqemvPJkc2SYbxJsiZXawJdtZNmkmnsPuE3IgsG0A==",
           "requires": {
             "inherits": "^2.0.3",
             "string_decoder": "^1.1.1",
@@ -4304,9 +4310,9 @@
       "dev": true
     },
     "postcss": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.6.tgz",
-      "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==",
+      "version": "7.0.7",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.7.tgz",
+      "integrity": "sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==",
       "requires": {
         "chalk": "^2.4.1",
         "source-map": "^0.6.1",
@@ -4859,9 +4865,9 @@
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
     "sanitize-html": {
-      "version": "1.19.3",
-      "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.19.3.tgz",
-      "integrity": "sha512-QpIjbF1rhUSQj9V7Wey/gv4DPqOso8KTebaI4rC97p0WCLnTpmhf7BJZUhS83MTtqRvUo8MuXH316CW2Nzd48w==",
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.0.tgz",
+      "integrity": "sha512-BpxXkBoAG+uKCHjoXFmox6kCSYpnulABoGcZ/R3QyY9ndXbIM5S94eOr1IqnzTG8TnbmXaxWoDDzKC5eJv7fEQ==",
       "requires": {
         "chalk": "^2.4.1",
         "htmlparser2": "^3.10.0",
diff --git a/public/javascripts/components/game.js b/public/javascripts/components/game.js
index 16e1fdbb..186c027c 100644
--- a/public/javascripts/components/game.js
+++ b/public/javascripts/components/game.js
@@ -326,7 +326,7 @@ Vue.component('my-game', {
 					]
 				);
 			}
-			if (this.mode == "friend")
+			if (["friend","problem"].includes(this.mode))
 			{
 				actionArray = actionArray.concat(
 				[
@@ -778,7 +778,7 @@ Vue.component('my-game', {
 							h('h3',
 								{
 									domProps: { innerHTML: "Show solution" },
-									on: { click: "toggleShowSolution" }
+									on: { click: this.toggleShowSolution },
 								}
 							),
 							h('p',
@@ -969,9 +969,10 @@ Vue.component('my-game', {
 	methods: {
 		toggleShowSolution: function() {
 			let problemSolution = document.getElementById("problem-solution");
-			problemSolution.style.display = problemSolution.style.display == "none"
-				? "block"
-				: "none";
+			problemSolution.style.display =
+				!problemSolution.style.display || problemSolution.style.display == "none"
+					? "block"
+					: "none";
 		},
 		download: function() {
 			let content = document.getElementById("pgn-game").innerHTML;
@@ -1142,7 +1143,7 @@ Vue.component('my-game', {
 				this.fenStart = localStorage.getItem(prefix+"fenStart");
 			}
 			else
-				this.fenStart = fen;
+				this.fenStart = V.ParseFen(fen).position; //this is enough
 			if (mode=="human")
 			{
 				// Opponent found!
@@ -1168,6 +1169,8 @@ Vue.component('my-game', {
 		},
 		playComputerMove: function() {
 			const timeStart = Date.now();
+			// TODO: next call asynchronous (avoid freezing interface while computer "think").
+			// This would also allow to remove some artificial setTimeouts
 			const compMove = this.vr.getComputerMove();
 			// (first move) HACK: avoid selecting elements before they appear on page:
 			const delay = Math.max(250-(Date.now()-timeStart), 0);
@@ -1375,7 +1378,18 @@ Vue.component('my-game', {
 		undoInGame: function() {
 			const lm = this.vr.lastMove;
 			if (!!lm)
+			{
 				this.vr.undo(lm);
+				const lmBefore = this.vr.lastMove;
+				if (!!lmBefore)
+				{
+					this.vr.undo(lmBefore);
+					this.incheck = this.vr.getCheckSquares(lmBefore);
+					this.vr.play(lmBefore, "ingame");
+				}
+				else
+					this.incheck = [];
+			}
 		},
 	},
 })
diff --git a/public/javascripts/components/problemSummary.js b/public/javascripts/components/problemSummary.js
index c0b409de..e7e1db7c 100644
--- a/public/javascripts/components/problemSummary.js
+++ b/public/javascripts/components/problemSummary.js
@@ -1,20 +1,25 @@
-// Show a problem summary on variant page
+// Show a problem summary on variant page or new problem preview
 Vue.component('my-problem-summary', {
-	props: ['prob'],
+	props: ['prob','preview'],
 	template: `
-		<div class="problem col-sm-12" @click="showProblem()">
-			<div class="diagram" v-html="getDiagram(prob.fen)"></div>
-			<div class="problem-instructions" v-html="prob.instructions.substr(0,32)"></div>
-			<div class="problem-time">{{ timestamp2date(prob.added) }}</div>
+		<div class="problem row" @click="showProblem()">
+			<div class="col-sm-12 col-md-6 col-lg-3 diagram"
+				v-html="getDiagram(prob.fen)">
+			</div>
+			<div class="col-sm-12 col-md-6 col-lg-9">
+				<p v-html="prob.instructions"></p>
+				<p v-if="preview" v-html="prob.solution"></p>
+				<p v-else class="problem-time">{{ timestamp2date(prob.added) }}</p>
+			</div>
 		</div>
 	`,
 	methods: {
 		getDiagram: function(fen) {
-			const fenParts = fen.split(" ");
+			const fenParsed = V.ParseFen(fen);
 			return getDiagram({
-				position: fenParts[0],
+				position: fenParsed.position,
+				turn: fenParsed.turn,
 				// No need for flags here
-				turn: fenParts[2],
 			});
 		},
 		timestamp2date(ts) {
diff --git a/public/javascripts/components/problems.js b/public/javascripts/components/problems.js
index 3bd13f63..9117ebfc 100644
--- a/public/javascripts/components/problems.js
+++ b/public/javascripts/components/problems.js
@@ -16,7 +16,8 @@ Vue.component('my-problems', {
 			<button @click="fetchProblems('forward')">Next</button>
 			<button @click="showNewproblemModal">New</button>
 			<my-problem-summary v-on:show-problem="bubbleUp(p)"
-				v-for="(p,idx) in sortedProblems" v-bind:prob="p" v-bind:key="idx">
+				v-for="(p,idx) in sortedProblems"
+				v-bind:prob="p" v-bind:preview="false" v-bind:key="idx">
 			</my-problem-summary>
 			<input type="checkbox" id="modal-newproblem" class="modal">
 			<div role="dialog" aria-labelledby="newProblemTxt">
@@ -32,18 +33,22 @@ Vue.component('my-problems', {
 							<p class="emphasis">Safe HTML tags allowed</p>
 							<label for="newpbInstructions">Instructions</label>
 							<textarea id="newpbInstructions" v-model="newProblem.instructions"
-								placeholder="Explain the problem here"/>
+								placeholder="Explain the problem here"></textarea>
 							<label for="newpbSolution">Solution</label>
 							<textarea id="newpbSolution" v-model="newProblem.solution"
-								placeholder="How to solve the problem?"/>
+								placeholder="How to solve the problem?"></textarea>
 							<button class="center-btn">Preview</button>
 						</fieldset>
 					</form>
 				</div>
 				<div v-show="newProblem.stage=='preview'" class="card newproblem-preview">
-					// TODO: we don't want exactly the same display (-date +solution)
-					<my-problem-summary v-bind:prob="newProblem"></my-problem-summary>
-					<button @click="sendNewProblem()" class="center-btn">Send</button>
+					<label for="modal-newproblem" class="modal-close"></label>
+					<my-problem-summary v-bind:prob="newProblem" v-bind:preview="true">
+					</my-problem-summary>
+					<div class="col-sm-12 col-md-6 col-lg-3 col-lg-offset-3 topspace">
+						<button @click="sendNewProblem()">Send</button>
+						<button @click="newProblem.stage='nothing'">Cancel</button>
+					</div>
 				</div>
 			</div>
 		</div>
@@ -99,6 +104,8 @@ Vue.component('my-problems', {
 				instructions: this.newProblem.instructions,
 				solution: this.newProblem.solution,
 			}, response => {
+				this.newProblem.added = Date.now();
+				this.problems.push(JSON.parse(JSON.stringify(this.newProblem)));
 				document.getElementById("modal-newproblem").checked = false;
 				this.newProblem.stage = "nothing";
 			});
diff --git a/public/stylesheets/variant.sass b/public/stylesheets/variant.sass
index 6472e3d8..856f22b6 100644
--- a/public/stylesheets/variant.sass
+++ b/public/stylesheets/variant.sass
@@ -246,7 +246,7 @@ ul:not(.browser-default) > li
   margin-left: 0
   margin-right: 0
 
-.newproblem input, .newproblem textarea
+.newproblem-form input, .newproblem-form textarea
   width: 100%
 
 .emphasis
@@ -270,3 +270,13 @@ ul:not(.browser-default) > li
 
 #problem-solution
   display: none
+
+.topspace
+  margin-top: 15px
+
+.problem
+  cursor: pointer
+  margin-bottom: 15px
+
+#solution-div h3
+  cursor: pointer
diff --git a/routes/all.js b/routes/all.js
index b1e0fda3..b9665327 100644
--- a/routes/all.js
+++ b/routes/all.js
@@ -69,7 +69,7 @@ router.post("/problems/:variant([a-zA-Z0-9]+)", (req,res) => {
 	const timestamp = Date.now();
 	// Sanitize them
 	const fen = req.body["fen"];
-	if (!fen.match(/^[a-zA-Z0-9 /]*$/))
+	if (!fen.match(/^[a-zA-Z0-9, /-]*$/))
 		return res.json({errmsg: "Bad characters in FEN string"});
 	const instructions = sanitizeHtml(req.body["instructions"]);
 	const solution = sanitizeHtml(req.body["solution"]);
diff --git a/views/index.pug b/views/index.pug
index 9ef19675..62690a3c 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -21,7 +21,7 @@ block content
 						p.section.
 							All games start with a random assymetric position. #[br]
 							Games are untimed, and played anonymously. #[br]
-							No chat, to rather focus on the moves :)
+							No chat while playing, to focus on the moves :)
 						h3.red.section Bug report
 						p.section
 							| If you find a bug in a game, please follow this procedure: #[br]
-- 
2.44.0