]> git.sesse.net Git - remoteglot-book/blobdiff - www/js/book.js
Highlight the last move.
[remoteglot-book] / www / js / book.js
index bed05c1de71c8011c197842597c81fa73a27f125..78dfed815cf8c3c782266ab5e47cf377383a58a0 100644 (file)
@@ -1,20 +1,59 @@
 (function() {
 
 var board = null;
-var moves = [];
+var history = [];
 var move_override = 0;
 
+var entity_map = {
+       "&": "&",
+       "<": "&lt;",
+       ">": "&gt;",
+       '"': '&quot;',
+       "'": '&#39;',
+};
+
+function escape_html(string) {
+       return String(string).replace(/[&<>"']/g, function (s) {
+               return entity_map[s];
+       });
+}
+
 var get_game = function() {
        var game = new Chess();
        for (var i = 0; i < move_override; ++i) {
-               game.move(moves[i]);
+               game.move(history[i]);
        }
        return game;
 }
 
 var update = function() {
+       var text = "";
+       for (var i = 0; i < history.length; ++i) {
+               if (i % 2 == 0) {
+                       text += (i/2 + 1) + ". ";
+               }
+               if (i + 1 == move_override) {
+                       text += '<strong>' + history[i] + '</strong>';
+               } else {
+                       text += '<a href="javascript:set_move(' + (i + 1) + ')">' + history[i] + '</a>';
+               }
+               text += " ";
+       }
+       $('#gamehistory').html(text);
+
        var game = get_game();
        board.position(game.fen());
+
+       var all_moves = game.history({ verbose: true });
+       if (all_moves.length > 0) {
+               var last_move = all_moves.pop();
+               var highlight_from = last_move.from;
+               var highlight_to = last_move.to;
+               $("#board").find('.square-55d63').removeClass('nonuglyhighlight');
+               $("#board").find('.square-' + highlight_from).addClass('nonuglyhighlight');
+               $("#board").find('.square-' + highlight_to).addClass('nonuglyhighlight');
+       }
+
        fetch_analysis();
 }
 
@@ -34,94 +73,166 @@ var add_td = function(tr, value) {
        $(td).text(value);
 }
 
+var TYPE_MOVE = 0;
+var TYPE_INTEGER = 1;
+var TYPE_FLOAT = 2;
+var TYPE_RATIO = 3;
+
+var headings = [
+       [ "Move", TYPE_MOVE ],
+       [ "Games", TYPE_INTEGER ],
+       [ "%", TYPE_RATIO ],
+       [ "Win%", TYPE_RATIO ],
+       [ "WWin", TYPE_INTEGER ],
+       [ "%WW", TYPE_RATIO ],
+       [ "Bwin", TYPE_INTEGER ],
+       [ "%BW", TYPE_RATIO ],
+       [ "Draw", TYPE_INTEGER ],
+       [ "Draw%", TYPE_RATIO ],
+       [ "AvWElo", TYPE_FLOAT ],
+       [ "AvBElo", TYPE_FLOAT ],
+       [ "EloVar", TYPE_FLOAT ],
+       [ "AWin%", TYPE_RATIO ],
+];
+var sort_by = 1;
+var direction = 1;
+
 var show_lines = function(data, game) {
        var moves = data['moves'];
        $('#numviewers').text(data['opening']);
+
+       if (data['root_game']) {
+               var text = escape_html(data['root_game']['white']);
+               if (data['root_game']['white_elo']) {
+                       text += " (" + escape_html(data['root_game']['white_elo']) + ")";
+               }
+               text += " &ndash; " + escape_html(data['root_game']['black']);
+               if (data['root_game']['black_elo']) {
+                       text += " (" + escape_html(data['root_game']['black_elo']) + ")";
+               }
+               text += " &nbsp;" + escape_html(data['root_game']['result']).replace(/-/, "&ndash;") + "<br />";
+               if (data['root_game']['eco']) {
+                       text += "[" + escape_html(data['root_game']['eco']) + "] ";
+               }
+               text += "(" + data['root_game']['moves'] + ") ";
+               text += escape_html(data['root_game']['event']) + " &nbsp;" + escape_html(data['root_game']['date']);
+               $('#gamesummary').html(text);
+       }
+
        var total_num = 0;
        for (var i = 0; i < moves.length; ++i) {
                var move = moves[i];
-               total_num += parseInt(move['white']);
-               total_num += parseInt(move['draw']);
-               total_num += parseInt(move['black']);
-       }       
+               if (move['move']) {
+                       total_num += parseInt(move['white']);
+                       total_num += parseInt(move['draw']);
+                       total_num += parseInt(move['black']);
+               }
+       }
 
-       var tbl = $("#lines");
-       tbl.empty();
+       var headings_tr = $("#headings");
+       headings_tr.empty();
+       for (var i = 0; i < headings.length; ++i) {
+               var th = document.createElement("th");
+               headings_tr.append(th);
+               $(th).text(headings[i][0]);
+               (function(new_sort_by) {
+                       $(th).click(function() {
+                               if (sort_by == new_sort_by) {
+                                       direction = -direction;
+                               } else {
+                                       sort_by = new_sort_by;
+                                       direction = 1;
+                               }
+                               show_lines(data, game);
+                       });
+               })(i);
+       }
 
+       var lines = [];
        for (var i = 0; i < moves.length; ++i) {
                var move = moves[i];
-               var tr = document.createElement("tr");
+               var line = [];
 
                var white = parseInt(move['white']);
                var draw = parseInt(move['draw']);
                var black = parseInt(move['black']);
 
-               // Move.
-               var move_td = document.createElement("td");
-               tr.appendChild(move_td);
-               $(move_td).addClass("move");
-
-               var move_a = document.createElement("a");
-               move_a.href = "javascript:make_move('" + move['move'] + "')";
-               move_td.appendChild(move_a);
-               $(move_a).text(move['move']);
-
-               // #.
+               line.push(move['move']);  // Move.
                var num = white + draw + black;
-               add_td(tr, num);
-
-               // %.
-               add_td(tr, (100.0 * num / total_num).toFixed(1) + "%");
+               line.push(num);  // N.
+               line.push(num / total_num);  // %.
 
                // Win%.
                var white_win_ratio = (white + 0.5 * draw) / num;
                var win_ratio = (game.turn() == 'w') ? white_win_ratio : 1.0 - white_win_ratio;
-               add_td(tr, ((100.0 * win_ratio).toFixed(1) + "%"));
-
-               // Elo.
-               add_td(tr, move['white_avg_elo'].toFixed(1));
-               add_td(tr, move['black_avg_elo'].toFixed(1));
-
-               // Win% corrected for Elo.
-               var win_elo = -400.0 * Math.log(1.0 / white_win_ratio - 1.0) / Math.LN10;
-               win_elo -= (move['white_avg_elo'] - move['black_avg_elo']);
-               white_win_ratio = 1.0 / (1.0 + Math.pow(10, win_elo / -400.0));
-               win_ratio = (game.turn() == 'w') ? white_win_ratio : 1.0 - white_win_ratio;
-               add_td(tr, ((100.0 * win_ratio).toFixed(1) + "%"));
-
-               if (false) {
-                       // Win bars (W/D/B).
-                       var winbar_td = document.createElement("td");
-                       $(winbar_td).addClass("winbars");
-                       tr.appendChild(winbar_td);
-                       var winbar_table = document.createElement("table");
-                       winbar_td.appendChild(winbar_table);
-                       var winbar_tr = document.createElement("tr");
-                       winbar_table.appendChild(winbar_tr);
-
-                       if (white > 0) {
-                               var white_percent = (100.0 * white / num).toFixed(0) + "%";
-                               var white_td = document.createElement("td");
-                               winbar_tr.appendChild(white_td);
-                               $(white_td).addClass("white");
-                               white_td.style.width = white_percent;
-                               $(white_td).text(white_percent);
-                       }
-                       if (draw > 0) {
-                               var draw_percent = (100.0 * draw / num).toFixed(0) + "%";
-                               var draw_td = document.createElement("td");
-                               winbar_tr.appendChild(draw_td);
-                               $(draw_td).addClass("draw");
-                               draw_td.style.width = draw_percent;
-                               $(draw_td).text(draw_percent);
-                       }
-                       if (black > 0) {
-                               var black_percent = (100.0 * black / num).toFixed(0) + "%";
-                               var black_td = document.createElement("td");
-                               winbar_tr.appendChild(black_td);
-                               $(black_td).addClass("black");
-                               black_td.style.width = black_percent;
-                               $(black_td).text(black_percent);
+               line.push(win_ratio);
+
+               line.push(white);        // WWin.
+               line.push(white / num);  // %WW.
+               line.push(black);        // BWin.
+               line.push(black / num);  // %BW.
+               line.push(draw);         // Draw.
+               line.push(draw / num);   // %Draw.
+
+               if (move['num_elo'] >= 10) {
+                       // Elo.
+                       line.push(move['white_avg_elo']);
+                       line.push(move['black_avg_elo']);
+                       line.push(move['white_avg_elo'] - move['black_avg_elo']);
+
+                       // Win% corrected for Elo.
+                       var win_elo = -400.0 * Math.log(1.0 / white_win_ratio - 1.0) / Math.LN10;
+                       win_elo -= (move['white_avg_elo'] - move['black_avg_elo']);
+                       white_win_ratio = 1.0 / (1.0 + Math.pow(10, win_elo / -400.0));
+                       win_ratio = (game.turn() == 'w') ? white_win_ratio : 1.0 - white_win_ratio;
+                       line.push(win_ratio);
+               } else {
+                       line.push(null);
+                       line.push(null);
+                       line.push(null);
+                       line.push(null);
+               }
+               lines.push(line);
+       }
+
+       lines.sort(function(a, b) { return direction * ( b[sort_by] - a[sort_by]); });
+
+       var tbl = $("#lines");
+       tbl.empty();
+
+       for (var i = 0; i < moves.length; ++i) {
+               var line = lines[i];
+               var tr = document.createElement("tr");
+
+               if (line[0] === undefined) {
+                       $(tr).addClass("totals");
+               }
+
+               for (var j = 0; j < line.length; ++j) {
+                       if (line[j] === null) {
+                               add_td(tr, "");
+                       } else if (headings[j][1] == TYPE_MOVE) {
+                               var td = document.createElement("td");
+                               tr.appendChild(td);
+                               $(td).addClass("move");
+                               if (line[j] !== undefined) {
+                                       if (move_override % 2 == 0) {
+                                               $(td).text(((move_override / 2) + 1) + ". ");
+                                       } else {
+                                               $(td).text(((move_override / 2) + 0.5) + "…");
+                                       }
+                               }
+
+                               var move_a = document.createElement("a");
+                               move_a.href = "javascript:make_move('" + line[j] + "')";
+                               td.appendChild(move_a);
+                               $(move_a).text(line[j]);
+                       } else if (headings[j][1] == TYPE_INTEGER) {
+                               add_td(tr, line[j]);
+                       } else if (headings[j][1] == TYPE_FLOAT) {
+                               add_td(tr, line[j].toFixed(1));
+                       } else {
+                               add_td(tr, (100.0 * line[j]).toFixed(1) + "%");
                        }
                }
 
@@ -130,9 +241,14 @@ var show_lines = function(data, game) {
 }
 
 var make_move = function(move) {
-       moves.length = move_override;
-       moves.push(move);
-       move_override = moves.length;
+       if (move_override < history.length && history[move_override] == move) {
+               // User effectively only moved forward in history.
+               ++move_override;
+       } else {
+               history.length = move_override;
+               history.push(move);
+               move_override = history.length;
+       }
        update();
 }
 window['make_move'] = make_move;
@@ -146,13 +262,19 @@ var prev_move = function() {
 window['prev_move'] = prev_move;
 
 var next_move = function() {
-       if (move_override < moves.length) {
+       if (move_override < history.length) {
                ++move_override;
                update();
        }
 }
 window['next_move'] = next_move;
 
+var set_move = function(n) {
+       move_override = n;
+       update();
+}
+window['set_move'] = set_move;
+
 // almost all of this stuff comes from the chessboard.js example page
 var onDragStart = function(source, piece, position, orientation) {
        var game = get_game();
@@ -175,8 +297,13 @@ var onDrop = function(source, target) {
        // illegal move
        if (move === null) return 'snapback';
 
-       moves = game.history({ verbose: true });
-       move_override = moves.length;
+       var new_history = game.history({ verbose: true });
+       history = [];
+       for (var i = 0; i < new_history.length; ++i) {
+               history.push(new_history[i].san);
+       }
+       move_override = history.length;
+       update();
 };
 
 // update the board position after the piece snap