<commit_before>/**
 * # ClientList widget for nodeGame
 * Copyright(c) 2017 <NAME>
 * MIT Licensed
 *
 * Shows list of clients and allows selection.
 *
 * www.nodegame.org
 * ---
 */
(function(node) {

    "use strict";

    node.widgets.register('ClientList', ClientList);

    var J = node.JSUS,
        Table = node.window.Table,
        GameStage = node.GameStage;

    var stageLevels;
    stageLevels = {};
    (function(stageLevels) {
        var level;
        for (level in node.constants.stageLevels) {
            if (node.constants.stageLevels.hasOwnProperty(level)) {
                stageLevels[node.constants.stageLevels[level]] = level;
            }
        }
    })(stageLevels);

    // ## Meta-data

    ClientList.version = '0.5.1';
    ClientList.description = 'Displays all clients of a room.';

    ClientList.title = 'Clients';
    ClientList.className = 'clientlist';

    // ## Dependencies

    ClientList.dependencies = {
        JSUS: {},
        Table: {}
    };

    function renderClientCell(o) {
        var content;
        var elem;

        content = o.content;
        if (J.isElement(content)) {
            return content;
        }
        else if ('object' === typeof content) {
            switch (o.y) {
            case 0:
                // Checkbox
                elem = document.createElement('input');
                elem.type = 'checkbox';
                elem.onclick = function() {
                    content.that.updateSelection(false);
                };
                if (content.prevSel.hasOwnProperty(content.id)) {
                    elem.checked = content.prevSel[content.id];
                }
                else {
                    elem.checked = !content.that.selectAll.indeterminate &&
                        content.that.selectAll.checked;
                }
                content.that.checkboxes[content.id] = elem;
                break;

            case 3:
                // Type
                if (content.thisMonitor) {
                    elem = document.createElement('em');
                    elem.title = 'This monitor.';
                }
                else {
                    elem = document.createElement('span');
                }

                elem.innerHTML = content.type;
                break;

            default:
                elem = document.createElement('span');
                elem.innerHTML = 'N/A';
                break;
            }
        }
        else {
            elem = document.createTextNode(content);
        }

        return elem;
    }

    function ClientList(options) {
        var that;

        that = this;

        // Channel currently selected.
        this.channelName = options.channel || null;

        // Rooms available in currently selected channel.
        this.availableRooms = options.availableRooms || {};

        // Currently selected room id.
        this.roomId = options.roomId || null;

        // Currently selected room name.
        this.roomName = options.roomName || null;

        // Id of the logic of the currently selected room.
        this.roomLogicId = null;

        // Table displaying the channels.
        this.channelTable = new Table();

        // Table displaying the rooms.
        this.roomTable = new Table();

        // Table displaying the clients.
        this.clientTable = new Table({
            render: {
                pipeline: renderClientCell,
                returnAt: 'first'
            }
        });

        // Div containing the commands for the waiting room (when selected).
        this.waitroomCommandsDiv = null;

        // Maps client IDs to the selection checkbox elements:
        this.checkboxes = {};

        // Create "Select All" checkbox:
        this.selectAll = document.createElement('input');
        this.selectAll.type = 'checkbox';
        this.selectAll.checked = true;
        this.selectAll.title = 'Select All';
        this.selectAll.onclick = function() {
            that.updateSelection(true);
        };

        // Create header for client table:
        this.channelTable.setHeader(['Channel']);
        this.roomTable.setHeader(['Room']);
        this.clientTable.setHeader([
            this.selectAll,
            'ID', 'SID', 'Type', 'Admin', 'Stage', 'Level', 'Paused', 'Last Error'
        ]);

        this.clientsField = null;
        this.msgBar = {};
    }

    ClientList.prototype.setChannel = function(channelName) {
        if (!channelName || channelName !== this.channelName) {
            // Hide room table if channel changed or no channel is selected:
            if (this.roomTable && this.roomTable.table.parentNode) {
                this.roomTable.table.parentNode.style.display = 'none';
            }
            if (node.game.roomInUse !== null) this.setRoom(null);
        }

        this.channelName = channelName;
        node.emit('CHANNEL_SELECTED', channelName);
        node.game.refreshRooms();
    };

    ClientList.prototype.setRoom = function(roomId) {
        var roomObj, roomName;

        if (null === roomId) {
            roomName = null;
            // Hide client table if no room is selected:
            if (this.clientTable && this.clientTable.table.parentNode) {
                this.clientTable.table.parentNode.style.display = 'none';
            }
        }
        else {
            roomObj = this.availableRooms[roomId];
            if (!roomObj) {
                throw new Error('ClientList.setRoom: roomId not found: ' +
                                roomId);
            }
            roomName = roomObj.name;
        }
        
        this.roomId = roomId;
        this.roomName = roomName;
        this.roomLogicId = null;
    
        node.emit('ROOM_SELECTED', roomObj);

        node.game.refreshClients();
    };

    ClientList.prototype.refresh = function() {
        node.game.refreshChannels();
        node.game.refreshRooms();
        node.game.refreshClients();
    };

    ClientList.prototype.append = function() {
        var that;

        var tableStructure;
        var commandPanel, commandPanelHeading, commandPanelBody;

        var buttonDiv, button, forceCheckbox, label;

        var waitRoomCommandsDiv, dispatchNGamesInput, dispatchGroupSizeInput;
        var treatmentInput;
        var labelDNGI, labelDGSI, labelDTI;

        var buttonTable, tableRow, tableCell;
        var setupOpts, btnLabel;
        var selectionDiv, recipientSelector;

        that = this;

        // Add tables in a 3x1 table element:
        tableStructure = document.createElement('table');
        this.bodyDiv.appendChild(tableStructure);
        tableRow = document.createElement('tr');
        tableRow.style['vertical-align'] = 'top';
        tableStructure.appendChild(tableRow);

        tableCell = document.createElement('td');
        tableCell.style['border-right'] = '1px solid #ccc';
        tableRow.appendChild(tableCell);
        tableCell.appendChild(this.channelTable.table);

        tableCell = document.createElement('td');
        tableRow.appendChild(tableCell);
        tableCell.style['border-right'] = '1px solid #ccc';
        tableCell.style.display = 'none';
        tableCell.appendChild(this.roomTable.table);

        tableCell = document.createElement('td');
        tableCell.style.display = 'none';
        tableRow.appendChild(tableCell);
        tableCell.appendChild(this.clientTable.table);

        // Add client selection field:
        selectionDiv = document.createElement('div');
        this.bodyDiv.appendChild(selectionDiv);
        selectionDiv.appendChild(document.createTextNode('Selected IDs: '));

        this.clientsField = document.createElement('textarea');
        this.clientsField.rows = 1;
        this.clientsField.style['vertical-align'] = 'top';
        this.clientsField.style.width = '50em';

        selectionDiv.appendChild(this.clientsField);
        recipientSelector = W.getRecipientSelector();
        recipientSelector.onchange = function() {
            that.clientsField.value = recipientSelector.value;
        };
        selectionDiv.appendChild(recipientSelector);
        selectionDiv.style['padding'] = '10px 0px';
        selectionDiv.style['border-top'] = '1px solid #ddd';
        selectionDiv.style['border-bottom'] = '1px solid #ddd';
        selectionDiv.style['margin'] = '10px 0px';

        commandPanel = W.add('div', this.bodyDiv, {
            className: ['panel', 'panel-default', 'commandbuttons']
        });
        commandPanelHeading = W.add('div', commandPanel, {
            className: ['panel-heading']
        });
        commandPanelHeading.innerHTML = 'Commands';
        commandPanelBody = W.add('div', commandPanel, {
            className: ['panel-body', 'commandbuttons']
        });

        // Add row for buttons:
        buttonDiv = document.createElement('div');
        commandPanelBody.appendChild(buttonDiv);

        // Force checkbox:
        label = document.createElement('label');
        forceCheckbox = document.createElement('input');
        forceCheckbox.type = 'checkbox';
        forceCheckbox.style['margin-left'] = '5px';
        label.appendChild(forceCheckbox);
        label.appendChild(document.createTextNode(' Force'));

        // Add buttons to control waiting room (displayed only when needed).
        this.waitroomCommandsDiv = document.createElement('div');
        this.waitroomCommandsDiv.style.display = 'none';

        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
                    'OPEN', 'Open'));
        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
                    'CLOSE', 'Close'));
        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
                    'PLAYWITHBOTS', 'Connects Bots'));

        
        this.waitroomCommandsDiv.appendChild(document.createElement('hr'));
        
        //this.waitroomCommandsDiv.appendChild(document.createElement('br'));

        // Need to create inputs before Dispatch button.
        dispatchNGamesInput = document.createElement('input');
        dispatchNGamesInput.size = 2;

        dispatchGroupSizeInput = document.createElement('input');        
        dispatchGroupSizeInput.size = 2;

        treatmentInput = document.createElement('input');
        treatmentInput.size = 5;

        // Dispatch N Groups label.
   
        labelDNGI = document.createElement('label');
        labelDNGI.style['margin-left'] = '5px';
        labelDNGI.appendChild(document.createTextNode('#Groups'));
        labelDNGI.appendChild(dispatchNGamesInput);
        this.waitroomCommandsDiv.appendChild(labelDNGI);

        // Dispatch Group Size label.

        labelDGSI = document.createElement('label');
        labelDGSI.style['margin-left'] = '5px';
        labelDGSI.appendChild(document.createTextNode('#Size'));
        labelDGSI.appendChild(dispatchGroupSizeInput);
        this.waitroomCommandsDiv.appendChild(labelDGSI);
        
        // Treatment Label.
        labelDTI = document.createElement('label');
        labelDTI.style['margin-left'] = '5px';
        labelDTI.appendChild(document.createTextNode('Treatment'));
        labelDTI.appendChild(treatmentInput);
        this.waitroomCommandsDiv.appendChild(labelDTI);

        // Dispatch Button.
        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
            'DISPATCH', 'Dispatch', dispatchNGamesInput,
            dispatchGroupSizeInput, treatmentInput));

        // TODO


        this.waitroomCommandsDiv.appendChild(document.createElement('hr'));
        
        buttonDiv.appendChild(this.waitroomCommandsDiv);
        // End waiting Room controls.

        // Add buttons for setup/start/stop/pause/resume:
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'SETUP',  'Setup', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'START',  'Start', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'STOP',   'Stop', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'PAUSE',  'Pause', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'RESUME', 'Resume', forceCheckbox));

        buttonDiv.appendChild(label);
        
        buttonDiv.appendChild(document.createElement('hr'));

        // Add StateBar:
        this.appendStateBar(commandPanelBody);

        // Add a table for buttons:
        buttonTable = document.createElement('table');
        commandPanelBody.appendChild(buttonTable);

        // Add buttons for disable right click/ESC, prompt on leave, waitscreen.
        setupOpts = {
            'Disable right-click': 'disableRightClick',
            'Disable Esc': 'noEscape',
            'Prompt on leave': 'promptOnleave',
            'Wait-screen': 'waitScreen'
        };

        for (btnLabel in setupOpts) {
            if (setupOpts.hasOwnProperty(btnLabel)) {
                tableRow = document.createElement('tr');
                buttonTable.appendChild(tableRow);

                tableCell = document.createElement('td');
                tableCell.innerHTML = btnLabel;
                tableRow.appendChild(tableCell);

                tableCell = document.createElement('td');
                tableRow.appendChild(tableCell);

                button = document.createElement('button');
                button.className = 'btn';
                button.innerHTML = 'On';
                button.onclick = (function(optName) {
                    return function() {
                        var opts = {};
                        opts[optName] = true;
                        node.remoteSetup('window', that.getSelectedClients(),
                                         opts);
                    };
                })(setupOpts[btnLabel]);
                tableCell.appendChild(button);

                button = document.createElement('button');
                button.className = 'btn';
                button.innerHTML = 'Off';
                button.onclick = (function(optName) {
                    return function() {
                        var opts = {};
                        opts[optName] = false;
                        node.remoteSetup('window', that.getSelectedClients(),
                                         opts);
                    };
                })(setupOpts[btnLabel]);
                tableCell.appendChild(button);
            }
        }

        // TODO: see if we need this now.
        
//         commandPanelBody.appendChild(document.createElement('hr'));
//         
//         // Add bot-start button:
//         button = document.createElement('button');
//         button.innerHTML = 'Start bot';
//         button.className = 'btn';
//         button.onclick = function() {
//             node.socket.send(node.msg.create({
//                 target: 'SERVERCOMMAND',
//                 text:   'STARTBOT'
//             }));
//         };
//         commandPanelBody.appendChild(button);

        // Add MsgBar:
        this.appendMsgBar();

        this.channelTable.parse();
    };

    ClientList.prototype.listeners = function() {
        var that;

        that = this;

        // Upon successful connection select current channel.
        node.on('NODEGAME_READY', function() {
            setTimeout(function() {
                that.setChannel(node.socket.channelName);
            });
        });<commit_msg>Added Redirect Bar in Commands<commit_after>/**
 * # ClientList widget for nodeGame
 * Copyright(c) 2018 <NAME>
 * MIT Licensed
 *
 * Shows list of clients and allows selection.
 *
 * www.nodegame.org
 * ---
 */
(function(node) {

    "use strict";

    node.widgets.register('ClientList', ClientList);

    var J = node.JSUS,
        Table = node.window.Table,
        GameStage = node.GameStage;

    var stageLevels;
    stageLevels = {};
    (function(stageLevels) {
        var level;
        for (level in node.constants.stageLevels) {
            if (node.constants.stageLevels.hasOwnProperty(level)) {
                stageLevels[node.constants.stageLevels[level]] = level;
            }
        }
    })(stageLevels);

    // ## Meta-data

    ClientList.version = '0.6.0';
    ClientList.description = 'Displays all clients of a room.';

    ClientList.title = 'Clients';
    ClientList.className = 'clientlist';

    // ## Dependencies

    ClientList.dependencies = {
        JSUS: {},
        Table: {}
    };

    function renderClientCell(o) {
        var content;
        var elem;

        content = o.content;
        if (J.isElement(content)) {
            return content;
        }
        else if ('object' === typeof content) {
            switch (o.y) {
            case 0:
                // Checkbox
                elem = document.createElement('input');
                elem.type = 'checkbox';
                elem.onclick = function() {
                    content.that.updateSelection(false);
                };
                if (content.prevSel.hasOwnProperty(content.id)) {
                    elem.checked = content.prevSel[content.id];
                }
                else {
                    elem.checked = !content.that.selectAll.indeterminate &&
                        content.that.selectAll.checked;
                }
                content.that.checkboxes[content.id] = elem;
                break;

            case 3:
                // Type
                if (content.thisMonitor) {
                    elem = document.createElement('em');
                    elem.title = 'This monitor.';
                }
                else {
                    elem = document.createElement('span');
                }

                elem.innerHTML = content.type;
                break;

            default:
                elem = document.createElement('span');
                elem.innerHTML = 'N/A';
                break;
            }
        }
        else {
            elem = document.createTextNode(content);
        }

        return elem;
    }

    function ClientList(options) {
        var that;

        that = this;

        // Channel currently selected.
        this.channelName = options.channel || null;

        // Rooms available in currently selected channel.
        this.availableRooms = options.availableRooms || {};

        // Currently selected room id.
        this.roomId = options.roomId || null;

        // Currently selected room name.
        this.roomName = options.roomName || null;

        // Id of the logic of the currently selected room.
        this.roomLogicId = null;

        // Table displaying the channels.
        this.channelTable = new Table();

        // Table displaying the rooms.
        this.roomTable = new Table();

        // Table displaying the clients.
        this.clientTable = new Table({
            render: {
                pipeline: renderClientCell,
                returnAt: 'first'
            }
        });

        // Div containing the commands for the waiting room (when selected).
        this.waitroomCommandsDiv = null;

        // Maps client IDs to the selection checkbox elements:
        this.checkboxes = {};

        // Create "Select All" checkbox:
        this.selectAll = document.createElement('input');
        this.selectAll.type = 'checkbox';
        this.selectAll.checked = true;
        this.selectAll.title = 'Select All';
        this.selectAll.onclick = function() {
            that.updateSelection(true);
        };

        // Create header for client table:
        this.channelTable.setHeader(['Channel']);
        this.roomTable.setHeader(['Room']);
        this.clientTable.setHeader([
            this.selectAll,
            'ID', 'SID', 'Type', 'Admin', 'Stage', 'Level', 'Paused', 'Last Error'
        ]);

        this.clientsField = null;
        this.msgBar = {};
    }

    ClientList.prototype.setChannel = function(channelName) {
        if (!channelName || channelName !== this.channelName) {
            // Hide room table if channel changed or no channel is selected:
            if (this.roomTable && this.roomTable.table.parentNode) {
                this.roomTable.table.parentNode.style.display = 'none';
            }
            if (node.game.roomInUse !== null) this.setRoom(null);
        }

        this.channelName = channelName;
        node.emit('CHANNEL_SELECTED', channelName);
        node.game.refreshRooms();
    };

    ClientList.prototype.setRoom = function(roomId) {
        var roomObj, roomName;

        if (null === roomId) {
            roomName = null;
            // Hide client table if no room is selected:
            if (this.clientTable && this.clientTable.table.parentNode) {
                this.clientTable.table.parentNode.style.display = 'none';
            }
        }
        else {
            roomObj = this.availableRooms[roomId];
            if (!roomObj) {
                throw new Error('ClientList.setRoom: roomId not found: ' +
                                roomId);
            }
            roomName = roomObj.name;
        }
        
        this.roomId = roomId;
        this.roomName = roomName;
        this.roomLogicId = null;
    
        node.emit('ROOM_SELECTED', roomObj);

        node.game.refreshClients();
    };

    ClientList.prototype.refresh = function() {
        node.game.refreshChannels();
        node.game.refreshRooms();
        node.game.refreshClients();
    };

    ClientList.prototype.append = function() {
        var that;

        var tableStructure;
        var commandPanel, commandPanelHeading, commandPanelBody;

        var buttonDiv, button, forceCheckbox, label;
        var extraButtonsDiv;
        
        var waitRoomCommandsDiv, dispatchNGamesInput, dispatchGroupSizeInput;
        var treatmentInput;
        var labelDNGI, labelDGSI, labelDTI;

        var buttonTable, tableRow, tableCell;
        var setupOpts, btnLabel;
        var selectionDiv, recipientSelector;

        that = this;

        // Add tables in a 3x1 table element:
        tableStructure = document.createElement('table');
        this.bodyDiv.appendChild(tableStructure);
        tableRow = document.createElement('tr');
        tableRow.style['vertical-align'] = 'top';
        tableStructure.appendChild(tableRow);

        tableCell = document.createElement('td');
        tableCell.style['border-right'] = '1px solid #ccc';
        tableRow.appendChild(tableCell);
        tableCell.appendChild(this.channelTable.table);

        tableCell = document.createElement('td');
        tableRow.appendChild(tableCell);
        tableCell.style['border-right'] = '1px solid #ccc';
        tableCell.style.display = 'none';
        tableCell.appendChild(this.roomTable.table);

        tableCell = document.createElement('td');
        tableCell.style.display = 'none';
        tableRow.appendChild(tableCell);
        tableCell.appendChild(this.clientTable.table);

        // Add client selection field:
        selectionDiv = document.createElement('div');
        this.bodyDiv.appendChild(selectionDiv);
        selectionDiv.appendChild(document.createTextNode('Selected IDs: '));

        this.clientsField = document.createElement('textarea');
        this.clientsField.rows = 1;
        this.clientsField.style['vertical-align'] = 'top';
        this.clientsField.style.width = '50em';

        selectionDiv.appendChild(this.clientsField);
        recipientSelector = W.getRecipientSelector();
        recipientSelector.onchange = function() {
            that.clientsField.value = recipientSelector.value;
        };
        selectionDiv.appendChild(recipientSelector);
        selectionDiv.style['padding'] = '10px 0px';
        selectionDiv.style['border-top'] = '1px solid #ddd';
        selectionDiv.style['border-bottom'] = '1px solid #ddd';
        selectionDiv.style['margin'] = '10px 0px';

        commandPanel = W.add('div', this.bodyDiv, {
            className: ['panel', 'panel-default', 'commandbuttons']
        });
        commandPanelHeading = W.add('div', commandPanel, {
            className: ['panel-heading']
        });
        commandPanelHeading.innerHTML = 'Commands';
        commandPanelBody = W.add('div', commandPanel, {
            className: ['panel-body', 'commandbuttons']
        });

        // Add row for buttons:
        buttonDiv = document.createElement('div');
        commandPanelBody.appendChild(buttonDiv);

        // Force checkbox:
        label = document.createElement('label');
        forceCheckbox = document.createElement('input');
        forceCheckbox.type = 'checkbox';
        forceCheckbox.style['margin-left'] = '5px';
        label.appendChild(forceCheckbox);
        label.appendChild(document.createTextNode(' Force'));

        // Add buttons to control waiting room (displayed only when needed).
        this.waitroomCommandsDiv = document.createElement('div');
        this.waitroomCommandsDiv.style.display = 'none';

        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
                    'OPEN', 'Open'));
        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
                    'CLOSE', 'Close'));
        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
                    'PLAYWITHBOTS', 'Connects Bots'));

        
        this.waitroomCommandsDiv.appendChild(document.createElement('hr'));

        //this.waitroomCommandsDiv.appendChild(document.createElement('br'));

        // Need to create inputs before Dispatch button.
        dispatchNGamesInput = document.createElement('input');
        dispatchNGamesInput.size = 2;

        dispatchGroupSizeInput = document.createElement('input');        
        dispatchGroupSizeInput.size = 2;

        treatmentInput = document.createElement('input');
        treatmentInput.size = 5;

        // Dispatch N Groups label.
   
        labelDNGI = document.createElement('label');
        labelDNGI.style['margin-left'] = '5px';
        labelDNGI.appendChild(document.createTextNode('#Groups'));
        labelDNGI.appendChild(dispatchNGamesInput);
        this.waitroomCommandsDiv.appendChild(labelDNGI);

        // Dispatch Group Size label.

        labelDGSI = document.createElement('label');
        labelDGSI.style['margin-left'] = '5px';
        labelDGSI.appendChild(document.createTextNode('#Size'));
        labelDGSI.appendChild(dispatchGroupSizeInput);
        this.waitroomCommandsDiv.appendChild(labelDGSI);
        
        // Treatment Label.
        labelDTI = document.createElement('label');
        labelDTI.style['margin-left'] = '5px';
        labelDTI.appendChild(document.createTextNode('Treatment'));
        labelDTI.appendChild(treatmentInput);
        this.waitroomCommandsDiv.appendChild(labelDTI);

        // Dispatch Button.
        this.waitroomCommandsDiv.appendChild(this.createWaitRoomCommandButton(
            'DISPATCH', 'Dispatch', dispatchNGamesInput,
            dispatchGroupSizeInput, treatmentInput));

        // TODO


        this.waitroomCommandsDiv.appendChild(document.createElement('hr'));
        
        buttonDiv.appendChild(this.waitroomCommandsDiv);
        // End waiting Room controls.

        // Add buttons for setup/start/stop/pause/resume:
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'SETUP',  'Setup', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'START',  'Start', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'STOP',   'Stop', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'PAUSE',  'Pause', forceCheckbox));
        buttonDiv.appendChild(this.createRoomCommandButton(
                    'RESUME', 'Resume', forceCheckbox));

        buttonDiv.appendChild(label);
        
        buttonDiv.appendChild(document.createElement('hr'));

        // Add StateBar:
        this.appendStateBar(commandPanelBody);

        commandPanelBody.appendChild(document.createElement('hr'));
        
        // Add a table for buttons:
        buttonTable = document.createElement('table');
        commandPanelBody.appendChild(buttonTable);

        // Add buttons for disable right click/ESC, prompt on leave, waitscreen.
        setupOpts = {
            'Disable right-click': 'disableRightClick',
            'Disable Esc': 'noEscape',
            'Prompt on leave': 'promptOnleave',
            'Wait-screen': 'waitScreen'
        };

        for (btnLabel in setupOpts) {
            if (setupOpts.hasOwnProperty(btnLabel)) {
                tableRow = document.createElement('tr');
                buttonTable.appendChild(tableRow);

                tableCell = document.createElement('td');
                tableCell.innerHTML = btnLabel;
                tableRow.appendChild(tableCell);

                tableCell = document.createElement('td');
                tableRow.appendChild(tableCell);

                button = document.createElement('button');
                button.className = 'btn';
                button.innerHTML = 'On';
                button.onclick = (function(optName) {
                    return function() {
                        var opts = {};
                        opts[optName] = true;
                        node.remoteSetup('window', that.getSelectedClients(),
                                         opts);
                    };
                })(setupOpts[btnLabel]);
                tableCell.appendChild(button);

                button = document.createElement('button');
                button.className = 'btn';
                button.innerHTML = 'Off';
                button.onclick = (function(optName) {
                    return function() {
                        var opts = {};
                        opts[optName] = false;
                        node.remoteSetup('window', that.getSelectedClients(),
                                         opts);
                    };
                })(setupOpts[btnLabel]);
                tableCell.appendChild(button);
            }
        }

        // TODO: see if we need this now.
        
        commandPanelBody.appendChild(document.createElement('hr'));

        var inputGroup = document.createElement('div');
        inputGroup.className = "input-group";

        var myInput = document.createElement('input');
        myInput.type = "text";
        myInput.className ="form-control"
        myInput.placeholder = "Full URI or a page within the game"
        myInput["aria-label"] = "Full URI or a page within the game"

        inputGroup.appendChild(myInput);
        
        var tmp = document.createElement('span');
        tmp.className = "input-group-btn";
        
        button = document.createElement('button');
        button.className = 'btn btn-default';
        button.innerHTML = 'Redirect';
        button.type = 'button';
        button.onclick = function() {
            var uri, clients;
            uri = myInput.value;
            if (!uri) {
                node.warn('cannot redirect, empty uri.');
                return false;
            }
            clients = that.getSelectedClients();
            if (!clients || !clients.length) {
                node.warn('cannot redirect, no client selected.');
                return false;
            }
            node.redirect(uri, clients);
        };

        tmp.appendChild(button)
        inputGroup.appendChild(tmp);
        
        
        commandPanelBody.appendChild(inputGroup);
            
//         
//         // Add bot-start button:
//         button = document.createElement('button');
//         button.innerHTML = 'Start bot';
//         button.className = 'btn';
//         button.onclick = function() {
//             node.socket.send(node.msg.create({
//                 target: 'SERVERCOMMAND',
//                 text:   'STARTBOT'
//             }));
//         };
//         commandPanelBody.appendChild(button);

        // Add MsgBar:
        this.appendMsgBar();

        this.channelTable.parse();
    };

    ClientList.prototype.listeners = function() {
        var that;

        that = this;

        // Upon successful connection select current channel.
        node.on('NODEGAME_READY', function() {
            setTimeout(function() {
                that.setChannel(node.socket.channelName);
            });
        });
{'fraction_non_alphanumeric': 0.11370601936986638, 'fraction_numerical': 0.0019002084099546401, 'mean_word_length': 1.868987953925965, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5289390', 'n_tokens_mistral': 8420, 'n_tokens_neox': 8053, 'n_words': 1580}