<commit_before><!doctype html>
<html lang="en">
<head>

<meta charset="utf-8" />
<title>Goban</title>
<meta name="author" content="<NAME>" />

<link rel="stylesheet" type="text/css" href="../style/app.css" />
<link rel="stylesheet" type="text/css" href="../style/goban.css" />
<link rel="stylesheet" type="text/css" href="../style/index.css" />
<script src="../lib/mootools.js"></script>

</head>
<body>

<section id="main">
    <section id="goban" class="goban">
        <div></div>
    </section>

    <section id="bar">
        <section id="edit">
            <ul>
                <li class="selected stone-tool">
                    <a href="#" title="Stone Tool"><img src="../img/edit/stone_1.png" /></a>
                </li>
                <li class="cross-tool">
                    <a href="#" title="Cross Tool"><img src="../img/edit/cross.png" /></a>
                </li>
                <li class="triangle-tool">
                    <a href="#" title="Triangle Tool"><img src="../img/edit/triangle.png" /></a>
                </li>
                <li class="square-tool">
                    <a href="#" title="Square Tool"><img src="../img/edit/square.png" /></a>
                </li>
                <li class="circle-tool">
                    <a href="#" title="Circle Tool"><img src="../img/edit/circle.png" /></a>
                </li>
                <li class="label-tool">
                    <a href="#" title="Label Tool"><img src="../img/edit/label.png" /></a>
                </li>
                <li class="number-tool">
                    <a href="#" title="Number Tool"><img src="../img/edit/number.png" /></a>
                </li>
            </ul>
            <a class="close" href="#" onclick="setEditMode(false)"></a>
        </section>
        <header>
            <span id="player_1"><span class="captures">0</span> <span class="name">Black</span></span>
            <img class="currentplayer" src="../img/ui/blacktoplay.png" />
            <span id="player_-1"><span class="name">White</span> <span class="captures">0</span></span>
            <img id="headermenu" src="../img/ui/menu.png" onclick="openHeaderMenu()" />
        </header>
        <section id="scoring">
            <button onclick="showScore()">Done</button> Please select dead stones.
            <a class="close" href="#" onclick="setScoringMode(false)"></a>
        </section>
    </section>

    <section id="info">
        <form>
            <section>
                <input type="text" name="rank_1" placeholder="Rank" />
                <input type="text" name="name_1" placeholder="Player Black" />
                <img class="currentplayer" src="../img/ui/blacktoplay.png" onload="this.draggable = false" />
                <input type="text" name="name_-1" placeholder="Player White" />
                <input type="text" name="rank_-1" placeholder="Rank" />
            </section>

            <ul>
                <li>
                    <label><span>Result:</span>
                    <input type="text" name="result" placeholder="None" /></label>
                </li>
                <li>
                    <label><span>Komi:</span>
                    <input type="text" name="komi" placeholder="0" /></label>
                </li>
                <li>
                    <label><span>Handicap:</span>
                    <select name="handicap">
                        <option>No stones</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>
                    </select></label>
                </li>
                <li>
                    <label><span>Board size:</span>
                    <input type="number" name="size" placeholder="19" max="25" min="9" /></label>
                </li>
            </ul>

            <p>
                <button type="submit" onclick="updateGameInfo(); closeGameInfo(); return false">OK</button>
                <button type="reset" onclick="closeGameInfo(); return false">Cancel</button>
            </p>
        </form>
    </section>

    <section id="score">
        <h2>Score</h2>

        <ul class="method">
            <li class="area"><a href="#" onclick="setScoringMethod('area')">Area</a></li>
            <li class="current territory"><a href="#" onclick="setScoringMethod('territory')">Territory</a></li>
        </ul>

        <table>
            <thead>
                <tr>
                    <th class="area territory"></th>
                    <th class="area disabled">Area</th>
                    <th class="territory">Territory</th>
                    <th class="territory">Captures</th>
                    <th class="area territory">Komi</th>
                    <th class="area territory">Total</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th class="area territory">
                        <img src="../img/goban/stone_1.png" alt="Black" width="24" height="24" />
                    </th>
                    <td class="area disabled">0</td>
                    <td class="territory">0</td>
                    <td class="territory">0</td>
                    <td class="area territory"></td>
                    <td class="area territory">0</td>
                </tr>
                <tr>
                    <th class="area territory">
                        <img src="../img/goban/stone_-1.png" alt="White" width="24" height="24" />
                    </th>
                    <td class="area disabled">0</td>
                    <td class="territory">0</td>
                    <td class="territory">0</td>
                    <td class="area territory">0</td>
                    <td class="area territory">0</td>
                </tr>
            </tbody>
        </table>

        <form>
            <p>
                <button type="submit" onclick="updateScore(); closeScore(); return false">Update Result</button>
                <button type="submit" onclick="closeScore(); return false">Cancel</button>
            </p>
        </form>
    </section>
</section>

<section id="loading"></section>

<script>
    var remote = require('remote')
    var fs = require('fs')
    var shell = require('shell')
    var sgf = require('../module/sgf.js')
    var process = remote.require('process')
    var app = remote.require('app');
    var dialog = remote.require('dialog')
    var setting = remote.require('./module/setting.js')

    var Menu = remote.require('menu')
    var Tuple = require('../lib/tuple')
    var Board = require('../module/board.js')

    /**
     * Getters and setters
     */

    function setIsLoading(loading) {
        if (loading) document.body.addClass('loading')
        else $('loading').tween('opacity', 0).get('tween').addEvent('complete', function() {
            document.body.removeClass('loading')
            $('loading').setStyle('opacity', null)
        })
    }

    function getShowVariations() {
        return $('goban').hasClass('variations')
    }

    function setShowVariations(show) {
        if (show) $('goban').addClass('variations')
        else $('goban').removeClass('variations')
        setting.set('view.show_variations', show)
    }

    function getFuzzyStonePlacement() {
        return $('goban').hasClass('fuzzy')
    }

    function setFuzzyStonePlacement(fuzzy) {
        if (fuzzy) $('goban').addClass('fuzzy')
        else $('goban').removeClass('fuzzy')
        setting.set('view.fuzzy_stone_placement', fuzzy)
    }

    function getShowCoordinates() {
        return $('goban').hasClass('coordinates')
    }

    function setShowCoordinates(show) {
        if (show) $('goban').addClass('coordinates')
        else $('goban').removeClass('coordinates')
        setting.set('view.show_coordinates', show)
    }

    function getPlayerName(sign) {
        return $$('#player_' + sign + ' .name')[0].get('text')
    }

    function setPlayerName(sign, name) {
        if (name.trim() == '') name = sign > 0 ? 'Black' : 'White'
        $$('#player_' + sign + ' .name')[0].set('text', name)
    }

    function getCaptures() {
        return {
            '-1': $$('#player_-1 .captures')[0].get('text').toInt(),
            '1': $$('#player_1 .captures')[0].get('text').toInt()
        }
    }

    function setCaptures(captures) {
        $$('#player_-1 .captures')[0].set('text', captures['-1'])
            .setStyle('opacity', captures['-1'] == 0 ? 0 : .7)
        $$('#player_1 .captures')[0].set('text', captures['1'])
            .setStyle('opacity', captures['1'] == 0 ? 0 : .7)
    }

    function getCurrentPlayer() {
        return $$('.currentplayer')[0].get('src') == '../img/ui/blacktoplay.png' ? 1 : -1
    }

    function setCurrentPlayer(sign) {
        $$('.currentplayer').set('src', sign > 0 ? '../img/ui/blacktoplay.png' : '../img/ui/whitetoplay.png')
    }

    function getRootTree() {
        return getCurrentTreePosition().unpack(function(tree, index) {
            while (tree.parent != null) tree = tree.parent
            return tree
        })
    }

    function setRootTree(tree) {
        if (tree.nodes.length == 0) return

        tree.parent = null
        setCurrentTreePosition(sgf.addBoards(tree), 0)

        if ('PB' in tree.nodes[0]) setPlayerName(1, tree.nodes[0].PB[0])
        if ('PW' in tree.nodes[0]) setPlayerName(-1, tree.nodes[0].PW[0])
    }

    function setCurrentTreePosition(tree, index) {
        $('goban').store('position', new Tuple(tree, index))

        if (tree.parent != null) tree.parent.current = tree.parent.subtrees.indexOf(tree)

        setBoard(sgf.addBoard(tree, index).nodes[index].board)
        setCurrentPlayer(1)

        if ('B' in tree.nodes[index]) setCurrentPlayer(-1)
        else if ('W' in tree.nodes[index]) setCurrentPlayer(1)
        else if ('PL' in tree.nodes[index])
            setCurrentPlayer(tree.nodes[index].PL[0] == 'W' ? -1 : 1)
        else if ('HA' in tree.nodes[index] && tree.nodes[index].HA[0].toInt() >= 1)
            setCurrentPlayer(-1)
    }

    function getCurrentTreePosition() {
        return $('goban').retrieve('position')
    }

    function getSelectedTool() {
        var li = $$('#edit .selected')[0]
        var tool = li.get('class').replace('selected', '').replace('-tool', '').trim()

        if (tool == 'stone') {
            return li.getElement('img').get('src').contains('_1') ? 'stone_1' : 'stone_-1'
        } else {
            return tool
        }
    }

    function getBoard() {
        return $('goban').retrieve('board')
    }

    function setBoard(board) {
        if (!getBoard() || getBoard().size != board.size) {
            $('goban').store('board', board)
            buildBoard()
        }

        $('goban').store('board', board)
        setCaptures(board.captures)

        for (var x = 0; x < board.size; x++) {
            for (var y = 0; y < board.size; y++) {
                var li = $('goban').getElement('.pos_' + x + '-' + y)
                var sign = board.arrangement[li.retrieve('tuple')]
                var types = ['ghost_1', 'ghost_-1', 'circle', 'triangle',
                    'cross', 'square', 'label', 'point']

                types.each(function(x) {
                    if (li.hasClass(x)) li.removeClass(x)
                })

                if (li.retrieve('tuple') in board.overlays) {
                    board.overlays[li.retrieve('tuple')].unpack(function(type, ghost, label) {
                        if (type != '') li.addClass(type)
                        if (ghost != 0) li.addClass('ghost_' + ghost)
                        if (label != '') li.set('data-label', label)
                    })
                }

                if (li.hasClass('sign_' + sign)) continue

                for (var i = -1; i <= 1; i++) {
                    if (li.hasClass('sign_' + i)) li.removeClass('sign_' + i)
                }

                li.addClass('sign_' + sign)
                    .getElement('img').set('src', '../img/goban/stone_' + sign + '.png')
            }
        }
    }

    function getEditMode() {
        return $('bar').hasClass('edit')
    }

    function setEditMode(editMode) {
        if (editMode) {
            $('bar').addClass('edit')
            closeScore()
            closeGameInfo()
        } else {
            $('bar').removeClass('edit')
        }
    }

    function getScoringMode() {
        return $$('body')[0].hasClass('scoring')
    }

    function setScoringMode(scoringMode) {
        if (scoringMode) {
            $$('body').addClass('scoring')
            setEditMode(false)
            closeGameInfo()

            var deadstones = getBoard().guessDeadStones()
            deadstones.each(function(v) {
                $$('#goban .pos_' + v[0] + '-' + v[1]).addClass('dead')
            })

            updateAreaMap()
        } else {
            $$('body').removeClass('scoring')
            $$('.dead').removeClass('dead')
        }
    }

    function getScoringMethod() {
        return $$('#score .method .territory')[0].hasClass('current') ? 'territory' : 'area'
    }

    function setScoringMethod(method) {
        $$('#score .method li').removeClass('current')
        $$('#score .method .' + method).addClass('current')
        $$('#score tr > *').addClass('disabled')
        $$('#score table .' + method).removeClass('disabled')

        setting.set('scoring.method', method)

        // Update UI
        for (var sign = -1; sign <= 1; sign += 2) {
            var tr = $$('#score tbody tr' + (sign < 0 ? ':last-child' : ''))[0]
            var tds = tr.getElements('td')

            tds[4].set('text', 0)

            for (var i = 0; i <= 3; i++) {
                if (tds[i].hasClass('disabled') || isNaN(tds[i].get('text').toFloat())) continue
                tds[4].set('text', tds[4].get('text').toFloat() + tds[i].get('text').toFloat())
            }
        }
    }

    /**
     * Methods
     */

    function loadSettings() {
        if (setting.get('view.fuzzy_stone_placement'))
            $('goban').addClass('fuzzy')
        if (setting.get('view.show_coordinates'))
            $('goban').addClass('coordinates')
        if (setting.get('view.show_variations'))
            $('goban').addClass('variations')
    }

    function selectTool(tool) {
        $$('#edit .' + tool + '-tool a').fireEvent('click')
    }

    function makeMove(vertex) {
        if (getBoard().hasVertex(vertex) && getBoard().arrangement[vertex] != 0)
            return

        var position = getCurrentTreePosition()
        var tree = position[0], index = position[1]
        var color = getCurrentPlayer() > 0 ? 'B' : 'W'
        var sign = color == 'B' ? 1 : -1

        // Play sounds
        if (getBoard().hasVertex(vertex)) {
            // Detect captured stones
            if (getBoard().getNeighborhood(vertex).some(function(v) {
                return getBoard().arrangement[v] == -sign && getBoard().getLiberties(v).length == 1
            })) setTimeout(function() {
                new Audio('../sound/capture' + Math.floor(Math.random() * 5) + '.wav').play()
            }, 400 + Math.floor(Math.random() * 200))

            new Audio('../sound/' + Math.floor(Math.random() * 5) + '.wav').play()
        } else new Audio('../sound/pass.wav').play()

        // Randomize shift and readjust
        var li = $$('#goban .pos_' + vertex[0] + '-' + vertex[1])
        var direction = Math.floor(Math.random() * 9)

        for (var i = 0; i < 9; i++) li.removeClass('shift_' + i)
        li.addClass('shift_' + direction)

        if (direction == 1 || direction == 5 || direction == 8) {
            // Left
            $$('#goban .pos_' + (vertex[0] - 1) + '-' + vertex[1])
                .removeClass('shift_3').removeClass('shift_7').removeClass('shift_6')
        } else if (direction == 2 || direction == 5 || direction == 6) {
            // Top
            $$('#goban .pos_' + vertex[0] + '-' + (vertex[1] - 1))
                .removeClass('shift_4').removeClass('shift_7').removeClass('shift_8')
        } else if (direction == 3 || direction == 7 || direction == 6) {
            // Right
            $$('#goban .pos_' + (vertex[0] + 1) + '-' + vertex[1])
                .removeClass('shift_1').removeClass('shift_5').removeClass('shift_8')
        } else if (direction == 4 || direction == 7 || direction == 8) {
            // Bottom
            $$('#goban .pos_' + vertex[0] + '-' + (vertex[1] + 1))
                .removeClass('shift_2').removeClass('shift_5').removeClass('shift_6')
        }

        if (tree.current == null && tree.nodes.length - 1 == index) {
            // Append move

            var node = {}
            node[color] = [sgf.vertex2point(vertex)]
            tree.nodes.push(node)

            setCurrentTreePosition(tree, tree.nodes.length - 1)
        } else {
            if (index != tree.nodes.length - 1) {
                // Search for next move

                var nextNode = tree.nodes[index + 1]
                var moveExists = color in nextNode
                    && sgf.point2vertex(nextNode[color][0]).equals(vertex)

                if (moveExists) {
                    setCurrentTreePosition(tree, index + 1)
                    return
                }
            } else {
                // Search for variation

                var variations = tree.subtrees.filter(function(subtree) {
                    return subtree.nodes.length > 0
                        && color in subtree.nodes[0]
                        && sgf.point2vertex(subtree.nodes[0][color][0]).equals(vertex)
                })

                if (variations.length > 0) {
                    setCurrentTreePosition(sgf.addBoards(variations[0]), 0)
                    return
                }
            }

            // Create variation

            var splitted = sgf.splitTree(tree, index)
            var node = {}; node[color] = [sgf.vertex2point(vertex)]
            var newtree = { nodes: [node], subtrees: [], parent: splitted, current: null }

            splitted.subtrees.push(newtree)
            splitted.current = splitted.subtrees.length - 1

            sgf.addBoard(newtree, newtree.nodes.length - 1)
            setCurrentTreePosition(newtree, 0)
        }
    }

    function vertexClicked() {
        closeGameInfo()

        if (!getEditMode() && !getScoringMode()) {
            if (event.button != 0) return
            makeMove(this)

            return
        }

        // Scoring mode activated
        if (getScoringMode()) {
            if (getBoard().arrangement[this] == 0) return

            getBoard().getRelatedChains(this).each(function(vertex) {
                $$('#goban .pos_' + vertex[0] + '-' + vertex[1]).toggleClass('dead')
            })

            updateAreaMap()
            return
        }

        // Edit mode activated
        getCurrentTreePosition().unpack(function(tree, index) {
            var node = tree.nodes[index]
            var tool = getSelectedTool()
            var board = getBoard()
            var dictionary = {
                'cross': 'MA',
                'triangle': 'TR',
                'circle': 'CR',
                'square': 'SQ',
                'number': 'LB',
                'label': 'LB'
            }

            if (tool.contains('stone')) {
                if ('B' in node || 'W' in node) {
                    // New variation needed
                    var splitted = sgf.splitTree(tree, index)

                    if (splitted != tree || splitted.subtrees.length != 0) {
                        tree = { nodes: [], subtrees: [], current: null, parent: splitted }
                        splitted.subtrees.push(tree)
                    }

                    node = { PL: getCurrentPlayer() > 0 ? ['B'] : ['W'] }
                    index = tree.nodes.length
                    tree.nodes.push(node)
                }

                var sign = tool.contains('_1') ? 1 : -1
                if (event.button == 2) sign = -sign

                var oldSign = board.arrangement[this]
                var ids = ['AW', 'AE', 'AB']
                var id = ids[sign + 1]
                var point = sgf.vertex2point(this)

                for (var i = -1; i <= 1; i++) {
                    if (!(ids[i + 1] in node)) continue

                    k = node[ids[i + 1]].indexOf(point)
                    if (k >= 0) {
                        node[ids[i + 1]].splice(k, 1)

                        if (node[ids[i + 1]].length == 0) {
                            delete node[ids[i + 1]]
                        }
                    }
                }

                if (oldSign != sign) {
                    if (id in node) node[id].push(point)
                    else node[id] = [point]
                } else if (oldSign == sign) {
                    if ('AE' in node) node.AE.push(point)
                    else node.AE = [point]
                }
            } else {
                if (event.button != 0) return

                if (tool != 'label' && tool != 'number') {
                    if (this in board.overlays && board.overlays[this][0] == tool) {
                        delete board.overlays[this]
                    } else {
                        board.overlays[this] = new Tuple(tool, 0, '')
                    }
                } else if (tool == 'number') {
                    if (this in board.overlays && board.overlays[this][0] == 'label') {
                        delete board.overlays[this]
                    } else {
                        var number = 1

                        if ('LB' in node) {
                            node.LB.each(function(value) {
                                var label = value.substr(3).toInt()
                                if (!isNaN(label)) number = Math.max(number, label + 1)
                            })
                        }

                        board.overlays[this] = new Tuple(tool, 0, number.toString())
                    }
                } else if (tool == 'label') {
                    if (this in board.overlays && board.overlays[this][0] == 'label') {
                        delete board.overlays[this]
                    } else {
                        var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                        var k = 0

                        if ('LB' in node) {
                            node.LB.each(function(value) {
                                if (value.length != 4 || !alpha.contains(value[3])) return

                                var label = value[3]
                                k = Math.max(k, (alpha.indexOf(label) + 1) % alpha.length)
                            })
                        }

                        board.overlays[this] = new Tuple(tool, 0, alpha[k])
                    }
                }

                Object.each(dictionary, function(id) { delete node[id] })

                $$('#goban .row li').each(function(li) {
                    var vertex = li.retrieve('tuple')
                    if (!(vertex in board.overlays)) return

                    var id = dictionary[board.overlays[vertex][0]]
                    var pt = sgf.vertex2point(vertex)
                    if (id == 'LB') pt += ':' + board.overlays[vertex][2]

                    if (id in node) node[id].push(pt)
                    else node[id] = [pt]
                })
            }

            setCurrentTreePosition(tree, index)
        }.bind(this))
    }

    function buildBoard() {
        var board = getBoard()
        var rows = []
        var hoshi = board.getHandicapPlacement(9)

        for (var y = 0; y < board.size; y++) {
            var ol = new Element('ol.row')

            for (var x = 0; x < board.size; x++) {
                var vertex = new Tuple(x, y)
                var li = new Element('li.pos_' + x + '-' + y)
                    .store('tuple', vertex)
                    .addClass('shift_' + Math.floor(Math.random() * 9))
                var img = new Element('img', { src: '../img/goban/stone_0.png' })

                if (hoshi.some(function(v) { return v.equals(vertex) }))
                    li.addClass('hoshi')

                ol.adopt(li.adopt(img).addEvent('mouseup', vertexClicked.bind(vertex)))
            }

            rows.push(ol)
        }

        var alpha = 'ABCDEFGHJKLMNOPQRSTUVWXYZ'
        var coordx = new Element('ol.coordx')
        var coordy = new Element('ol.coordy')

        for (var i = 0; i < board.size; i++) {
            coordx.adopt(new Element('li', { text: alpha[i] }))
            coordy.adopt(new Element('li', { text: board.size - i }))
        }

        var goban = $$('#goban div')[0]
        goban.empty().adopt(rows, coordx, coordy)
        goban.grab(coordx.clone(), 'top').grab(coordy.clone(), 'top')

        resizeBoard()
    }

    function resizeBoard() {
        var board = getBoard()
        if (!board) return

        var width = $('goban').getStyle('width').toInt()
        var height = $('goban').getStyle('height').toInt()
        var min = Math.min(width, height)
        var hasCoordinates = getShowCoordinates()

        var fieldsize = Math.floor(min / board.size)
        min = fieldsize * board.size

        if (hasCoordinates) {
            fieldsize = Math.floor(min / (board.size + 2))
            min = fieldsize * (board.size + 2)
        }

        $$('#goban > div').setStyle('width', min).setStyle('height', min)

        $$('#goban .row, #goban .coordx').setStyle('height', fieldsize).setStyle('line-height', fieldsize)
        $$('#goban .row, #goban .coordx').setStyle('margin-left', hasCoordinates ? fieldsize : 0)

        $$('#goban .coordy').setStyle('width', fieldsize).setStyle('top', fieldsize).setStyle('line-height', fieldsize)
        $$('#goban .coordy:last-child').setStyle('left', fieldsize * (board.size + 1))

        $$('#goban li').setStyle('width', fieldsize).setStyle('height', fieldsize)
    }

    function showGameInfo() {
        closeScore()

        var tree = getRootTree()
        var rootNode = tree.nodes[0]
        var info = $('info')

        info.addClass('show').getElement('input[name="name_1"]').focus()

        info.getElement('input[name="name_1"]').set('value', getPlayerName(1))
        info.getElement('input[name="name_-1"]').set('value', getPlayerName(-1))
        info.getElement('input[name="rank_1"]').set('value', 'BR' in rootNode ? rootNode.BR[0] : '')
        info.getElement('input[name="rank_-1"]').set('value', 'WR' in rootNode ? rootNode.WR[0] : '')
        info.getElement('input[name="result"]').set('value', 'RE' in rootNode ? rootNode.RE[0] : '')
        info.getElement('input[name="komi"]').set('value', 'KM' in rootNode ? rootNode.KM[0] : '')

        var size = info.getElement('input[name="size"]')
        size.set('value', 'SZ' in rootNode ? rootNode.SZ[0] : '')

        var handicap = info.getElement('select[name="handicap"]')
        if ('HA' in rootNode) handicap.selectedIndex = rootNode.HA[0].toInt() - 1
        else handicap.selectedIndex = 0

        var disabled = tree.nodes.length > 1 || tree.subtrees.length > 0
        handicap.disabled = disabled
        size.disabled = disabled
    }

    function closeGameInfo() {
        $('info').removeClass('show')
    }

    function updateGameInfo() {
        var rootNode = getRootTree().nodes[0]
        var info = $('info')
<commit_msg>Don't show komi past first decimal place<commit_after><!doctype html>
<html lang="en">
<head>

<meta charset="utf-8" />
<title>Goban</title>
<meta name="author" content="<NAME>" />

<link rel="stylesheet" type="text/css" href="../style/app.css" />
<link rel="stylesheet" type="text/css" href="../style/goban.css" />
<link rel="stylesheet" type="text/css" href="../style/index.css" />
<script src="../lib/mootools.js"></script>

</head>
<body>

<section id="main">
    <section id="goban" class="goban">
        <div></div>
    </section>

    <section id="bar">
        <section id="edit">
            <ul>
                <li class="selected stone-tool">
                    <a href="#" title="Stone Tool"><img src="../img/edit/stone_1.png" /></a>
                </li>
                <li class="cross-tool">
                    <a href="#" title="Cross Tool"><img src="../img/edit/cross.png" /></a>
                </li>
                <li class="triangle-tool">
                    <a href="#" title="Triangle Tool"><img src="../img/edit/triangle.png" /></a>
                </li>
                <li class="square-tool">
                    <a href="#" title="Square Tool"><img src="../img/edit/square.png" /></a>
                </li>
                <li class="circle-tool">
                    <a href="#" title="Circle Tool"><img src="../img/edit/circle.png" /></a>
                </li>
                <li class="label-tool">
                    <a href="#" title="Label Tool"><img src="../img/edit/label.png" /></a>
                </li>
                <li class="number-tool">
                    <a href="#" title="Number Tool"><img src="../img/edit/number.png" /></a>
                </li>
            </ul>
            <a class="close" href="#" onclick="setEditMode(false)"></a>
        </section>
        <header>
            <span id="player_1"><span class="captures">0</span> <span class="name">Black</span></span>
            <img class="currentplayer" src="../img/ui/blacktoplay.png" />
            <span id="player_-1"><span class="name">White</span> <span class="captures">0</span></span>
            <img id="headermenu" src="../img/ui/menu.png" onclick="openHeaderMenu()" />
        </header>
        <section id="scoring">
            <button onclick="showScore()">Done</button> Please select dead stones.
            <a class="close" href="#" onclick="setScoringMode(false)"></a>
        </section>
    </section>

    <section id="info">
        <form>
            <section>
                <input type="text" name="rank_1" placeholder="Rank" />
                <input type="text" name="name_1" placeholder="Player Black" />
                <img class="currentplayer" src="../img/ui/blacktoplay.png" onload="this.draggable = false" />
                <input type="text" name="name_-1" placeholder="Player White" />
                <input type="text" name="rank_-1" placeholder="Rank" />
            </section>

            <ul>
                <li>
                    <label><span>Result:</span>
                    <input type="text" name="result" placeholder="None" /></label>
                </li>
                <li>
                    <label><span>Komi:</span>
                    <input type="text" name="komi" placeholder="0" /></label>
                </li>
                <li>
                    <label><span>Handicap:</span>
                    <select name="handicap">
                        <option>No stones</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>
                    </select></label>
                </li>
                <li>
                    <label><span>Board size:</span>
                    <input type="number" name="size" placeholder="19" max="25" min="9" /></label>
                </li>
            </ul>

            <p>
                <button type="submit" onclick="updateGameInfo(); closeGameInfo(); return false">OK</button>
                <button type="reset" onclick="closeGameInfo(); return false">Cancel</button>
            </p>
        </form>
    </section>

    <section id="score">
        <h2>Score</h2>

        <ul class="method">
            <li class="area"><a href="#" onclick="setScoringMethod('area')">Area</a></li>
            <li class="current territory"><a href="#" onclick="setScoringMethod('territory')">Territory</a></li>
        </ul>

        <table>
            <thead>
                <tr>
                    <th class="area territory"></th>
                    <th class="area disabled">Area</th>
                    <th class="territory">Territory</th>
                    <th class="territory">Captures</th>
                    <th class="area territory">Komi</th>
                    <th class="area territory">Total</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th class="area territory">
                        <img src="../img/goban/stone_1.png" alt="Black" width="24" height="24" />
                    </th>
                    <td class="area disabled">0</td>
                    <td class="territory">0</td>
                    <td class="territory">0</td>
                    <td class="area territory"></td>
                    <td class="area territory">0</td>
                </tr>
                <tr>
                    <th class="area territory">
                        <img src="../img/goban/stone_-1.png" alt="White" width="24" height="24" />
                    </th>
                    <td class="area disabled">0</td>
                    <td class="territory">0</td>
                    <td class="territory">0</td>
                    <td class="area territory">0</td>
                    <td class="area territory">0</td>
                </tr>
            </tbody>
        </table>

        <form>
            <p>
                <button type="submit" onclick="updateScore(); closeScore(); return false">Update Result</button>
                <button type="submit" onclick="closeScore(); return false">Cancel</button>
            </p>
        </form>
    </section>
</section>

<section id="loading"></section>

<script>
    var remote = require('remote')
    var fs = require('fs')
    var shell = require('shell')
    var sgf = require('../module/sgf.js')
    var process = remote.require('process')
    var app = remote.require('app');
    var dialog = remote.require('dialog')
    var setting = remote.require('./module/setting.js')

    var Menu = remote.require('menu')
    var Tuple = require('../lib/tuple')
    var Board = require('../module/board.js')

    /**
     * Getters and setters
     */

    function setIsLoading(loading) {
        if (loading) document.body.addClass('loading')
        else $('loading').tween('opacity', 0).get('tween').addEvent('complete', function() {
            document.body.removeClass('loading')
            $('loading').setStyle('opacity', null)
        })
    }

    function getShowVariations() {
        return $('goban').hasClass('variations')
    }

    function setShowVariations(show) {
        if (show) $('goban').addClass('variations')
        else $('goban').removeClass('variations')
        setting.set('view.show_variations', show)
    }

    function getFuzzyStonePlacement() {
        return $('goban').hasClass('fuzzy')
    }

    function setFuzzyStonePlacement(fuzzy) {
        if (fuzzy) $('goban').addClass('fuzzy')
        else $('goban').removeClass('fuzzy')
        setting.set('view.fuzzy_stone_placement', fuzzy)
    }

    function getShowCoordinates() {
        return $('goban').hasClass('coordinates')
    }

    function setShowCoordinates(show) {
        if (show) $('goban').addClass('coordinates')
        else $('goban').removeClass('coordinates')
        setting.set('view.show_coordinates', show)
    }

    function getPlayerName(sign) {
        return $$('#player_' + sign + ' .name')[0].get('text')
    }

    function setPlayerName(sign, name) {
        if (name.trim() == '') name = sign > 0 ? 'Black' : 'White'
        $$('#player_' + sign + ' .name')[0].set('text', name)
    }

    function getCaptures() {
        return {
            '-1': $$('#player_-1 .captures')[0].get('text').toInt(),
            '1': $$('#player_1 .captures')[0].get('text').toInt()
        }
    }

    function setCaptures(captures) {
        $$('#player_-1 .captures')[0].set('text', captures['-1'])
            .setStyle('opacity', captures['-1'] == 0 ? 0 : .7)
        $$('#player_1 .captures')[0].set('text', captures['1'])
            .setStyle('opacity', captures['1'] == 0 ? 0 : .7)
    }

    function getCurrentPlayer() {
        return $$('.currentplayer')[0].get('src') == '../img/ui/blacktoplay.png' ? 1 : -1
    }

    function setCurrentPlayer(sign) {
        $$('.currentplayer').set('src', sign > 0 ? '../img/ui/blacktoplay.png' : '../img/ui/whitetoplay.png')
    }

    function getRootTree() {
        return getCurrentTreePosition().unpack(function(tree, index) {
            while (tree.parent != null) tree = tree.parent
            return tree
        })
    }

    function setRootTree(tree) {
        if (tree.nodes.length == 0) return

        tree.parent = null
        setCurrentTreePosition(sgf.addBoards(tree), 0)

        if ('PB' in tree.nodes[0]) setPlayerName(1, tree.nodes[0].PB[0])
        if ('PW' in tree.nodes[0]) setPlayerName(-1, tree.nodes[0].PW[0])
    }

    function setCurrentTreePosition(tree, index) {
        $('goban').store('position', new Tuple(tree, index))

        if (tree.parent != null) tree.parent.current = tree.parent.subtrees.indexOf(tree)

        setBoard(sgf.addBoard(tree, index).nodes[index].board)
        setCurrentPlayer(1)

        if ('B' in tree.nodes[index]) setCurrentPlayer(-1)
        else if ('W' in tree.nodes[index]) setCurrentPlayer(1)
        else if ('PL' in tree.nodes[index])
            setCurrentPlayer(tree.nodes[index].PL[0] == 'W' ? -1 : 1)
        else if ('HA' in tree.nodes[index] && tree.nodes[index].HA[0].toInt() >= 1)
            setCurrentPlayer(-1)
    }

    function getCurrentTreePosition() {
        return $('goban').retrieve('position')
    }

    function getSelectedTool() {
        var li = $$('#edit .selected')[0]
        var tool = li.get('class').replace('selected', '').replace('-tool', '').trim()

        if (tool == 'stone') {
            return li.getElement('img').get('src').contains('_1') ? 'stone_1' : 'stone_-1'
        } else {
            return tool
        }
    }

    function getBoard() {
        return $('goban').retrieve('board')
    }

    function setBoard(board) {
        if (!getBoard() || getBoard().size != board.size) {
            $('goban').store('board', board)
            buildBoard()
        }

        $('goban').store('board', board)
        setCaptures(board.captures)

        for (var x = 0; x < board.size; x++) {
            for (var y = 0; y < board.size; y++) {
                var li = $('goban').getElement('.pos_' + x + '-' + y)
                var sign = board.arrangement[li.retrieve('tuple')]
                var types = ['ghost_1', 'ghost_-1', 'circle', 'triangle',
                    'cross', 'square', 'label', 'point']

                types.each(function(x) {
                    if (li.hasClass(x)) li.removeClass(x)
                })

                if (li.retrieve('tuple') in board.overlays) {
                    board.overlays[li.retrieve('tuple')].unpack(function(type, ghost, label) {
                        if (type != '') li.addClass(type)
                        if (ghost != 0) li.addClass('ghost_' + ghost)
                        if (label != '') li.set('data-label', label)
                    })
                }

                if (li.hasClass('sign_' + sign)) continue

                for (var i = -1; i <= 1; i++) {
                    if (li.hasClass('sign_' + i)) li.removeClass('sign_' + i)
                }

                li.addClass('sign_' + sign)
                    .getElement('img').set('src', '../img/goban/stone_' + sign + '.png')
            }
        }
    }

    function getEditMode() {
        return $('bar').hasClass('edit')
    }

    function setEditMode(editMode) {
        if (editMode) {
            $('bar').addClass('edit')
            closeScore()
            closeGameInfo()
        } else {
            $('bar').removeClass('edit')
        }
    }

    function getScoringMode() {
        return $$('body')[0].hasClass('scoring')
    }

    function setScoringMode(scoringMode) {
        if (scoringMode) {
            $$('body').addClass('scoring')
            setEditMode(false)
            closeGameInfo()

            var deadstones = getBoard().guessDeadStones()
            deadstones.each(function(v) {
                $$('#goban .pos_' + v[0] + '-' + v[1]).addClass('dead')
            })

            updateAreaMap()
        } else {
            $$('body').removeClass('scoring')
            $$('.dead').removeClass('dead')
        }
    }

    function getScoringMethod() {
        return $$('#score .method .territory')[0].hasClass('current') ? 'territory' : 'area'
    }

    function setScoringMethod(method) {
        $$('#score .method li').removeClass('current')
        $$('#score .method .' + method).addClass('current')
        $$('#score tr > *').addClass('disabled')
        $$('#score table .' + method).removeClass('disabled')

        setting.set('scoring.method', method)

        // Update UI
        for (var sign = -1; sign <= 1; sign += 2) {
            var tr = $$('#score tbody tr' + (sign < 0 ? ':last-child' : ''))[0]
            var tds = tr.getElements('td')

            tds[4].set('text', 0)

            for (var i = 0; i <= 3; i++) {
                if (tds[i].hasClass('disabled') || isNaN(tds[i].get('text').toFloat())) continue
                tds[4].set('text', tds[4].get('text').toFloat() + tds[i].get('text').toFloat())
            }
        }
    }

    /**
     * Methods
     */

    function loadSettings() {
        if (setting.get('view.fuzzy_stone_placement'))
            $('goban').addClass('fuzzy')
        if (setting.get('view.show_coordinates'))
            $('goban').addClass('coordinates')
        if (setting.get('view.show_variations'))
            $('goban').addClass('variations')
    }

    function selectTool(tool) {
        $$('#edit .' + tool + '-tool a').fireEvent('click')
    }

    function makeMove(vertex) {
        if (getBoard().hasVertex(vertex) && getBoard().arrangement[vertex] != 0)
            return

        var position = getCurrentTreePosition()
        var tree = position[0], index = position[1]
        var color = getCurrentPlayer() > 0 ? 'B' : 'W'
        var sign = color == 'B' ? 1 : -1

        // Play sounds
        if (getBoard().hasVertex(vertex)) {
            // Detect captured stones
            if (getBoard().getNeighborhood(vertex).some(function(v) {
                return getBoard().arrangement[v] == -sign && getBoard().getLiberties(v).length == 1
            })) setTimeout(function() {
                new Audio('../sound/capture' + Math.floor(Math.random() * 5) + '.wav').play()
            }, 400 + Math.floor(Math.random() * 200))

            new Audio('../sound/' + Math.floor(Math.random() * 5) + '.wav').play()
        } else new Audio('../sound/pass.wav').play()

        // Randomize shift and readjust
        var li = $$('#goban .pos_' + vertex[0] + '-' + vertex[1])
        var direction = Math.floor(Math.random() * 9)

        for (var i = 0; i < 9; i++) li.removeClass('shift_' + i)
        li.addClass('shift_' + direction)

        if (direction == 1 || direction == 5 || direction == 8) {
            // Left
            $$('#goban .pos_' + (vertex[0] - 1) + '-' + vertex[1])
                .removeClass('shift_3').removeClass('shift_7').removeClass('shift_6')
        } else if (direction == 2 || direction == 5 || direction == 6) {
            // Top
            $$('#goban .pos_' + vertex[0] + '-' + (vertex[1] - 1))
                .removeClass('shift_4').removeClass('shift_7').removeClass('shift_8')
        } else if (direction == 3 || direction == 7 || direction == 6) {
            // Right
            $$('#goban .pos_' + (vertex[0] + 1) + '-' + vertex[1])
                .removeClass('shift_1').removeClass('shift_5').removeClass('shift_8')
        } else if (direction == 4 || direction == 7 || direction == 8) {
            // Bottom
            $$('#goban .pos_' + vertex[0] + '-' + (vertex[1] + 1))
                .removeClass('shift_2').removeClass('shift_5').removeClass('shift_6')
        }

        if (tree.current == null && tree.nodes.length - 1 == index) {
            // Append move

            var node = {}
            node[color] = [sgf.vertex2point(vertex)]
            tree.nodes.push(node)

            setCurrentTreePosition(tree, tree.nodes.length - 1)
        } else {
            if (index != tree.nodes.length - 1) {
                // Search for next move

                var nextNode = tree.nodes[index + 1]
                var moveExists = color in nextNode
                    && sgf.point2vertex(nextNode[color][0]).equals(vertex)

                if (moveExists) {
                    setCurrentTreePosition(tree, index + 1)
                    return
                }
            } else {
                // Search for variation

                var variations = tree.subtrees.filter(function(subtree) {
                    return subtree.nodes.length > 0
                        && color in subtree.nodes[0]
                        && sgf.point2vertex(subtree.nodes[0][color][0]).equals(vertex)
                })

                if (variations.length > 0) {
                    setCurrentTreePosition(sgf.addBoards(variations[0]), 0)
                    return
                }
            }

            // Create variation

            var splitted = sgf.splitTree(tree, index)
            var node = {}; node[color] = [sgf.vertex2point(vertex)]
            var newtree = { nodes: [node], subtrees: [], parent: splitted, current: null }

            splitted.subtrees.push(newtree)
            splitted.current = splitted.subtrees.length - 1

            sgf.addBoard(newtree, newtree.nodes.length - 1)
            setCurrentTreePosition(newtree, 0)
        }
    }

    function vertexClicked() {
        closeGameInfo()

        if (!getEditMode() && !getScoringMode()) {
            if (event.button != 0) return
            makeMove(this)

            return
        }

        // Scoring mode activated
        if (getScoringMode()) {
            if (getBoard().arrangement[this] == 0) return

            getBoard().getRelatedChains(this).each(function(vertex) {
                $$('#goban .pos_' + vertex[0] + '-' + vertex[1]).toggleClass('dead')
            })

            updateAreaMap()
            return
        }

        // Edit mode activated
        getCurrentTreePosition().unpack(function(tree, index) {
            var node = tree.nodes[index]
            var tool = getSelectedTool()
            var board = getBoard()
            var dictionary = {
                'cross': 'MA',
                'triangle': 'TR',
                'circle': 'CR',
                'square': 'SQ',
                'number': 'LB',
                'label': 'LB'
            }

            if (tool.contains('stone')) {
                if ('B' in node || 'W' in node) {
                    // New variation needed
                    var splitted = sgf.splitTree(tree, index)

                    if (splitted != tree || splitted.subtrees.length != 0) {
                        tree = { nodes: [], subtrees: [], current: null, parent: splitted }
                        splitted.subtrees.push(tree)
                    }

                    node = { PL: getCurrentPlayer() > 0 ? ['B'] : ['W'] }
                    index = tree.nodes.length
                    tree.nodes.push(node)
                }

                var sign = tool.contains('_1') ? 1 : -1
                if (event.button == 2) sign = -sign

                var oldSign = board.arrangement[this]
                var ids = ['AW', 'AE', 'AB']
                var id = ids[sign + 1]
                var point = sgf.vertex2point(this)

                for (var i = -1; i <= 1; i++) {
                    if (!(ids[i + 1] in node)) continue

                    k = node[ids[i + 1]].indexOf(point)
                    if (k >= 0) {
                        node[ids[i + 1]].splice(k, 1)

                        if (node[ids[i + 1]].length == 0) {
                            delete node[ids[i + 1]]
                        }
                    }
                }

                if (oldSign != sign) {
                    if (id in node) node[id].push(point)
                    else node[id] = [point]
                } else if (oldSign == sign) {
                    if ('AE' in node) node.AE.push(point)
                    else node.AE = [point]
                }
            } else {
                if (event.button != 0) return

                if (tool != 'label' && tool != 'number') {
                    if (this in board.overlays && board.overlays[this][0] == tool) {
                        delete board.overlays[this]
                    } else {
                        board.overlays[this] = new Tuple(tool, 0, '')
                    }
                } else if (tool == 'number') {
                    if (this in board.overlays && board.overlays[this][0] == 'label') {
                        delete board.overlays[this]
                    } else {
                        var number = 1

                        if ('LB' in node) {
                            node.LB.each(function(value) {
                                var label = value.substr(3).toInt()
                                if (!isNaN(label)) number = Math.max(number, label + 1)
                            })
                        }

                        board.overlays[this] = new Tuple(tool, 0, number.toString())
                    }
                } else if (tool == 'label') {
                    if (this in board.overlays && board.overlays[this][0] == 'label') {
                        delete board.overlays[this]
                    } else {
                        var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                        var k = 0

                        if ('LB' in node) {
                            node.LB.each(function(value) {
                                if (value.length != 4 || !alpha.contains(value[3])) return

                                var label = value[3]
                                k = Math.max(k, (alpha.indexOf(label) + 1) % alpha.length)
                            })
                        }

                        board.overlays[this] = new Tuple(tool, 0, alpha[k])
                    }
                }

                Object.each(dictionary, function(id) { delete node[id] })

                $$('#goban .row li').each(function(li) {
                    var vertex = li.retrieve('tuple')
                    if (!(vertex in board.overlays)) return

                    var id = dictionary[board.overlays[vertex][0]]
                    var pt = sgf.vertex2point(vertex)
                    if (id == 'LB') pt += ':' + board.overlays[vertex][2]

                    if (id in node) node[id].push(pt)
                    else node[id] = [pt]
                })
            }

            setCurrentTreePosition(tree, index)
        }.bind(this))
    }

    function buildBoard() {
        var board = getBoard()
        var rows = []
        var hoshi = board.getHandicapPlacement(9)

        for (var y = 0; y < board.size; y++) {
            var ol = new Element('ol.row')

            for (var x = 0; x < board.size; x++) {
                var vertex = new Tuple(x, y)
                var li = new Element('li.pos_' + x + '-' + y)
                    .store('tuple', vertex)
                    .addClass('shift_' + Math.floor(Math.random() * 9))
                var img = new Element('img', { src: '../img/goban/stone_0.png' })

                if (hoshi.some(function(v) { return v.equals(vertex) }))
                    li.addClass('hoshi')

                ol.adopt(li.adopt(img).addEvent('mouseup', vertexClicked.bind(vertex)))
            }

            rows.push(ol)
        }

        var alpha = 'ABCDEFGHJKLMNOPQRSTUVWXYZ'
        var coordx = new Element('ol.coordx')
        var coordy = new Element('ol.coordy')

        for (var i = 0; i < board.size; i++) {
            coordx.adopt(new Element('li', { text: alpha[i] }))
            coordy.adopt(new Element('li', { text: board.size - i }))
        }

        var goban = $$('#goban div')[0]
        goban.empty().adopt(rows, coordx, coordy)
        goban.grab(coordx.clone(), 'top').grab(coordy.clone(), 'top')

        resizeBoard()
    }

    function resizeBoard() {
        var board = getBoard()
        if (!board) return

        var width = $('goban').getStyle('width').toInt()
        var height = $('goban').getStyle('height').toInt()
        var min = Math.min(width, height)
        var hasCoordinates = getShowCoordinates()

        var fieldsize = Math.floor(min / board.size)
        min = fieldsize * board.size

        if (hasCoordinates) {
            fieldsize = Math.floor(min / (board.size + 2))
            min = fieldsize * (board.size + 2)
        }

        $$('#goban > div').setStyle('width', min).setStyle('height', min)

        $$('#goban .row, #goban .coordx').setStyle('height', fieldsize).setStyle('line-height', fieldsize)
        $$('#goban .row, #goban .coordx').setStyle('margin-left', hasCoordinates ? fieldsize : 0)

        $$('#goban .coordy').setStyle('width', fieldsize).setStyle('top', fieldsize).setStyle('line-height', fieldsize)
        $$('#goban .coordy:last-child').setStyle('left', fieldsize * (board.size + 1))

        $$('#goban li').setStyle('width', fieldsize).setStyle('height', fieldsize)
    }

    function showGameInfo() {
        closeScore()

        var tree = getRootTree()
        var rootNode = tree.nodes[0]
        var info = $('info')

        info.addClass('show').getElement('input[name="name_1"]').focus()

        info.getElement('input[name="name_1"]').set('value', getPlayerName(1))
        info.getElement('input[name="name_-1"]').set('value', getPlayerName(-1))
        info.getElement('input[name="rank_1"]').set('value', 'BR' in rootNode ? rootNode.BR[0] : '')
        info.getElement('input[name="rank_-1"]').set('value', 'WR' in rootNode ? rootNode.WR[0] : '')
        info.getElement('input[name="result"]').set('value', 'RE' in rootNode ? rootNode.RE[0] : '')
        info.getElement('input[name="komi"]').set('value', ('KM' in rootNode ? rootNode.KM[0] : '').toFloat())

        var size = info.getElement('input[name="size"]')
        size.set('value', 'SZ' in rootNode ? rootNode.SZ[0] : '')

        var handicap = info.getElement('select[name="handicap"]')
        if ('HA' in rootNode) handicap.selectedIndex = rootNode.HA[0].toInt() - 1
        else handicap.selectedIndex = 0

        var disabled = tree.nodes.length > 1 || tree.subtrees.length > 0
        handicap.disabled = disabled
        size.disabled = disabled
    }

    function closeGameInfo() {
        $('info').removeClass('show')
    }

    function updateGameInfo() {
        var rootNode = getRootTree().nodes[0]
        var info = $('info')

{'fraction_non_alphanumeric': 0.16707341323643726, 'fraction_numerical': 0.009544986170616629, 'mean_word_length': 1.6093396226415095, 'pattern_counts': {'":': 0, '<': 479, '<?xml version=': 0, '>': 481, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3167785', 'n_tokens_mistral': 16754, 'n_tokens_neox': 15628, 'n_words': 3167}