Init
This commit is contained in:
commit
3c936c75a6
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")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user