From 42eb4eaf62becc9260897277e1cfe5882eba2017 Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Wed, 6 Feb 2019 19:16:12 +0100
Subject: [PATCH] Seemingly failed attempt at 'componentifying VariantRules'

---
 client/package-lock.json               |  17 ++-
 client/package.json                    |   1 +
 client/src/components/Diagrammer.vue   | 181 +++++++++++++++++++++++++
 client/src/components/VariantRules.vue |  48 +++++++
 client/src/rules/Alice/en.pug          |   4 +-
 client/src/rules/Chess960/fr.pug       |   3 +-
 client/src/utils/printDiagram.js       | 110 ---------------
 client/src/views/Rules.vue             |  55 ++++----
 8 files changed, 270 insertions(+), 149 deletions(-)
 create mode 100644 client/src/components/Diagrammer.vue
 create mode 100644 client/src/components/VariantRules.vue
 delete mode 100644 client/src/utils/printDiagram.js

diff --git a/client/package-lock.json b/client/package-lock.json
index 020d47e0..76d6211d 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -8837,6 +8837,17 @@
         "pug-walk": "^1.1.7"
       }
     },
+    "pug-loader": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/pug-loader/-/pug-loader-2.4.0.tgz",
+      "integrity": "sha512-cD4bU2wmkZ1EEVyu0IfKOsh1F26KPva5oglO1Doc3knx8VpBIXmFHw16k9sITYIjQMCnRv1vb4vfQgy7VdR6eg==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0",
+        "pug-walk": "^1.0.0",
+        "resolve": "^1.1.7"
+      }
+    },
     "pug-parser": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.0.tgz",
@@ -9005,9 +9016,9 @@
       },
       "dependencies": {
         "ajv-keywords": {
-          "version": "3.2.0",
-          "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
-          "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.3.0.tgz",
+          "integrity": "sha512-CMzN9S62ZOO4sA/mJZIO4S++ZM7KFWzH3PPWkveLhy4OZ9i1/VatgwWMD46w/XbGCBy7Ye0gCk+Za6mmyfKK7g==",
           "dev": true
         },
         "schema-utils": {
diff --git a/client/package.json b/client/package.json
index 62781668..6f8f270a 100644
--- a/client/package.json
+++ b/client/package.json
@@ -21,6 +21,7 @@
     "lint-staged": "^8.1.0",
     "node-sass": "^4.9.0",
     "pug": "^2.0.3",
+    "pug-loader": "^2.4.0",
     "pug-plain-loader": "^1.0.0",
     "raw-loader": "^1.0.0",
     "sass-loader": "^7.0.1",
diff --git a/client/src/components/Diagrammer.vue b/client/src/components/Diagrammer.vue
new file mode 100644
index 00000000..04767dfe
--- /dev/null
+++ b/client/src/components/Diagrammer.vue
@@ -0,0 +1,181 @@
+<script>
+import { store } from "@/store";
+import { ArrayFun } from "@/utils/array";
+export default {
+  name: "my-diagrammer",
+  props: ["fen","vname"],
+  data: {
+    function() {
+      return {
+        st: store.state,
+        // args: object with position (mandatory), and
+        // orientation, marks, shadow (optional)
+        args: this.parseFen(this.fen),
+      };
+    }
+  },
+  render(h) {
+    if (!window.V)
+      return;
+    // Obtain the array of pieces images names:
+    const board = V.GetBoard(this.args.position);
+    const orientation = this.args.orientation || "w";
+    const markArray = this.getMarkArray(this.args.marks);
+    const shadowArray = this.getShadowArray(this.args.shadow);
+//    const [startX,startY,inc] = orientation == 'w'
+//      ? [0, 0, 1]
+//      : [V.size.x-1, V.size.y-1, -1];
+    const diagDiv = h(
+      'div',
+      {
+        'class': {
+          'diagram': true,
+        },
+      },
+      [...Array(V.size.x).keys()].map(i => {
+        let ci = (orientation=='w' ? i : sizeX-i-1);
+        return h(
+          'div',
+          {
+            'class': {
+              'row': true,
+            },
+          },
+          [...Array(V.size.y).keys()].map(j => {
+            let cj = (orientation=='w' ? j : sizeY-j-1);
+            let elems = [];
+            if (board[ci][cj] != V.EMPTY)
+            {
+              elems.push(
+                h(
+                  'img',
+                  {
+                    'class': {
+                      'piece': true,
+                    },
+                    attrs: {
+                      src: require("@/assets/images/pieces/" +
+                        V.getPpath(board[ci][cj]) + ".svg"),
+                    },
+                  }
+                )
+              );
+            }
+            if (markArray.length > 0 && markArray[ci][cj])
+            {
+              elems.push(
+                h(
+                  'img',
+                  {
+                    'class': {
+                      'mark-square': true,
+                    },
+                    attrs: {
+                      src: "/images/mark.svg",
+                    },
+                  }
+                )
+              );
+            }
+            return h(
+              'div',
+              {
+                'class': {
+                  'board': true,
+                  ['board'+V.size.y]: true,
+                  'light-square': (i+j)%2==0,
+                  'dark-square': (i+j)%2==1,
+                  [this.st.bcolor]: true,
+                  'in-shadow': shadowArray.length > 0 && shadowArray[ci][cj],
+                },
+              },
+              elems
+            );
+          })
+        );
+      })
+    );
+    return diagDiv;
+  },
+  methods: {
+    parseFen: function(fen) {
+      const fenParts = fen.split(" ");
+      return {
+        position: fenParts[0],
+        marks: fenParts[1],
+        orientation: fenParts[2],
+        shadow: fenParts[3],
+      };
+    },
+    // Turn (human) marks into coordinates
+    getMarkArray: function(marks) {
+      if (!marks || marks == "-")
+        return [];
+      let markArray = ArrayFun.init(V.size.x, V.size.y, false);
+      const squares = marks.split(",");
+      for (let i=0; i<squares.length; i++)
+      {
+        const coords = V.SquareToCoords(squares[i]);
+        markArray[coords.x][coords.y] = true;
+      }
+      return markArray;
+    },
+    // Turn (human) shadow indications into coordinates
+    getShadowArray: function(shadow) {
+      if (!shadow || shadow == "-")
+        return [];
+      let shadowArray = ArrayFun.init(V.size.x, V.size.y, false);
+      const squares = shadow.split(",");
+      for (let i=0; i<squares.length; i++)
+      {
+        const rownum = V.size.x - parseInt(squares[i]);
+        if (!isNaN(rownum))
+        {
+          // Shadow a full row
+          for (let i=0; i<V.size.y; i++)
+            shadowArray[rownum][i] = true;
+          continue;
+        }
+        if (squares[i].length == 1)
+        {
+          // Shadow a full column
+          const colnum = V.ColumnToCoord(squares[i]);
+          for (let i=0; i<V.size.x; i++)
+            shadowArray[i][colnum] = true;
+          continue;
+        }
+        if (squares[i].indexOf("-") >= 0)
+        {
+          // Shadow a range of squares, horizontally or vertically
+          const firstLastSq = squares[i].split("-");
+          const range =
+          [
+            V.SquareToCoords(firstLastSq[0]),
+            V.SquareToCoords(firstLastSq[1])
+          ];
+          const step =
+          [
+            range[1].x == range[0].x
+              ? 0
+              : (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x),
+            range[1].y == range[0].y
+              ? 0
+              : (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y)
+          ];
+          // Convention: range always from smaller to larger number
+          for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y;
+            x += step[0], y += step[1])
+          {
+            shadowArray[x][y] = true;
+          }
+          continue;
+        }
+        // Shadow just one square:
+        const coords = V.SquareToCoords(squares[i]);
+        shadowArray[coords.x][coords.y] = true;
+      }
+      return shadowArray;
+    },
+  },
+};
+</script>
diff --git a/client/src/components/VariantRules.vue b/client/src/components/VariantRules.vue
new file mode 100644
index 00000000..a2c89e32
--- /dev/null
+++ b/client/src/components/VariantRules.vue
@@ -0,0 +1,48 @@
+<!--<template :src="require(`@/rules/${vname}/${st.lang}.pug`)">
+</template>
+-->
+
+<template lang="pug">
+.section-content(v-html="content")
+</template>
+
+<script>
+import Diagrammer from "@/components/Diagrammer";
+import { store } from "@/store";
+export default {
+  name: "my-variant-rules",
+  components: {
+    Diagrammer,
+  },
+  props: ["vname"],
+  data: function() {
+    return {
+      st: store.state,
+      content: "",
+    };
+  },
+  watch: {
+    vname: function() {
+      this.loadVariantFile();
+    },
+  },
+  methods: {
+    loadVariantFile: function() {
+      if (this.vname != "_unknown")
+      {
+        // TODO (to understand): no loader required here ? Pug preset ?
+        const content = require("raw-loader!@/rules/" + this.vname + "/" + this.st.lang + ".pug");
+        console.log(content);
+        this.content = content;
+      }
+    },
+  },
+  created: function() {
+    this.loadVariantFile();
+  },
+};
+</script>
+
+<!--
+  TODO: template + script dans rules/Alice/en.pug (-> .vue), puis dynamic import ici ?!
+-->
diff --git a/client/src/rules/Alice/en.pug b/client/src/rules/Alice/en.pug
index be97d1b4..6bba6407 100644
--- a/client/src/rules/Alice/en.pug
+++ b/client/src/rules/Alice/en.pug
@@ -21,8 +21,8 @@ ul
 
 figure.diagram-container
   // TODO: sub-component + use pug-loader instead of raw-loader
-  Diagram(:fen="rnbqkbnr/ppp1pppp/8/8/2p5/5O2/PP1PPPPP/RNBQKB1R")
-  .diagram
+  Diagram(fen="rnbqkbnr/ppp1pppp/8/8/2p5/5O2/PP1PPPPP/RNBQKB1R" vname="Alice")
+  //.diagram
     | fen:rnbqkbnr/ppp1pppp/8/8/2p5/5O2/PP1PPPPP/RNBQKB1R:
   figcaption After the moves 1.Nf3 Pd5 2.Pc4 Sxc4
 
diff --git a/client/src/rules/Chess960/fr.pug b/client/src/rules/Chess960/fr.pug
index 9b62de16..3325f810 100644
--- a/client/src/rules/Chess960/fr.pug
+++ b/client/src/rules/Chess960/fr.pug
@@ -16,8 +16,7 @@ p.
 	en arrivant sur une case occupée (avec une seule exception, détaillée plus bas).
 
 figure.diagram-container
-	.diagram
-		| fen:rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR:
+	Diagrammer(fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" vname="Chess960")
 	figcaption Position de départ habituelle.
 
 p.
diff --git a/client/src/utils/printDiagram.js b/client/src/utils/printDiagram.js
deleted file mode 100644
index 7ae23228..00000000
--- a/client/src/utils/printDiagram.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import { ArrayFun } from "@/utils/array";
-
-// Turn (human) marks into coordinates
-function getMarkArray(marks)
-{
-	if (!marks || marks == "-")
-		return [];
-	let markArray = ArrayFun.init(V.size.x, V.size.y, false);
-	const squares = marks.split(",");
-	for (let i=0; i<squares.length; i++)
-	{
-		const coords = V.SquareToCoords(squares[i]);
-		markArray[coords.x][coords.y] = true;
-	}
-	return markArray;
-}
-
-// Turn (human) shadow indications into coordinates
-function getShadowArray(shadow)
-{
-	if (!shadow || shadow == "-")
-		return [];
-	let shadowArray = ArrayFun.init(V.size.x, V.size.y, false);
-	const squares = shadow.split(",");
-	for (let i=0; i<squares.length; i++)
-	{
-		const rownum = V.size.x - parseInt(squares[i]);
-		if (!isNaN(rownum))
-		{
-			// Shadow a full row
-			for (let i=0; i<V.size.y; i++)
-				shadowArray[rownum][i] = true;
-			continue;
-		}
-		if (squares[i].length == 1)
-		{
-			// Shadow a full column
-			const colnum = V.ColumnToCoord(squares[i]);
-			for (let i=0; i<V.size.x; i++)
-				shadowArray[i][colnum] = true;
-			continue;
-		}
-		if (squares[i].indexOf("-") >= 0)
-		{
-			// Shadow a range of squares, horizontally or vertically
-			const firstLastSq = squares[i].split("-");
-			const range =
-			[
-				V.SquareToCoords(firstLastSq[0]),
-				V.SquareToCoords(firstLastSq[1])
-			];
-			const step =
-			[
-				range[1].x == range[0].x
-					? 0
-					: (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x),
-				range[1].y == range[0].y
-					? 0
-					: (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y)
-			];
-			// Convention: range always from smaller to larger number
-			for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y;
-				x += step[0], y += step[1])
-			{
-				shadowArray[x][y] = true;
-			}
-			continue;
-		}
-		// Shadow just one square:
-		const coords = V.SquareToCoords(squares[i]);
-		shadowArray[coords.x][coords.y] = true;
-	}
-	return shadowArray;
-}
-
-// args: object with position (mandatory), and
-// orientation, marks, shadow (optional)
-export function getDiagram(args)
-{
-	// Obtain the array of pieces images names:
-	const board = V.GetBoard(args.position);
-	const orientation = args.orientation || "w";
-	const markArray = getMarkArray(args.marks);
-	const shadowArray = getShadowArray(args.shadow);
-	let boardDiv = "";
-	const [startX,startY,inc] = orientation == 'w'
-		? [0, 0, 1]
-		: [V.size.x-1, V.size.y-1, -1];
-	for (let i=startX; i>=0 && i<V.size.x; i+=inc)
-	{
-		boardDiv += "<div class='row'>";
-		for (let j=startY; j>=0 && j<V.size.y; j+=inc)
-		{
-			boardDiv += "<div class='board board" + V.size.y + " " +
-				((i+j)%2==0 ? "light-square-diag" : "dark-square-diag") +
-				(shadowArray.length > 0 && shadowArray[i][j] ? " in-shadow" : "") +
-				"'>";
-			if (board[i][j] != V.EMPTY)
-			{
-				boardDiv += "<img :src='require(`@/assets/images/pieces/" +
-					V.getPpath(board[i][j]) + ".svg`) class='piece'/>";
-			}
-			if (markArray.length > 0 && markArray[i][j])
-				boardDiv += "<img src='/images/mark.svg' class='mark-square'/>";
-			boardDiv += "</div>";
-		}
-		boardDiv += "</div>";
-	}
-	return boardDiv;
-}
diff --git a/client/src/views/Rules.vue b/client/src/views/Rules.vue
index fd26f16b..fd668462 100644
--- a/client/src/views/Rules.vue
+++ b/client/src/views/Rules.vue
@@ -9,7 +9,7 @@
         | Beat the computer!
       button(v-show="gameInProgress" @click="stopGame")
         | Stop game
-    div(v-show="display=='rules'" v-html="content" class="section-content")
+    VariantRules(v-show="display=='rules'" :vname="variant.name")
     Game(v-show="display=='computer'" :gid-or-fen="fen"
       :mode="mode" :sub-mode="subMode" :variant="variant"
       @computer-think="gameInProgress=false" @game-over="stopGame")
@@ -18,17 +18,17 @@
 <script>
 import Game from "@/components/Game.vue";
 import { store } from "@/store";
-import { getDiagram } from "@/utils/printDiagram";
+import VariantRules from "@/components/VariantRules";
 export default {
   name: 'my-rules',
   components: {
     Game,
+    VariantRules,
   },
   data: function() {
     return {
       st: store.state,
-      variant: {id: 0, name: "Unknown"}, //...yet
-      content: "",
+      variant: {id: 0, name: "_unknown"}, //...yet
       display: "rules",
       mode: "computer",
       subMode: "", //'auto' for game CPU vs CPU
@@ -37,37 +37,28 @@ export default {
       fen: "",
     };
   },
-  mounted: async function() {
+  watch: {
+    $route: function(newRoute) {
+      this.tryChangeVariant(newRoute.params["vname"]);
+    },
+  },
+  created: async function() {
     // NOTE: variant cannot be set before store is initialized
-    const vname = this.$route.params["vname"];
-    //const idxOfVar = this.st.variants.indexOf(e => e.name == vname);
-    //this.variant = this.st.variants[idxOfVar]; //TODO: is it the right timing?!
-    this.variant.name = vname;
-    const vModule = await import("@/variants/" + vname + ".js");
-    window.V = vModule.VariantRules;
-    // Method to replace diagrams in loaded HTML
-    const replaceByDiag = (match, p1, p2) => {
-      const args = this.parseFen(p2);
-      return getDiagram(args);
-    };
-    // (AJAX) Request to get rules content (plain text, HTML)
-    // TODO: find a way to have Diagram(er) as a component,
-    // thus allowing images import through require(), handled by Webpack
-    // ==> the rules files should be less static
-    this.content =
-      // TODO: why doesn't this work? require("raw-loader!pug-plain-loader!@/rules/"...)
-      require("raw-loader!@/rules/" + vname + "/" + this.st.lang + ".pug")
-      .replace(/(fen:)([^:]*):/g, replaceByDiag);
+    this.tryChangeVariant(this.$route.params["vname"]);
   },
   methods: {
-    parseFen(fen) {
-      const fenParts = fen.split(" ");
-      return {
-        position: fenParts[0],
-        marks: fenParts[1],
-        orientation: fenParts[2],
-        shadow: fenParts[3],
-      };
+    tryChangeVariant: async function(vname) {
+      if (vname == "_unknown")
+        return;
+      if (this.st.variants.length > 0)
+      {
+        const idxOfVar = this.st.variants.findIndex(e => e.name == vname);
+        this.variant = this.st.variants[idxOfVar];
+      }
+      else
+        this.variant.name = vname;
+      const vModule = await import("@/variants/" + vname + ".js");
+      window.V = vModule.VariantRules;
     },
     startGame: function() {
       if (this.gameInProgress)
-- 
2.44.0