Init
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
./node_modules
 | 
			
		||||
./log
 | 
			
		||||
./pid
 | 
			
		||||
./routes/tenhou
 | 
			
		||||
							
								
								
									
										53
									
								
								app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								app.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
var express = require('express'),
 | 
			
		||||
	http = require('http'),
 | 
			
		||||
	favicon = require('serve-favicon'),
 | 
			
		||||
	morgan = require('morgan'),
 | 
			
		||||
	bodyParser = require('body-parser'),
 | 
			
		||||
	methodOverride = require('method-override'),
 | 
			
		||||
	cookieParser = require('cookie-parser'),
 | 
			
		||||
	cookieSession = require('cookie-session'),
 | 
			
		||||
	moment = require('moment'),
 | 
			
		||||
	flash = require('connect-flash'),
 | 
			
		||||
	mj = require('./routes/mj');
 | 
			
		||||
 | 
			
		||||
var app = express();
 | 
			
		||||
 | 
			
		||||
// all environments
 | 
			
		||||
app.set('views', __dirname + '/views');
 | 
			
		||||
app.set('view engine', 'pug');
 | 
			
		||||
app.use(express.static('public'));
 | 
			
		||||
app.use(favicon(__dirname + '/public/img/favicon.ico'));
 | 
			
		||||
 | 
			
		||||
morgan.token('now', function() {
 | 
			
		||||
	return moment().format('DD.MM.YY HH:mm:ss');
 | 
			
		||||
});
 | 
			
		||||
app.use(morgan(':now :req[x-forwarded-for] :method :url :status :res[content-length] - :response-time ms'));
 | 
			
		||||
 | 
			
		||||
app.use(bodyParser.urlencoded({
 | 
			
		||||
	extended: false
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
app.use(bodyParser.json());
 | 
			
		||||
app.use(methodOverride('X-HTTP-Method-Override'));
 | 
			
		||||
app.use(cookieParser());
 | 
			
		||||
app.use(cookieParser('z vfktymrfz kjiflrf'));
 | 
			
		||||
app.use(cookieSession({
 | 
			
		||||
	secret: 'z vfktymrfz kjiflrf'
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
app.use(flash());
 | 
			
		||||
 | 
			
		||||
app.route('/').get(mj.index).post(mj.parse);
 | 
			
		||||
app.get('/get_log', function(req, res) {
 | 
			
		||||
	res.render('get_log');
 | 
			
		||||
});
 | 
			
		||||
app.get('/tiles', function(req, res) {
 | 
			
		||||
	res.render('tiles_svg');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.use(function(req, res) {
 | 
			
		||||
	res.render('404');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
var server = http.createServer(app);
 | 
			
		||||
server.listen(8081);
 | 
			
		||||
							
								
								
									
										31
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "paifu",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "Paifu generator",
 | 
			
		||||
  "main": "app.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1",
 | 
			
		||||
    "dev": "nodemon app.js"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "riichi",
 | 
			
		||||
    "paifu",
 | 
			
		||||
    "tenhou"
 | 
			
		||||
  ],
 | 
			
		||||
  "author": "WarL0ck",
 | 
			
		||||
  "license": "GPL-3.0",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "body-parser": "*",
 | 
			
		||||
    "connect-flash": "*",
 | 
			
		||||
    "cookie-parser": "*",
 | 
			
		||||
    "cookie-session": "*",
 | 
			
		||||
    "express": "*",
 | 
			
		||||
    "htmlparser2": "*",
 | 
			
		||||
    "method-override": "*",
 | 
			
		||||
    "moment": "*",
 | 
			
		||||
    "morgan": "*",
 | 
			
		||||
    "nodemon": "*",
 | 
			
		||||
    "pug": "*",
 | 
			
		||||
    "serve-favicon": "*"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										443
									
								
								public/css/bundle.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										443
									
								
								public/css/bundle.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,443 @@
 | 
			
		||||
.tile img,
 | 
			
		||||
.tile-rot img {
 | 
			
		||||
	position: relative
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.pull-left {
 | 
			
		||||
	float: left
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hide {
 | 
			
		||||
	display: none
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.show {
 | 
			
		||||
	display: block
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile,
 | 
			
		||||
.tile-rot {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
	border: 1px solid #333
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile {
 | 
			
		||||
	width: 38px;
 | 
			
		||||
	height: 52px;
 | 
			
		||||
	-webkit-box-shadow: 3px 3px 15px 0 rgba(0, 0, 0, .5);
 | 
			
		||||
	box-shadow: 3px 3px 15px 0 rgba(0, 0, 0, .5)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot {
 | 
			
		||||
	width: 52px;
 | 
			
		||||
	height: 38px;
 | 
			
		||||
	-webkit-box-shadow: 3px 3px 15px 0 rgba(0, 0, 0, .5);
 | 
			
		||||
	box-shadow: 3px 3px 15px 0 rgba(0, 0, 0, .5)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile .wrap,
 | 
			
		||||
.tile-rot .wrap {
 | 
			
		||||
	display: block;
 | 
			
		||||
	width: 500px;
 | 
			
		||||
	height: 500px;
 | 
			
		||||
	text-indent: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.closed-part {
 | 
			
		||||
	padding: 9px;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	margin-bottom: 10px;
 | 
			
		||||
	margin-right: 20px;
 | 
			
		||||
	background-color: #ccc;
 | 
			
		||||
	border-radius: 5px;
 | 
			
		||||
	-webkit-border-radius: 5px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-upper {
 | 
			
		||||
	margin-bottom: 40px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-lower {
 | 
			
		||||
	margin-left: -54px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m1 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m2 img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m3 img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m4 img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m5 img {
 | 
			
		||||
	left: -152px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m6 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -52px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m7 img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: -52px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m8 img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: -52px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-m9 img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: -52px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p1 img {
 | 
			
		||||
	left: -152px;
 | 
			
		||||
	top: -52px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p2 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -104px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p3 img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: -104px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p4 img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: -104px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p5 img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: -104px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p6 img {
 | 
			
		||||
	left: -152px;
 | 
			
		||||
	top: -104px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p7 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -156px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p8 img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: -156px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-p9 img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: -156px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s1 img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: -156px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s2 img {
 | 
			
		||||
	left: -152px;
 | 
			
		||||
	top: -156px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s3 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -208px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s4 img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: -208px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s5 img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: -208px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s6 img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: -208px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s7 img {
 | 
			
		||||
	left: -152px;
 | 
			
		||||
	top: -208px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s8 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -260px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-s9 img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: -260px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-tan img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: -260px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-nan img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: -260px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-xia img {
 | 
			
		||||
	left: -152px;
 | 
			
		||||
	top: -260px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-pei img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -312px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-hatsu img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: -312px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-chun img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: -312px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-haku img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: -312px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-ma img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -364px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-pa img {
 | 
			
		||||
	left: -38px;
 | 
			
		||||
	top: -364px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-sa img {
 | 
			
		||||
	left: -76px;
 | 
			
		||||
	top: -364px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-x img {
 | 
			
		||||
	left: -114px;
 | 
			
		||||
	top: -364px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m1 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m2 img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m3 img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m4 img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m5 img {
 | 
			
		||||
	left: -208px;
 | 
			
		||||
	top: 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m6 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -38px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m7 img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: -38px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m8 img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: -38px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-m9 img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: -38px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p1 img {
 | 
			
		||||
	left: -208px;
 | 
			
		||||
	top: -38px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p2 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -76px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p3 img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: -76px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p4 img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: -76px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p5 img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: -76px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p6 img {
 | 
			
		||||
	left: -208px;
 | 
			
		||||
	top: -76px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p7 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -114px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p8 img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: -114px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-p9 img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: -114px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s1 img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: -114px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s2 img {
 | 
			
		||||
	left: -208px;
 | 
			
		||||
	top: -114px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s3 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -152px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s4 img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: -152px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s5 img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: -152px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s6 img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: -152px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s7 img {
 | 
			
		||||
	left: -208px;
 | 
			
		||||
	top: -152px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s8 img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -190px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-s9 img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: -190px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-tan img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: -190px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-nan img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: -190px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-xia img {
 | 
			
		||||
	left: -208px;
 | 
			
		||||
	top: -190px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-pei img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -228px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-hatsu img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: -228px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-chun img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: -228px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-haku img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: -228px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-ma img {
 | 
			
		||||
	left: 0;
 | 
			
		||||
	top: -266px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-pa img {
 | 
			
		||||
	left: -52px;
 | 
			
		||||
	top: -266px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-sa img {
 | 
			
		||||
	left: -104px;
 | 
			
		||||
	top: -266px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-rot-x img {
 | 
			
		||||
	left: -156px;
 | 
			
		||||
	top: -266px
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										261
									
								
								public/css/main.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								public/css/main.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,261 @@
 | 
			
		||||
html {
 | 
			
		||||
	height: 99%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
* html body {
 | 
			
		||||
	height: 99%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
	background: #f2f2f2;
 | 
			
		||||
	min-height: 99%;
 | 
			
		||||
	position: relative;
 | 
			
		||||
	padding: 3px;
 | 
			
		||||
	font: 12px "Lucida Grande", Helvetica, Arial, sans-serif;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
	font-size: 15px;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h2 {
 | 
			
		||||
	font-size: 13px;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h3 {
 | 
			
		||||
	font-size: 11px;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h4 {
 | 
			
		||||
	font-size: 9px;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h5 {
 | 
			
		||||
	font-size: 7px;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
img {
 | 
			
		||||
	padding: 0;
 | 
			
		||||
	margin: 0;
 | 
			
		||||
	border: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table {
 | 
			
		||||
	padding: 2px;
 | 
			
		||||
	border-collapse: collapse;
 | 
			
		||||
	margin-left: auto;
 | 
			
		||||
	margin-right: auto;
 | 
			
		||||
	font: 12px "Lucida Grande", Helvetica, Arial, sans-serif;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th {
 | 
			
		||||
	background-color: #808080;
 | 
			
		||||
	color: #ffffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table td,
 | 
			
		||||
th {
 | 
			
		||||
	padding: 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vert {
 | 
			
		||||
	-ms-writing-mode: vertical-rl;
 | 
			
		||||
	-moz-writing-mode: vertical-rl;
 | 
			
		||||
	-webkit-writing-mode: vertical-rl;
 | 
			
		||||
	writing-mode: vertical-rl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.name {
 | 
			
		||||
	width: 1px;
 | 
			
		||||
	float: left;
 | 
			
		||||
	word-wrap: break-word;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.score {
 | 
			
		||||
	width: 80px;
 | 
			
		||||
	height: 180px;
 | 
			
		||||
	vertical-align: top;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.right {
 | 
			
		||||
	font: 9px "Lucida Grande", Helvetica, Arial, sans-serif;
 | 
			
		||||
	text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.center,
 | 
			
		||||
.event,
 | 
			
		||||
.win,
 | 
			
		||||
.call,
 | 
			
		||||
.call_blank,
 | 
			
		||||
.rt_call,
 | 
			
		||||
.rt_blank {
 | 
			
		||||
	text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.win,
 | 
			
		||||
.call,
 | 
			
		||||
.call_blank,
 | 
			
		||||
.rt_call,
 | 
			
		||||
.ev_call,
 | 
			
		||||
.ev_draw {
 | 
			
		||||
	vertical-align: bottom;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nopad {
 | 
			
		||||
	padding: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.call {
 | 
			
		||||
	font: 10px "Lucida Grande", Helvetica, Arial, sans-serif;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.blank {
 | 
			
		||||
	width: 12px;
 | 
			
		||||
	height: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.machi {
 | 
			
		||||
	width: 6px;
 | 
			
		||||
	height: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.event {
 | 
			
		||||
	width: 27px;
 | 
			
		||||
	height: 96px;
 | 
			
		||||
	vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.win {
 | 
			
		||||
	width: 27px;
 | 
			
		||||
	height: 48px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.call {
 | 
			
		||||
	width: 27px;
 | 
			
		||||
	height: 54px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.call_blank {
 | 
			
		||||
	width: 27px;
 | 
			
		||||
	height: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rt_call {
 | 
			
		||||
	width: 38px;
 | 
			
		||||
	height: 54px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rt_blank {
 | 
			
		||||
	width: 38px;
 | 
			
		||||
	height: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ev_call {
 | 
			
		||||
	font: 10px "Lucida Grande", Helvetica, Arial, sans-serif;
 | 
			
		||||
	height: 11px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ev_draw {
 | 
			
		||||
	height: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ev_reach {
 | 
			
		||||
	font: 10px "Lucida Grande", Helvetica, Arial, sans-serif;
 | 
			
		||||
	height: 10px;
 | 
			
		||||
	vertical-align: top;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ev_discard {
 | 
			
		||||
	height: 38px;
 | 
			
		||||
	vertical-align: top;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.border {
 | 
			
		||||
	border: 1px solid #c0c0c0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a {
 | 
			
		||||
	color: #4F6B72;
 | 
			
		||||
	font-family: "PT Sans", Verdana, Arial, sans-serif;
 | 
			
		||||
	font-weight: bold;
 | 
			
		||||
	font-size: 12px;
 | 
			
		||||
	text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.last {
 | 
			
		||||
	float: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a:hover {
 | 
			
		||||
	text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.demo {
 | 
			
		||||
	width: 600px;
 | 
			
		||||
	margin: 20px auto;
 | 
			
		||||
	padding: 10px;
 | 
			
		||||
	color: #4F6B72;
 | 
			
		||||
	font-family: "PT Sans", Verdana, Arial, sans-serif;
 | 
			
		||||
	font-size: 13px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.error-msg {
 | 
			
		||||
	padding: 5px;
 | 
			
		||||
	border: 1px solid red;
 | 
			
		||||
	margin-bottom: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.input {
 | 
			
		||||
	background-color: #FFFFFF;
 | 
			
		||||
	border: 1px solid #CCCCCC;
 | 
			
		||||
	border-radius: 3px;
 | 
			
		||||
	color: #555555;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	font-size: 12px;
 | 
			
		||||
	height: 16px;
 | 
			
		||||
	line-height: 16px;
 | 
			
		||||
	margin-bottom: 9px;
 | 
			
		||||
	padding: 3px 3px 3px 3px;
 | 
			
		||||
	width: 300px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.select {
 | 
			
		||||
	background-color: #FFFFFF;
 | 
			
		||||
	border: 1px solid #CCCCCC;
 | 
			
		||||
	border-radius: 3px;
 | 
			
		||||
	color: #555555;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	font-size: 13px;
 | 
			
		||||
	height: 24px;
 | 
			
		||||
	line-height: 16px;
 | 
			
		||||
	margin-bottom: 9px;
 | 
			
		||||
	padding: 3px 3px 3px 3px;
 | 
			
		||||
	width: 100px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.buttons {
 | 
			
		||||
	text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.buttons span {
 | 
			
		||||
	float: left;
 | 
			
		||||
	margin-top: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button {
 | 
			
		||||
	background-color: #575A82;
 | 
			
		||||
	border: none;
 | 
			
		||||
	color: #FFFFFF;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	font-weight: bold;
 | 
			
		||||
	padding: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
button:hover {
 | 
			
		||||
	background-color: #FF875E;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								public/css/svg.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								public/css/svg.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
.tile {
 | 
			
		||||
	width: 27px;
 | 
			
		||||
	height: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.kanup,
 | 
			
		||||
.kandown {
 | 
			
		||||
	width: 18px;
 | 
			
		||||
	height: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.kanup {
 | 
			
		||||
	position: relative;
 | 
			
		||||
	top: 0;
 | 
			
		||||
	right: 4px;
 | 
			
		||||
	z-index: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.kandown {
 | 
			
		||||
	position: relative;
 | 
			
		||||
	top: -15px;
 | 
			
		||||
	right: -4px;
 | 
			
		||||
	z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.down {
 | 
			
		||||
	width: 15px;
 | 
			
		||||
	height: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tenbo {
 | 
			
		||||
	width: 68px;
 | 
			
		||||
	height: 9px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.face,
 | 
			
		||||
.back {
 | 
			
		||||
	filter: url(#inset-shadow);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.face {
 | 
			
		||||
	fill: #ffffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.back {
 | 
			
		||||
	fill: #ffba1e;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rotate,
 | 
			
		||||
.rotate1,
 | 
			
		||||
.rotate2 {
 | 
			
		||||
	width: 38px;
 | 
			
		||||
	height: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rotate {
 | 
			
		||||
	transform: rotate(-90deg) translate(-5px, 0px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rotate1 {
 | 
			
		||||
	transform: rotate(-90deg) translate(6px, 0px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rotate2 {
 | 
			
		||||
	transform: rotate(-90deg) translate(17px, 0px);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								public/files/paifu.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/files/paifu.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								public/img/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/img/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 29 KiB  | 
							
								
								
									
										474
									
								
								public/img/tiles.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								public/img/tiles.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 146 KiB  | 
							
								
								
									
										6
									
								
								public/js/jquery-2.0.0.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								public/js/jquery-2.0.0.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										63
									
								
								views/get_log.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								views/get_log.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
extends layout.pug
 | 
			
		||||
 
 | 
			
		||||
block content
 | 
			
		||||
  h1 Как получить ссылку на лог игры
 | 
			
		||||
  div.demo(style="text-align: justify;")
 | 
			
		||||
    p Ссылка на лог игры имеет вид:
 | 
			
		||||
      br
 | 
			
		||||
      i http://tenhou.net/0/?log=2017012116gm-0001-0000-05a1bd94&tw=3
 | 
			
		||||
      br
 | 
			
		||||
      | для нас важно узнать часть в параметре log - уникальный код лога.
 | 
			
		||||
      br
 | 
			
		||||
      | В зависимости от игрового клиента код можно получить по-разному.
 | 
			
		||||
    h2 Android-клиент
 | 
			
		||||
    div Этот клиент представляет собой обычный браузер. Последние 40 ссылок хранятся в хранилище этого браузера.
 | 
			
		||||
    div Взять хранилище можно только при наличии root-доступа. Любым файловым менеджером копируем его на sdcard из внутренней памяти по пути:
 | 
			
		||||
      br
 | 
			
		||||
      i /data/data/net.tenhou.WebBrowserYYYYMMDD/app_webview/Local Storage/file__0.localstorage
 | 
			
		||||
    div Какие-то числа в пути могут быть другие но ничего страшного. Файл представляет собой sqlite3 базу данных, из которой можно 
 | 
			
		||||
      | взять что нам нужно. Пример для windows:
 | 
			
		||||
      ol
 | 
			
		||||
        li Скачиваем 
 | 
			
		||||
          a(href="https://www.sqlite.org/2017/sqlite-tools-win32-x86-3180000.zip") консольный клиент
 | 
			
		||||
        li Выгружаем инфу командой:
 | 
			
		||||
          br
 | 
			
		||||
          b: i sqlite3 %1 -noheader -list "select * from ItemTable where key like 'log%%'" > %1.txt
 | 
			
		||||
        li Нужные нам коды в строках вида:
 | 
			
		||||
          br
 | 
			
		||||
          i log33|{"type":137,"lobby":0,"log":"2017022402gm-0089-0000-51d23e14","oya":3, ...
 | 
			
		||||
          br
 | 
			
		||||
          | где в кавычках после "log" - код лога
 | 
			
		||||
    h2 Chrome
 | 
			
		||||
    div Все тоже самое что и выше только путь вида:
 | 
			
		||||
      ol
 | 
			
		||||
        li Мобильный Chrome
 | 
			
		||||
          br
 | 
			
		||||
          i /data/data/com.android.chrome/app_chrome/Default/Local Storage/http_tenhou_net_0.localstorage
 | 
			
		||||
        li Chrome под Windows
 | 
			
		||||
          br
 | 
			
		||||
          i %User%/AppData/Local/Google/Chrome/User Data/Default/Local Storage/http_tenhou_net_0.localstorage
 | 
			
		||||
    h2 Остальное
 | 
			
		||||
    div Все перечислять проблематично - есть куча браузеров, клиентов.
 | 
			
		||||
      br
 | 
			
		||||
      | В помощь можно посоветовать ресурс на 
 | 
			
		||||
      a(href="https://github.com/NegativeMjark/tenhou-log") GitHub
 | 
			
		||||
      | , где можно найти скрипт для выгрузки базы ссылок логов (tenhou-download-game-xml.py).
 | 
			
		||||
      br
 | 
			
		||||
      | Так же там есть полная информация по формату лога.
 | 
			
		||||
    h2 Для разработчиков
 | 
			
		||||
    div У меня лог парсится и отдается шаблонизатору 
 | 
			
		||||
      a(href="https://pugjs.org") pug
 | 
			
		||||
      | , который уже выводит как надо.
 | 
			
		||||
      br
 | 
			
		||||
      | Весь лог преобразуется в JSON-структуру, которую можно поглядеть есть добавить параметр json=1 к GET-запросу 
 | 
			
		||||
      | прямого линка пайфы.
 | 
			
		||||
    div P.S. В парсере еще не сделаны все мои хотелки, работа продолжается. Как будет все готов по моему мнению опубликую на GitHub.
 | 
			
		||||
    div Пока в планах есть несколько пунктов:
 | 
			
		||||
      ol
 | 
			
		||||
        li подписи к некоторым ключевым тайлам руки (рон, цумо)
 | 
			
		||||
        li возможность формирования сразу в pdf (разбивка по страница, оглавление)
 | 
			
		||||
        li кнопки поделиться в соцсети???
 | 
			
		||||
        li возможно сбор статистики в tenhou (типа 
 | 
			
		||||
          a(href="http://arcturus.su/tenhou/ranking/") arcturus
 | 
			
		||||
          |  с возможностью привязки показанных логов
 | 
			
		||||
							
								
								
									
										45
									
								
								views/index.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								views/index.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
extends layout.pug
 | 
			
		||||
 
 | 
			
		||||
block content
 | 
			
		||||
  h1 Пайфу ( 牌譜paifu)
 | 
			
		||||
  div.demo(style="text-align: justify;") Пошаговая запись игры, в виде таблицы.
 | 
			
		||||
    div  На игровых серверах маджонга, обычно каждая сыгранная игра записывается в память и ее потом можно посмотреть в качестве видеореплея с пошаговым просмотром.
 | 
			
		||||
    div  Так же может быть предоставлена пошаговая запись в качестве таблицы, которая называется 
 | 
			
		||||
      b Paifu 
 | 
			
		||||
      | (предоставляется не на всех серверах и на разных серверах, имеет свои обозначения записи).
 | 
			
		||||
    div  Paifu – "Ручная История", является отчетом развития руки, и отображается в виде картинки на которой показана последовательность всех взятых тайлов и дискардов сделанных игроком.
 | 
			
		||||
    div  Пайфу напоминает термин Кифу (棋譜 kifu), который является пошаговым отчетом, сделанным для игры shogi (японские шахматы).
 | 
			
		||||
    div  В отличии от видеореплея, в пайфу сразу видна стартовая рука, и конечная.
 | 
			
		||||
      br
 | 
			
		||||
      |  Можно увидеть все дискарды и все произведенные замены, что позволяет быстрее понять, развитие рук в игре.
 | 
			
		||||
      br
 | 
			
		||||
      |  Пайфу удобно еще тем, что его можно распечатать на листке буаги и изучать в удобное время или передать кому-нибудь.
 | 
			
		||||
  div.demo
 | 
			
		||||
    #error
 | 
			
		||||
      if(error.length>0)
 | 
			
		||||
        div.error-msg= error
 | 
			
		||||
    form(action="/" method="post")
 | 
			
		||||
      table
 | 
			
		||||
        tr
 | 
			
		||||
          td(align="right") Paifu language
 | 
			
		||||
          td
 | 
			
		||||
            select.select(name="lang")
 | 
			
		||||
              option(value="ru" selected) Russian
 | 
			
		||||
              option(value="en") English
 | 
			
		||||
              option(value="jp") Japan
 | 
			
		||||
        tr
 | 
			
		||||
          td(align="right") URL 
 | 
			
		||||
            a(href="http://www.tenhou.net") tenhou.net
 | 
			
		||||
            |  log
 | 
			
		||||
          td  
 | 
			
		||||
            input.input(type="text" name="url" placeholder="Log URL")
 | 
			
		||||
        tr
 | 
			
		||||
          td(colspan='2').right
 | 
			
		||||
           button(type="submit") Обработать
 | 
			
		||||
    h2 Полезное
 | 
			
		||||
    div
 | 
			
		||||
      a(href="/files/paifu.pdf") Как читать пайфу
 | 
			
		||||
      | , вырезка из 
 | 
			
		||||
      a(href="http://dora.ucoz.net/") книги
 | 
			
		||||
      | (стр.907 версия 8 книги)
 | 
			
		||||
    div: a(href="/get_log") Как получить ссылку на лог игры и прочее
 | 
			
		||||
							
								
								
									
										9
									
								
								views/layout.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								views/layout.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
doctype html
 | 
			
		||||
html
 | 
			
		||||
  head
 | 
			
		||||
    title Paifu
 | 
			
		||||
    link(rel='stylesheet', href='/css/main.css')
 | 
			
		||||
    block header
 | 
			
		||||
  body
 | 
			
		||||
    block content
 | 
			
		||||
    
 | 
			
		||||
							
								
								
									
										258
									
								
								views/paifu.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								views/paifu.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,258 @@
 | 
			
		||||
extends layout.pug
 | 
			
		||||
 | 
			
		||||
block header
 | 
			
		||||
  link(rel='stylesheet', href='/css/svg.css')
 | 
			
		||||
 | 
			
		||||
block content
 | 
			
		||||
  svg(width="0" height="0")
 | 
			
		||||
    defs
 | 
			
		||||
      filter#inset-shadow
 | 
			
		||||
        feOffset(dx="0" dy="0")
 | 
			
		||||
        feGaussianBlur(stdDeviation="1.5" result="offset-blur")
 | 
			
		||||
        feComposite(operator="out" in="SourceGraphic" in2="offset-blur" result="inverse")
 | 
			
		||||
        feFlood(flood-color="black" flood-opacity="1" result="color")
 | 
			
		||||
        feComposite(operator="in" in="color" in2="inverse" result="shadow")
 | 
			
		||||
        feComposite(operator="over" in="shadow" in2="SourceGraphic")
 | 
			
		||||
  table.border
 | 
			
		||||
    tr
 | 
			
		||||
      th= str.name
 | 
			
		||||
      th= str.sex
 | 
			
		||||
      th= str.rate
 | 
			
		||||
      th= str.dan
 | 
			
		||||
    each player,ind in data.players
 | 
			
		||||
      tr
 | 
			
		||||
        if (ind == data.diler)
 | 
			
		||||
          td: b= player.name
 | 
			
		||||
        else
 | 
			
		||||
          td= player.name
 | 
			
		||||
        td= str['sex'+player.sex]
 | 
			
		||||
        td= player.rate
 | 
			
		||||
        td= str.dans[player.dan]
 | 
			
		||||
    tr
 | 
			
		||||
      td(colspan='4' align='left')
 | 
			
		||||
        a(href='/?log='+data.log+'&lang='+lang)= str.link
 | 
			
		||||
  each round in data.rounds
 | 
			
		||||
    table.border
 | 
			
		||||
      tr
 | 
			
		||||
        td(style='vertical-align: top')
 | 
			
		||||
          table
 | 
			
		||||
            tr
 | 
			
		||||
              th(colspan='2')
 | 
			
		||||
                h1= str.rounds[round.name]
 | 
			
		||||
            tr
 | 
			
		||||
              td: svg.tenbo: use.face(xlink:href="/img/tiles.svg#t1k")
 | 
			
		||||
              td= round.riichi.count
 | 
			
		||||
            tr  
 | 
			
		||||
              td: svg.tenbo: use.face(xlink:href="/img/tiles.svg#t100")
 | 
			
		||||
              td= round.honba
 | 
			
		||||
            tr
 | 
			
		||||
              td(colspan='2')
 | 
			
		||||
                each dora in round.dora
 | 
			
		||||
                  svg.tile: use.face(xlink:href="/img/tiles.svg#"+dora)
 | 
			
		||||
                - for (var x = 0; x < 5 - round.dora.length; x++)  
 | 
			
		||||
                  svg.tile: use.back(xlink:href="/img/tiles.svg#tile")
 | 
			
		||||
                br  
 | 
			
		||||
                each uradora in round.uradora
 | 
			
		||||
                  svg.tile: use.face(xlink:href="/img/tiles.svg#"+uradora)
 | 
			
		||||
                - for (var x = 0; x < 5 - round.uradora.length; x++)  
 | 
			
		||||
                  svg.tile: use.back(xlink:href="/img/tiles.svg#tile")
 | 
			
		||||
            tr(style="border-top: 1px dotted black;")
 | 
			
		||||
              td(colspan='2' style="text-align: center")
 | 
			
		||||
                table
 | 
			
		||||
                  tr
 | 
			
		||||
                    - for(var s = 0; s < 4; s++)
 | 
			
		||||
                      td.center= str.seat[s]
 | 
			
		||||
                  tr
 | 
			
		||||
                    - for(var s = 0; s < 4; s++)
 | 
			
		||||
                      - var cs = (s+round.diler)%4
 | 
			
		||||
                      td.right(style="border-top: 1px solid black;")= round.score[cs].begin 
 | 
			
		||||
                  tr
 | 
			
		||||
                    - for(var s = 0; s < 4; s++)
 | 
			
		||||
                      - var cs = (s+round.diler)%4
 | 
			
		||||
                      td.right= round.score[cs].diff 
 | 
			
		||||
                  tr
 | 
			
		||||
                    - for(var s = 0; s < 4; s++)
 | 
			
		||||
                      - var cs = (s+round.diler)%4
 | 
			
		||||
                      td.right(style="border-top: 1px solid black;")= round.score[cs].end
 | 
			
		||||
            tr(style="border-top: 1px dotted black;")
 | 
			
		||||
              td(colspan='2' style="text-align: center")
 | 
			
		||||
                if round.draw
 | 
			
		||||
                  h2= str.ryuukyoku.title
 | 
			
		||||
                  if round.draw.type
 | 
			
		||||
                    = str.ryuukyoku[round.draw.type]
 | 
			
		||||
                  if round.draw.tenpai.length > 0
 | 
			
		||||
                    p(style="text-align: left")
 | 
			
		||||
                      b= str.ryuukyoku["tenpai"]+':'
 | 
			
		||||
                      each t,i in round.draw.tenpai
 | 
			
		||||
                        if t.length > 0
 | 
			
		||||
                          br
 | 
			
		||||
                          i= '- '+data.players[i].name
 | 
			
		||||
                    if round.riichi.player.length > 0
 | 
			
		||||
                      p(style="text-align: left")
 | 
			
		||||
                        b= str.reach+':'
 | 
			
		||||
                        each p in round.riichi.player
 | 
			
		||||
                          br
 | 
			
		||||
                          i= '- '+data.players[p].name
 | 
			
		||||
                else if round.win
 | 
			
		||||
                  each w in round.win
 | 
			
		||||
                    p
 | 
			
		||||
                      b= str[w.type]+': '+data.players[w.player].name
 | 
			
		||||
                      if w.type === 'ron'
 | 
			
		||||
                        br
 | 
			
		||||
                        = str.from[data.players[w.player].sex]
 | 
			
		||||
                        br
 | 
			
		||||
                        b= data.players[w.fromPlayer].name
 | 
			
		||||
                    if w.yakulist    
 | 
			
		||||
                      table(width='100%')
 | 
			
		||||
                        tr
 | 
			
		||||
                          th(colspan='2')= str.yakulist
 | 
			
		||||
                          - var cnt = 0  
 | 
			
		||||
                        each y in w.yakulist
 | 
			
		||||
                          tr
 | 
			
		||||
                            td(align='left'): b= str.yaku[y.yaku]
 | 
			
		||||
                            td(align='rigth'): i= y.han
 | 
			
		||||
                          - var cnt = cnt + y.han  
 | 
			
		||||
                        tr
 | 
			
		||||
                          td(colspan='2' style="border-top: 1px solid black;" align='rigth')
 | 
			
		||||
                            if w.limit
 | 
			
		||||
                              = str.limits[w.limit]
 | 
			
		||||
                            else
 | 
			
		||||
                              = cnt+' '+str.han+' '+w.fu+' '+str.fu
 | 
			
		||||
                    else if w.yakuman
 | 
			
		||||
                      table(width='100%')
 | 
			
		||||
                        tr
 | 
			
		||||
                          th= str.limits[w.limit]
 | 
			
		||||
                        each y in w.yakuman
 | 
			
		||||
                          tr
 | 
			
		||||
                            td(align='left'): b= str.yaku[y]
 | 
			
		||||
                    p 
 | 
			
		||||
                      b= str.score+': '+w.points
 | 
			
		||||
                  if round.riichi.player.length > 0
 | 
			
		||||
                    p(style="text-align: left")
 | 
			
		||||
                      b= str.reach+':'
 | 
			
		||||
                      each p in round.riichi.player
 | 
			
		||||
                        br
 | 
			
		||||
                        i= '- '+data.players[p].name
 | 
			
		||||
        td
 | 
			
		||||
          - for(var p = 0; p < 4; p++)
 | 
			
		||||
            - var cp = (p+round.diler)%4
 | 
			
		||||
            table(align='left' width='100%').border
 | 
			
		||||
              tr
 | 
			
		||||
                th(width="20px")= str.seat[p]
 | 
			
		||||
                td(width="10px").nopad: span.vert.call= str.start
 | 
			
		||||
                td(width='500' colspan='2').nopad
 | 
			
		||||
                  table(align='left')
 | 
			
		||||
                    tr
 | 
			
		||||
                      each t in round.hands[cp]
 | 
			
		||||
                        td.nopad: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
              tr
 | 
			
		||||
                td(rowspan="3" style="text-align: center;"): span.name: b= data.players[cp].name
 | 
			
		||||
                td.nopad: span.vert.call= str.draw 
 | 
			
		||||
                td(rowspan="2" colspan='2').nopad
 | 
			
		||||
                  table(align='left')
 | 
			
		||||
                    tr
 | 
			
		||||
                      each t in round.events[cp]
 | 
			
		||||
                        td.nopad
 | 
			
		||||
                         div.event
 | 
			
		||||
                            if t
 | 
			
		||||
                              if t.call
 | 
			
		||||
                                div.ev_call= str[t.call.type]
 | 
			
		||||
                              else  
 | 
			
		||||
                                div.ev_call
 | 
			
		||||
                              if t.tsumogiri
 | 
			
		||||
                                div.ev_draw: svg.down: use(xlink:href="/img/tiles.svg#down")
 | 
			
		||||
                                div.ev_discard: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t.tsumogiri)
 | 
			
		||||
                              else if t.call
 | 
			
		||||
                                if t.call.type === 'kan' || t.call.type === 'chakan'
 | 
			
		||||
                                  div.blank
 | 
			
		||||
                                    svg.kanup: use.face(xlink:href="/img/tiles.svg#"+t.call.tiles[t.call.called])
 | 
			
		||||
                                    svg.kandown: use.face(xlink:href="/img/tiles.svg#"+t.draw)
 | 
			
		||||
                                else
 | 
			
		||||
                                  div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t.call.tiles[t.call.called])
 | 
			
		||||
                                div.ev_discard: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t.discard)
 | 
			
		||||
                              else
 | 
			
		||||
                                div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t.draw)
 | 
			
		||||
                                div.ev_discard: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t.discard)
 | 
			
		||||
                              if t.reach  
 | 
			
		||||
                                div.ev_reach= str.reach
 | 
			
		||||
                              else if t.furikomi
 | 
			
		||||
                                div.ev_reach= str.furikomi
 | 
			
		||||
                              else
 | 
			
		||||
                                div.ev_reach
 | 
			
		||||
              tr
 | 
			
		||||
                td.nopad: span.vert.call= str.discard
 | 
			
		||||
              tr                
 | 
			
		||||
                td.nopad: span.vert.call= str.final 
 | 
			
		||||
                td(width='27px' height='54px')
 | 
			
		||||
                  table
 | 
			
		||||
                    tr
 | 
			
		||||
                      if round.win
 | 
			
		||||
                        each winner in round.win
 | 
			
		||||
                          if (winner.player == cp)
 | 
			
		||||
                            each t,i in winner.hand
 | 
			
		||||
                              if i === winner.machi
 | 
			
		||||
                                - var win_tile = t
 | 
			
		||||
                              else
 | 
			
		||||
                                td.nopad: div.win
 | 
			
		||||
                                  div.ev_call
 | 
			
		||||
                                  div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                            td.nopad: div.machi
 | 
			
		||||
                            td.nopad: div.win
 | 
			
		||||
                              div.ev_call= str[winner.type]
 | 
			
		||||
                              div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+win_tile)
 | 
			
		||||
                      if round.draw
 | 
			
		||||
                        each t in round.draw.tenpai[cp]
 | 
			
		||||
                          if t
 | 
			
		||||
                            td.nopad: div.win
 | 
			
		||||
                              div.ev_call
 | 
			
		||||
                              div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                      each hand, index in round.finish
 | 
			
		||||
                        if (hand.length > 0 && index == cp)
 | 
			
		||||
                          each t in round.finish[cp]
 | 
			
		||||
                            td.nopad: div.win
 | 
			
		||||
                              div.ev_call
 | 
			
		||||
                              div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                td(align='left' height='54px')
 | 
			
		||||
                  table(align='left')
 | 
			
		||||
                    tr
 | 
			
		||||
                      each calls, index in round.meld
 | 
			
		||||
                        if (calls.length > 0 && index == cp)
 | 
			
		||||
                          td.blank
 | 
			
		||||
                          each call in calls
 | 
			
		||||
                            if call.type === 'chi'
 | 
			
		||||
                              td.nopad: div.rt_call
 | 
			
		||||
                                div.rt_blank
 | 
			
		||||
                                div.ev_draw: svg.rotate: use.face(xlink:href="/img/tiles.svg#"+call.tiles[call.called])
 | 
			
		||||
                              each t,i in call.tiles
 | 
			
		||||
                                if i != call.called
 | 
			
		||||
                                  td.nopad: div.call
 | 
			
		||||
                                    div.call_blank
 | 
			
		||||
                                    div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                            else if call.rotate < 4
 | 
			
		||||
                              each t,i in call.tiles
 | 
			
		||||
                                if call.type != 'chakan' || i < 3
 | 
			
		||||
                                  td.nopad
 | 
			
		||||
                                    if i === call.rotate
 | 
			
		||||
                                      div.rt_call
 | 
			
		||||
                                        if call.type === 'chakan'
 | 
			
		||||
                                          div.ev_draw: svg.rotate1: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                                          div.ev_draw: svg.rotate2: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                                        else  
 | 
			
		||||
                                          div.rt_blank
 | 
			
		||||
                                          div.ev_draw: svg.rotate: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                                    else
 | 
			
		||||
                                      div.call
 | 
			
		||||
                                        div.call_blank
 | 
			
		||||
                                        div.ev_draw: svg.tile: use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                            else      
 | 
			
		||||
                              each t,i in call.tiles
 | 
			
		||||
                                td.nopad: div.call
 | 
			
		||||
                                  div.call_blank
 | 
			
		||||
                                  div.ev_draw: svg.tile
 | 
			
		||||
                                    if i === 0 || i === 3
 | 
			
		||||
                                      use.back(xlink:href="/img/tiles.svg#tile")
 | 
			
		||||
                                    else  
 | 
			
		||||
                                      use.face(xlink:href="/img/tiles.svg#"+t)
 | 
			
		||||
                            td(widht="10px")
 | 
			
		||||
            br            
 | 
			
		||||
    br 
 | 
			
		||||
							
								
								
									
										38
									
								
								views/tiles_svg.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								views/tiles_svg.pug
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
doctype html
 | 
			
		||||
html
 | 
			
		||||
  head
 | 
			
		||||
    title Example SVG mahjong tiles
 | 
			
		||||
  link(rel='stylesheet', href='/css/svg.css')
 | 
			
		||||
  body
 | 
			
		||||
    svg(width="0" height="0")
 | 
			
		||||
      defs
 | 
			
		||||
        filter#inset-shadow
 | 
			
		||||
          feOffset(dx="0" dy="0")
 | 
			
		||||
          feGaussianBlur(stdDeviation="1.5" result="offset-blur")
 | 
			
		||||
          feComposite(operator="out" in="SourceGraphic" in2="offset-blur" result="inverse")
 | 
			
		||||
          feFlood(flood-color="black" flood-opacity="1" result="color")
 | 
			
		||||
          feComposite(operator="in" in="color" in2="inverse" result="shadow")
 | 
			
		||||
          feComposite(operator="over" in="shadow" in2="SourceGraphic")
 | 
			
		||||
    svg.rotate: use.face(xlink:href="/img/tiles.svg#m1")
 | 
			
		||||
    |  
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#m1")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#m9")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#p1")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#p9")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#s1")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#s9")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#tan")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#nan")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#xia")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#pei")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#chun")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#haku")
 | 
			
		||||
    svg.tile: use.face(xlink:href="/img/tiles.svg#hatsu")
 | 
			
		||||
    br
 | 
			
		||||
    svg.tenbo: use.face(xlink:href="/img/tiles.svg#t100")
 | 
			
		||||
    svg.tenbo: use.face(xlink:href="/img/tiles.svg#t1k")
 | 
			
		||||
    svg.tenbo: use.face(xlink:href="/img/tiles.svg#t5k")
 | 
			
		||||
    svg.tenbo: use.face(xlink:href="/img/tiles.svg#t10k")
 | 
			
		||||
    br
 | 
			
		||||
    svg.down: use(xlink:href="/img/tiles.svg#down")
 | 
			
		||||
    
 | 
			
		||||
		Reference in New Issue
	
	Block a user