// ==UserScript==
// @name KDice Flag Enhancer
// @version 1.0
// @namespace panthar
// @run-at document-end
// @description KDice flag system enhancements.
// @include http://www.kdice.com/*
// @copyright 2014+, Bobby Kramer, http://www.panthar.net
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @grant none
// ==/UserScript==
$(document).ready(function () {
var allPlayers = null;
//run the update on the table after 5 seconds from load. Just an approximation
setTimeout(updateNewTable, 5000);
setTimeout(showAutoTurn, 5000);
setTimeout(showSmartAutoEndTurn, 5000);
//go/cancel buttons, from kdice, when clicked, update our table.
//do this every 3 seconds, because it refreshes every time its your turn
$(document).delegate('.iogc-DialogOuter .iogc-NewButton, .iogc-GameWindow-commands .iogc-NewButton', 'click', function () {
setTimeout(updateNewTable, 3000);
setTimeout(showAutoTurn, 3000);
});
//smart auto end turn checkbox, need to update an attribute on it
$(document).delegate('.iogc-Controls .gwt-CheckBox input#kdice-flags-smart-end-turn', 'change', function () {
if ($(this).is(':checked')) {
$(this).attr('checked', true);
if(!$('.iogc-Controls .gwt-CheckBox input#gwt-uid-1').is(':checked')){
$('.iogc-Controls .gwt-CheckBox input#gwt-uid-1').click();
}
} else {
$(this).attr('checked', false);
}
});
//just a change event on the auto end turn
$(document).delegate('.iogc-Controls .gwt-CheckBox input#gwt-uid-1', 'change', function () {
if ($(this).is(':checked')) {
$(this).attr('checked', true);
} else {
$(this).attr('checked', false);
}
});
$(document).delegate('#kdice-flag-table input:checkbox', 'click', function () {
//if its not a kdice auto-checkbox checking mechanism, and it's the "current player's"
//row were looking at. If thats the case, then show a confirm dialog.
if(!$(this).hasClass('kdice-auto-check') && $(this).hasClass('kdice-flag-table-local')){
if($(this).is(':checked')){
if (confirm('Are you sure you wish to flag ' + $(this).val() + '?')) {
$('.iogc-ChatPanel-input .gwt-TextBox').val('flag ' + $(this).val());
$('.iogc-ChatPanel-input .iogc-NewButton').click();
} else {
$(this).attr('checked', false);
}
}else{
$(this).attr('checked', false);
}
}else if ($(this).is(':checked')) {
$(this).attr('checked', true);
}else {
$(this).attr('checked', false);
}
$(this).removeClass('kdice-auto-check');
});
//Use non-jquery, to add event listenr.
//jquery seems to have some issues with chrome.
document.addEventListener('DOMNodeInserted', function(e) {
if (e.target != null && e.target != undefined){
//if its a message panel message
if($(e.target).is('.iogc-MessagePanel-messages .gwt-HTML')) {
var statusHtml = $(e.target).html();
if(statusHtml != null && statusHtml != undefined){
//every time a player enters a new game, lets update player array
if (statusHtml.indexOf('takes a seat') > -1) {
showAutoTurn();
//creates a new table until the total # of players are in game
updateNewTable();
}else if (statusHtml.indexOf('\'s turn') > -1) {
//update the auto end turn, keep is visible
showAutoTurn();
}else{
autoLastTurnAttackCheck(e.target);
userLeavingCheck(e.target);
}
}
}else if($(e.target).is('.iogc-ChatPanel-messages .gwt-HTML')){
playerFlaggedCheck(e.target);
}
}
}, false);
//creates the "flag" table, that a user an click on
//flag table is only recreated when going to a different table,
//or when someone "sits in" on a table
function createFlagTable(players) {
var loggedInPlayer = $('.iogc-LoginPanel-nameHeading').html();
var tableStr = '';
var firstRowPlayerCount = 1;
var trBackgroundColor = '';
$('#kdice-flag-table-top').remove();
$('#kdice-flag-table').remove();
tableStr = '
Flagged To:
';
tableStr += '
';
tableStr += '
Name
';
//create table head
for (var player in players) {
tableStr += '
' + '
';
}
//create table body
for (var firstRowPlayer in players) {
if (firstRowPlayerCount % 2 == 1) {
trBackgroundColor = 'background-color:#EEEEEE;';
} else {
trBackgroundColor = '';
}
tableStr += '
';
tableStr += '
' + players[firstRowPlayer].name + '
';
for (var secondRowPlayer in players) {
//tableStr += '
';
$('#menu-out').append(tableStr);
}
//get list of all players and data from the game
function getPlayerArray() {
var gamePlayers = [];
//look at the game window table
$('.iogc-GameWindow-table .gwt-HTML').each(function () {
var hidden = $(this).attr('aria-hidden');
var parentTable = $(this).closest('table');
var parentDiv = $(parentTable).closest('table').closest('div');
var playerData = $(parentTable).find('.iogc-PlayerPanel-name');
//get actual game statistics
var playerName = $(playerData).children('a').html();
//should we add game player?
var addGamePlayer = true;
var playerColor = $(playerData).css('borderTopColor');
var playerColorName = '';
if (playerName != undefined && $(parentDiv).css('display') != 'none') {
//some bug, where duplicate players can get added, so,
for (var player in gamePlayers) {
if (gamePlayers[player].name == playerName) {
addGamePlayer = false;
break;
}
}
//after removing duplicates,
if (addGamePlayer == true) {
//get color name from color values
switch (playerColor) {
case 'rgb(191, 48, 105)':
playerColorName = 'red';
break;
case 'rgb(48, 191, 86)':
playerColorName = 'green';
break;
case 'rgb(158, 48, 191)':
playerColorName = 'purple';
break;
case 'rgb(165, 180, 14)':
playerColorName = 'yellow';
break;
case 'rgb(48, 57, 191)':
playerColorName = 'blue';
break;
case 'rgb(191, 134, 48)':
playerColorName = 'brown';
break;
case 'rgb(48, 177, 191)':
playerColorName = 'teal';
break;
}
//push to our player array
gamePlayers.push({
name: playerName,
color: playerColor,
colorName: playerColorName
});
}
}
});
//special case, switch order of green/red, so the order of array makes sense for players
if (gamePlayers != null && gamePlayers.length > 1) {
if (gamePlayers[0].colorName == 'green' && gamePlayers[1].colorName == 'red') {
var tmpPlayer = gamePlayers[0];
gamePlayers[0] = gamePlayers[1];
gamePlayers[1] = tmpPlayer;
}
}
//return all the game players found.
return gamePlayers;
}
//just check a checkbox when flagged
function updateCheckboxForFlag(flaggerColor, flaggedColor) {
var flaggedCheckbox = $('#kdice-flag-table tr td input.' + flaggerColor + '-flagger.' + flaggedColor + '-flagged');
//update flag checkbox
if(!$(flaggedCheckbox).is(':checked')){
$(flaggedCheckbox).addClass('kdice-auto-check');
$(flaggedCheckbox).click();
}
//find all people the flagged player has flagged to (if they have any)
//basically, we just update the "parent" flags, with the current flagger
$('#kdice-flag-table tr td input.' + flaggedColor + '-flagger:checked').each(function () {
var elementClasses = $(this).attr('class').split(/\s+/);
//search through all the CSS classes to see who they flagged to
for (var i = 0; i < elementClasses.length; i++) {
//look at the class element to see if it contains flags,
//if so, set the checkboxed on the original "flagger color"'s parent flags
var selectorStr = "";
switch (elementClasses[i]) {
case 'red-flagged':
selectorStr = '#kdice-flag-table tr td input.' + flaggerColor + '-flagger.red-flagged';
if(!$(selectorStr).is(':checked')){
$(selectorStr).addClass('kdice-auto-check');
$(selectorStr).click();
}
break;
case 'green-flagged':
selectorStr = '#kdice-flag-table tr td input.' + flaggerColor + '-flagger.green-flagged';
if(!$(selectorStr).is(':checked')){
$(selectorStr).addClass('kdice-auto-check');
$(selectorStr).click();
}
break;
case 'purple-flagged':
selectorStr = '#kdice-flag-table tr td input.' + flaggerColor + '-flagger.purple-flagged';
if(!$(selectorStr).is(':checked')){
$(selectorStr).addClass('kdice-auto-check');
$(selectorStr).click();
}
break;
case 'yellow-flagged':
selectorStr = '#kdice-flag-table tr td input.' + flaggerColor + '-flagger.yellow-flagged';
if(!$(selectorStr).is(':checked')){
$(selectorStr).addClass('kdice-auto-check');
$(selectorStr).click();
}
break;
case 'blue-flagged':
selectorStr = '#kdice-flag-table tr td input.' + flaggerColor + '-flagger.blue-flagged';
if(!$(selectorStr).is(':checked')){
$(selectorStr).addClass('kdice-auto-check');
$(selectorStr).click();
}
break;
case 'brown-flagged':
selectorStr = '#kdice-flag-table tr td input.' + flaggerColor + '-flagger.brown-flagged';
if(!$(selectorStr).is(':checked')){
$(selectorStr).addClass('kdice-auto-check');
$(selectorStr).click();
}
break;
case 'teal-flagged':
selectorStr = '#kdice-flag-table tr td input.' + flaggerColor + '-flagger.teal-flagged';
if(!$(selectorStr).is(':checked')){
$(selectorStr).addClass('kdice-auto-check');
$(selectorStr).click();
}
break;
}
}
});
}
//keeps the auto turn checkbox visible no matter what
function showAutoTurn() {
var autoEndTurnCheckbox = $('.iogc-Controls .gwt-CheckBox').css('display', 'block');
}
function showSmartAutoEndTurn(){
var closestTr = $('.iogc-Controls .gwt-CheckBox #gwt-uid-1').closest('tr');
$(closestTr).prepend('
');
}
//just create a new flag table
function updateNewTable() {
allPlayers = getPlayerArray();
createFlagTable(allPlayers);
}
//we don't want the "auto last turn" to work if we have been attacked
//so we check to see if the local-player has been attacked,
//if so, we uncheck the checkbox
function autoLastTurnAttackCheck(elementToCheck) {
//make sure auto end turn is checked before doing this
if ($('.iogc-Controls .gwt-CheckBox input#kdice-flags-smart-end-turn').prop('checked')) {
var loggedInPlayer = $('.iogc-LoginPanel-nameHeading').html().trim();
//only if we have a "current player"
if (loggedInPlayer != null && loggedInPlayer != undefined) {
//get the span, this is how we know its an "attack event"
var firstChild = $(elementToCheck).children('span').html();
//make sure we have a defined element
if (firstChild != undefined) {
//we have to trim it, because it has a space in it
if (loggedInPlayer == firstChild.trim()) {
//uncheck the auto end turn
$('.iogc-Controls .gwt-CheckBox input#gwt-uid-1').attr('checked', false);
$('.iogc-Controls .gwt-CheckBox input#gwt-uid-1').change();
$('.iogc-Controls .gwt-CheckBox input#kdice-flags-smart-end-turn').attr('checked', false);
$('.iogc-Controls .gwt-CheckBox input#kdice-flags-smart-end-turn').change();
}
}
}
}
}
//performs the check for when a users leaves
//it looks at the message window, and if a player actually left
//then remove them from the table.
function userLeavingCheck(elementToCheck) {
//check for users leaving
var userLeaving = $(elementToCheck).children('b').html();
var playerName = null;
//finishes has to be grater than 0, to assure the substring has a positive index
if (userLeaving != null && userLeaving != undefined) {
if (userLeaving.indexOf('finishes') > - 1 && userLeaving.indexOf('in round') > - 1) {
playerName = userLeaving.substring(0, userLeaving.indexOf('finishes') - 1).trim();
var playerWhoLeft = null;
for (var player in allPlayers) {
if (allPlayers[player].name == playerName) {
playerWhoLeft = allPlayers[player];
var checkboxLeavingPlayer = $('#kdice-flag-table tr td input.' + playerWhoLeft.colorName + '-flagger').first();
$(checkboxLeavingPlayer).closest('tr').remove();
$('#kdice-flag-table th.' + playerWhoLeft.colorName + '-header').remove();
$('#kdice-flag-table .' + playerWhoLeft.colorName + '-flagged').closest('td').remove();
//update all the players, our array is smaller
allPlayers = getPlayerArray();
break;
}
}
}
}
}
function playerFlaggedCheck(elementToCheck){
var playerName = $(elementToCheck).children('b').html();
if (playerName != undefined) {
var playerToUpdate = null;
//find the player from the name
for (var player in allPlayers) {
if (allPlayers[player].name == playerName) {
playerToUpdate = allPlayers[player];
break;
}
}
//if we found the player who chatted in the game.
if (playerToUpdate != null) {
var playerText = $(elementToCheck).text();
var tmpPlayerText ;
//important, we do toLowercase, so the regex does NOT need /gi
playerText = playerText.replace(playerName + ': ', '').toLowerCase();
//regex to check for string occurence
var flagPatt = /flag|flg|flga|falg|fagl|lagf|lafg|lfga|lfag/g;
//regex to get everything after the flag with a (.*) in it
var flagPattReplace = /^\bflag|\bflg|\bflga|\bfalg|\bfagl|\blagf|\blafg|\blfga|\blfag+(.*)/g;
//color pattern to match for players, includes "purp" and "yel" since those are common
var colorFullPatt = /\bred|\bgreen|\bpurple|\byellow|\bblue|\bbrown|\bteal(\w+)(?!\w)/g;
//if they did not spell the color name fully
var colorPartPatt = /\bre|\bgr|\bpu|\bye|\bbl|\bbr|\bte(\w+)(?!\w)/g;
//color matching variables
var colorMatch;
var colorMatchPlayer;
//for matching player names
var playerPatt;
var playerResult;
//have we found player(s)?
var hasFoundPlayer = false;
//var colorMatches = [];
//test the regex
var hasFlagged = flagPatt.test(playerText);
//only if they flagged, and it's not a question
if (hasFlagged == true ) {
$(elementToCheck).css('background-color', '#DDDDDD');
//remove player flag text for regex checking, we want everything after the word flag
playerText = playerText.replace(flagPattReplace, '');
//STEP 1: See if the user typed out a full color name (no misspellings)
while (colorMatch = colorFullPatt.exec(playerText)) {
colorMatchPlayer = colorMatch[0];
updateCheckboxForFlag(playerToUpdate.colorName, colorMatchPlayer);
hasFoundPlayer = true;
}
//STEP 2: Second best option, lets match player names
//by creating regex for each.
if (hasFoundPlayer == false) {
for (var player in allPlayers) {
if (allPlayers[player].name.length > 2) {
playerPatt = new RegExp('\\b' + allPlayers[player].name.substr(0, 2).toLowerCase() + '(\\w+)(?!\\w)', 'g');
playerResult = playerPatt.test(playerText);
if (playerResult == true) {
updateCheckboxForFlag(playerToUpdate.colorName, allPlayers[player].colorName);
hasFoundPlayer = true;
}
}
}
}
//STEP 3: third best option, try to find player from similar color words
//we only do this if none of the words matched before now.
if (hasFoundPlayer == false) {
while (colorMatch = colorPartPatt.exec(playerText)) {
colorMatchPlayer = colorMatch[0];
switch (colorMatchPlayer) {
case 're':
updateCheckboxForFlag(playerToUpdate.colorName, 'red');
break;
case 'gr':
updateCheckboxForFlag(playerToUpdate.colorName, 'green');
break;
case 'pu':
updateCheckboxForFlag(playerToUpdate.colorName, 'purple');
break;
case 'ye':
updateCheckboxForFlag(playerToUpdate.colorName, 'yellow');
break;
case 'bl':
updateCheckboxForFlag(playerToUpdate.colorName, 'blue');
break;
case 'br':
updateCheckboxForFlag(playerToUpdate.colorName, 'brown');
break;
case 'te':
updateCheckboxForFlag(playerToUpdate.colorName, 'teal');
break;
default:
//we don't know anything about the flag, highlight it
$(elementToCheck).css('background-color', '#660000');
break;
}
}
}
}
}
}
}
});