embed board editor in study
parent
7e99e6cc1c
commit
8de43f2624
|
@ -24,13 +24,31 @@ object Editor extends LilaController {
|
|||
def load(urlFen: String) = Open { implicit ctx =>
|
||||
val fenStr = Some(urlFen.trim.replace("_", " ")).filter(_.nonEmpty) orElse get("fen")
|
||||
fuccess {
|
||||
val decodedFen = fenStr.map { java.net.URLDecoder.decode(_, "UTF-8").trim }.filter(_.nonEmpty)
|
||||
val situation = (decodedFen flatMap Forsyth.<<< map (_.situation)) | Situation(chess.variant.Standard)
|
||||
val fen = Forsyth >> situation
|
||||
Ok(html.board.editor(situation, fen, positionsJson, animationDuration = Env.api.EditorAnimationDuration))
|
||||
val situation = readFen(fenStr)
|
||||
Ok(html.board.editor(
|
||||
sit = situation,
|
||||
fen = Forsyth >> situation,
|
||||
positionsJson,
|
||||
animationDuration = Env.api.EditorAnimationDuration))
|
||||
}
|
||||
}
|
||||
|
||||
def data = Open { implicit ctx =>
|
||||
fuccess {
|
||||
val situation = readFen(get("fen"))
|
||||
Ok(html.board.JsData(
|
||||
sit = situation,
|
||||
fen = Forsyth >> situation,
|
||||
animationDuration = Env.api.EditorAnimationDuration)) as JSON
|
||||
}
|
||||
}
|
||||
|
||||
private def readFen(fen: Option[String]): Situation =
|
||||
fen.map {
|
||||
java.net.URLDecoder.decode(_, "UTF-8").trim
|
||||
}.filter(_.nonEmpty)
|
||||
.flatMap(Forsyth.<<<).map(_.situation) | Situation(chess.variant.Standard)
|
||||
|
||||
def game(id: String) = Open { implicit ctx =>
|
||||
OptionResult(GameRepo game id) { game =>
|
||||
Redirect {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package views.html.board
|
||||
|
||||
import controllers.routes
|
||||
import play.api.libs.json.{ JsArray, Json }
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
|
||||
object JsData extends lila.Steroids {
|
||||
|
||||
def apply(
|
||||
sit: chess.Situation,
|
||||
fen: String,
|
||||
animationDuration: Duration)(implicit ctx: Context) = Json.obj(
|
||||
"fen" -> fen.split(" ").headOption,
|
||||
"baseUrl" -> s"$netBaseUrl${routes.Editor.load("")}",
|
||||
"color" -> sit.color.letter.toString,
|
||||
"castles" -> Json.obj(
|
||||
"K" -> (sit canCastle chess.White on chess.KingSide),
|
||||
"Q" -> (sit canCastle chess.White on chess.QueenSide),
|
||||
"k" -> (sit canCastle chess.Black on chess.KingSide),
|
||||
"q" -> (sit canCastle chess.Black on chess.QueenSide)
|
||||
),
|
||||
"animation" -> Json.obj(
|
||||
"duration" -> ctx.pref.animationFactor * animationDuration.toMillis
|
||||
),
|
||||
"i18n" -> i18nJsObject(
|
||||
trans.startPosition,
|
||||
trans.clearBoard,
|
||||
trans.flipBoard,
|
||||
trans.loadPosition,
|
||||
trans.castling,
|
||||
trans.whiteCastlingKingside,
|
||||
trans.whiteCastlingQueenside,
|
||||
trans.blackCastlingKingside,
|
||||
trans.blackCastlingQueenside,
|
||||
trans.whitePlays,
|
||||
trans.blackPlays,
|
||||
trans.continueFromHere,
|
||||
trans.playWithTheMachine,
|
||||
trans.playWithAFriend,
|
||||
trans.analysis)
|
||||
)
|
||||
}
|
|
@ -1,46 +1,14 @@
|
|||
@(sit: chess.Situation, fen: String, positionsJson: String, animationDuration: scala.concurrent.duration.Duration)(implicit ctx: Context)
|
||||
|
||||
@import chess.Color.{ White, Black }
|
||||
@import chess.{ KingSide, QueenSide }
|
||||
|
||||
@moreCss = {
|
||||
@cssTag("boardEditor.css")
|
||||
}
|
||||
@moreJs = {
|
||||
@jsAt(s"compiled/lichess.editor${isProd??(".min")}.js")
|
||||
@embedJs {
|
||||
LichessEditor(document.getElementById('board_editor'), {
|
||||
fen: "@fen.split(" ").headOption",
|
||||
baseUrl: "@netBaseUrl@routes.Editor.load("")",
|
||||
color: "@sit.color.letter",
|
||||
castles: {
|
||||
K: @sit.canCastle(White).on(KingSide),
|
||||
Q: @sit.canCastle(White).on(QueenSide),
|
||||
k: @sit.canCastle(Black).on(KingSide),
|
||||
q: @sit.canCastle(Black).on(QueenSide)
|
||||
},
|
||||
animation: {
|
||||
duration: @{ctx.pref.animationFactor * animationDuration.toMillis}
|
||||
},
|
||||
positions: @Html(positionsJson),
|
||||
i18n: @Html(J.stringify(i18nJsObject(
|
||||
trans.startPosition,
|
||||
trans.clearBoard,
|
||||
trans.flipBoard,
|
||||
trans.loadPosition,
|
||||
trans.castling,
|
||||
trans.whiteCastlingKingside,
|
||||
trans.whiteCastlingQueenside,
|
||||
trans.blackCastlingKingside,
|
||||
trans.blackCastlingQueenside,
|
||||
trans.whitePlays,
|
||||
trans.blackPlays,
|
||||
trans.continueFromHere,
|
||||
trans.playWithTheMachine,
|
||||
trans.playWithAFriend,
|
||||
trans.analysis
|
||||
)))
|
||||
});
|
||||
var data = @J.stringify(JsData(sit, fen, animationDuration));
|
||||
data.positions = @positionsJson;
|
||||
LichessEditor(document.getElementById('board_editor'), data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,5 +21,5 @@ openGraph = lila.app.ui.OpenGraph(
|
|||
title = "Chess board editor",
|
||||
url = s"$netBaseUrl${routes.Editor.index.url}",
|
||||
description = "Load opening positions or create your own chess position on a chess board editor").some) {
|
||||
<div id="board_editor" class="cg-512"></div>
|
||||
<div id="board_editor" class="board_editor cg-512"></div>
|
||||
}
|
||||
|
|
|
@ -349,6 +349,7 @@ POST /import controllers.Importer.sendGame
|
|||
GET /import/master/$id<\w{8}>/:color controllers.Importer.masterGame(id: String, color: String)
|
||||
|
||||
# Edit
|
||||
GET /editor.json controllers.Editor.data
|
||||
GET /editor/*urlFen controllers.Editor.load(urlFen: String)
|
||||
GET /editor controllers.Editor.index
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ case class Study(
|
|||
|
||||
object Study {
|
||||
|
||||
def toName(str: String) = str.trim take 100
|
||||
|
||||
case class Data(
|
||||
name: String,
|
||||
visibility: String,
|
||||
|
|
|
@ -252,7 +252,7 @@ final class StudyApi(
|
|||
|
||||
def editStudy(byUserId: User.ID, studyId: Study.ID, data: Study.Data) = sequenceStudy(studyId) { study =>
|
||||
data.settings ?? { settings =>
|
||||
val newStudy = study.copy(name = data.name, settings = settings)
|
||||
val newStudy = study.copy(name = Study toName data.name, settings = settings)
|
||||
(newStudy != study) ?? {
|
||||
studyRepo.update(newStudy) >>- sendTo(study, Socket.ReloadAll)
|
||||
}
|
||||
|
|
|
@ -54,13 +54,13 @@ body.dark #board_editor .spare.white {
|
|||
left: 0;
|
||||
top: -36%;
|
||||
}
|
||||
#editor-side {
|
||||
#board_editor .editor-side {
|
||||
position: absolute;
|
||||
left: 552px;
|
||||
top: 150px;
|
||||
width: 228px;
|
||||
}
|
||||
#editor-side > div {
|
||||
#board_editor .editor-side > div {
|
||||
margin-bottom: 3em;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -72,10 +72,10 @@ body.dark #board_editor .spare.white {
|
|||
#board_editor .button.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
#editor-side select.positions {
|
||||
#board_editor .editor-side select.positions {
|
||||
width: 100%;
|
||||
}
|
||||
#editor-side select.positions option:checked {
|
||||
#board_editor .editor-side select.positions option:checked {
|
||||
font-style: italic;
|
||||
}
|
||||
#board_editor table td, #board_editor table th {
|
||||
|
@ -97,14 +97,14 @@ body.dark #board_editor .spare.white {
|
|||
#board_editor .copyable {
|
||||
width: 462px;
|
||||
}
|
||||
#editor-side > div.metadata {
|
||||
#board_editor .editor-side > div.metadata {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#editor-side > div.metadata div.color {
|
||||
#board_editor .editor-side > div.metadata div.color {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
#editor-side .castling label,
|
||||
#editor-side .castling input {
|
||||
#board_editor .editor-side .castling label,
|
||||
#board_editor .editor-side .castling input {
|
||||
display:inline-block;
|
||||
margin: 3px;
|
||||
vertical-align: middle;
|
||||
|
|
|
@ -267,6 +267,12 @@ body:not(.dark) .material.form .study_tabs a {
|
|||
.study_overboard .form {
|
||||
padding: 20px;
|
||||
}
|
||||
.study_overboard .form .editor {
|
||||
margin: -30px 0 30px 0;
|
||||
}
|
||||
.study_overboard .form .editor .spinner {
|
||||
padding-top: 80px;
|
||||
}
|
||||
.study_buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -361,3 +367,54 @@ form.delete_study button {
|
|||
form.delete_study button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
form .editor {
|
||||
position: relative;
|
||||
}
|
||||
form .editor .spare {
|
||||
height: 26px;
|
||||
width: 224px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
form .editor .spare .no-square {
|
||||
position: relative;
|
||||
display:inline-block;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
form .editor .spare piece {
|
||||
cursor: pointer;
|
||||
}
|
||||
form .editor .cg-board-wrap {
|
||||
width: 224px;
|
||||
height: 224px;
|
||||
}
|
||||
form .editor .editor-side {
|
||||
position: absolute;
|
||||
left: 233px;
|
||||
top: 50px;
|
||||
text-align: left;
|
||||
}
|
||||
form .editor .editor-side .content_box {
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
form .editor .castling {
|
||||
margin-top: 10px;
|
||||
}
|
||||
form .editor .castling strong {
|
||||
display: none;
|
||||
}
|
||||
form .editor .castling label {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
form .editor .castling input {
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
}
|
||||
form .editor a.button {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
function copy(obj, newValues) {
|
||||
var k, c = {};
|
||||
for (k in obj) {
|
||||
c[k] = obj[k];
|
||||
}
|
||||
for (k in newValues) {
|
||||
c[k] = newValues[k];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
default: function(ply) {
|
||||
return [{
|
||||
ply: ply || 0,
|
||||
variation: null
|
||||
}];
|
||||
},
|
||||
|
||||
read: function(str) {
|
||||
return str.split(',').map(function(step) {
|
||||
var s = step.split(':');
|
||||
return {
|
||||
ply: parseInt(s[0]),
|
||||
variation: s[1] ? parseInt(s[1]) : null
|
||||
};
|
||||
})
|
||||
},
|
||||
|
||||
write: function(path) {
|
||||
return path.map(function(step) {
|
||||
return step.variation ? step.ply + ':' + step.variation : step.ply;
|
||||
}).join(',');
|
||||
},
|
||||
|
||||
isRoot: function(path) {
|
||||
return path.length === 1;
|
||||
},
|
||||
|
||||
contains: function(p1, p2) {
|
||||
if (p2.length < p1.length) return;
|
||||
for (var i = 0; i < p2.length; i++) {
|
||||
if (!p1[i].variation) return true;
|
||||
if (p1[i].ply !== p2[i].ply || p1[i].variation !== p2[i].variation) return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
currentPly: function(path) {
|
||||
return path[path.length - 1].ply;
|
||||
},
|
||||
|
||||
withPly: function(path, ply) {
|
||||
var p2 = path.slice(0);
|
||||
var last = p2.length - 1;
|
||||
p2[last] = copy(p2[last], {
|
||||
ply: ply
|
||||
});
|
||||
return p2;
|
||||
},
|
||||
|
||||
withVariation: function(path, index) {
|
||||
var p2 = path.slice(0);
|
||||
var last = p2.length - 1;
|
||||
var ply = p2[last].ply;
|
||||
p2[last] = copy(p2[last], {
|
||||
ply: ply,
|
||||
variation: index
|
||||
});
|
||||
p2.push({
|
||||
ply: ply,
|
||||
variation: null
|
||||
});
|
||||
return p2;
|
||||
},
|
||||
|
||||
withoutVariation: function(path) {
|
||||
var p2 = path.slice(0, path.length - 1);
|
||||
var last = p2.length - 1;
|
||||
p2[last] = copy(p2[last], {
|
||||
variation: null
|
||||
});
|
||||
return p2;
|
||||
}
|
||||
};
|
|
@ -9,13 +9,14 @@ function implicitVariant(tab) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
ctrl: function(send, chapters, setTab) {
|
||||
ctrl: function(send, chapters, setTab, root) {
|
||||
|
||||
var vm = {
|
||||
variants: [],
|
||||
open: false,
|
||||
initial: m.prop(false),
|
||||
tab: storedProp('study.form.tab', 'blank'),
|
||||
tab: storedProp('study.form.tab', 'init'),
|
||||
editorFen: m.prop(null)
|
||||
};
|
||||
|
||||
var loadVariants = function() {
|
||||
|
@ -37,6 +38,7 @@ module.exports = {
|
|||
return {
|
||||
vm: vm,
|
||||
open: open,
|
||||
root: root,
|
||||
openInitial: function() {
|
||||
open();
|
||||
vm.initial(true);
|
||||
|
@ -74,14 +76,14 @@ module.exports = {
|
|||
return dialog.form({
|
||||
onClose: ctrl.close,
|
||||
content: [
|
||||
m('h2', 'New chapter'),
|
||||
activeTab === 'edit' ? null : m('h2', 'New chapter'),
|
||||
m('form.material.form', {
|
||||
onsubmit: function(e) {
|
||||
ctrl.submit({
|
||||
name: fieldValue(e, 'name'),
|
||||
game: fieldValue(e, 'game'),
|
||||
variant: fieldValue(e, 'variant'),
|
||||
fen: fieldValue(e, 'fen'),
|
||||
fen: fieldValue(e, 'fen') || (activeTab === 'edit' ? ctrl.vm.editorFen() : null),
|
||||
pgn: fieldValue(e, 'pgn'),
|
||||
orientation: fieldValue(e, 'orientation')
|
||||
});
|
||||
|
@ -89,8 +91,11 @@ module.exports = {
|
|||
return false;
|
||||
}
|
||||
}, [
|
||||
m('div.game.form-group', [
|
||||
m('div.form-group', [
|
||||
m('input#chapter-name', {
|
||||
required: true,
|
||||
minlength: 2,
|
||||
maxlength: 80,
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate && !el.value) {
|
||||
el.value = 'Chapter ' + (ctrl.initial() ? 1 : (ctrl.chapters().length + 1));
|
||||
|
@ -103,26 +108,50 @@ module.exports = {
|
|||
m('i.bar')
|
||||
]),
|
||||
m('div.study_tabs', [
|
||||
makeTab('blank', 'Blank', 'Start from initial position'),
|
||||
makeTab('init', 'Init', 'Start from initial position'),
|
||||
makeTab('edit', 'Edit', 'Start from custom position'),
|
||||
makeTab('game', 'game', 'Load a lichess game'),
|
||||
makeTab('fen', 'FEN', 'Load a FEN position'),
|
||||
makeTab('pgn', 'PGN', 'Load a PGN game')
|
||||
]),
|
||||
activeTab === 'game' ? m('div.game.form-group', [
|
||||
activeTab === 'edit' ? m('div', {
|
||||
config: function(el, isUpdate, ctx) {
|
||||
if (ctx.editor) return;
|
||||
$.when(
|
||||
lichess.loadScript('/assets/compiled/lichess.editor.js'),
|
||||
$.get('/editor.json', {
|
||||
fen: ctrl.root.vm.node.fen
|
||||
})
|
||||
).then(function(a, b) {
|
||||
var data = b[0];
|
||||
data.embed = true;
|
||||
data.options = {
|
||||
inlineCastling: true,
|
||||
onChange: function(fen) {
|
||||
ctrl.vm.editorFen(fen);
|
||||
m.redraw();
|
||||
}
|
||||
};
|
||||
ctx.editor = LichessEditor(el, data);
|
||||
ctrl.vm.editorFen(ctx.editor.getFen());
|
||||
});
|
||||
}
|
||||
}, m.trust(lichess.spinnerHtml)) : null,
|
||||
activeTab === 'game' ? m('div.form-group', [
|
||||
m('input#chapter-game', {
|
||||
placeholder: 'Game ID or URL'
|
||||
}),
|
||||
m('label.control-label[for=chapter-game]', 'From played or imported game'),
|
||||
m('i.bar')
|
||||
]) : null,
|
||||
activeTab === 'fen' ? m('div.game.form-group', [
|
||||
activeTab === 'fen' ? m('div.form-group', [
|
||||
m('input#chapter-fen', {
|
||||
placeholder: 'Initial position'
|
||||
}),
|
||||
m('label.control-label[for=chapter-fen]', 'From FEN position'),
|
||||
m('i.bar')
|
||||
]) : null,
|
||||
activeTab === 'pgn' ? m('div.game.form-group', [
|
||||
activeTab === 'pgn' ? m('div.form-group', [
|
||||
m('textarea#chapter-pgn', {
|
||||
placeholder: 'PGN tags and moves'
|
||||
}),
|
||||
|
@ -130,7 +159,7 @@ module.exports = {
|
|||
m('i.bar')
|
||||
]) : null,
|
||||
m('div', [
|
||||
m('div.game.form-group.half', [
|
||||
m('div.form-group.half', [
|
||||
m('select#chapter-variant', {
|
||||
disabled: implicitVariant(activeTab)
|
||||
}, implicitVariant(activeTab) ? [
|
||||
|
@ -144,7 +173,7 @@ module.exports = {
|
|||
m('label.control-label[for=chapter-variant]', 'Variant'),
|
||||
m('i.bar')
|
||||
]),
|
||||
m('div.game.form-group.half', [
|
||||
m('div.form-group.half', [
|
||||
m('select#chapter-orientation', ['White', 'Black'].map(function(color) {
|
||||
return m('option', {
|
||||
value: color.toLowerCase()
|
||||
|
|
|
@ -4,12 +4,12 @@ var partial = require('chessground').util.partial;
|
|||
var chapterForm = require('./chapterForm');
|
||||
|
||||
module.exports = {
|
||||
ctrl: function(initChapters, send, setTab) {
|
||||
ctrl: function(initChapters, send, setTab, root) {
|
||||
|
||||
var confing = m.prop(null); // which chapter is being configured by us
|
||||
var list = m.prop(initChapters);
|
||||
|
||||
var form = chapterForm.ctrl(send, list, setTab);
|
||||
var form = chapterForm.ctrl(send, list, setTab, root);
|
||||
|
||||
return {
|
||||
confing: confing,
|
||||
|
|
|
@ -34,7 +34,7 @@ module.exports = {
|
|||
return data;
|
||||
});
|
||||
var members = memberCtrl(data.members, ctrl.userId, data.ownerId, send, partial(vm.tab, 'members'));
|
||||
var chapters = chapterCtrl(data.chapters, send, partial(vm.tab, 'chapters'));
|
||||
var chapters = chapterCtrl(data.chapters, send, partial(vm.tab, 'chapters'), ctrl);
|
||||
|
||||
var currentChapterId = function() {
|
||||
return vm.chapterId || data.position.chapterId;
|
||||
|
|
|
@ -71,10 +71,13 @@ module.exports = {
|
|||
}, [
|
||||
m('div.game.form-group', [
|
||||
m('input#study-name', {
|
||||
required: true,
|
||||
minlength: 3,
|
||||
maxlength: 100,
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate && !el.value) {
|
||||
el.value = data.name;
|
||||
el.select();
|
||||
if (isNew) el.select();
|
||||
el.focus();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ function inputs(ctrl) {
|
|||
m('input.copyable.autoselect[spellCheck=false]', {
|
||||
value: ctrl.vm.node.fen,
|
||||
onchange: function(e) {
|
||||
if (e.target.value !== ctrl.vm.step.fen) ctrl.changeFen(e.target.value);
|
||||
if (e.target.value !== ctrl.vm.node.fen) ctrl.changeFen(e.target.value);
|
||||
}
|
||||
}),
|
||||
m('div.pgn', [
|
||||
|
|
|
@ -7,6 +7,8 @@ var keyboard = require('./keyboard');
|
|||
module.exports = function(cfg) {
|
||||
|
||||
this.data = editor.init(cfg);
|
||||
this.options = cfg.options;
|
||||
this.embed = cfg.embed;
|
||||
|
||||
this.trans = partial(editor.trans, this.data.i18n);
|
||||
|
||||
|
@ -26,13 +28,14 @@ module.exports = function(cfg) {
|
|||
}];
|
||||
|
||||
this.positionIndex = {};
|
||||
cfg.positions.forEach(function(p, i) {
|
||||
cfg.positions && cfg.positions.forEach(function(p, i) {
|
||||
this.positionIndex[p.fen.split(' ')[0]] = i;
|
||||
}.bind(this));
|
||||
|
||||
this.chessground = new chessground.controller({
|
||||
fen: cfg.fen,
|
||||
orientation: 'white',
|
||||
orientation: cfg.options.orientation || 'white',
|
||||
coordinates: !this.embed,
|
||||
movable: {
|
||||
free: true,
|
||||
color: 'both',
|
||||
|
@ -56,19 +59,37 @@ module.exports = function(cfg) {
|
|||
enabled: false
|
||||
},
|
||||
events: {
|
||||
change: m.redraw
|
||||
change: function() {
|
||||
onChange();
|
||||
m.redraw();
|
||||
}.bind(this)
|
||||
},
|
||||
disableContextMenu: true
|
||||
});
|
||||
|
||||
var onChange = function() {
|
||||
this.options.onChange && this.options.onChange(this.computeFen());
|
||||
}.bind(this);
|
||||
|
||||
this.computeFen = partial(editor.computeFen, this.data, this.chessground.getFen);
|
||||
|
||||
this.setColor = function(letter) {
|
||||
this.data.color(letter);
|
||||
onChange();
|
||||
}.bind(this);
|
||||
|
||||
this.setCastle = function(id, value) {
|
||||
this.data.castles[id](value);
|
||||
onChange();
|
||||
}.bind(this);
|
||||
|
||||
this.startPosition = function() {
|
||||
this.chessground.set({
|
||||
fen: 'start'
|
||||
});
|
||||
this.data.castles = editor.castlesAt(true);
|
||||
this.data.color('w');
|
||||
onChange();
|
||||
}.bind(this);
|
||||
|
||||
this.clearBoard = function() {
|
||||
|
@ -76,6 +97,7 @@ module.exports = function(cfg) {
|
|||
fen: '8/8/8/8/8/8/8/8'
|
||||
});
|
||||
this.data.castles = editor.castlesAt(false);
|
||||
onChange();
|
||||
}.bind(this);
|
||||
|
||||
this.loadNewFen = function(fen) {
|
||||
|
|
|
@ -8,4 +8,8 @@ module.exports = function(element, config) {
|
|||
controller: function () { return controller; },
|
||||
view: view
|
||||
});
|
||||
|
||||
return {
|
||||
getFen: controller.computeFen
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,8 +7,8 @@ var m = require('mithril');
|
|||
function castleCheckBox(ctrl, id, label, reversed) {
|
||||
var input = m('input[type=checkbox]', {
|
||||
checked: ctrl.data.castles[id](),
|
||||
onchange: function() {
|
||||
ctrl.data.castles[id](this.checked);
|
||||
onchange: function(e) {
|
||||
ctrl.setCastle(id, e.target.checked);
|
||||
}
|
||||
});
|
||||
return m('label', reversed ? [input, label] : [label, input]);
|
||||
|
@ -22,7 +22,7 @@ function optgroup(name, opts) {
|
|||
|
||||
function controls(ctrl, fen) {
|
||||
var positionIndex = ctrl.positionIndex[fen.split(' ')[0]];
|
||||
var currentPosition = positionIndex !== -1 ? ctrl.data.positions[positionIndex] : null;
|
||||
var currentPosition = ctrl.data.positions && positionIndex !== -1 ? ctrl.data.positions[positionIndex] : null;
|
||||
var encodedFen = fen.replace(/\s/g, '_');
|
||||
var position2option = function(pos) {
|
||||
return {
|
||||
|
@ -34,9 +34,9 @@ function controls(ctrl, fen) {
|
|||
children: [pos.name]
|
||||
};
|
||||
}
|
||||
return m('div#editor-side', [
|
||||
m('div', [
|
||||
m('select.positions', {
|
||||
return m('div.editor-side', [
|
||||
ctrl.embed ? null : m('div', [
|
||||
ctrl.data.positions ? m('select.positions', {
|
||||
onchange: function(e) {
|
||||
ctrl.loadNewFen(e.target.value);
|
||||
}
|
||||
|
@ -51,31 +51,39 @@ function controls(ctrl, fen) {
|
|||
optgroup('Popular openings',
|
||||
ctrl.data.positions.map(position2option)
|
||||
)
|
||||
])
|
||||
]) : null
|
||||
]),
|
||||
m('div.metadata.content_box', [
|
||||
m('div.color', [
|
||||
m('div.color',
|
||||
m('select', {
|
||||
value: ctrl.data.color(),
|
||||
onchange: m.withAttr('value', ctrl.data.color)
|
||||
}, [
|
||||
m('option[value=w]', ctrl.trans('whitePlays')),
|
||||
m('option[value=b]', ctrl.trans('blackPlays'))
|
||||
])
|
||||
]),
|
||||
onchange: m.withAttr('value', ctrl.setColor)
|
||||
}, ['whitePlays', 'blackPlays'].map(function(key) {
|
||||
return m('option', {
|
||||
value: key[0],
|
||||
selected: ctrl.data.color() === key[0]
|
||||
}, ctrl.trans(key));
|
||||
}))
|
||||
),
|
||||
m('div.castling', [
|
||||
m('strong', ctrl.trans('castling')),
|
||||
m('div', [
|
||||
castleCheckBox(ctrl, 'K', ctrl.trans('whiteCastlingKingside'), false),
|
||||
castleCheckBox(ctrl, 'K', ctrl.trans('whiteCastlingKingside'), ctrl.options.inlineCastling),
|
||||
castleCheckBox(ctrl, 'Q', ctrl.trans('whiteCastlingQueenside'), true)
|
||||
]),
|
||||
m('div', [
|
||||
castleCheckBox(ctrl, 'k', ctrl.trans('blackCastlingKingside'), false),
|
||||
castleCheckBox(ctrl, 'k', ctrl.trans('blackCastlingKingside'), ctrl.options.inlineCastling),
|
||||
castleCheckBox(ctrl, 'q', ctrl.trans('blackCastlingQueenside'), true)
|
||||
])
|
||||
])
|
||||
]),
|
||||
m('div', [
|
||||
ctrl.embed ? m('div', [
|
||||
m('a.button.frameless', {
|
||||
onclick: ctrl.startPosition
|
||||
}, 'Initial position'),
|
||||
m('a.button.frameless', {
|
||||
onclick: ctrl.clearBoard
|
||||
}, 'Empty board')
|
||||
]) : m('div', [
|
||||
m('a.button.text[data-icon=B]', {
|
||||
onclick: ctrl.chessground.toggleOrientation
|
||||
}, ctrl.trans('flipBoard')),
|
||||
|
@ -92,7 +100,7 @@ function controls(ctrl, fen) {
|
|||
},
|
||||
m('span.text[data-icon=U]', ctrl.trans('continueFromHere')))
|
||||
]),
|
||||
m('div.continue_with', [
|
||||
ctrl.embed ? null : m('div.continue_with', [
|
||||
m('a.button', {
|
||||
href: '/?fen=' + encodedFen + '#ai',
|
||||
rel: 'nofollow'
|
||||
|
@ -107,6 +115,7 @@ function controls(ctrl, fen) {
|
|||
}
|
||||
|
||||
function inputs(ctrl, fen) {
|
||||
if (ctrl.embed) return;
|
||||
if (ctrl.vm.redirecting) return m.trust(lichess.spinnerHtml);
|
||||
return m('div.copyables', [
|
||||
m('p', [
|
||||
|
|
Loading…
Reference in New Issue