Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ trait HtmlBranchWriter {
case _ => ""
}

def toHtmlString(isJapanese: Boolean = true)(implicit stateCache: StateCache): String = {
def toHtmlString(isJapanese: Boolean = true, comments: Map[StateHash, String])(implicit stateCache: StateCache): String = {
val stateHistory = history.map(stateCache.get)
val states = stateHistory.zip(None +: moves.map(Some.apply)).zipWithIndex.map {
case ((Some(st), Some(m)), i) => st.toHtmlString(s"#${offset + i}: ${moveToString(m, isJapanese)}", Some(m.to))
case ((Some(st), Some(m)), i) => st.toHtmlString(s"#${offset + i}: ${moveToString(m, isJapanese)}", Some(m.to), comments.get(st.hash))
case ((Some(st), None), _) => st.toHtmlString(s"Start", None)
case _ => ""
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ trait HtmlStateWriter {
val shogiBody = "shogi-body"
val shogiHand = "shogi-hand"
val shogiBoard = "shogi-board"
val shogiComment = "shogi-comment"
val fileIndex = "sb-f"
val rankIndex = "sb-r"
val pieceInverted = "sp-i"
Expand Down Expand Up @@ -51,13 +52,48 @@ trait HtmlStateWriter {
(horizontalIndex +: brd).mkString("<tr>", "</tr><tr>", "</tr>")
}

def toHtmlString(header: String = "", lastMove: Option[Square] = None): String = Seq(
def toHtmlString(header: String = "", lastMove: Option[Square] = None, comment: Option[String] = None): String = Seq(
s"""<div class="${shogiState}">""",
s"""<div class="${shogiHeader}">${header}</div>""",
s"""<div class=${shogiBody}>""",
s"""<div class="${shogiBody}">""",
comment.isDefined.fold("""<table><tbody><tr><td>""", ""),
s"""<div class="${shogiHand}">${createHandString(Player.WHITE)}</div>""",
s"""<table class="${shogiBoard}"><tbody>${createBoardHtml(lastMove)}</tbody></table>""",
s"""<div class="${shogiHand}">${createHandString(Player.BLACK)}</div>""",
comment.map(c => s"""</td><td class="${shogiComment}"><p>${escape(c)}</p></td></tr></tbody></table>""").getOrElse(""),
"""</div></div>"""
).mkString

/**
* Code to escape text HTML nodes. Taken from scala.xml
*/
private[this] def escape(text: String, s: StringBuilder): Unit = {
// Implemented per XML spec:
// http://www.w3.org/International/questions/qa-controls
// imperative code 3x-4x faster than current implementation
// dpp (David Pollak) 2010/02/03
val len = text.length
var pos = 0

while (pos < len) {
text.charAt(pos) match {
case '<' => s.append("&lt;")
case '>' => s.append("&gt;")
case '&' => s.append("&amp;")
case '"' => s.append("&quot;")
case '\n' => s.append('\n')
case '\r' => s.append('\r')
case '\t' => s.append('\t')
case c if c < ' ' =>
case c => s.append(c)
}
pos += 1
}
}

private[this] def escape(text: String): String = {
val builder = new StringBuilder
escape(text, builder)
builder.result()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class HtmlStateWriterSpec extends FlatSpec with MustMatchers with GeneratorDrive

"HtmlStateWriter#toHtmlString" must "set reflect the position" in {
TestHtmlStateWriter(HIRATE.board, HIRATE.hand).toHtmlString() mustBe
"<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=shogi-body><div class=\"shogi-hand\">☖</div>" +
"<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=\"shogi-body\"><div class=\"shogi-hand\">☖</div>" +
"<table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td>" +
"<td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td>" +
"<td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td class=\"sp-i\">香</td><td class=\"sp-i\">桂</td>" +
Expand All @@ -30,12 +30,12 @@ class HtmlStateWriterSpec extends FlatSpec with MustMatchers with GeneratorDrive
"<td>玉</td><td>金</td><td>銀</td><td>桂</td><td>香</td><td class=\"sb-r\">9</td></tr>" +
"</tbody></table><div class=\"shogi-hand\">☗</div></div></div>"

TestHtmlStateWriter(MATING_BLACK.board, MATING_BLACK.hand).toHtmlString() mustBe "<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=shogi-body><div class=\"shogi-hand\">☖飛2角2金4銀4桂4香4歩18</div><table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td><td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td><td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td/><td/><td/><td/><td class=\"sp-i\">玉</td><td/><td/><td/><td/><td class=\"sb-r\">1</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">2</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">3</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">4</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">5</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">6</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">7</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">8</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">9</td></tr></tbody></table><div class=\"shogi-hand\">☗</div></div></div>"
TestHtmlStateWriter(MATING_WHITE.board, MATING_WHITE.hand).toHtmlString() mustBe "<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=shogi-body><div class=\"shogi-hand\">☖</div><table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td><td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td><td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">1</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">2</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">3</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">4</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">5</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">6</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">7</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">8</td></tr><tr><td/><td/><td/><td/><td>玉</td><td/><td/><td/><td/><td class=\"sb-r\">9</td></tr></tbody></table><div class=\"shogi-hand\">☗飛2角2金4銀4桂4香4歩18</div></div></div>"
TestHtmlStateWriter(MATING_BLACK.board, MATING_BLACK.hand).toHtmlString() mustBe "<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=\"shogi-body\"><div class=\"shogi-hand\">☖飛2角2金4銀4桂4香4歩18</div><table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td><td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td><td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td/><td/><td/><td/><td class=\"sp-i\">玉</td><td/><td/><td/><td/><td class=\"sb-r\">1</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">2</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">3</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">4</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">5</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">6</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">7</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">8</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">9</td></tr></tbody></table><div class=\"shogi-hand\">☗</div></div></div>"
TestHtmlStateWriter(MATING_WHITE.board, MATING_WHITE.hand).toHtmlString() mustBe "<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=\"shogi-body\"><div class=\"shogi-hand\">☖</div><table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td><td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td><td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">1</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">2</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">3</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">4</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">5</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">6</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">7</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">8</td></tr><tr><td/><td/><td/><td/><td>玉</td><td/><td/><td/><td/><td class=\"sb-r\">9</td></tr></tbody></table><div class=\"shogi-hand\">☗飛2角2金4銀4桂4香4歩18</div></div></div>"
}
it must "reflect the header" in {
TestHtmlStateWriter(HIRATE.board, HIRATE.hand).toHtmlString("Header header") mustBe
"<div class=\"shogi-state\"><div class=\"shogi-header\">Header header</div><div class=shogi-body><div class=\"shogi-hand\">☖</div>" +
"<div class=\"shogi-state\"><div class=\"shogi-header\">Header header</div><div class=\"shogi-body\"><div class=\"shogi-hand\">☖</div>" +
"<table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td>" +
"<td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td>" +
"<td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td class=\"sp-i\">香</td><td class=\"sp-i\">桂</td>" +
Expand All @@ -53,7 +53,7 @@ class HtmlStateWriterSpec extends FlatSpec with MustMatchers with GeneratorDrive
}
it must "reflect last moves" in {
TestHtmlStateWriter(HIRATE.board, HIRATE.hand).toHtmlString(lastMove = Some(P33)) mustBe
"<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=shogi-body><div class=\"shogi-hand\">☖</div>" +
"<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=\"shogi-body\"><div class=\"shogi-hand\">☖</div>" +
"<table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td>" +
"<td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td>" +
"<td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td class=\"sp-i\">香</td><td class=\"sp-i\">桂</td>" +
Expand All @@ -72,4 +72,23 @@ class HtmlStateWriterSpec extends FlatSpec with MustMatchers with GeneratorDrive
it must "has 100 td elements" in forAll(StateGen.statesWithFullPieces, minSuccessful(10)) { st =>
TestHtmlStateWriter(st.board, st.hand).toHtmlString().split("<td").length mustBe 101
}
it must "escape comments" in {
TestHtmlStateWriter(HIRATE.board, HIRATE.hand).toHtmlString(comment = Some("<comment&123>")) mustBe
"<div class=\"shogi-state\"><div class=\"shogi-header\"></div><div class=\"shogi-body\"><table><tbody><tr><td><div class=\"shogi-hand\">☖</div>" +
"<table class=\"shogi-board\"><tbody><tr><td class=\"sb-f\">9</td><td class=\"sb-f\">8</td><td class=\"sb-f\">7</td>" +
"<td class=\"sb-f\">6</td><td class=\"sb-f\">5</td><td class=\"sb-f\">4</td><td class=\"sb-f\">3</td><td class=\"sb-f\">2</td>" +
"<td class=\"sb-f\">1</td><td class=\"sb-f sb-r\"/></tr><tr><td class=\"sp-i\">香</td><td class=\"sp-i\">桂</td>" +
"<td class=\"sp-i\">銀</td><td class=\"sp-i\">金</td><td class=\"sp-i\">玉</td><td class=\"sp-i\">金</td><td class=\"sp-i\">銀</td>" +
"<td class=\"sp-i\">桂</td><td class=\"sp-i\">香</td><td class=\"sb-r\">1</td></tr><tr><td/><td class=\"sp-i\">飛</td><td/><td/>" +
"<td/><td/><td/><td class=\"sp-i\">角</td><td/><td class=\"sb-r\">2</td></tr><tr><td class=\"sp-i\">歩</td>" +
"<td class=\"sp-i\">歩</td><td class=\"sp-i\">歩</td><td class=\"sp-i\">歩</td><td class=\"sp-i\">歩</td><td class=\"sp-i\">歩</td>" +
"<td class=\"sp-i\">歩</td><td class=\"sp-i\">歩</td><td class=\"sp-i\">歩</td><td class=\"sb-r\">3</td></tr><tr><td/><td/><td/>" +
"<td/><td/><td/><td/><td/><td/><td class=\"sb-r\">4</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/>" +
"<td class=\"sb-r\">5</td></tr><tr><td/><td/><td/><td/><td/><td/><td/><td/><td/><td class=\"sb-r\">6</td></tr><tr><td>歩</td>" +
"<td>歩</td><td>歩</td><td>歩</td><td>歩</td><td>歩</td><td>歩</td><td>歩</td><td>歩</td><td class=\"sb-r\">7</td></tr><tr><td/>" +
"<td>角</td><td/><td/><td/><td/><td/><td>飛</td><td/><td class=\"sb-r\">8</td></tr><tr><td>香</td><td>桂</td><td>銀</td><td>金</td>" +
"<td>玉</td><td>金</td><td>銀</td><td>桂</td><td>香</td><td class=\"sb-r\">9</td></tr>" +
"</tbody></table><div class=\"shogi-hand\">☗</div></td><td class=\"shogi-comment\"><p>&lt;comment&amp;123&gt;</p></td></tr></tbody></table></div></div>"

}
}