Created
May 22, 2014 15:49
-
-
Save denkiwakame/8ff7748ec14c557e97ec to your computer and use it in GitHub Desktop.
わかめカメラ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title>active background</title> | |
<style> | |
<!-- | |
html body{ | |
background-color: #cccccc; | |
height:100%; | |
} | |
#main { | |
position: relative; | |
} | |
div.continer { | |
height: 100%; | |
} | |
h4.pattern { | |
text-align: center; | |
} | |
#contents { | |
padding-top: 50px; | |
} | |
div.jumbotron { | |
margin-bottom:0px; | |
background-color: #00ddee; | |
height: 500px; | |
color: #ffffff; | |
} | |
#startbtn { | |
height: 80px; | |
} | |
.row .row { | |
margin:10px; | |
} | |
#canvas { | |
padding: 0; | |
margin: 0; | |
} | |
#js-live-preview { | |
width: 100%; | |
height: 100%; | |
margin: auto; | |
} | |
--> | |
</style> | |
<script src="http://code.jquery.com/jquery-1.9.0.js"></script> | |
<script src="http://code.jquery.com/jquery-migrate-1.0.0.js"></script> | |
<!-- | |
<script type="text/javascript" src="ab.js"></script> | |
--> | |
<script type="text/javascript" src="wacam.js"></script> | |
<!-- Bootstrap --> | |
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet"> | |
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | |
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | |
<!--[if lt IE 9]> | |
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> | |
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> | |
<![endif]--> | |
</head> | |
<body> | |
<div id="main" class="container"> | |
<div id="contents" class="row"> | |
<div class="col-md-6"> | |
<!-- Preview --> | |
<img id="js-live-preview-img" src="" width="100%" style="display:none"></img> | |
<canvas id="canvas-preview" style="display:none" width="480" height="360"></canvas> | |
<div class="js-live-preview-params" style="display:none"></div> | |
<!-- placeholder --> | |
<div id="logo" class="jumbotron"> | |
<h1><strong>Active<br \> background</strong></h1> | |
<span class="glyphicon glyphicon-globe"> supports Google Chrome / Firefox</span> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<div class="row well"> | |
<div class="col-md-7"> | |
<div class="input-group row"> | |
<span class="input-group-addon">persec</span> | |
<select id="pv-persec" name="persec" class="form-control"> | |
<option>0.2</option> | |
<option>0.5</option> | |
<option>1</option> | |
</select> | |
</div> | |
</div> | |
<div class="col-md-5"> | |
<div class="row"> | |
<button id="pv-capture" class="btn btn-default btn-md col-lg-12" type="button"> | |
<span class="glyphicon glyphicon-facetime-video"></span> testcap | |
</button> | |
<button id="pv-capture-stop" class="btn btn-danger btn-md col-lg-12" type="button" style="display:none"> | |
<span class="glyphicon glyphicon-ban-circle"></span> testcap | |
</button> | |
</div> | |
</div> | |
</div> | |
<div class="row well"> | |
<div class="col-md-7"> | |
<div class="input-group row"> | |
<span class="input-group-addon">cam</span> | |
<select id="gc-cam" name="cam" class="form-control"> | |
<option>1</option> | |
<option>2</option> | |
<option>3</option> | |
<option>4</option> | |
<option>5</option> | |
</select> | |
<span class="input-group-addon">set</span> | |
<select id="gc-set" name="set" class="form-control"> | |
<option>1</option> | |
<option>2</option> | |
<option>3</option> | |
<option>4</option> | |
<option>5</option> | |
<option>6</option> | |
<option>7</option> | |
<option>8</option> | |
<option>9</option> | |
<option>10</option> | |
<option>11</option> | |
<option>12</option> | |
</select> | |
</div> | |
<div class="input-group row"> | |
<span class="input-group-addon">width </span> | |
<select id="gc-width" name="width" class="form-control"> | |
<option>2048</option> | |
<option>1024</option> | |
<option>512</option> | |
<option>4096</option> | |
</select> | |
</div> | |
<div class="input-group row"> | |
<span class="input-group-addon">height</span> | |
<select id="gc-height" name="height" class="form-control"> | |
<option>1024</option> | |
<option>512</option> | |
<option>4096</optionv> | |
<option>2048</option> | |
</select> | |
</div> | |
<div class="input-group row"> | |
<span class="input-group-addon">shutter_speeds</span> | |
<select id="gc-shutter" name="shutter" class="form-control"> | |
<option>multi</option> | |
<option>off</option> | |
</select> | |
</div> | |
</div> <!-- col-md-7 --> | |
<div class="col-md-5"> | |
<div class="row"> | |
<button id="gc-demo" class="btn btn-default btn-sm col-md-12" type="button"> | |
<span class="glyphicon glyphicon-picture"></span> demo | |
</button> | |
</div> | |
<div class="row"> | |
<button id="gc-capture" class="btn btn-primary btn-lg col-md-12" type="button"> | |
<span class="glyphicon glyphicon-camera"></span> capture | |
</button> | |
</div> | |
</div> <!-- col-md-5 --> | |
</div> | |
<div class="row well"> | |
<div class="col-md-7"> | |
<div class="input-group row"> | |
<span class="input-group-addon">row x col</span> | |
<select id="cb-row-col" name="rowxcol" class="form-control"> | |
<option>5x8</option> | |
<option>7x10</option> | |
<option>11x16</option> | |
</select> | |
</div> | |
<div class="input-group row"> | |
<span class="input-group-addon">unit [px]</span> | |
<select id="cb-unit-px" name="rowxcol" class="form-control"> | |
<option>100</option> | |
<option>150</option> | |
<option>50</option> | |
</select> | |
</div> | |
<div class="input-group row"> | |
<span class="input-group-addon">times</span> | |
<select id="cb-times" name="times" class="form-control"> | |
<option>20</option> | |
<option>10</option> | |
<option>30</option> | |
</select> | |
</div> | |
</div> | |
<div class="col-md-5"> | |
<div class="row"> | |
<button id="cb-demo" class="btn btn-default btn-sm col-md-12" type="button"> | |
<span class="glyphicon glyphicon-picture"></span> demo | |
</button> | |
</div> | |
<div class="row"> | |
<button id="cb-capture" class="btn btn-success btn-lg col-md-12" type="button"> | |
<span class="glyphicon glyphicon-camera"></span> capture | |
</button> | |
</div> | |
</div> | |
</div><!-- row --> | |
</div><!-- col-md-7 --> | |
</div><!-- row --> | |
</div><!-- container --> | |
<canvas id="canvas"></canvas> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(function(){ | |
// util func | |
Util = { | |
getParams: function(){ | |
var params_str = location.search.slice(1); // delete "?" | |
if (!params_str) return {}; | |
var params_ary = decodeURIComponent(params_str).split("&"); | |
var params = {}; | |
for(var i=0; i<params_ary.length; i++){ | |
var key_val = params_ary[i].split("="); | |
params[key_val[0]] = key_val[1].split("/")[0]; | |
} | |
return params; | |
}, | |
// XXX イベントにつけないと作動しない | |
onFullscreen: function(){ | |
canvas = document.getElementById("canvas"); | |
if (canvas.webkitRequestFullscreen){ | |
canvas.webkitRequestFullscreen(); | |
} else if (canvas.mozRequestFullScreen){ | |
canvas.mozRequestFullScreen(); | |
} else { | |
alert("use google chrome or mozzila fire fox"); | |
} | |
}, | |
// XXX Exit するときはdocument要素でないとX | |
offFullscreen: function(){ | |
if (document.webkitExitFullscreen){ | |
document.webkitExitFullscreen(); | |
} | |
}, | |
errLog: function(log){ | |
msg = "ERROR: " + log; | |
console.log(msg); | |
}, | |
alignNum: function(num){ | |
var num = Number(num); | |
if (num < 10) return "00" + num; | |
if (num < 100) return "0" + num; | |
return String(num); | |
}, | |
}; | |
// prepare canvas | |
var canvas = document.getElementById("canvas"); | |
var ctx = canvas.getContext('2d'); | |
if (!canvas || !canvas.getContext) alert('this browser does not support canvas'); | |
var GrayCode = { | |
width: 2048, | |
height: 1024, | |
xSplitNum: Math.log(2048) / Math.log(2), | |
ySplitNum: Math.log(1024) / Math.log(2), | |
fill: function(col){ | |
if (col){ | |
ctx.fillStyle = 'rgb(255,255,255)'; | |
} else { | |
ctx.fillStyle = 'rgb(0,0,0)'; | |
} | |
ctx.fillRect(0,0,canvas.width,canvas.height); | |
}, | |
_draw: function(x1,y1,w1,h1,x2,y2,w2,h2){ | |
ctx.fillStyle = 'rgb(0,0,0)'; | |
ctx.fillRect(x1, y1, w1, h1); | |
ctx.fillStyle = 'rgb(255,255,255)'; | |
ctx.fillRect(x2, y2, w2, h2); | |
}, | |
_drawGrayCode: function(x,y,w,h,XorY,bit,isAnti){ | |
var gc = ( XorY == 'X' ) ? x ^ (x >> 1) : y ^ (y >> 1); | |
if(!isAnti){ // NORMAL | |
ctx.fillStyle = gc & (1 << (bit-1)) ? 'rgb(255,255,255)' : 'rgb(0,0,0)'; | |
} else { // anti-pattern | |
console.log('anti pattern!'); | |
ctx.fillStyle = gc & (1 << (bit-1)) ? 'rgb(0,0,0)' : 'rgb(255,255,255)'; | |
} | |
ctx.fillRect(x, y, w, h); | |
}, | |
splitY: function(y, width, height, num, target_num, isAnti) { // === | |
if (target_num > num) { | |
this.splitY(y, width, height/2, num+1, target_num, isAnti); | |
this.splitY(y+height/2, width, height/2, num+1, target_num, isAnti); | |
} else { | |
//GrayCode._draw(0, y, width, height/2, 0, y+height/2, width, height/2); | |
GrayCode._drawGrayCode(0, y, width, height/2, 'Y', GrayCode.ySplitNum - target_num, isAnti); | |
GrayCode._drawGrayCode(0, y+height/2, width, height/2, 'Y', GrayCode.ySplitNum - target_num, isAnti); | |
} | |
}, | |
splitX: function(x, width, height, num, target_num, isAnti) { // || | |
if (target_num > num) { | |
this.splitX(x, width/2, height, num+1, target_num, isAnti); | |
this.splitX(x+width/2, width/2, height, num+1, target_num, isAnti); | |
} else { | |
//GrayCode._draw(x, 0, width/2, height, x+width/2, 0, width/2, height); | |
GrayCode._drawGrayCode(x, 0, width/2, height, 'X', GrayCode.xSplitNum - target_num, isAnti); | |
GrayCode._drawGrayCode(x+width/2, 0, width/2, height, 'X', GrayCode.xSplitNum - target_num, isAnti); | |
} | |
}, | |
getImageData: function(){ | |
try { | |
var imageData = canvas.toDataURL("image/png"); | |
} catch(e) { | |
var imageData = "fails"; | |
} | |
return imageData; | |
}, | |
}; | |
// TODO | |
var shutterSpeeds = {}; | |
var mode = { 'blink': 0, 'splitX': 1, 'splitY': 2, 'antiSplitX': 3, 'antiSplitY':4, 'END': 5 }; | |
var Sync = { | |
cam: 1, | |
set: 1, | |
capture: 0, | |
pattern_num: 0, | |
//shutter_speeds: [ 1, 10, 50, 100 ], // TODO | |
shutter_speeds: [ 1, 10, 50 ], // TODO | |
mode: mode['blink'], // init | |
//mode: mode['splitX'], // init | |
setUp: function(param_hash){ | |
GrayCode.width = Number( $("#gc-width option:selected").val() ); | |
GrayCode.height = Number( $("#gc-height option:selected").val() ); | |
GrayCode.xSplitNum = Math.log(GrayCode.width) / Math.log(2); | |
GrayCode.ySplitNum = Math.log(GrayCode.height) / Math.log(2); | |
if (!canvas) { Util.errLog('canvas yet set'); return; }; | |
canvas.width = GrayCode.width; | |
canvas.height = GrayCode.height; | |
Sync.cam = Number($("#gc-cam").val()); | |
Sync.set = Number($("#gc-set").val()); | |
Sync.capture = param_hash['capture']; | |
Sync.shutter_speeds = $("#gc-shutter option:selected").val() == "multi" ? 1 : 0; | |
Util.onFullscreen(); | |
Sync.drawAndCapture(); | |
}, | |
evalMode: function(){ | |
if (this.mode == mode['blink']) { | |
this.mode = Sync.pattern_num < 2 ? mode['blink'] : mode['splitX']; | |
// reset | |
if(this.mode == mode['splitX']) Sync.pattern_num = 0; | |
} else if (this.mode == mode['splitX']) { | |
// XXX confirm ------------------------- | |
//TestDecoder.pushGrayCodes(); | |
//console.log(TestDecoder.graycodes); | |
//TestDecoder.evalGrayCodes(); | |
// XXX confirm ------------------------- | |
this.mode = Sync.pattern_num < GrayCode.xSplitNum ? mode['splitX'] : mode['splitY']; | |
// reset | |
if(this.mode == mode['splitY']) Sync.pattern_num = 0; | |
} else if (this.mode == mode['splitY']) { | |
this.mode = Sync.pattern_num < GrayCode.ySplitNum ? mode['splitY'] : mode['antiSplitX']; | |
if(this.mode == mode['antiSplitX']) Sync.pattern_num = 0; | |
} else if (this.mode == mode['antiSplitX']){ | |
this.mode = Sync.pattern_num < GrayCode.xSplitNum ? mode['antiSplitX'] : mode['antiSplitY']; | |
if(this.mode == mode['antiSplitY']) Sync.pattern_num = 0; | |
} else if (this.mode == mode['antiSplitY']){ | |
this.mode = Sync.pattern_num < GrayCode.ySplitNum ? mode['antiSplitY'] : mode['END']; | |
} | |
}, | |
drawAndCapture: function(){ | |
this.evalMode(); | |
if (this.mode == mode['blink']){ | |
var col = Sync.pattern_num % 2 ? 0 : 1; | |
GrayCode.fill(col); | |
} else if (this.mode == mode['splitX']){ | |
GrayCode.splitX(0, canvas.width, canvas.height, 0, Sync.pattern_num, false); | |
} else if (this.mode == mode['splitY']){ | |
GrayCode.splitY(0, canvas.width, canvas.height, 0, Sync.pattern_num, false); | |
} else if (this.mode == mode['antiSplitX']){ | |
GrayCode.splitX(0, canvas.width, canvas.height, 0, Sync.pattern_num, true); // anti-pattern | |
} else if (this.mode == mode['antiSplitY']){ | |
GrayCode.splitY(0, canvas.width, canvas.height, 0, Sync.pattern_num, true); // anti-pattern | |
} else { | |
console.log("GRAYCODE END"); | |
Util.offFullscreen(); | |
//setTimeout(function(){ location.href = "/"; }, 1000); | |
return; | |
} | |
$.getJSON("/cgi-bin/capture.py", { | |
'dir_name' : "gray_code/set" + Sync.set + "/", | |
'fname' : Sync.mode + "_" + Util.alignNum(Sync.pattern_num) + ".pgm", | |
'shutter_speeds' : Sync.shutter_speeds, | |
'capture': Sync.capture, | |
}, function(data) { | |
console.log(data) | |
Sync.pattern_num++; | |
Sync.drawAndCapture(); | |
//next_values = $.parseJSON(data) | |
//console.log(next_values) | |
}); | |
}, | |
} | |
// Add events: | |
// GrayCode::demo | |
$("#gc-demo").on('click', function(){ | |
Sync.setUp({ | |
'capture': 0, | |
}); | |
}); | |
// GrayCode::capture | |
$("#gc-capture").on('click', function(){ | |
Sync.setUp({ | |
'capture': 1, | |
}); | |
}); | |
TestDecoder = { | |
graycodes: [], | |
init: function() { // undefinedでもjavascriptは勝手に0が詰まってうれしい | |
for(var i=0; i<100; i++) { this.graycodes[i] = 0; } | |
}, | |
getImageData: function() { | |
var image = ctx.getImageData(0,0,100,100); | |
return image; | |
}, | |
getPixel: function(image,x,y) { | |
var pixel = { | |
'r': image.data[(image.width*y+x)*4 + 0], | |
'g': image.data[(image.width*y+x)*4 + 1], | |
'b': image.data[(image.width*y+x)*4 + 2], | |
'a': image.data[(image.width*y+x)*4 + 3], | |
}; | |
return pixel; | |
}, | |
pushGrayCodes: function() { | |
var image = this.getImageData(); | |
// CASE:splitX | |
for(var i=0; i<100; i++) { | |
var pixel = this.getPixel(image,i,0); | |
var code = pixel['r'] == 0 ? 0 : 1; | |
this.graycodes[i] = (this.graycodes[i] << 1) + code; | |
} | |
}, | |
evalGrayCodes: function() { | |
for (var i=0; i<100; i++) { | |
var binary = this.toBinary(this.graycodes[i]); | |
var gc = this.toGrayCode(i); | |
var gc_disp = this.graycodes[i]; | |
console.log([i, binary, gc, gc_disp]); | |
} | |
}, | |
toGrayCode: function(binary) { | |
return binary ^ (binary >> 1); | |
}, | |
toBinary: function(graycode) { | |
var tmp = 0; | |
tmp |= graycode; | |
while (graycode > 0) { | |
graycode >>= 1; | |
tmp ^= graycode; | |
} | |
return tmp; | |
}, | |
} | |
// Preview::testcapture | |
Preview = { | |
persec: 1, | |
run: false, | |
capture: function(){ | |
if (!Preview.run) return; | |
$.getJSON("/cgi-bin/capture.py", { | |
'persec': Preview.persec, | |
'capture': 1, | |
}, function(data){ | |
// TODO camera param返せたら便利? | |
console.log(data); | |
var timestamp = new Date().getTime(); | |
// $("#js-live-preview-img").attr("src", "data/preview.png" + '?' + timestamp); | |
// 丁寧に.load() でonloadイベント待ったら非同期で無限にタイミング増えて笑った | |
// draw | |
var canvasPv = document.getElementById("canvas-preview"); | |
var ctxPv = canvasPv.getContext('2d'); | |
var img = new Image(); | |
img.src = "data/preview.png" + '?' + timestamp; | |
img.onload = function(){ | |
ctxPv.drawImage(img, 0, 0, canvasPv.width, canvasPv.height); | |
Preview.capture(); | |
}; | |
}); | |
}, | |
}; | |
$("#pv-capture").on('click', function(){ | |
$(this).hide(); | |
$("#pv-capture-stop").show(); | |
$("#logo").hide(); | |
$("#js-live-preview-img").show(); | |
$("#canvas-preview").show(); | |
//$("#js-live-preview-params").show(); | |
Preview.run = true; | |
Preview.persec = Number($("#pv-persec option:selected").val()); | |
Preview.capture(); | |
}); | |
$("#pv-capture-stop").on('click', function(){ | |
$(this).hide(); | |
$("#pv-capture").show(); | |
Preview.run = false; | |
$("#canvas-preview").hide(); | |
$("#js-live-preview-img").hide(); | |
//$("#js-live-preview-params").hide(); | |
$("#logo").show(); | |
}); | |
// ChessBoard::Rendering | |
var ChessBoard = { | |
row: 5, | |
col: 8, | |
unitPx: 20, | |
shotCount: 0, | |
shotNum: 10, | |
capture: 1, | |
init: function(param_hash){ | |
// chessboard size | |
var selected = $("#cb-row-col").val(); | |
if (selected == "5x8") { | |
ChessBoard.row = 5; | |
ChessBoard.col = 8; | |
} else if (selected == "7x10"){ | |
ChessBoard.row = 7; | |
ChessBoard.col = 10; | |
} else { | |
ChessBoard.row = 11; | |
ChessBoard.col = 16; | |
}; | |
// unit | |
ChessBoard.unitPx = Number($("#cb-unit-px").val()); | |
// capture number | |
ChessBoard.shotNum = Number($("#cb-times").val()); | |
canvas.width = ChessBoard.unitPx * (ChessBoard.col+3); | |
canvas.height = ChessBoard.unitPx * (ChessBoard.row+3); | |
// shotNum | |
ChessBoard.shotNum = Number($("#cb-times option:selected").val()); | |
// capture flag | |
ChessBoard.capture = param_hash['capture']; | |
// preview | |
ChessBoard.preview_img = new Image(); | |
// toggleFullscreen | |
Util.onFullscreen(); | |
}, | |
render: function(){ | |
ctx.fillStyle = 'rgb(255,255,255)'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
var unitPx = ChessBoard.unitPx; | |
ctx.fillStyle = 'rgb(0,0,0)'; | |
for (var row=0; row<=ChessBoard.row; row++) { | |
for (var col=0; col<=ChessBoard.col; col++) { | |
if (row % 2 != 0 && col % 2 == 0) { | |
ctx.fillRect(unitPx + col*unitPx, unitPx + row*unitPx, unitPx, unitPx); | |
} else if (row % 2 == 0 && col % 2 != 0){ | |
ctx.fillRect(unitPx + col*unitPx, unitPx + row*unitPx, unitPx, unitPx); | |
} | |
} | |
} | |
}, | |
putText: function(text){ | |
ctx.fillStyle = 'rgb(0, 0, 0)'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
ctx.fillStyle = 'rgb(255,255,255)'; | |
ctx.font = "bold 32px sans-serif"; | |
ctx.fillText(text, 100, 100); | |
}, | |
onMove: false, | |
preview_img: undefined, | |
preview: function(){ | |
if (!ChessBoard.onMove) { | |
ChessBoard.incalib(); // fire next | |
return; | |
} | |
$.getJSON("/cgi-bin/capture.py", { | |
'persec': 0, | |
'capture': 1, | |
}, function(data){ | |
console.log(data); | |
var timestamp = new Date().getTime(); | |
ChessBoard.preview_img = new Image(); | |
ChessBoard.preview_img.src = "data/preview.png" + '?' + timestamp; | |
ChessBoard.preview_img.onload = function(){ | |
ctx.drawImage(ChessBoard.preview_img, canvas.width/2-160,canvas.height/2-120,320,240); | |
ChessBoard.preview(); | |
}; | |
}); | |
}, | |
incalib: function(){ | |
// END | |
if (ChessBoard.shotCount == ChessBoard.shotNum) { | |
Util.offFullscreen(); | |
$.getJSON('/cgi-bin/incalib.py', { | |
'chessboard_col' : ChessBoard.col, | |
'chessboard_row' : ChessBoard.row, | |
'unit_size' : ChessBoard.unitPx, | |
'num_of_images' : ChessBoard.shotNum, | |
}, function(data){ | |
alert(data['status']); | |
}); | |
return; | |
} | |
ChessBoard.render(); | |
$.getJSON('/cgi-bin/capture.py', { | |
'dir_name' : 'chessboard/', | |
'fname' : Util.alignNum(ChessBoard.shotCount) + ".pgm", | |
'capture' : ChessBoard.capture, | |
}, function(data){ | |
console.log(data) | |
ChessBoard.putText("Captured..." + ++ChessBoard.shotCount + "/" + ChessBoard.shotNum + " goNext"); | |
// preview | |
ChessBoard.onMove = true; | |
// preview captured | |
// ChessBoard.preview(); | |
// next | |
setTimeout(function(){ ChessBoard.onMove = false; }, 5000); | |
}); | |
} | |
}; | |
//ChessBoard::demo | |
$("#cb-demo").on('click', function(){ | |
ChessBoard.init({ 'capture': 0 }); | |
// ChessBoard.incalib({ 'capture': 0 }); | |
ChessBoard.render(); | |
Util.onFullscreen(); | |
setTimeout("Util.offFullscreen()", 3000); | |
}); | |
$("#cb-capture").on('click', function(){ | |
ChessBoard.init({ 'capture': 1 }); | |
ChessBoard.incalib(); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
バッドノウハウの塊感ある