Browse Source

[add] Parse grid
[add] create grid
[add] api poll grid

isundil 8 years ago
parent
commit
547dc2e7cd
4 changed files with 292 additions and 85 deletions
  1. 2 3
      crosswords.js
  2. 123 10
      src/Grid.js
  3. 25 4
      src/GridManager.js
  4. 142 68
      src/httpServer.js

+ 2 - 3
crosswords.js

@@ -2,9 +2,8 @@
 const config = require("./config.js");
 
 process.on("uncaughtException", function (err) {
-    console.error('err uncaught Exception  : ', err);
+	console.error('err uncaught Exception  : ', err);
 });
 
-//require("./src/httpServer.js").serve(config);
-var g = new (require('./src/Grid.js').Grid)(require('./test.js'));
+require("./src/httpServer.js").serve(config);
 

+ 123 - 10
src/Grid.js

@@ -5,6 +5,10 @@ function GridCell(x, y) {
     this.version = 0;
 }
 
+GridCell.prototype.toStatic = function() {
+    return { x: this.px, y: this.py };
+};
+
 function LetterCell(x, y, letter) {
     GridCell.call(this, x, y);
     this.letter = letter;
@@ -13,55 +17,164 @@ function LetterCell(x, y, letter) {
 LetterCell.prototype = Object.create(GridCell);
 LetterCell.prototype.constructor = LetterCell;
 
+LetterCell.prototype.toStatic = function() {
+    var ret = GridCell.prototype.toStatic.call(this);
+    if (this.found) {
+        ret.letter = this.letter;
+    }
+    return ret;
+};
+
 var debugFound = {};
 
 function Definition(position, alignment, text) {
+    this.position = position;
+    this.alignment = alignment;
+    this.textArray = text;
 }
 
-Definition.POSITION_TOP = {};
 Definition.POSITION_BOTTOM = {};
 Definition.POSITION_RIGHT = {};
-Definition.POSITION_LEFT = {};
 
 Definition.ALIGNMENT_HORIZONTAL = {};
 Definition.ALIGNMENT_VERTICAL = {};
 
+Definition.prototype.toStatic = function() {
+    var ret = {
+        text: this.textArray
+    };
+    if (this.position === Definition.POSITION_RIGHT && this.alignment === Definition.ALIGNMENT_HORIZONTAL) {
+        ret.pos = 1;
+    } else if (this.position === Definition.POSITION_RIGHT && this.alignment === Definition.ALIGNMENT_VERTICAL) {
+        ret.pos = 2;
+    } else if (this.position === Definition.POSITION_BOTTOM && this.alignment === Definition.ALIGNMENT_HORIZONTAL) {
+        ret.pos = 3;
+    } else {
+        ret.pos = 4;
+    }
+    return ret;
+};
+
 function DefinitionCell(x, y, code, definitionArray, currentDefinition) {
     if (debugFound[code]) debugFound[code].push({x:x,y:y}); else debugFound[code] = [{x:x,y:y}];
     GridCell.call(this, x, y);
     this.definitions = [];
-    if ()
+    switch (code) {
+        case 0: // a
+            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
+        break;
+
+        case 1: // b
+            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
+        break;
+
+        case 2: // c
+            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
+        break;
+
+        case 3: // d
+            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
+        break;
+
+        case 4: // e
+        case 5: // f
+        case 6: // g
+        case 7: // h
+        case 8: // i
+            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
+            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
+        break;
+
+        case 9: // j
+        case 10: // k
+        case 11: // l
+        case 12: // m
+        case 13: // n
+            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
+            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
+        break;
+
+        case 14: // o
+        case 15: // p
+        case 16: // q
+        case 17: // r
+        case 18: // s
+            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
+            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
+        break;
+
+        case 19: // t
+        case 20: // u
+        case 21: // v
+        case 22: // w
+        case 23: // x
+            this.definitions.push(new Definition(Definition.POSITION_RIGHT, Definition.ALIGNMENT_VERTICAL, definitionArray[currentDefinition++]));
+            this.definitions.push(new Definition(Definition.POSITION_BOTTOM, Definition.ALIGNMENT_HORIZONTAL, definitionArray[currentDefinition++]));
+        break;
+    }
 }
 DefinitionCell.prototype = Object.create(GridCell);
 DefinitionCell.prototype.constructor = DefinitionCell;
 
+DefinitionCell.prototype.toStatic = function() {
+    var ret = GridCell.prototype.toStatic.call(this);
+    ret.definitions = [];
+    this.definitions.forEach((def) => {
+        ret.definitions.push(def.toStatic());
+    });
+    return ret;
+};
+
 function parseGrid(grid, definitions, w, h) {
     var currentDefinition = 0
         ,resultGrid = []
         ,firstCharCode = 'a'.charCodeAt(0);
 
-    for (var i =0; i < h; i++) {
-        for (var j =0; j < w; j++) {
-            var c = grid[i][j];
+    for (let i =0; i < h; i++) {
+        for (let j =0; j < w; j++) {
+            let c = grid[i][j];
             if (c == c.toUpperCase()) {
-                resultGrid.push(new LetterCell(i, j, c));
+                resultGrid.push(new LetterCell(j, i, c));
+            } else if (c == 'z') {
+                resultGrid.push(null);
             } else {
-                var cell = new DefinitionCell(i, j, c.charCodeAt(0) -firstCharCode, definitions, currentDefinition);
+                let cell = new DefinitionCell(j, i, c.charCodeAt(0) -firstCharCode, definitions, currentDefinition);
                 currentDefinition += cell.definitions.length;
                 resultGrid.push(cell);
             }
         }
     }
-    console.log(debugFound);
     return resultGrid;
 }
 
-function Grid(data) {
+function Grid(publicId, data) {
     this.title = data["titre"];
     this.difficulty = data["force"];
     this.width = data["nbcaseslargeur"];
     this.height = data["nbcaseshauteur"];
     this.grid = parseGrid(data["grille"], data["definitions"], this.width, this.height);
+    this.publicId = publicId;
+};
+
+Grid.prototype.toStatic = function(v) {
+    var ret = {};
+    if (!v) {
+        ret.title = this.title;
+        ret.difficulty = this.difficulty;
+        ret.w = this.width;
+        ret.h = this.height;
+    }
+    var grid = [];
+    this.grid.forEach((cell) => {
+        if (cell instanceof DefinitionCell && !v) {
+            grid.push(cell.toStatic());
+        } else if (cell instanceof LetterCell && cell.found && cell.v >= v) {
+            grid.push(cell.toStatic());
+        }
+    });
+    if (grid.length)
+        ret.grid = grid;
+    return grid.length || !v ? ret : null;
 };
 
 module.exports.Grid = Grid;

+ 25 - 4
src/GridManager.js

@@ -1,6 +1,7 @@
 
 const http = require('http')
     ,util = require('util')
+    ,crypto = require('crypto')
     ,Grid = require('./Grid.js').Grid;
 
 const RCILIST = 'http://rcijeux.fr/drupal_game/20minutes/menu/js/jeux_mfleches.js?date=1495635261516'
@@ -11,7 +12,8 @@ const RCILIST = 'http://rcijeux.fr/drupal_game/20minutes/menu/js/jeux_mfleches.j
 const GRID_CACHE = {
     data: {},
     expireAt: 0
-};
+}
+    ,GRID_DATA = {}; // TODO expiracy
 
 function extractVar(data) {
     var first = data.indexOf('{')
@@ -21,7 +23,6 @@ function extractVar(data) {
         return null;
     try {
         var dataObj;
-        console.log(data.substr(first, last));
         eval("dataObj="+data.substr(first, last));
         return dataObj;
     } catch (e) {
@@ -75,10 +76,30 @@ module.exports.listGrids = function(cb) {
     }
 };
 
-module.exports.getGrid = function(gridId, cb) {
+module.exports.createGrid = function(gridPublicId, gridId, cb) {
     console.log("[gridManager] get grid " +gridId);
+    // FIXME DEBUG
+    var grid = GRID_DATA[gridPublicId] = new Grid(gridPublicId, require('../test.js'));
+    cb(grid);
+    return;
+    // END DEBUG
     getVar(util.format(RCIGETGRID, gridId), (data) => {
-        cb(data ? new Grid(data) : null);
+        if (data) {
+            var grid = GRID_DATA[gridPublicId] = new Grid(gridPublicId, data);
+            cb(grid);
+        } else {
+            cb(null);
+        }
     });
 };
 
+module.exports.get = function(gridPublicId) {
+    return GRID_DATA[gridPublicId] || null;
+};
+
+module.exports.hash = function(toHash) {
+    var md5sum = crypto.createHash('sha1');
+    md5sum.update(toHash);
+    return md5sum.digest('base64');
+};
+

+ 142 - 68
src/httpServer.js

@@ -1,94 +1,168 @@
 
 const http = require('http')
-	,fs = require('fs');
+    ,fs = require('fs')
+
+    ,GridManager = require('./GridManager.js');
 
 function HttpServer(config) {
-	var ctx = this;
-
-	this.config = config;
-	this.httpServ = http.createServer(function(req, res) {
-		req.reqT = new Date();
-		res.ended = false;
-		res.once('end', () => {
-			res.ended = true;
-		});
-		if (ctx.isRequestPublic(req.url)) {
-			try {
-				ctx.servePublic(req.url, res);
-			} catch (e) {
-				if (e instanceof HttpServer.Error404) {
-					res.writeHeader("404", "Not found");
-					ctx.servePublic("/404.html", res);
-				} else {
-					console.error("Unknown error", e);
-				}
-			}
-		} else {
-			ctx.serveApi(req, res);
-		}
-		ctx.logRequest(req, res);
-	});
+    var ctx = this;
+
+    this.config = config;
+    this.httpServ = http.createServer(function(req, res) {
+        req.reqT = new Date();
+        res.ended = false;
+        res.once('end', () => {
+            res.ended = true;
+        });
+        try {
+            if (ctx.isRequestPublic(req.url)) {
+                ctx.servePublic(req.url, res);
+            } else {
+                var url = req.url.substr(req.url.indexOf("/api/") +5);
+                ctx.serveApi(req, url, res);
+            }
+        } catch (e) {
+            if (e instanceof HttpServer.Error404) {
+                res.writeHeader("404", "Not found");
+                ctx.servePublic("/404.html", res);
+            } else {
+                console.error("Unknown error", e);
+            }
+        }
+        ctx.logRequest(req, res);
+    });
 };
 
 HttpServer.prototype.run = function() {
-	this.httpServ.listen(this.config.port, () => {
-		console.log("[http] listening on " +this.config.port);
-	});
+    this.httpServ.listen(this.config.port, () => {
+        console.log("[http] listening on " +this.config.port);
+    });
 };
 
 HttpServer.prototype.logRequest = function(req, res) {
-	console.log("[http] "
-			+req.reqT.toISOString()
-			+" "
-			+req.socket.remoteAddress
-			+" "
-			+res.statusCode);
+    console.log("[http] "
+            +req.reqT.toISOString()
+            +" "
+            +req.socket.remoteAddress
+            +" "
+            +res.statusCode);
 };
 
 HttpServer.prototype.isRequestPublic = function(url) {
-	return url.indexOf("/api/") === -1;
+    return url.indexOf("/api/") === -1;
 };
 
 HttpServer.prototype.servePublic = function(url, res) {
-	url.replace(/\W/g, '');
-	var pathTokens = url.split('/')
-		,localPath = './public';
-
-	pathTokens.forEach(function(i) {
-		i = i.trim();
-		if (i != '' && i[0] != '.') {
-			localPath += '/' +i;
-		}
-	});
-	var stat;
-	try {
-		var stat = fs.lstatSync(localPath);
-		if (stat.isDirectory()) {
-			localPath += "/index.html";
-			stat = fs.lstatSync(localPath);
-		}
-	} catch (e) {
-		throw new HttpServer.Error404(localPath);
-	}
-	if (!res.headersSent) {
-		if (!this.config.isDebug) {
-			res.setHeader("Cache-Control", "private, max-age=900");
-		}
-		res.setHeader("Content-Length", stat.size);
-	}
-	fs.createReadStream(localPath).pipe(res, { end: true });
+    url.replace(/\W/g, '');
+    var pathTokens = url.split('/')
+        ,localPath = './public';
+
+    pathTokens.forEach(function(i) {
+        i = i.trim();
+        if (i != '' && i[0] != '.') {
+            localPath += '/' +i;
+        }
+    });
+    var stat;
+    try {
+        var stat = fs.lstatSync(localPath);
+        if (stat.isDirectory()) {
+            localPath += "/index.html";
+            stat = fs.lstatSync(localPath);
+        }
+    } catch (e) {
+        throw new HttpServer.Error404(localPath);
+    }
+    if (!res.headersSent) {
+        if (!this.config.isDebug) {
+            res.setHeader("Cache-Control", "private, max-age=900");
+        }
+        res.setHeader("Content-Length", stat.size);
+    }
+    fs.createReadStream(localPath).pipe(res, { end: true });
 };
 
-HttpServer.prototype.serveApi = function(req, res) {
-	res.end("Coucou");
+HttpServer.parseUrlParams = function(urlSearch) {
+    var urlToken = {};
+
+    urlSearch.split('&').forEach((token) => {
+        var tokenSplit = token.indexOf('=')
+            ,tokenKey
+            ,tokenValue;
+
+        if (tokenSplit === -1) {
+            tokenKey = token;
+            tokenValue = true;
+        } else {
+            tokenKey = token.substr(0, tokenSplit);
+            tokenValue = token.substr(tokenSplit +1);
+        }
+        if (urlToken[tokenKey]) {
+            urlToken[tokenKey].push(tokenValue);
+        } else {
+            urlToken[tokenKey] = [ tokenValue ];
+        }
+    });
+    return urlToken;
+};
+
+HttpServer.prototype.serveApi = function(req, url, res) {
+    var pos = url.indexOf('?')
+        ,urlToken = {};
+
+    if (pos !== -1) {
+        urlToken = HttpServer.parseUrlParams(url.substr(pos +1));
+        url = url.substr(0, pos);
+    }
+    if (url === "create") {
+        // TODO limit grid creation 1 minute by ip
+        var gridId = urlToken["gridId"] ? urlToken["gridId"][0] : null;
+        if (!gridId) {
+            var dd = req.reqT.getDate()
+                ,mm = req.reqT.getMonth() +1
+                ,yy = req.reqT.getFullYear() -2000;
+            gridId = (dd < 10 ? '0' +dd :dd) +(mm < 10 ? '0' +mm : mm) +yy;
+        }
+        GridManager.createGrid(GridManager.hash('' +req.socket.remoteAddress +req.reqT.getTime() +gridId), gridId, (grid)=>{
+            if (grid) {
+                res.setHeader("Location", "/#" +grid.publicId);
+                res.writeHeader("302");
+            } else {
+                res.writeHeader("400", "Bad request");
+            }
+            res.end();
+        });
+    } else if (url === "poll") {
+        if (!urlToken["v"] || !urlToken["grid"]) {
+            res.writeHeader("400", "Bad request");
+            res.end();
+            return;
+        }
+        var knownVersion = parseFloat(urlToken["v"][0])
+            ,grid = GridManager.get(urlToken["grid"][0]);
+        if (!grid) {
+            throw new HttpServer.Error404();
+        }
+        var data = grid.toStatic(knownVersion);
+        if (data) {
+            res.setHeader("Content-Type", "application/json; charset=utf-8");
+            res.end(JSON.stringify(data));
+        } else {
+            res.writeHeader("204");
+            res.end();
+        }
+
+    } else {
+        throw new HttpServer.Error404(url);
+    }
 };
 
 HttpServer.Error404 = function(localPath) {
-	console.log("[http] file not found: " +localPath);
+    console.log("[http] file not found: " +localPath);
 };
 
 module.exports.serve = function(config) {
-	var server = new HttpServer(config);
-	server.run();
+    var server = new HttpServer(config);
+    server.run();
 };