// ==UserScript==
// @name GrepoData City Indexer
// @namespace grepodata
// @version 2.0.0
// @author grepodata.com
// @homepage https://grepodata.com/indexer
// @updateURL https://api.grepodata.com/script/indexer.user.js
// @downloadURL https://api.grepodata.com/script/indexer.user.js
// @description This script allows you to collect and share enemy city intelligence
// @include https://*.grepolis.com/game/*
// @include https://grepodata.com*
// @exclude view-source://*
// @icon https://grepodata.com/assets/images/grepodata_icon.ico
// @copyright 2016+, grepodata.com
// @grant none
// ==/UserScript==
//JoeMan
(function() {
var rand = Math.floor((Date.now()/1000)/(60*60)) + "";
//var CustomStyleJS = document.createElement('script');
// CustomStyleJS.type = 'text/javascript';
//CustomStyleJS.src = 'https://api.grepodata.com/script/indexer.js?v=' + rand;
// document.getElementsByTagName("head")[0].appendChild(CustomStyleJS);
var CustomStyleCSS = document.createElement('link');
CustomStyleCSS.rel = 'stylesheet';
CustomStyleCSS.type = 'text/css';
CustomStyleCSS.href = 'https://api.grepodata.com/script/indexer.css?v=' + rand;
document.getElementsByTagName("head")[0].appendChild(CustomStyleCSS);
console.log("Added GrepoData City Indexer by Tamper/GreaseMonkey");
})();
var gd_version = "5.0.0";
var verbose = false;
var errorSubmissions = [];
(function() { try {
// Globals
var backend_url = 'https://api.grepodata.com'
var frontend_url = 'https://grepodata.com'
var time_regex = /([0-5]\d)(:)([0-5]\d)(:)([0-5]\d)(?!.*([0-5]\d)(:)([0-5]\d)(:)([0-5]\d))/gm;
var gd_icon = "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAXCAYAAAAV1F8QAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNvyMY98AAAG0SURBVEhLYwACASA2AGIHGmGQ2SA7GGzf7oj4//5g7v/3B7L+vz+U///NVv//r9ZY/3+7K/b/683e/9/tSSTIf7M9DGhGzv8PR4r/v9uX9v/D0TKw+MdTzf9BdoAsSnm13gnEoQn+dLYLRKcAMUPBm62BYMH/f/9QFYPMfL3JE0QXQCzaFkIziz6d60FYBApvdIt07AJQ+ORgkJlfrs2DW1T9ar0jxRZJ7JkDxshiIDPf744B0dUgiwrebA8l2iJsBuISB5l5q58dREOC7u3OKJpZdHmKEsKi1xvdybIIpAamDpdFbze5ISzClrypZdGLZboIiz6d7cRrES4DibHozdYghEWfL0ygmUVvtwcjLPpwuJBmFj1ZpImw6N3uBNpZNE8ByaK9KXgtIheDzHy12gJuUfG7falYLSIHI5sBMvPlCiMQXQy2CFQPoVtEDQwy88VScByBLSqgpUVQH0HjaH8GWJAWGFR7A2mwRSkfjlUAM1bg/9cbXMAVFbhaBib5N9uCwGxQdU2ID662T9aDMag5AKrOQVX9u73JIIvANSyoPl8CxOdphEFmg9sMdGgFMQgAAH4W0yWXhEbUAAAAAElFTkSuQmCC')";
var gd_icon_intel = "url('data:image/png;base64,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')";
var react_icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAxhSURBVHhe5ZsLXBTVHsd3ZmffLLsg8pDkIaKG4MW8RahdDbmSJKbho+4npTRBLkg+E59XUyFfEIqAGqapaUZKKqGRhVfU1FTEVEwEQVGE5bnv3dmZzuw9cCX2MQOLdG/fz0f3/P4z7GfOf8/5n///zAzC6kYKCgqQW4eW+SplD4INWlUgiyQGEQTuCT6dSYK0ZxF6jvFElKPn8IX572z6bqLn4CDSaKNJ8uuSiXqNMosw4C6URlC2FuPyswa8Ejl/yvLPrX6XzR2QfzADKzmyKUSvbJxM4LpwQqdyh4fMgqCoQuTgErDo68f3oYkWOUlR2M3v99eShMEBmtrgiSQhS/Oaf4TSLCj87DLb40JckyKc1lz+bOF9dV3FKVzVNJtO5ykwDn81085T1D0sF5vqPAWKcX1h0yJddkBGfIjb2nCHNFnp2QqdXLaK0KlpdboVlI1VOfcP3A4lM0jzI5zuPOq0Aw5/soT78UTXRFlp0V2DqjGBNOj58BAj2Bzeltnbz2ugfOZ0ygGpM/yH3slPv6xpqkkG81wEzYwBAUsldfXaA2WPwMgBBw8eZG2c7Dm35WHpBYNWOQSaOw0b4+TH7bnZAmWPQNsBR1Pm8iq+mPuZSla5lSRwHjR3CZTNOQ6bPQYtB6TGhohun96XB4Lcu9BkEwT2jv+GzR7Dah6QOidE1HLvwglSrx4NTTYBRP+mgLFRjpOWZJsN2Bve6P08rlWNIEnS5A+FsDGhXtWSCmU7ML5oP0kQZ6FsB9VpjCeocvIcfMqiA46mJPBun96bp1M2j4EmmwGyteIV32mGQtkBkOGN0apavgVLHReabA6HL0ozOwUOHDjAKi08uKM7Ok9hwPWPYdMkIL19rzs7T2HAddFmHVB9dOlcbUtdFJQ2B2Rwetg0Ccjtu7XzRkiWwKQDUqb7D1U3PNoE5f81HRzwZepirrK2Yo+tljpzgCBoMYECGWINbHYfCKuqgwPKC/ctsEWSYw0EQfvCpkl4dtIk4IRLNBaqToFinDquQBzT7tsz4ka7ye6cv8s0vQWdMfAkLkm9AsKyWKSBVVdyKlqvqF8J5rnZGAPSYJ1vcITkH+tzLdYBxzOXi0GwwqBsR8uTSkl50eEKKNth5/Tc/IEhM/ZC2Q5QfrMcXT2ahk+MJts5YN04+zRc3ZIAJW34jn3jE488aFfRbZrqlaCsvZ8GpUnETu7BC3Oqf4KSMZmxIx2e3C5qgLIdfHunmMRjsp1QmqXtF9oaO8rVoFVFQ0kbTCC+OjTm0wwo2xg4LXkbJrC/BqVJNIrGV2Gzx2hzgOLhzVgQ+BiXtBjfPjssLKxDNjch8m0S5Qh2QWkSAte/AZs9htEBeQfSMRD4ZhstDBE4eZjN57kSF4u5vsGAB6VM6TsYyh7B6IAbRzeHGHRqN6OFIYMnLamGzQ5IBr1aBSIOVCYgSZZaXv8BVD2C8epwZcNko+oEpIVliiAI2DIPrtNEpUz16AflMweltq5BVhoONWNu5qzrA5sdkJcVeYCcFyrTgIqNq2qq2wglI3hCsdkvt5Zqt4Le/mrVAKYbmU+jqn/wN9jsgLbh4UjYtAiuU0cmh9u/CSVtZm7JbwbJ0hUo2wDrvI7LF5kshX8PKq8pfxm2O4VBq5h1Oj+3wzw4lLoYIfRq2oFVp1Hs2vymK+OpILB3Gs/hCdOAI75ic7jgH38vTygZszDnYRk8xSLI2tfEqQaNfB7UnULo5JHwYU7VNiiNbIjsG6+uf9DOZg2UzbkllPZ+ZdHXj0wmN90BsjZMlA+WwNeg7hxUKizulST2eXkHjuOkqurqHL28bhmY32x4Bm1AkXRFKHUe+6ycgHw0VnAbxIBBUHeR1plA97aEaaiRILB3jFh89Ek5NHULhfuSUBRES2eobQDV8a51ngIEcD9Vs+znpHBx5E+5mdBqWzZHunmfO7gxF1nzdwFJ6tXQ/McD4wqOgNGwCBRNJqs+puxOGMV/fOdyPK7XriYRlIusHm2Dn6yboZY1jGuM7p8sOvL4FjQzImPmEGlTTcW7uFY9nzDgHtDM+p9wQBsIwmKzsYsoxvmGJ5L82GdQULG5/YTjqXHIvUv53qqm2ldAhyMMuH4cmO5CeLiNP/wUsAS1qYKi7EqSJKpAJ5XQxgEriRvorDewSYwnmoPN0SNrQrn1JK5zhKY/FQjGrUcRFKuF+s8Hwq5FEQRh9GSGnWv/1cBzfzingVS4XujssxZKWlDTBwWTphRqWiAcwbEBr04fwJe6LEM5/CfQ3GOgGK+WL3Vb6fzCBB8WR5ALzfRA2aUomycqhpIW2pZav7eXZzcn5j5Jdh8921Pg6P4uJhCfB46EZzwDwGqA8cUX+Y7uszxGzfBKzH28LmbDV824QuYHz6AFmysoRrbGjBzYcKeI9ijAhNKtK75t6rCLs3V2sK+ytnwarlVOAKn1MEtb4p0CQQk2T1iMYtxcUR+/w/N2Ft2BR9pYN06Shqubae9qO/gGD0QKCwuRs0nhDwx0n+jiiX5ZcUoZAKVJdi6Z7NxQdnEUrpYHg7R2GIsk/AmdBqw0dFMOhIVy+Y1gjP4C1vyrYKqdc/AKPBObdtJi7AGF3Q1Q2PlDaRHgzOqQVQV9jdXL+nDJTr2qmV7tDoa6vccQ7wV7i2kHz3PnziHXj23rJa+85oVrFG4sg97JoFUI+GJHe+q4Rt7QAqaiGkxoGfh8LOkfXOkbEiULDQ2lnaRtmfEXL/mDGxXWdqBa4Ygcdi3Pa4w2OmDD1H5j1bXlp4xHaMC1771q2bE6RhG3u0mK6L1SJ6/7CEqrCF18wz788u53xnk6dPKSH1CuwOzu7u8Bv+L7B9LXmrxd1RMcykzCcK3ifSitAqbUw79OS/zB2Kb+Gzs1Bse4wk+pNh1AkPOoKdozHcoe59GZ7OnUNUFpFYxvlx3y5iycardFaqlXYBbC5tB+YFHZ8Gjlp4lTuvUWOh32rHiLR10LlFah+ujYb2gWlKy2Lau8SxWKMX5SZ1AYBUGTRUgD7qBXNhLflyoKoalHCHKoW6FXNU2C0iqYyCFjwb5fcqD87wigcOjrlwyKKTmUVtE01y1NnREwDMpnTsqMgBd0ivrlUFoF5BDyXp7+yVAaaeeA+KyiGhDhk6C0CkngHEVN2ZfpcSHPvJrMmh/uqKy5dxiMxP+8c0ADrthpfez2M+2ePGnnAIqBoTNTwFpcAqVVDDq1T1P55dz9KUs6/cwwU/anLBXWl549CpI3H2iyCgh8JQHhMR2eKTR5Y2/LO4MDFY/u/MTkOSEwcgrd/UeNj0rKMW5MdBdf/Ott0b1LeV8b1C1h0GQVhI1pJc/5vTxvb0mHusekAyg2TvaMV8kqGd3YYPPsLks9AybN3XmBdk7BhIw5I9zrK64fAVnkS9BEC5GTR/zinCqT7yR0mAKtBEbvSudJXBg9yg4u7MXGimvXNk71Hg9NNmPjNJ/XZWU/X2HaeTAydwcl7Df7QobZEUBxfNsCXsnJ3Sf0yqZQaKIHKFc5QmmOo9eQpSDo0LpHZ4706KB+LdW/fqxTNU2x9IaIKcA1FAyJiIuIiF2vhaYOWHQARca8cSLZrTMnCJ2K8cPSKBvDwbQ4xHPok9UrcML59xZ/TKsHh9LXII8u5gxX1z+IwTXyt0BpTTvStwICeaFrwJjxszcfsxiTrDqAgnJC46/nc0HFyGwkPAXKFVYhKHYSlKEXwcXdlHgPqy4rK6unjvXv79OrpfK6O0isnidxLVVCh4HU1tP4h52AI5IWuPiNmvT+pm+sBmRaDqA4QU2Hgn2Zupa696Cpy7Q+XYLY8NYEV9x7d0B49D8tDfunoe0AipMnT7JKsmPi1Q3Vm0EC0uN1wNNQS53Aoc/CkQsObR8+fDi0WoeRA1pJme4fqKq7vxeUxd3+SC0dqCRH5OwdNf/zG4z2NynMLoOWAMVEccAb81/kS10TmdQOtgYBuT1f6pY4ZOLCFzvTeYpOjYCn2fFBqFtd2dVEg0Ye3dl3B5lClbRsvv1OBw+/5LjMs116qrzLDmglY+4Y16b71+eAaTGL0Gueg2abQu3ksPl22eI+AzMTdpyzyT0Jmzmgle9zdmFXDm8IwRUy48vTdHebzcHmCqvBUP+WY+eU4x4UefqdhRsM8JBNsLkDnobacr9+YKmvsrYiGKzrgSChGUQQBur1eRcWgjq23pVGOAKqtm4AticoilaCMV5K3QMQOHleeGlmyt0RI0bYbp1sB4v1G3Kc1Sdy50vzAAAAAElFTkSuQmCC";
var gd_icon_svg = '';
var launch_icon = '';
// Ensure jquery
if (window.jQuery) {
} else {
var script = document.createElement('script');
script.src = 'https://code.jquery.com/jquery-2.1.4.min.js';
script.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(script);
}
function loadCityIndex(globals) {
// Settings
var world = Game.world_id;
var gd_settings = {
inbox: true,
forum: true,
stats: true,
context: true,
keys_enabled: true,
command_share: true,
command_cancel_time: true,
forum_reactions: true,
bug_reports: true,
key_inbox_prev: '[',
key_inbox_next: ']',
};
readSettingsCookie();
setTimeout(function () {
if (gd_settings.inbox === true || gd_settings.forum === true || gd_settings.forum_reactions === true) {
loadIndexHashlist(false, true, false); // Get list of recently indexed report ids
}
}, 300);
checkLogin(false);
// Check for other scripts
var molehole_active = false;
try {
molehole_active = document.body.innerHTML.includes("grmh.pl")
} catch (e) {}
// Set locale
var translate = {
ADD: 'Index',
SEND: 'sending..',
ADDED: 'Indexed',
ERROR: 'Error',
VIEW: 'View intel',
CMD_SHARE_UPLOAD: 'Share with team',
CMD_SHARE_SYNCED: 'Synchronized 👍',
CMD_SHARE_NONE: 'No commands',
CMD_SHARE_NONEW: 'No new commands 👍',
TOWN_INTEL: 'Town intelligence',
STATS_LINK: 'Show buttons that link to player/alliance statistics on grepodata.com',
STATS_LINK_TITLE: 'Link to statistics',
CHECK_UPDATE: 'Check for updates',
ABOUT: 'This tool allows you to collect and browse enemy city intelligence. You can also join a private GrepoData team to share the collected intel with your allies',
INDEX_LOGGED_IN: 'You are currently signed in as',
INDEX_LOGGED_OUT: 'You are currently not signed in.',
COUNT_1: 'You have contributed ',
COUNT_2: ' reports in this session',
SHORTCUTS: 'Keyboard shortcuts',
SHORTCUTS_ENABLED: 'Enable keyboard shortcuts',
SHORTCUTS_INBOX_PREV: 'Previous report (inbox)',
SHORTCUTS_INBOX_NEXT: 'Next report (inbox)',
MY_TEAMS: 'Your teams on world ',
MY_TEAMS_CONTRIBUTE: 'If you enable the contribute checkbox, newly indexed reports and uploaded commands will be shared with the respective team.',
TEAM_NAME: 'Team name',
TEAM_ROLE: 'Your role',
TEAM_CONTRIBUTE: 'Contribute',
TEAM_ACTION: 'Action (opens in new tab)',
TEAM_ACTION_OVERVIEW: 'View team overview',
COLLECT_INTEL: 'Collecting intel',
COLLECT_INTEL_INBOX: 'Inbox (adds an "index+" button to inbox reports)',
COLLECT_INTEL_FORUM: 'Alliance forum (adds an "index+" button to alliance forum reports)',
SHORTCUT_FUNCTION: 'Function',
SAVED: 'Settings saved',
SHARE: 'Share',
FORUM_REACTIONS_TITLE: 'Forum reactions',
FORUM_REACTIONS_INFO: 'Add team reactions to the in-game alliance forum. All users on the same GrepoData team can see eachothers reactions.',
CMD_OVERVIEW_TITLE: 'Share command overview',
CMD_DEPARTURE_INFO: 'Add the return and cancel time to your own movements and add a link to town intel for incoming enemy movements.',
CMD_SHARING_INFO: 'Add a button to share your commands with your GrepoData teams.',
CONTEXT_TITLE: 'Expand context menu',
CONTEXT_INFO: 'Add an intel shortcut to the town context menu. The shortcut opens the intel for this town.',
BUG_REPORTS: 'Upload anonymous bug reports to help improve our script.',
SETTINGS_OTHER: 'Miscellaneous settings',
DEPARTED_FROM: 'Departed from',
RUNTIME_CANCELABLE: 'Cancellable until',
RUNTIME_RETURNS: 'Returns at',
INTEL_NOTE_TITLE: 'Notes',
INTEL_NOTE_NONE: 'There are no notes for this town',
INTEL_UNITS: 'Units',
INTEL_SHOW_PLAYER: 'Player intel',
INTEL_SHOW_ALLIANCE: 'Alliance intel',
};
if ('undefined' !== typeof Game) {
switch (Game.locale_lang.substring(0, 2)) {
case 'pt':
translate = {
Titre: 'GrepoData configurações do indexador de cidade',
ADD: 'Indexar',
SEND: 'enviando..',
ADDED: 'indexado',
ERROR: 'Erro',
VIEW: 'Ver Intel',
CMD_SHARE_UPLOAD: 'Compartilhar com a equipe',
CMD_SHARE_SYNCED: 'sincronizado',
CMD_SHARE_NONE: 'Sem pedidos',
CMD_SHARE_NONEW: 'Nenhum novo pedido',
TOWN_INTEL: 'Cidade intel',
STATS_LINK: 'Adicione botões que apontam para as estatísticas do jogador/aliança em grepodata.com',
STATS_LINK_TITLE: 'Link para estatísticas',
CHECK_UPDATE: 'Verifique se há atualizações',
ABOUT: 'Esta ferramenta permite que você colete informações sobre as cidades inimigas. Você também pode se juntar a uma equipe GrepoData para compartilhar as informações coletadas com seus companheiros de aliança',
INDEX_LOGGED_IN: 'Você está logado como',
INDEX_LOGGED_OUT: 'No momento você não está logado.',
COUNT_1: 'Você já tem ',
COUNT_2: ' relatórios coletados nesta sessão',
SHORTCUTS: 'Atalhos do teclado',
SHORTCUTS_ENABLED: 'Ativar atalhos de teclado',
SHORTCUTS_INBOX_PREV: 'Relatório anterior (inbox)',
SHORTCUTS_INBOX_NEXT: 'Próximo relatório (inbox)',
MY_TEAMS: 'Suas equipes no mundo ',
MY_TEAMS_CONTRIBUTE: 'Se você ativar a caixa de seleção contribuir, os relatórios recém-indexados e os comandos carregados serão compartilhados com a respectiva equipe.',
TEAM_NAME: 'Nome do time',
TEAM_ROLE: 'Seu papel',
TEAM_CONTRIBUTE: 'Contribuições',
TEAM_ACTION: 'Ação (nova aba)',
TEAM_ACTION_OVERVIEW: 'Visão geral da equipe',
COLLECT_INTEL: 'Intel coletar',
COLLECT_INTEL_INBOX: 'Inbox (adiciona um botão "index+" aos relatórios da caixa de entrada)',
COLLECT_INTEL_FORUM: 'Fórum da aliança (adiciona um botão "index+" aos relatórios do fórum da aliança)',
SHORTCUT_FUNCTION: 'Função',
SAVED: 'Configurações salvas',
SHARE: 'Compartilhar',
FORUM_REACTIONS_TITLE: 'Comentários do fórum',
FORUM_REACTIONS_INFO: 'Adicione comentários da equipe no fórum da aliança. Todos os membros de uma equipe GrepoData podem ver os comentários de uns dos outros no fórum.',
CMD_OVERVIEW_TITLE: 'Compartilhar resumo do comando',
CMD_DEPARTURE_INFO: 'Adicione o tempo de cancelamento e retorno aos seus próprios comandos. Adicione um link de inteligência aos comandos inimigos.',
CMD_SHARING_INFO: 'Adicionar botão para fazer upload de pedidos para suas equipes GrepoData.',
CONTEXT_TITLE: 'Expandir menu de contexto',
CONTEXT_INFO: 'Adicione um atalho Intel ao menu de contexto ao clicar em uma cidade. O atalho refere-se à informação acumulada da cidade.',
BUG_REPORTS: 'Carregue relatórios de bugs anônimos para melhorar o script.',
SETTINGS_OTHER: 'Outros ajustes',
DEPARTED_FROM: 'Enviado de',
RUNTIME_CANCELABLE: 'Cancelável até',
RUNTIME_RETURNS: 'Voltar em',
INTEL_NOTE_TITLE: 'Notas',
INTEL_NOTE_NONE: 'Ainda não há notas para esta cidade',
INTEL_UNITS: 'Unidades',
INTEL_SHOW_PLAYER: 'Inteligência do jogador',
INTEL_SHOW_ALLIANCE: 'Aliança Intel',
};
break;
default:
break;
}
}
// Scan for inbox reports
function parseInbox() {
if (gd_settings.inbox === true) {
parseInboxReport();
}
}
setInterval(parseInbox, 500);
// Listen for game events
var last_hashlist_refresh = Date.now();
$(document).ajaxComplete(function (e, xhr, opt) {
try {
var url = opt.url.split("?"), action = "";
if (typeof(url[1]) !== "undefined" && typeof(url[1].split(/&/)[1]) !== "undefined") {
action = url[0].substr(5) + "/" + url[1].split(/&/)[1].substr(7);
}
if (verbose) {
console.log(action);
}
switch (action) {
case "/report/view":
// Parse reports straight from inbox
parseInbox();
break;
case "/town_info/info":
viewTownIntel(xhr);
break;
case "/message/view": // catch inbox previews
case "/message/preview": // catch inbox messages
case "/alliance_forum/forum": // catch forum messages
// Reload hashlist if last refresh was more than 10 minutes ago
if (Date.now() - last_hashlist_refresh >= 10 * 60 * 1000) {
last_hashlist_refresh = Date.now();
loadIndexHashlist(false, false, false);
}
// Parse reports from forum and messages
if (gd_settings.forum === true) {
setTimeout(parseForumReport, 200);
}
// Add reactions to posts
if (gd_settings.forum_reactions === true) {
setTimeout(parseForumTopicReactions, 20);
}
break;
case "/player/index":
settings();
break;
case "/player/get_profile_html":
case "/alliance/profile":
linkToStats(action, opt);
break;
case "/town_overviews/command_overview":
if (gd_settings.command_share === true) {
setTimeout(_ => {parseCommandOverview(xhr)}, 20);
}
if (gd_settings.command_cancel_time === true) {
setTimeout(enhanceCommandOverview, 50);
}
break;
}
} catch (error) {
errorHandling(error, "handleAjaxCompleteObserver");
}
});
function getCommandId(command) {
// command hash format: {command.id}_{command.return}_{command.power_id}
// command.id: id is unique for each command and is consistent between sessions
// command.arrival_at: arrival_at helps to track changes staged commands (e.g. colonization)
// command.return: returning commands have the same id, so we need to use return to see if it is a returning command
// command.power_id: power_id is used to see if changes have been made such as wisdom or sea storm
return command.id + '_' + command.arrival_at + '_' + command.return + '_' + command.power_id;
}
function updateOpsSyncButton(text, enabled = true, status = 'ok', callback = (_ => { return 1 })) {
$('#gd_cmd_vrvw_share_txt').html(text);
if (enabled && callback != null) {
$('#gd_cmd_vrvw_share').removeClass("disabled");
$('#gd_cmd_vrvw_share').unbind("click").bind("click", callback);
} else if (!enabled) {
$('#gd_cmd_vrvw_share').addClass("disabled");
$('#gd_cmd_vrvw_share').off("click");
}
if (status == 'ok') {
$('#gd_cmd_vrvw_share_txt').css("color", '#36cd5b');
} else if (status == 'error') {
$('#gd_cmd_vrvw_share_txt').css("color", '#ff5252');
} else {
$('#gd_cmd_vrvw_share_txt').css("color", '#fc6');
}
}
function showSyncStatusToast(toastHtml, timeout = 20000) {
if ($('#gd_cmd_vrvw_toast').length == 0) {
$('#place_defense').append(``)
}
$('#gd_cmd_vrvw_toast').html(toastHtml);
$('#gd_cmd_vrvw_toast').show();
setTimeout(function() { $("#gd_cmd_vrvw_toast").hide(); }, timeout);
}
let is_filtered_upload = false;
function parseCommandOverview(xhr) {
try {
if (xhr === undefined || !('responseText' in xhr) || xhr.responseText === undefined) {
// Request aborted or failed
return;
}
var xhr_data = JSON.parse(xhr.responseText);
if (!('json' in xhr_data) || !('data' in xhr_data.json) || !('commands' in xhr_data.json.data) || xhr_data.json.data.commands === undefined) {
// if no advisor or city is being conquered, then commands is undefined
return;
}
var commands = xhr_data.json.data.commands;
var current_time = Date.now() / 1000;
verbose ? console.log('========================') : null;
verbose ? console.log('parsing command overview', commands) : null;
if ($('#gd_cmd_vrvw_share').length == 0) {
let share_html = `
Select the teams you want to share with, then press upload.
';
sharing_dialog += 'Team Upload yes/no Share commands yes/no ';
// for each user team
let team_keys = []
for (var j = 0; j < Object.keys(globals.active_teams).length; j++) {
let team = globals.active_teams[j];
team_keys.push(team.key);
if (!(team.key in cmd_upload_settings)) {
// Init team settings on first run
let do_share = (team.contribute == 1 && team.role !== 'read')
// See if we have cached settings for this team
let cached_settings = {};
if (do_share && 'cmd_team_settings' in gd_settings && team.key in gd_settings.cmd_team_settings) {
cached_settings = gd_settings.cmd_team_settings[team.key]
}
cmd_upload_settings[team.key] = {
do_share: 'do_share' in cached_settings ? cached_settings.do_share : do_share,
attacks: 'attacks' in cached_settings ? cached_settings.attacks : do_share,
supports: 'supports' in cached_settings ? cached_settings.supports : do_share,
returns: 'returns' in cached_settings ? cached_settings.returns : do_share,
}
}
// create table row
sharing_dialog += `${team.name} `;
if (team.role === 'read') {
sharing_dialog += 'You do not have permission to write to this team. ';
} else {
sharing_dialog += `
Attacks & spies
Supports
Returns
`
}
sharing_dialog += '';
}
sharing_dialog += '
';
// Select all
let show_select_all = team_keys.length > 1;
sharing_dialog += ` `;
// Show dialog
cmd_upload_dialog = Layout.wnd.Create(
GPWindowMgr.TYPE_DIALOG,
"GrepoData Team Operations",
{width: 400, height: 220, minimizable: false}
)
// set content
cmd_upload_dialog.setContent(sharing_dialog);
try {
dialog_z_index += 41
cmd_upload_dialog.setZIndex(dialog_z_index)
} catch (e) {}
// Checkbox click handlers
let select_all = false;
let options = ['do_share', 'attacks', 'supports', 'returns'];
team_keys.forEach(team => {
options.forEach(option => {
$(".cmd_cbx_"+team+"_"+option).click(function () {
let value = !cmd_upload_settings[team][option];
cmdCbx(team, option, value);
if (value == false && show_select_all) {
select_all = false;
$(".cmd_cbx_all").get(0).classList.remove("checked");
}
});
})
})
if (show_select_all) {
// select all
$(".cmd_cbx_all").click(function () {
select_all = !select_all
team_keys.forEach(team => {
cmdCbx(team, 'do_share', select_all)
})
if (select_all) {
$(".cmd_cbx_all").get(0).classList.add("checked");
} else {
$(".cmd_cbx_all").get(0).classList.remove("checked");
}
});
}
// Do upload handler
$(".gd_cmd_do_upload").click(function () {
verbose ? console.log('uploading with settings: ', cmd_upload_settings) : null;
// Save settings for next time
gd_settings.cmd_team_settings = cmd_upload_settings;
saveSettings();
// Do upload
uploadCommands(new_commands, missing_tracked_commands, missing_tracked_command_ids, cmd_upload_settings);
cmd_upload_dialog.close();
});
}
function cmdCbx(team, option, value) {
// Update class
if (value === true) {
$(".cmd_cbx_"+team+"_"+option).get(0).classList.add("checked");
}
else {
$(".cmd_cbx_"+team+"_"+option).get(0).classList.remove("checked");
}
// Set value
cmd_upload_settings[team][option] = value;
// Group overrides
if (option === 'do_share') {
cmdCbx(team, 'attacks', value);
cmdCbx(team, 'supports', value);
cmdCbx(team, 'returns', value);
} else if (value === true) {
$(".cmd_cbx_"+team+"_do_share").get(0).classList.add("checked");
cmd_upload_settings[team]['do_share'] = true;
}
}
var tracked_commands = {}; // dict format: {getCommandId(command): {arrival_at: command.arrival_at, id: command.id}}
let uploading_commands = false;
function uploadCommands(new_commands, del_commands, del_commands_ids, share_settings=null) {
if (uploading_commands) {
console.log('already uploading')
return;
}
uploading_commands = true;
updateOpsSyncButton('Uploading â™»', true, '', null);
let share_settings_encoded = '';
if (share_settings!=null) {
share_settings_encoded = JSON.stringify(share_settings)
// Check if upload was clean; if not, we can not trust that tracked id list is accurate
try {
is_filtered_upload = Object.keys(share_settings).reduce((prev, team) => prev + share_settings[team].attacks==false + share_settings[team].supports==false + share_settings[team].returns==false + share_settings[team].do_share==false, 0);
verbose ? console.log('is filtered upload: ', is_filtered_upload, share_settings) : null;
} catch (e) {
is_filtered_upload = true;
}
}
getAccessToken().then(access_token => {
if (access_token === false) {
HumanMessage.error('GrepoData: login required to upload command overview');
uploading_commands = false;
updateOpsSyncButton('Login required', true, 'error', null);
showLoginPopup();
} else {
var data = {
'access_token': access_token,
'world': Game.world_id,
'del_commands': JSON.stringify(del_commands),
'commands': JSON.stringify(new_commands),
'share_settings': share_settings_encoded,
'player_name': Game.player_name || '',
'player_id': Game.player_id || 0,
'alliance_id': Game.alliance_id || 0
};
$.ajax({
url: "https://api.grepodata.com/commands/upload",
data: data,
type: 'post',
crossDomain: true,
dataType: 'json',
success: function (data) {
console.log('upload complete', data);
if ('error_code' in data) {
switch (data.error_code) {
case 7201:
// no teams
HumanMessage.error('GrepoData: you are not part of any GrepoData teams on this world. Join or create a team to share your command overview.');
updateOpsSyncButton('Not in a team', true, 'error', null);
break;
default:
HumanMessage.error('GrepoData: unexpected error. Please try again later or contact us if this error persists.');
updateOpsSyncButton('Error. Try again', true, 'error', null);
}
} else {
if ('added_teams' in data && data.added_teams.length <= 0) {
HumanMessage.error('GrepoData: you are not contributing to any teams. Enable contributions to synchronize your command overview');
updateOpsSyncButton(translate.CMD_SHARE_UPLOAD, true, '', null);
} else {
// Get list of new command ids, their arrival and their real id
var new_command_ids = {}
new_commands.forEach(command => {
new_command_ids[getCommandId(command)] = {
arrival_at: command.arrival_at,
id: command.id
}
});
verbose ? console.log('new commands ids: ', new_command_ids) : null;
// add uploaded commands to tracked commands
tracked_commands = Object.assign({}, tracked_commands, new_command_ids);
// Remove deleted commands from tracked commands
tracked_commands = Object.keys(tracked_commands).reduce(function (filtered, command_id) {
if (del_commands_ids.indexOf(command_id) < 0) filtered[command_id] = tracked_commands[command_id];
return filtered;
}, {});
// Show operation link toast
if (data.added_teams.length == 1) {
// Show notification with link to Ops center
let team = data.added_teams[0];
toastHtml = `View Operation`;
} else {
// Show notification with link to Ops center
toastHtml = `View Operations`;
}
showSyncStatusToast(toastHtml);
updateOpsSyncButton(translate.CMD_SHARE_SYNCED, true, 'ok', null);
}
verbose ? console.log('synced command ids: ', tracked_commands) : null;
}
uploading_commands = false;
},
error: function (jqXHR, textStatus) {
console.error("error saving commands", jqXHR);
if (jqXHR && 'status' in jqXHR && jqXHR.status == 503 && jqXHR.responseJSON && 'message' in jqXHR.responseJSON) {
HumanMessage.error('GrepoData: '+jqXHR.responseJSON.message);
updateOpsSyncButton('Service Unavailable', false, 'error', null);
} else {
HumanMessage.error('GrepoData: unexpected error. Please try again later or contact us if this error persists.');
updateOpsSyncButton('Error. Try again', true, 'error', null);
}
uploading_commands = false;
},
timeout: 120000
});
}
});
}
var threadReactions = {};
function parseForumTopicReactions() {
/**
* This function adds reactions to in-game forum posts
* Post id's are persistent and unique within each game world
* This allows users to react to forum posts and see eachothers reactions, as long as they are part of the same GrepoData team.
*/
try {
var thread_id = $('#forum_thread_id_input').val()
if (!thread_id || !user_has_team || isNaN(thread_id)) {
return;
}
// Only load thread reactions if thread is active or active threads are unknown
if (globals.active_threads === undefined || globals.active_threads.includes(parseInt(thread_id))) {
if (verbose) {
console.log("Loading reactions for active thread: " + thread_id)
}
// Load thread reactions before parsing posts
threadReactions = {};
getAccessToken().then(access_token => {
if (access_token === false) {
HumanMessage.error('GrepoData: login required to use forum reactions');
// Die graceful without popup
// showLoginPopup();
} else {
var data = {
'world': world,
'thread_id': thread_id,
'access_token': access_token
};
$.ajax({
url: backend_url + "/reactions/thread",
data: data,
type: 'get',
crossDomain: true,
dataType: 'json',
success: function (data) {
if (data && 'success' in data) {
threadReactions = data.posts;
renderForumReactions(thread_id);
}
},
error: function (jqXHR, textStatus) {
console.log("error getting forum reactions");
},
timeout: 120000
});
}
});
} else {
// Allow new reactions but skip loading previous reactions because this post is not active
renderForumReactions(thread_id);
}
} catch (error) {
errorHandling(error, "parseForumTopicReactions");
}
}
var emojilist = [
128077, // Thumbs up
128078, // Thumbs down
128516, // happy eyes
128533, // unhappy
128525, // love
127881, // party popper
128640, // rocket
128064, // eyes
129315, // rofl
// 128512, // happy
// 128528, // poker face
// 128533, // unhappy
// 129300, // think
// 128517, // cold sweat
// 128525, // love
// 128540, // crazy face
]
function renderForumReactions(thread_id) {
try {
// Popup html
$('#postlist').prepend(`
`)
// Click outside closes our popup
$('#postlist').click(function () {$('#gd_new_reactions').hide();})
$('#gd_new_reactions').click(function (event) {event.stopPropagation();})
// Show more info dialog
$('#gd_react_more_info').click(forumReactionsInfo);
// Close reactions popup
$('#gd_react_close').click(function () {$('#gd_new_reactions').hide();});
// Populate reaction popup
for (var i = 0; i < emojilist.length; i++) {
var emoteHtml = `${emojilist[i]};`
$('#gd_new_reactions_options').append(emoteHtml);
$(`#gd_react_new_${emojilist[i]}`).click(function () {
var reaction = $(this).data('emote')
addPostReaction(thread_id, active_react_post, reaction);
$('#gd_new_reactions').hide();
})
}
// Parse forum posts
$('#postlist>li').each(function () {
// Get post features
var post_id = this.id
var post_id = post_id.replace(/\D/g,'')
var data = {}
if (post_id in threadReactions) {
data = threadReactions[post_id]
}
renderPostReactions(thread_id, post_id, data)
});
} catch (error) {
errorHandling(error, "renderForumReactions");
}
}
var active_react_post = null;
function renderPostReactions(thread_id, post_id, data = {}) {
/**
* Renders the reactions for the given post
*/
try {
var post_header = $('#post_' + post_id).find('.author').eq(0);
$(`#gd_react_${post_id}`).remove();
//Primary container
var alignment_class = Object.keys(data).length > 0 ? 'gd_react_top' : ''
reactionsHtml = `
`;
post_header.append(reactionsHtml);
// Add each emote
for (var i = emojilist.length-1; i >= 0; i--) {
var emote = emojilist[i]
if (!(emote in data)) {
continue;
}
var player_list = data[emote].players.join(", ");
var num_players = data[emote].players.length;
var react_class = 'gd_react_box' + (data[emote].active ? ' active':'');
var emote_html = `${emote}; ${num_players}`
$(`#gd_react_${post_id}`).prepend(emote_html);
$(`#gd_reactions_${post_id}_${emote}`).tooltip(`${player_list} reagiu com ${emote};`);
$(`#gd_reactions_${post_id}_${emote}`).click(function () {
var toggled = !($(this).hasClass('active') ? true : false);
var count = $(this).find('.count').get(0).innerText;
var new_count = parseInt(count) + (toggled ? 1 : -1);
if (new_count <= 0) {
$(this).remove();
} else {
$(this).find('.count').get(0).innerText = new_count;
$(this).toggleClass('active', toggled);
}
var emote = $(this).data('emote');
if (toggled) {
addPostReaction(thread_id, post_id, emote);
} else {
deletePostReaction(thread_id, post_id, emote);
}
});
}
// Listerner for new reaction popup
$(`#gd_reactions_add_${post_id}`).click(function (event) {
event.stopPropagation();
active_react_post = post_id;
$(`#gd_new_reactions`).show();
$("#gd_new_reactions").css({top: event.target.offsetParent.offsetTop + 27});
});
} catch (error) {
errorHandling(error, "renderPostReactions");
}
}
function forumReactionsInfo() {
var content = 'Forum reactions powered by GrepoData
' +
' - Você pode deixar reações às postagens do fórum porque instalou o indexador de cidade GrepoData usercript
' +
' - Os membros da sua equipe podem ver suas reações e você pode ver as deles, desde que eles também tenham o userscript GrepoData instalado e se você faça parte da mesma equipe GrepoData
' +
'
' +
'Click aqui para desativar as reações do fórum
' +
' ' +
'
Obrigado por usar GrepoData!';
Layout.wnd.Create(GPWindowMgr.TYPE_DIALOG).setContent(content)
$('#gd-disable-forum-reactions').click(function () {
gd_settings.forum_reactions = false;
saveSettings();
$('#gd-disabled-forum-reactions').show();
$('#gd-disable-forum-reactions').hide();
$('.gd_react_container').hide();
$('.gd_new_reactions').hide();
})
}
function addPostReaction(thread_id, post_id, reaction) {
try {
getAccessToken().then(access_token => {
if (access_token !== false) {
$.ajax({
url: backend_url + "/reactions/new",
data: {
access_token: access_token,
reaction: reaction,
world: Game.world_id,
player_id: Game.player_id,
thread_id: thread_id,
post_id: post_id
},
type: 'get',
crossDomain: true,
dataType: 'json',
timeout: 30000
}).fail(function (err) {
console.log("Error adding reaction: ", err);
}).done(function (response) {
if ('success' in response) {
var new_reactions = response.posts[post_id];
threadReactions[post_id] = new_reactions;
renderPostReactions(thread_id, post_id, threadReactions[post_id]);
}
});
} else {
showLoginPopup();
}
});
globals.active_threads.push(parseInt(thread_id));
} catch (error) {
errorHandling(error, "addPostReaction");
}
}
function deletePostReaction(thread_id, post_id, reaction) {
try {
getAccessToken().then(access_token => {
if (access_token !== false) {
$.ajax({
url: backend_url + "/reactions/delete",
data: {
access_token: access_token,
reaction: reaction,
world: Game.world_id,
thread_id: thread_id,
post_id: post_id
},
type: 'get',
crossDomain: true,
dataType: 'json',
timeout: 30000
}).fail(function (err) {
console.log("Error deleting reaction: ", err);
}).done(function (response) {
if ('success' in response) {
var new_reactions = response.posts[post_id];
threadReactions[post_id] = new_reactions;
renderPostReactions(thread_id, post_id, threadReactions[post_id]);
}
});
} else {
showLoginPopup();
}
});
} catch (error) {
errorHandling(error, "deletePostReaction");
}
}
var parsedCommands = {};
function enhanceCommandOverview() {
try {
// Parse overview
if (MM.getModels().MovementsUnits) {
var commandList = $('#command_overview');
var commands = $(commandList).find('li');
var parseLimit = 100; // Limit number of parsed commands
let movements = Object.values(MM.getModels().MovementsUnits);
commands.each(function (c) {
if (c>=parseLimit) {
return
}
try {
var command_id = this.id;
if (!command_id) {
return
}
command_id = command_id.replace(/[^\d]+/g, '');
if (!(command_id in parsedCommands)) {
var cmd_units = $(this).find('.command_overview_units');
if (cmd_units.length != 0) {
parsedCommands[command_id] = {
is_enemy: false,
movement_id: 0
};
} else {
// Command is incoming enemy, parse ids
var cmd_span = $(this).find('.cmd_span').get(0);
var cmd_entities = $(cmd_span).find('a');
if (cmd_entities.length == 4) {
var command_info = {
source_town: decodeHashToJson(cmd_entities.get(0).hash),
source_player: decodeHashToJson(cmd_entities.get(1).hash),
target_town: decodeHashToJson(cmd_entities.get(2).hash),
target_player: decodeHashToJson(cmd_entities.get(3).hash),
is_enemy: true,
movement_id: 0
};
parsedCommands[command_id] = command_info;
} else {
parsedCommands[command_id] = {
is_enemy: false,
movement_id: 0
};
}
}
movements.map(movement => {
if (command_id == movement.attributes.command_id && parsedCommands[command_id].movement_id === 0) {
parsedCommands[command_id].movement_id = movement.id
}
});
}
enhanceCommand(command_id);
} catch (error) {
errorHandling(error, "enhanceCommandOverviewParseCommand");
}
});
}
} catch (error) {
errorHandling(error, "enhanceCommandOverview");
}
}
function enhanceCommand(id, force=false) {
try {
var cmd = parsedCommands[id];
var cmdInfoBox = $('#command_'+id).find('.cmd_info_box');
var returnsElem = document.getElementById('gd_runtime_'+id);
if (!returnsElem && gd_settings.command_cancel_time === true && cmd.movement_id > 0) {
var movement = MM.getModels().MovementsUnits[cmd.movement_id];
if (movement && movement.attributes) {
var runtimeHtml = '(';
var returnText = '';
var cancelText = '';
var bHasReturnTime = false;
var bHasCancelTime = false;
if (!movement.isIncommingMovement() && movement.attributes.hasOwnProperty('started_at') && movement.getType() != 'support') {
bHasReturnTime = true;
var returns = getReturnTimeFromMovement(movement);
returnText = translate.RUNTIME_RETURNS + ' '+returns.return_readable;
}
if (movement.attributes.hasOwnProperty('cancelable_until') && movement.attributes.cancelable_until != null && movement.attributes.cancelable_until > 0) {
var diff = movement.attributes.cancelable_until - Date.now() / 1000;
if (diff>0) {
bHasCancelTime = true;
var cancelable_until = getHumanReadableDateTime(movement.attributes.cancelable_until, false);
cancelText = translate.RUNTIME_CANCELABLE + ' ' + cancelable_until;
}
}
if (bHasReturnTime || bHasCancelTime) {
if (bHasCancelTime) {
runtimeHtml = runtimeHtml + cancelText;
} else {
runtimeHtml = runtimeHtml + returnText;
}
runtimeHtml = runtimeHtml + ')';
cmdInfoBox.append(runtimeHtml);
} else if (verbose) {
console.log("no times found", movement);
}
}
}
// Insert intel link
var cmd_units = document.getElementById('gd_cmd_units_'+id);
if ((!cmd_units || force) && gd_settings.command_cancel_time === true && cmd.is_enemy === true) {
if (cmd_units && force) {
$('#gd_cmd_units_'+id).remove();
}
// show a shortcut to view town intel
var units = '';
cmdInfoBox.after(units);
$('#gd_cmd_units_'+id).click(function () {
loadTownIntel(cmd.source_town.id, cmd.source_town.name, cmd.source_player.name, id);
});
}
} catch (error) {
errorHandling(error, "enhanceCommand");
}
}
function getReturnTimeFromMovement(movement) {
var arrival_time = movement.attributes.arrival_at;
var departure_time = movement.attributes.started_at;
var returns_at = arrival_time + (arrival_time - departure_time);
return {
arrival_time: arrival_time,
returns_at: returns_at,
return_readable: getHumanReadableDateTime(returns_at, false),
};
}
function getHumanReadableDateTime(timestamp, includeDate = true) {
var time = dateFromTimestamp(timestamp);
var hours = time.getUTCHours(),
minutes = time.getUTCMinutes(),
seconds = time.getUTCSeconds(),
day = time.getUTCDate(),
month = time.getUTCMonth() + 1,
year = time.getUTCFullYear();
if (hours < 10) {
hours = '0' + hours;
}
if (minutes < 10) {
minutes = '0' + minutes;
}
if (seconds < 10) {
seconds = '0' + seconds;
}
if (day < 10) {
day = '0' + day;
}
if (month < 10) {
month = '0' + month;
}
return (includeDate?(day + '/' + month + '/' + year + ' '):'') + hours + ':' + minutes + ':' + seconds;
}
function dateFromTimestamp(timestamp) {
return new Date((timestamp + Game.server_gmt_offset) * 1000);
}
function readSettingsCookie() {
try {
var settingsJson = getLocalToken('globals_s');
if (!settingsJson) {
console.log('no local settings', settingsJson);
return false;
}
settingsJson = decodeHashToJson(settingsJson);
if (settingsJson != null) {
result = JSON.parse(settingsJson);
if (result != null) {
result.forum = result.forum === false ? false : true;
result.inbox = result.inbox === false ? false : true;
if (!('stats' in result)) {
result.stats = true;
}
if (!('context' in result)) {
result.context = true;
}
if (!('forum_reactions' in result)) {
result.forum_reactions = true;
}
if (!('command_share' in result)) {
result.command_share = true;
}
if ('departure_time' in result && !('command_cancel_time' in result)) {
result.command_cancel_time = result.departure_time;
} else if (!('command_cancel_time' in result)) {
result.command_cancel_time = true;
}
if (!('bug_reports' in result)) {
result.bugreports = true;
}
if (!('cmd_team_settings' in result)) {
result.cmd_team_settings = {};
}
gd_settings = result;
}
}
} catch (error) {
errorHandling(error, "readSettingsCookie");
}
}
// Expand context menu
$.Observer(GameEvents.map.town.click).subscribe(async (e, data) => {
try {
if (gd_settings.context && data && data.id) {
if (!data.player_id || data.player_id != Game.player_id) {
expandContextMenu(data.id, (data.name?data.name:''), (data.player_name?data.player_name:''));
}
}
} catch (error) {
errorHandling(error, "handleMapTownObserver");
}
});
$.Observer(GameEvents.map.context_menu.click).subscribe(async (e) => {
try {
if (gd_settings.context && e.currentTarget && e.currentTarget.activeElement && e.currentTarget.activeElement.hash) {
var hash = e.currentTarget.activeElement.hash;
if (hash==='#confirm' || hash==='#setMax' || hash==='#show_sidebar') {
return false;
}
var data = decodeHashToJson(hash);
if (data.id && data.name) {
expandContextMenu(data.id, data.name, '');
}
}
} catch (error) {
var hash = '';
try {
hash = e.currentTarget.activeElement.hash;
} catch (e) {}
errorHandling(error, "handleContextMenuObserver", {hash: hash});
}
});
function expandContextMenu(town_id, town_name, player_name = '') {
var intelHtml = ' ';
var menuItems = $("#context_menu").find('.context_icon');
if (!menuItems || menuItems.length >= 5) {
$("#context_menu").append(intelHtml);
$("#gd_context_intel").animate({top: (menuItems.length>5?100:120)+'px'}, 120);
//$("#gd_context_intel").animate({left: '140px'}, 120);
$('#gd_context_intel').click(function() {
loadTownIntel(town_id, town_name, player_name);
});
}
}
function setCookie(name,value,days) {
verbose ? console.log('setting cookie', name, value, days) : null;
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
document.cookie = name +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
}
function getLocalToken(name) {
try {
var local_value = localStorage.getItem(name);
if (local_value) {
return local_value;
}
var local_value = getCookie(name);
if (local_value) {
return local_value;
}
} catch (error) {
errorHandling(error, "getLocalToken", {name: name});
}
return null;
}
function setLocalToken(name, value) {
try {
setCookie(name, value, 1000);
localStorage.setItem(name, value);
} catch (error) {
errorHandling(error, "setLocalToken", {name: name, value: value});
}
}
function deleteLocalToken(name) {
try {
localStorage.removeItem(name);
eraseCookie(name);
} catch (e) {}
}
function getAccessToken(force_refresh = false) {
return new Promise(resolve => {
try {
// Get access token from local storage
var access_token = getLocalToken('gd_indexer_access_token');
if (!access_token) {
resolve(false);
}
// if timed out, get new access token using refresh token
let payload = parseJwt(access_token);
if (payload && payload.hasOwnProperty('exp')) {
let expiration = payload['exp'];
let currentTime = new Date().getTime() / 1000;
if ((currentTime > expiration - 60) || force_refresh===true) {
// Token expired, try to refresh
console.log("GrepoData: Access token expired.");
var refresh_token = getLocalToken('gd_indexer_refresh_token');
if (!refresh_token) {
// New login required
deleteLocalToken('gd_indexer_access_token');
resolve(false);
}
// Get new access token
$.ajax({
url: backend_url + "/auth/refresh",
data: {refresh_token: refresh_token},
type: 'post',
crossDomain: true,
dataType: 'json',
success: function (data) {
if (data.success_code && data.success_code === 1101) {
console.log('GrepoData: Renewed access token.');
setLocalToken('gd_indexer_access_token', data.access_token);
setLocalToken('gd_indexer_refresh_token', data.refresh_token);
resolve(data.access_token);
} else {
resolve(false);
}
},
error: function (jqXHR, textStatus) {
console.log("GrepoData: Error renewing access token");
// New login required
deleteLocalToken('gd_indexer_access_token');
errorHandling(null, "refreshAccessToken", JSON.stringify({xhr: jqXHR, text: textStatus}));
resolve(false);
},
timeout: 30000
});
} else {
resolve(access_token)
}
} else {
// otherwise show login screen
resolve(false);
}
} catch (error) {
errorHandling(error, "getAccessToken");
}
});
}
function getScriptToken() {
return new Promise(resolve => {
try {
// Get script token from local storage
script_token = getLocalToken('gd_indexer_script_token');
if (!script_token) {
// Get a new script token
$.ajax({
url: backend_url + "/auth/newscriptlink",
data: {},
type: 'get',
crossDomain: true,
dataType: 'json',
success: function (data) {
if (data.success_code && data.success_code === 1150) {
console.log('GrepoData: Retrieved script token.');
setLocalToken('gd_indexer_script_token', data.script_token);
resolve(data.script_token);
} else {
console.log("GrepoData: Error retrieving script token");
deleteLocalToken('gd_indexer_script_token');
resolve(false);
}
},
error: function (jqXHR, textStatus) {
console.log("GrepoData: Error retrieving script token");
deleteLocalToken('gd_indexer_script_token');
resolve(false);
},
timeout: 30000
});
} else {
// Check if existing script token has already been linked
setTimeout(checkScriptToken, 2000);
resolve(script_token);
}
} catch (error) {
errorHandling(error, "getScriptToken");
}
});
}
function showNoTeamNotification() {
try {
if (getLocalToken('gd_no_team_dont_show')) {
return;
}
if (7 < $("#notification_area>.notification").length) {
setTimeout(function() {
showNoTeamNotification();
}, 10000);
} else {
var notificationHandler = ("undefined" == typeof Layout || "undefined" == typeof Layout.notify ? new NotificationHandler : Layout);
var notification = notificationHandler.notify(
$("#notification_area>.notification").length + 1,
'gd_notification gd_no_team_notification',
'GrepoData city indexer: create or join a team to share your intel!',
null
);
$('.gd_no_team_notification').click(function () {
showTeamsPopup();
$('.gd_no_team_notification').hide();
});
}
} catch (e) {
errorHandling(e, "showNoTeamNotification")
}
}
function showLoginNotification() {
try {
if (7 < $("#notification_area>.notification").length) {
setTimeout(function() {
showLoginNotification();
}, 10000);
} else {
var notificationHandler = ("undefined" == typeof Layout || "undefined" == typeof Layout.notify ? new NotificationHandler : Layout);
var notification = notificationHandler.notify(
$("#notification_area>.notification").length + 1,
'gd_notification gd_login_required_notification',
'GrepoData city indexer: sign in required to start indexing',
null
);
$('.gd_login_required_notification').click(function () {
showLoginPopup();
$('.gd_login_required_notification').hide();
});
}
} catch (e) {
errorHandling(e, "showLoginNotification")
}
}
var login_window = null;
var script_token_interval = null;
var refreshing_scripttoken = false;
function showLoginPopup() {
// This function is called when there is no access_token available
// First ensure we have a script token
getScriptToken().then(script_token => {
if (login_window != null) {
login_window.close();
login_window = null;
}
// login_window = Layout.wnd.Create(GPWindowMgr.TYPE_DIALOG,
// ' GrepoData login required',
// {position: ['center','center'], width: 630, height: 405, minimizable: true});
login_window = Layout.wnd.Create(GPWindowMgr.TYPE_DIALOG,
'GrepoData login required',
{width: 630, height: 405, minimizable: true});
// Window content
var content = '' +
'';
login_window.setContent(content);
var login_window_element = $('.gdloginpopup').parent();
$(login_window_element).css({ top: 43 });
// Form Content
login_form_content = `
Click the link below to sign in with your GrepoData account
Sign in is required to use the city indexer userscript
`
// Build login form
formHtml = `
`;
$('.gdloginpopup').append(formHtml);
if (refreshing_scripttoken) {
loginError('Token was refreshed! Click the link to sign in', false, 5000);
refreshing_scripttoken=false;
}
// Handle actions
$('#gd-request-new-token-btn').click(function () {
// try with new token
deleteLocalToken('gd_indexer_script_token');
showLoginPopup();
clearInterval(script_token_interval);
refreshing_scripttoken = true;
});
$('#gd-request-token-check').click(function () {
$('#grepodataltip').hide();
checkScriptToken(true);
});
$('#gd_script_auth_link').click(function () {
console.log("GrepoData: script link clicked");
startPollCheckScriptToken();
$('#grepodatalerror').hide();
$('#grepodataltip').show();
});
});
}
var interval_count = 0;
function startPollCheckScriptToken() {
clearInterval(script_token_interval);
interval_count = 0;
script_token_interval = setInterval(checkScriptToken, 3000);
}
var teams_window = null;
function showTeamsPopup() {
if (teams_window != null) {
teams_window.close();
teams_window = null;
}
// teams_window = Layout.wnd.Create(GPWindowMgr.TYPE_DIALOG,
// ' `;
$(share_html).insertAfter($('#command_overview_tabs').parent().find('#type_filter'))
}
$('#command_overview_tabs').parent().find('#command_filter > div').eq(0).children("*").css('margin', '2px 1px');
// Filter new commands (subset of current commands that is not in tracked commands)
let new_commands = commands.filter(command => {
return Object.keys(tracked_commands).indexOf(getCommandId(command)) < 0;
});
verbose ? console.log('new commands: ', new_commands, ' filtered: ', is_filtered_upload) : null;
// Get current command id list
let current_command_ids = commands.map((command) => getCommandId(command));
// Filter cancelled commands (subset of [active] tracked_commands that is not in current commands)
let missing_tracked_command_ids = Object.keys(tracked_commands).filter(command_id => {
let is_active = tracked_commands[command_id].arrival_at > current_time; // command is active if its arrival time is in the future
let is_current = current_command_ids.indexOf(command_id) >= 0; // command is current if it exists in the list of current commands
return is_active && !is_current;
});
let missing_tracked_commands = missing_tracked_command_ids.map(command_id => tracked_commands[command_id]);
verbose ? console.log("missing tracked commands: ", missing_tracked_commands) : null
// User filtered during precious uploads so history is no longer clean
let upload_commands = new_commands;
if (is_filtered_upload) {
upload_commands = commands;
}
// Upload callback
let upload_callback = function () {
cmdUploadDialog(upload_commands, missing_tracked_commands, missing_tracked_command_ids);
}
// Check if there are any updates
let num_updates = new_commands.length + missing_tracked_commands.length;
if (num_updates <= 0) {
if (commands.length <= 0) {
updateOpsSyncButton(translate.CMD_SHARE_NONE, false, '');
} else {
updateOpsSyncButton(translate.CMD_SHARE_NONEW, true, '', upload_callback);
}
return
} else {
// Update upload button
updateOpsSyncButton(`${translate.CMD_SHARE_UPLOAD} (${num_updates})`, true, '', upload_callback);
}
} catch (error) {
errorHandling(error, "parseCommandOverview");
}
}
let cmd_upload_settings = {}
var cmd_upload_dialog = null;
let dialog_z_index = 1100
function cmdUploadDialog(new_commands, missing_tracked_commands, missing_tracked_command_ids) {
if (!globals || !('active_teams' in globals) || globals.active_teams.length <= 0) {
// No settings to display, hard route
verbose ? console.log('No local teams list; uploading without settings') : null;
uploadCommands(new_commands, missing_tracked_commands, missing_tracked_command_ids);
return;
}
if (cmd_upload_dialog != null) {
cmd_upload_dialog.close();
cmd_upload_dialog = null;
}
// header
let sharing_dialog = ' GrepoData indexer teams',
// {position: ['center','center'], width: 630, height: 405, minimizable: true});
teams_window = Layout.wnd.Create(GPWindowMgr.TYPE_DIALOG,
'GrepoData indexer teams',
{width: 630, height: 405, minimizable: true});
// Window content
var content = '' +
'';
teams_window.setContent(content);
var teams_window_element = $('.gdteamspopup').parent();
$(teams_window_element).css({ top: 43 });
// Build form
formHtml = `
`;
$('.gdteamspopup').append(formHtml);
// Handle actions
$('#gd-no-team-dont-show').click(function () {
// hide notification
setLocalToken('gd_no_team_dont_show', true);
teams_window.close();
teams_window = null;
});
}
function parseJwt(token) {
if (!token) {
return null;
}
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
};
function loginError(message, verbose_check = false, timeout = 0) {
console.log('login error: ', message);
let errormsg = message==''?"Unable to authenticate. Please try again later":message;
$('#grepodatalerror').text(errormsg);
$('#grepodatalerror').show();
if (timeout>0) {
setTimeout(_ => {$('#grepodatalerror').hide();}, timeout);
}
verbose_check ? HumanMessage.error(errormsg) : null;
}
function checkScriptToken(verbose_check=false) {
interval_count += 1;
if (interval_count>100) {
clearInterval(script_token_interval);
}
var script_token = getLocalToken('gd_indexer_script_token');
$.ajax({
url: backend_url + "/auth/verifyscriptlink",
data: {
script_token: script_token
},
type: 'post',
crossDomain: true,
dataType: 'json',
success: function (data) {
console.log(data);
if (data.success_code && data.success_code === 1111) {
console.log('GrepoData: Script token verified');
setLocalToken('gd_indexer_access_token', data.access_token);
setLocalToken('gd_indexer_refresh_token', data.refresh_token);
deleteLocalToken('gd_indexer_script_token');
HumanMessage.success('GrepoData login succesful!');
$('#gd-login-container').hide();
$('#gd-script-linked').show();
clearInterval(script_token_interval);
} else {
// Unable
loginError('Unknown error. Please try again later or let us know if this error persists.', verbose_check);
}
},
error: function (error, textStatus) {
if (error.responseJSON.error_code
&& (
error.responseJSON.error_code === 3041 // Token not found
|| error.responseJSON.error_code === 3042 // Expired (7 days)
|| error.responseJSON.error_code === 3043 // Invalid client
)
) {
// Unknown, invalid or expired script token. remove token and try again
clearInterval(script_token_interval);
deleteLocalToken('gd_indexer_script_token');
showLoginPopup();
setTimeout(_ => {loginError('Expired script token. Please try again or contact us if this error persists.')}, 1000);
} else if (error.responseJSON.error_code && error.responseJSON.error_code === 3040) {
// Token is not yet linked
if (verbose_check) {
loginError('Your script token is not yet verified. Click the link and sign in using your GrepoData account.', verbose_check);
window.open("https://grepodata.com/link/" + script_token);
startPollCheckScriptToken();
}
} else {
// Unknown
loginError('Unknown error. Please try again later or let us know if this error persists.', verbose_check);
}
},
timeout: 30000
});
}
function checkLogin(show_login_popup = true) {
// Check if grepodata access token or refresh token is in local storage and use it to verify
// if not verified: login required!
getAccessToken().then(access_token => {
if (access_token === false) {
if (show_login_popup === true) {
// show login popup
showLoginPopup();
} else {
// show login notification
setTimeout(showLoginNotification, 2000);
}
} else {
console.log("GrepoData: Succesful authentication for player "+Game.player_id);
}
});
}
// Decode entity hash
function decodeHashToJson(hash) {
// Remove hashtag prefix
if (hash.slice(0, 1) === '#') {
hash = hash.slice(1);
}
// Remove trailing =
for (var g = 0; g < 10; g++) {
if (hash.slice(hash.length - 1) === '=') {
hash = hash.slice(0, hash.length - 1)
}
}
var data = atob(hash);
var json = JSON.parse(data);
if (verbose) {
console.log("parsed from hash " + hash, json);
}
return json;
}
// Encode entity hash
function encodeJsonToHash(json) {
var hash = btoa(JSON.stringify(json));
if (verbose) {
console.log("parsed to hash " + hash, json);
}
return hash;
}
// Create town hash
function getTownHash(id, name='', x=0, y=0) {
return encodeJsonToHash({
id: id,
ix: x,
iy: y,
tp: 'town',
name: name
});
}
// Create player hash
function getPlayerHash(id, name) {
return encodeJsonToHash({
id: id,
name: name
});
}
// settings btn
var gdsettings = false;
$('.gods_area').append(' ');
$('.gd_settings_icon').click(function () {
if (!GPWindowMgr.getOpenFirst(Layout.wnd.TYPE_PLAYER_SETTINGS)) {
gdsettings = true;
}
Layout.wnd.Create(GPWindowMgr.TYPE_PLAYER_SETTINGS, 'Settings');
setTimeout(function () {
gdsettings = false
}, 5000)
});
$('.gd_settings_icon').tooltip('GrepoData City Indexer');
// report info is converted to a 32 bit hash to be used as unique id
// https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
String.prototype.report_hash = function () {
var hash = 0, i, chr;
if (this.length === 0) return hash;
for (i = 0; i < this.length; i++) {
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash;
};
// Add the given forum report to the index
function addToIndexFromForum(reportId, reportElement, reportPoster, reportHash, is_retry_attempt = false) {
var reportJson = JSON.parse(mapDOM(reportElement, true));
var reportText = reportElement.innerText;
getAccessToken().then(access_token => {
if (access_token === false) {
HumanMessage.error('GrepoData: login required to index reports');
showLoginPopup();
$('.rh' + reportHash).each(function () {
$(this).find('.middle').get(0).innerText = translate.ADD + ' +';
});
} else {
var data = {
'report_type': 'forum',
'access_token': access_token,
'world': world,
'report_hash': reportHash,
'report_text': reportText,
'report_json': reportJson,
'script_version': gd_version,
'report_poster': reportPoster || 'Undefined',
'report_poster_id': Game.player_id || 0,
'report_poster_ally_id': Game.alliance_id || 0
};
$('.rh' + reportHash).each(function () {
$(this).css("color", '#36cd5b');
$(this).find('.middle').get(0).innerText = translate.ADDED + ' 👍';
$(this).off("click");
});
$.ajax({
url: backend_url + "/indexer/v2/indexreport",
data: data,
type: 'post',
crossDomain: true,
dataType: 'json',
success: function (data) {
},
error: function (error, textStatus) {
console.log("error saving forum report: ", error);
if (error.responseJSON.error_code
&& error.responseJSON.error_code === 3003
&& is_retry_attempt === false
) {
// invalid JWT (probably expired, not caught because local client time is out of sync)
// try to force refresh the access token
getAccessToken(true).then(access_token => {
if (access_token === false) {
// If the force refresh was not succesful, we need a new explicit login from the user
HumanMessage.error('GrepoData: login required to index reports');
showLoginPopup();
$('.rh' + reportHash).each(function () {
$(this).css("color", '#ea6153');
$(this).find('.middle').get(0).innerText = translate.ERROR + ' ✗';
$(this).off("click");
});
} else {
// try again with new token
addToIndexFromForum(reportId, reportElement, reportPoster, reportHash, true);
}
});
} else {
errorHandling(Error(error.responseText), 'ajaxIndexForumReport');
$('.rh' + reportHash).each(function () {
$(this).css("color", '#ea6153');
$(this).find('.middle').get(0).innerText = translate.ERROR + ' ✗';
$(this).off("click");
});
}
},
timeout: 120000
});
pushHash(reportHash);
gd_indicator();
}
});
}
// Add the given inbox report to the index
function addToIndexFromInbox(reportHash, reportElement, is_retry_attempt = false) {
var reportJson = JSON.parse(mapDOM(reportElement, true));
var reportText = reportElement.innerText;
var has_combat_experience = false;
try {
// Check if 10% boost is enabled for friendly attack on enemy town in order to parse the killed units from the battle points gained if enemy units are invisible
var attacker_town = reportElement.getElementsByClassName('gp_town_link')[0];
if (attacker_town && attacker_town.getAttribute("href")) {
attacker_town = decodeHashToJson(attacker_town.getAttribute("href"));
if (attacker_town.id && MM.getModels().Town[attacker_town.id]) {
var combat_experience = MM.getModels().Town[attacker_town.id].researches().attributes.combat_experience
if (combat_experience === true || combat_experience === false) {
has_combat_experience = combat_experience
}
}
}
} catch (e) {
errorHandling(e, 'getCombatExperience');
}
getAccessToken().then(access_token => {
if (access_token === false) {
HumanMessage.error('GrepoData: login required to index reports');
showLoginPopup();
$('#gd_index_rep_txt').get(0).innerText = translate.ADD + ' +';
} else {
var data = {
'report_type': 'inbox',
'access_token': access_token,
'world': world,
'report_hash': reportHash,
'report_text': reportText,
'report_json': reportJson,
'script_version': gd_version,
'report_poster': Game.player_name || 'undefined',
'report_poster_id': Game.player_id || 0,
'report_poster_ally_id': Game.alliance_id || 0,
'has_combat_experience': has_combat_experience || false,
};
if (gd_settings.inbox === true) {
var btn = document.getElementById("gd_index_rep_txt");
var btnC = document.getElementById("gd_index_rep_");
btnC.setAttribute('style', 'color: #36cd5b; float: right;');
btn.innerText = translate.ADDED + ' 👍';
}
$.ajax({
url: backend_url + "/indexer/v2/indexreport",
data: data,
type: 'post',
crossDomain: true,
success: function (data) {
},
error: function (error, textStatus) {
console.log("error saving inbox report: ", error);
if (error.responseJSON.error_code
&& error.responseJSON.error_code === 3003
&& is_retry_attempt === false
) {
// invalid JWT (probably expired, not caught because local client time is out of sync)
// try to force refresh the access token
getAccessToken(true).then(access_token => {
if (access_token === false) {
// If the force refresh was not succesful, we need a new explicit login from the user
HumanMessage.error('GrepoData: login required to index reports');
showLoginPopup();
var btn = document.getElementById("gd_index_rep_txt");
var btnC = document.getElementById("gd_index_rep_");
btnC.setAttribute('style', 'color: #ea6153; float: right;');
btn.innerText = translate.ERROR + ' ✗';
} else {
// try again with new token
addToIndexFromInbox(reportHash, reportElement, true);
}
});
} else {
errorHandling(Error(error.responseText), 'ajaxIndexForumReport');
var btn = document.getElementById("gd_index_rep_txt");
var btnC = document.getElementById("gd_index_rep_");
btnC.setAttribute('style', 'color: #ea6153; float: right;');
btn.innerText = translate.ERROR + ' ✗';
btn.setAttribute('title', 'Oops, something went wrong. Developers have been notified (if you enabled bug reports).');
}
},
timeout: 120000
});
pushHash(reportHash);
gd_indicator();
}
});
}
function pushHash(hash) {
if (globals.reportsFound === undefined) {
globals.reportsFound = [];
}
globals.reportsFound.push(hash);
}
function mapDOM(element, json) {
var treeObject = {};
// If string convert to document Node
if (typeof element === "string") {
if (window.DOMParser) {
parser = new DOMParser();
docNode = parser.parseFromString(element, "text/xml");
} else { // Microsoft strikes again
docNode = new ActiveXObject("Microsoft.XMLDOM");
docNode.async = false;
docNode.loadXML(element);
}
element = docNode.firstChild;
}
//Recursively loop through DOM elements and assign properties to object
function treeHTML(element, object) {
object["type"] = element.nodeName;
var nodeList = element.childNodes;
if (nodeList != null) {
if (nodeList.length) {
object["content"] = [];
for (var i = 0; i < nodeList.length; i++) {
if (nodeList[i].nodeType == 3) {
object["content"].push(nodeList[i].nodeValue);
} else {
object["content"].push({});
treeHTML(nodeList[i], object["content"][object["content"].length - 1]);
}
}
}
}
if (element.attributes != null) {
if (element.attributes.length) {
object["attributes"] = {};
for (var i = 0; i < element.attributes.length; i++) {
object["attributes"][element.attributes[i].nodeName] = element.attributes[i].nodeValue;
}
}
}
}
treeHTML(element, treeObject);
return (json) ? JSON.stringify(treeObject) : treeObject;
}
// Inbox reports
function parseInboxReport() {
try {
var reportElement = document.getElementById("report_report");
if (reportElement != null) {
var footerElement = reportElement.getElementsByClassName("game_list_footer")[0];
var reportText = reportElement.outerHTML;
var footerText = footerElement.outerHTML;
if (footerText.indexOf('gd_index_rep_') < 0
&& reportText.indexOf('report_town_bg_quest') < 0
&& reportText.indexOf('support_report_cities') < 0
&& reportText.indexOf('big_horizontal_report_separator') < 0
&& reportText.indexOf('report_town_bg_attack_spot') < 0
&& (reportText.indexOf('/images/game/towninfo/support.png') < 0 || reportText.indexOf('flagpole ghost_town') < 0)
&& (reportText.indexOf('/images/game/towninfo/attack.png') >= 0
|| reportText.indexOf('/images/game/towninfo/espionage') >= 0
|| reportText.indexOf('/images/game/towninfo/breach.png') >= 0
|| reportText.indexOf('/images/game/towninfo/attackSupport.png') >= 0
|| reportText.indexOf('/images/game/towninfo/take_over.png') >= 0
|| reportText.indexOf('/images/game/towninfo/support.png') >= 0
|| reportText.indexOf('power_icon86x86 wisdom') >= 0)
) {
// Build report hash using default method
var headerElement = reportElement.querySelector("#report_header");
var dateElement = footerElement.querySelector("#report_date");
var headerText = headerElement.innerText;
var dateText = dateElement.innerText;
var hashText = headerText + dateText;
// Try to build report hash using town ids (robust against object name changes)
try {
var towns = headerElement.getElementsByClassName('town_name');
if (towns.length === 2) {
var ids = [];
for (var m = 0; m < towns.length; m++) {
var href = towns[m].getElementsByTagName("a")[0].getAttribute("href");
var townJson = decodeHashToJson(href);
ids.push(townJson.id);
}
if (ids.length === 2) {
ids.push(dateText); // Add date to report info
hashText = ids.join('');
}
}
} catch (e) {
console.log(e);
}
// Try to parse units and buildings
var reportUnits = reportElement.getElementsByClassName('unit_icon40x40');
var reportBuildings = reportElement.getElementsByClassName('report_unit');
var reportContent = '';
try {
for (var u = 0; u < reportUnits.length; u++) {
reportContent += reportUnits[u].outerHTML;
}
for (var u = 0; u < reportBuildings.length; u++) {
reportContent += reportBuildings[u].outerHTML;
}
} catch (e) {
console.log("Unable to parse inbox report units: ", e);
}
if (typeof reportContent === 'string' || reportContent instanceof String) {
hashText += reportContent;
}
// add player id to hash to avoid inbox conflicts
if (Game.player_id > 0) {
hashText += Game.player_id;
}
reportHash = hashText.report_hash();
if (verbose) console.log('Parsed inbox report with hash: ' + reportHash);
// Create index button
var addBtn = document.createElement('a');
var txtSpan = document.createElement('span');
var rightSpan = document.createElement('span');
var leftSpan = document.createElement('span');
txtSpan.innerText = translate.ADD + ' +';
addBtn.setAttribute('href', '#');
addBtn.setAttribute('id', 'gd_index_rep_');
addBtn.setAttribute('class', 'button gd_btn_index');
addBtn.setAttribute('style', 'float: right;');
txtSpan.setAttribute('id', 'gd_index_rep_txt');
txtSpan.setAttribute('style', 'min-width: 50px; margin: 0 3px;');
txtSpan.setAttribute('class', 'middle');
rightSpan.setAttribute('class', 'right');
leftSpan.setAttribute('class', 'left');
rightSpan.appendChild(txtSpan);
leftSpan.appendChild(rightSpan);
addBtn.appendChild(leftSpan);
// Check if this report was already indexed
var reportFound = false;
if (globals && globals.reportsFound) {
for (var j = 0; j < globals.reportsFound.length; j++) {
if (globals.reportsFound[j] === reportHash) {
reportFound = true;
}
}
}
if (reportFound) {
addBtn.setAttribute('style', 'color: #36cd5b; float: right;');
txtSpan.setAttribute('style', 'cursor: default;');
txtSpan.innerText = translate.ADDED + ' 👍';
} else {
addBtn.addEventListener('click', function () {
if ($('#gd_index_rep_txt').get(0)) {
$('#gd_index_rep_txt').get(0).innerText = translate.SEND;
}
addToIndexFromInbox(reportHash, reportElement, false);
}, false);
}
// Create share button
var shareBtn = document.createElement('a');
var shareInput = document.createElement('input');
var rightShareSpan = document.createElement('span');
var leftShareSpan = document.createElement('span');
var txtShareSpan = document.createElement('span');
shareInput.setAttribute('type', 'text');
shareInput.setAttribute('id', 'gd_share_rep_inp');
shareInput.setAttribute('style', 'float: right;');
txtShareSpan.setAttribute('id', 'gd_share_rep_txt');
txtShareSpan.setAttribute('class', 'middle');
txtShareSpan.setAttribute('style', 'min-width: 50px; margin: 0 3px;');
rightShareSpan.setAttribute('class', 'right');
leftShareSpan.setAttribute('class', 'left');
leftShareSpan.appendChild(rightShareSpan);
rightShareSpan.appendChild(txtShareSpan);
shareBtn.appendChild(leftShareSpan);
shareBtn.setAttribute('href', '#');
shareBtn.setAttribute('id', 'gd_share_rep_');
shareBtn.setAttribute('class', 'button gd_btn_share');
shareBtn.setAttribute('style', 'float: right;');
txtShareSpan.innerText = translate.SHARE;
shareBtn.addEventListener('click', () => {
if ($('#gd_share_rep_txt').get(0)) {
var hashI = ('r' + reportHash).replace('-', 'm');
var content = '' +
' - 1. Install the GrepoData bot in your Discord server (link).
' +
' - 2. Paste the slash command shown below in your Discord server. A popup with slash commands should appear.
' +
' - 3. Press enter to select the GrepoData command, and press enter again to confirm.
' +
'
Copy to clipboard ' +
'
' +
'
';
Layout.wnd.Create(
GPWindowMgr.TYPE_DIALOG,
"Share this report on Discord",
{width: 400, height: 280, minimizable: false}
).setContent(content);
addToIndexFromInbox(reportHash, reportElement, false);
$(".gd_copy_command_" + reportHash).click(function () {
$(".gd_copy_input_" + reportHash).select();
document.execCommand('copy');
$('.gd_copy_done_' + reportHash).get(0).style.display = 'block';
setTimeout(function () {
if ($('.gd_copy_done_' + reportHash).get(0)) {
$('.gd_copy_done_' + reportHash).get(0).style.display = 'none';
}
}, 3000);
});
}
});
// Create custom footer
var grepodataFooter = document.createElement('div');
grepodataFooter.setAttribute('id', 'gd_inbox_footer');
grepodataFooter.appendChild(addBtn);
grepodataFooter.appendChild(shareBtn);
if ($('#report_report').find('.azasasasd').last().length > 0) {
$(grepodataFooter).insertAfter($('#report_report').find('a').last());
} else {
footerElement.appendChild(grepodataFooter);
}
// Set footer button placement
var folderElement = footerElement.querySelector('#select_folder_id');
footerElement.style.backgroundSize = 'auto 100%';
footerElement.style.padding = '6px 0';
dateElement.style.marginTop = '-4px';
dateElement.style.marginLeft = '3px';
dateElement.style.position = 'absolute';
dateElement.style.zIndex = '7';
dateElement.style.background = 'url(https://gpnl.innogamescdn.com/images/game/border/footer.png) repeat-x 0px -6px';
if (folderElement !== null) {
folderElement.style.position = 'absolute';
folderElement.style.marginTop = '12px';
folderElement.style.marginLeft = '3px';
folderElement.style.zIndex = '6';
}
}
// Handle inbox keyboard shortcuts
document.removeEventListener('keyup', inboxNavShortcut);
document.addEventListener('keyup', inboxNavShortcut);
}
} catch (error) {
errorHandling(error, "parseInboxReport");
}
}
function inboxNavShortcut(e) {
try {
var reportElement = document.getElementById("report_report");
if (gd_settings.keys_enabled === true && !['textarea', 'input'].includes(e.srcElement.tagName.toLowerCase()) && reportElement !== null) {
switch (e.key) {
case gd_settings.key_inbox_prev:
var prev = reportElement.getElementsByClassName('previous_button');
if (prev.length === 1 && prev[0] != null) {
prev[0].click();
}
break;
case gd_settings.key_inbox_next:
var next = reportElement.getElementsByClassName('next_button');
if (next.length === 1 && next[0] != null) {
next[0].click();
}
break;
default:
break;
}
}
} catch (error) {
console.log(error);
}
}
function addForumReportById(reportId, reportHash) {
var reportElement = document.getElementById(reportId);
if (!reportElement) return
if (!reportHash || reportHash == '') {
throw new Error("Unable to find forum report hash.");
return;
}
// Find report poster
var inspectedElement = reportElement.parentElement;
var search_limit = 20;
var found = false;
var reportPoster = '_';
while (!found && search_limit > 0 && inspectedElement !== null) {
try {
var owners = inspectedElement.getElementsByClassName("bbcodes_player");
if (owners.length !== 0) {
for (var g = 0; g < owners.length; g++) {
if (owners[g].parentElement.classList.contains('author')) {
reportPoster = owners[g].innerText;
if (reportPoster === '') reportPoster = '_';
found = true;
}
}
}
inspectedElement = inspectedElement.parentElement;
}
catch (err) {
}
search_limit -= 1;
}
addToIndexFromForum(reportId, reportElement, reportPoster, reportHash, false);
}
// Forum reports
function parseForumReport() {
try {
var reportsInView = document.getElementsByClassName("bbcodes published_report");
//process reports
if (reportsInView && reportsInView.length > 0) {
for (var i = 0; i < reportsInView.length; i++) {
var reportElement = reportsInView[i];
var reportId = reportElement.id;
if (reportId && !$('#gd_index_f_' + reportId).get(0)) {
var bSpy = false;
var spyReportElems = reportElement.getElementsByClassName("espionage_report");
var unitElems = reportElement.getElementsByClassName("report_units");
var conquestElems = reportElement.getElementsByClassName("conquest");
if (spyReportElems && spyReportElems.length > 0) {
bSpy = true;
} else if ((unitElems && unitElems.length < 2)
|| (conquestElems && conquestElems.length > 0)) {
// ignore non intel reports
continue;
}
var reportHash = null;
try {
// === Build report hash to create a unique identifier for this report that is consistent between sessions
var header = reportElement.getElementsByClassName('published_report_header bold')[0];
// Try to parse time string
try {
var dateText = header.getElementsByClassName('reports_date small')[0].innerText;
var time = dateText.match(time_regex);
if (time != null) {
dateText = time[0];
}
} catch (error) {
errorHandling(error, "parseForumReportNoTimeFound");
}
// Try to parse town ids from report header
try {
var headerText = header.getElementsByClassName('bold')[0].innerText;
var towns = header.getElementsByClassName('gp_town_link');
if (towns.length === 2) {
var ids = [];
for (var m = 0; m < towns.length; m++) {
var href = towns[m].getAttribute("href");
var townJson = decodeHashToJson(href);
ids.push(townJson.id);
}
if (ids.length === 2) {
headerText = ids.join('');
}
}
} catch (error) {
errorHandling(error, "parseForumReportReportTownIds");
}
// Try to parse units and buildings
try {
var reportUnits = reportElement.getElementsByClassName('unit_icon40x40');
var reportBuildings = reportElement.getElementsByClassName('report_unit');
var reportDetails = reportElement.getElementsByClassName('report_details');
var reportResources = reportElement.getElementsByClassName('resources');
var reportContent = '';
for (var u = 0; u < reportUnits.length; u++) {
reportContent += reportUnits[u].outerHTML;
}
for (var u = 0; u < reportBuildings.length; u++) {
reportContent += reportBuildings[u].outerHTML;
}
if (reportDetails.length === 1) {
reportContent += reportDetails[0].innerText;
}
if (reportResources.length === 1) {
reportContent += reportResources[0].innerText;
}
} catch (error) {
errorHandling(error, "parseForumReportReportUnits");
}
// Combine intel and generate hash
var reportText = dateText + headerText + reportContent;
if (reportText !== null && reportText !== '') {
reportHash = reportText.report_hash();
}
} catch (error) {
errorHandling(error, "parseForumReportCreateHashError");
reportHash = null;
}
console.log('Parsed forum report with hash: ' + reportHash);
var exists = false;
if (reportHash !== null && reportHash !== 0 && globals && globals.reportsFound) {
for (var j = 0; j < globals.reportsFound.length; j++) {
if (globals.reportsFound[j] == reportHash) {
exists = true;
}
}
}
if (reportHash == null) {
reportHash = '';
}
let index_btn_f_html = '' + translate.ADD + ' +\n';
let share_btn_f_html = '' + translate.SHARE + '\n';
if (bSpy === true) {
$(reportElement).append(' ');
$(reportElement).find('.resources, .small').css("text-align", "left");
} else {
$(reportElement).append(' ');
$(reportElement).find('.button, .simulator, .all').parent().css("padding-top", "24px");
$(reportElement).find('.button, .simulator, .all').siblings("span").css("margin-top", "-24px");
}
// Index click
if (exists === true) {
$('#gd_index_f_' + reportId).get(0).style.color = '#36cd5b';
$('#gd_index_f_txt_' + reportId).get(0).innerText = translate.ADDED + ' 👍';
} else {
$('#gd_index_f_' + reportId).click(function () {
addForumReportById($(this).attr('report_id'), $(this).attr('report_hash'));
});
}
// Share click
$('#gd_share_f_' + reportId).click(function () {
console.log('jquery hash: ',$(this).attr('report_hash'));
console.log('jquery id: ',$(this).attr('report_id'));
var reportHash = $(this).attr('report_hash');
var reportId = $(this).attr('report_id');
var hashI = ('r' + reportHash).replace('-', 'm');
var content = '' +
' - 1. Install the GrepoData bot in your Discord server (link).
' +
' - 2. Paste the slash command shown below in your Discord server. A popup with slash commands should appear.
' +
' - 3. Press enter to select the GrepoData command, and press enter again to confirm.
' +
'
Copy to clipboard ' +
'
' +
'
';
Layout.wnd.Create(
GPWindowMgr.TYPE_DIALOG,
"Share this report on Discord",
{width: 400, height: 280, minimizable: false}
).setContent(content);
addForumReportById(reportId, reportHash);
$(".gd_copy_command_" + reportHash).click(function () {
$(".gd_copy_input_" + reportHash).select();
document.execCommand('copy');
$('.gd_copy_done_' + reportHash).get(0).style.display = 'block';
setTimeout(function () {
if ($('.gd_copy_done_' + reportHash).get(0)) {
$('.gd_copy_done_' + reportHash).get(0).style.display = 'none';
}
}, 3000);
});
});
}
}
}
} catch (error) {
errorHandling(error, "parseForumReport");
}
}
function settingsTeams() {
if ('active_teams' in globals) {
if (globals.active_teams.length > 0) {
teamHtml = ''+translate.TEAM_NAME+' '+translate.TEAM_ROLE+' '+translate.TEAM_CONTRIBUTE+' '+translate.TEAM_ACTION+' ';
for (var j = 0; j < Object.keys(globals.active_teams).length; j++) {
var team = globals.active_teams[j];
/* ${team.role.replace('read', 'read-only')} */
teamHtml += `
${team.name}
${team.role.replace('owner', 'Fundador')}
`;
if (team.role !== 'read') {
teamHtml += `
`;
}
teamHtml += `
${translate.TEAM_ACTION_OVERVIEW} >
`
}
teamHtml += '
';
$('#gd-settings-teams-container').html(teamHtml);
// actions
for (let j = 0; j < Object.keys(globals.active_teams).length; j++) {
$("#gd-team-cbx-contrib-"+globals.active_teams[j].key).click(function () {
var set_value = globals.active_teams[j].contribute == 1 ? 0 : 1
var do_contribute = set_value == 1;
let team = globals.active_teams[j];
toggleTeamContributions(team.key, do_contribute).then(response => {
if (response!==false) {
globals.active_teams[j].contribute = set_value;
savedSettingsIndicator();
if (do_contribute === true) {
$("#gd-team-cbx-contrib-"+team.key).get(0).classList.add("checked");
} else {
$("#gd-team-cbx-contrib-"+team.key).get(0).classList.remove("checked");
}
}
})
});
}
} else {
// user has no teams
$('#gd-settings-teams-container').html(` Você ainda não se juntou a nenhuma equipe no mundo ${Game.world_id}. Criar uma nova equipe`);
}
} else {
// Data is probably still loading, retry after a while
setTimeout(settingsTeams, 500);
}
}
function settings() {
try {
if (!$("#gd_indexer").get(0)) {
$(".settings-menu ul:last").append('GrepoData City Indexer ');
// contact/update
try {
var access_token = getLocalToken('gd_indexer_access_token');
var logged_in = !!access_token;
var jwtpayload = parseJwt(access_token);
} catch (e) {}
// Intro
// var layoutUrl = 'https' + window.getComputedStyle(document.getElementsByClassName('icon')[0], null).background.split('("https')[1].split('"')[0];
var settingsHtml = ' ';
// Insert settings menu
$(".settings-menu").parent().append(settingsHtml);
// Handle settings events
$(".settings-link").click(function () {
$('#gd_settings_container').get(0).style.display = "none";
$('.settings-container').get(0).style.display = "block";
gdsettings = false;
});
$("#gdsettingslogout").click(function () {
$("#gdsettingslogged_in").hide();
deleteLocalToken('gd_indexer_access_token');
deleteLocalToken('gd_indexer_refresh_token');
HumanMessage.success('GrepoData logged out succesfully.');
showLoginPopup();
try {
// forget cached teams
gd_settings.cmd_team_settings = {};
saveSettings();
globals.active_teams = [];
} catch (e) {}
});
$("#gdsettingslogin").click(function () {
$("#gdsettingslogged_in").hide();
showLoginPopup();
});
$("#gd_indexer").click(function () {
$('.settings-container').get(0).style.display = "none";
$('#gd_settings_container').get(0).style.display = "block";
});
$(".inbox_gd_enabled").click(function () {
settingsCbx('inbox', !gd_settings.inbox);
if (!gd_settings.inbox) {
settingsCbx('keys_enabled', false);
}
});
$(".forum_gd_enabled").click(function () {
settingsCbx('forum', !gd_settings.forum);
});
$(".stats_gd_enabled").click(function () {
settingsCbx('stats', !gd_settings.stats);
});
$(".command_share_gd_enabled").click(function () {
settingsCbx('command_share', !gd_settings.command_share);
});
$(".command_cancel_time_gd_enabled").click(function () {
settingsCbx('command_cancel_time', !gd_settings.command_cancel_time);
});
$(".forum_reactions_gd_enabled").click(function () {
settingsCbx('forum_reactions', !gd_settings.forum_reactions);
});
$(".context_gd_enabled").click(function () {
settingsCbx('context', !gd_settings.context);
});
$(".bug_reports_gd_enabled").click(function () {
settingsCbx('bug_reports', !gd_settings.bug_reports);
});
$(".keys_enabled_gd_enabled").click(function () {
settingsCbx('keys_enabled', !gd_settings.keys_enabled);
});
if (gdsettings === true) {
$('.settings-container').get(0).style.display = "none";
$('#gd_settings_container').get(0).style.display = "block";
}
settingsTeams();
}
} catch (error) {
errorHandling(error, "settings");
}
}
function settingsCbx(type, value) {
// Update class
if (value === true) {
$('.' + type + '_gd_enabled').get(0).classList.add("checked");
}
else {
$('.' + type + '_gd_enabled').get(0).classList.remove("checked");
}
// Set value
gd_settings[type] = value;
saveSettings();
savedSettingsIndicator();
}
function savedSettingsIndicator() {
$('#gd_s_saved').get(0).style.display = 'block';
setTimeout(function () {
if ($('#gd_s_saved').get(0)) {
$('#gd_s_saved').get(0).style.display = 'none';
}
}, 3000);
}
function saveSettings() {
setLocalToken('globals_s', encodeJsonToHash(JSON.stringify(gd_settings)))
}
function toggleTeamContributions(index_key, do_contribute) {
return new Promise(resolve => {
try {
getAccessToken().then(access_token => {
if (access_token === false) {
resolve(false);
} else {
// Toggle team contributions
$.ajax({
method: "put",
headers: {'access_token': access_token},
url: backend_url + "/indexer/settings/contribute",
data: {
index_key: index_key,
contribute: do_contribute
}
}).error(function (err) {
console.error(err);
resolve(false);
}).done(function (response) {
resolve(response);
});
}
});
} catch (error) {
errorHandling(error, "toggleTeamContributions");
resolve(false);
}
});
}
var openIntelWindows = {};
function loadTownIntel(id, town_name, player_name) {
try {
getAccessToken().then(access_token => {
if (access_token === false) {
HumanMessage.error('GrepoData: login is required to view intel');
showLoginPopup();
$('#gd_index_rep_txt').get(0).innerText = translate.ADD + ' +';
} else {
// Create a new dialog
var content_id = player_name + id;
content_id = content_id.replace(/[^a-zA-Z]+/g, '');
if (openIntelWindows[content_id]) {
try {
openIntelWindows[content_id].close();
} catch (e) {console.log("unable to close window", e);}
}
var intelUrl = frontend_url + '/intel/town/'+Game.world_id+'/'+id;
// var intel_window = Layout.wnd.Create(GPWindowMgr.TYPE_DIALOG,
// ' ' + translate.TOWN_INTEL + ': ' + town_name + (player_name!=''?(' (' + player_name + ')'):''),
// {position: ['center','center'], width: 660, height: 590, minimizable: true});
var intel_window = Layout.wnd.Create(GPWindowMgr.TYPE_DIALOG,
translate.TOWN_INTEL + ': ' + town_name + (player_name!=''?(' (' + player_name + ')'):''),
{width: 660, height: 590, minimizable: true});
// intel_window.setWidth(600);
// intel_window.setHeight(590);
openIntelWindows[content_id] = intel_window;
// Window content
var content = '' +
'Loading intel..
' +
'' + intelUrl + '' +
'';
intel_window.setContent(content);
var intelWindowElement = $('.gdintel_'+content_id).parent();
$(intelWindowElement).css({ top: 43 });
// Get town intel from backend
$.ajax({
method: "get",
headers: { 'access_token': access_token},
url: backend_url + "/indexer/v2/town?world=" + world + "&town_id=" + id
}).error(function (err) {
console.error(err);
renderTownIntelError(content_id, intelUrl);
}).done(function (response) {
renderTownIntelWindow(response, id, town_name, player_name, content_id);
});
}
});
} catch (error) {
errorHandling(error, "loadTownIntel");
renderTownIntelError(content_id, intelUrl);
}
}
function renderTownIntelError(content_id, intelUrl) {
$('.gdintel_'+content_id).empty();
$('.gdintel_'+content_id).append('' +
'Sorry, no intel available at the moment.
Please contact us if this error persists.
' +
'Alternatively, you can view this town\'s intel on grepodata.com:
' +
'' + intelUrl + '
');
}
function renderTownIntelWindow(data, id, town_name, player_name, content_id) {
var intelUrl = 'https://grepodata.com/indexer';
try {
console.log(data);
intelUrl = 'https://grepodata.com/intel/town/'+Game.world_id+'/'+id;
var unitHeight = 255;
var notesHeight = 170;
if (data.intel==null || data.intel.length <= 1) {
unitHeight = 150;
notesHeight = 275;
}
// Intel content
var tooltips = [];
$('.gdintel_'+content_id).empty();
// Title
var townHash = getTownHash(parseInt(id), town_name, data.ix, data.iy);
var playerHash = getPlayerHash(data.player_id, data.player_name);
var title = '';
$('.gdintel_'+content_id).append(title);
// Buildings
var build = '';
var date = '';
var hasBuildings = false;
for (var j = 0; j < Object.keys(data.buildings).length; j++) {
var name = Object.keys(data.buildings)[j];
var value = data.buildings[name].level.toString();
if (value != null && value != '' && value.indexOf('%') < 0) {
date = data.buildings[name].date;
build = build + '' +
'' + value + '' +
'';
}
if (name != 'wall') {
hasBuildings = true;
}
}
build = build + '';
if (hasBuildings == true) {
$('.gdintel_'+content_id).append(build);
$('.gd_build_' + id).tooltip('Buildings as of: ' + date);
unitHeight -= 40;
}
// Units table
var table =
'\n' +
' \n' +
' \n' +
' \n' +
translate.INTEL_UNITS + '\n' +
' \n' +
' ' +
' ';
$('.gdintel_'+content_id).append(table);
for (var j = 0; j < tooltips.length; j++) {
$('.' + tooltips[j].id).tooltip(tooltips[j].text);
}
// notes
var notesHtml =
'\n' +
' \n' +
' \n' +
' \n' +
translate.INTEL_NOTE_TITLE + '\n' +
' \n' +
' ' +
' ';
$('.gdintel_'+content_id).append(notesHtml);
// Add note
$('#gd_add_note_'+content_id).click(function () {
var town_id = $('#gd_add_note_'+content_id).attr('gd-town-id');
var note = $('#gd_note_input_'+content_id).val().split('<').join(' ').split('>').join(' ').split('#').join(' ');
if (note != '') {
$('.gd_note_error_msg').hide();
if (note.length > 500) {
$('#gd_new_note_'+content_id).after(''+
'Note is too long. A note can have a maximum of 500 characters.' +
' \n');
} else {
$('#gd_add_note_'+content_id).hide();
$('#gd_adding_note_'+content_id).show();
$('#gd_note_input_'+content_id).prop('disabled',true);
saveNewNote(town_id, note, content_id);
}
}
});
// Del note
$('.gd_del_note_'+content_id).click(function () {
var note_id = $(this).attr('gd-note-id');
$(this).hide();
$(this).after('Note deleted
');
$('#gd_note_'+content_id+'_'+note_id).css({ opacity: 0.4 });
saveDelNote(note_id);
});
var world = Game.world_id;
var exthtml =
'' +
(data.player_id != null && data.player_id != 0 ? '
'+translate.INTEL_SHOW_PLAYER+' (' + data.player_name + ')' : '') +
(data.alliance_id != null && data.alliance_id != 0 ? '
'+translate.INTEL_SHOW_ALLIANCE+'' : '') +
'';
$('.gdintel_'+content_id).append(exthtml);
$('.gd_ext_ref').tooltip('Abre em nova guia');
} catch (error) {
errorHandling(error, "renderTownIntelWindow");
renderTownIntelError(content_id, intelUrl);
}
}
function getNoteRowHtml(note, content_id, i=0) {
var row = '';
row = row + '' +
(note.poster_id > 0 ? '': '') +
'
' +
note.poster_name+(note.poster_id > 0 ?'':'')+'
'+note.date+
'';
row = row + ''+note.message+'';
if (Game.player_name == note.poster_name) {
row = row + '';
} else {
row = row + '';
}
row = row + ' \n';
return row;
}
function saveNewNote(town_id, note, content_id) {
try {
getAccessToken().then(access_token => {
if (access_token !== false) {
$.ajax({
url: backend_url + "/indexer/v2/addnote",
data: {
access_token: access_token,
town_id: town_id,
message: note,
world: Game.world_id,
poster_name: Game.player_name,
poster_id: Game.player_id,
},
type: 'post',
crossDomain: true,
dataType: 'json',
timeout: 30000
}).fail(function (err) {
console.log("Error saving note: ", err);
var errormsg = 'Please try again later or contact us if this error persists.';
if (err.responseJSON.error_code
&& (
err.responseJSON.error_code === 7201 // No teams for user/world
)
) {
var errormsg = 'You need to join a GrepoData team (on this world) in order to use notes.';
}
$('#gd_new_note_'+content_id).after(''+
'Error saving note. '+errormsg+'' +
' \n');
$('#gd_add_note_'+content_id).show();
$('#gd_adding_note_'+content_id).hide();
$('#gd_note_input_'+content_id).prop('disabled',false);
}).done(function (response) {
if (response.note) {
$('#gd_new_note_'+content_id).after(getNoteRowHtml(response.note, content_id));
$('#gd_note_input_'+content_id).val('');
$('#gd_del_note_'+content_id+'_'+response.note.note_id).click(function () {
var note_id = $(this).attr('gd-note-id');
$(this).hide();
$(this).after('Note deleted
');
$('#gd_note_'+content_id+'_'+note_id).css({ opacity: 0.4 });
saveDelNote(note_id);
});
}
$('#gd_add_note_'+content_id).show();
$('#gd_adding_note_'+content_id).hide();
$('#gd_note_input_'+content_id).prop('disabled',false);
});
} else {
showLoginPopup();
}
});
} catch (error) {
errorHandling(error, "saveNewNote");
}
}
function saveDelNote(note_id) {
try {
getAccessToken().then(access_token => {
if (access_token !== false) {
$.ajax({
url: backend_url + "/indexer/v2/delnote",
data: {
access_token: access_token,
note_id: note_id,
world: Game.world_id,
},
type: 'post',
crossDomain: true,
dataType: 'json',
timeout: 30000
}).fail(function (err) {
console.log("Error deleting note: ", err);
}).done(function (response) {
console.log("Note deleted: ", response);
});
} else {
showLoginPopup();
}
});
} catch (error) {
errorHandling(error, "saveDeletedNote");
}
}
function linkToStats(action, opt) {
if (gd_settings.stats === true && opt && 'url' in opt) {
try {
var url = decodeURIComponent(opt.url);
var json = url.match(/&json={.*}&/g)[0];
json = json.substring(6, json.length - 1);
json = JSON.parse(json);
if ('player_id' in json && action.search("/player") >= 0) {
// Add stats button to player profile
var player_id = json.player_id;
var statsBtn = ' ';
$('#player_buttons').filter(':first').append(statsBtn);
} else if ('alliance_id' in json && action.search("/alliance") >= 0) {
// Add stats button to alliance profile
var alliance_id = json.alliance_id;
var statsBtn = ' ';
$('#player_info > ul > li').filter(':first').append(statsBtn);
}
} catch (error) {
console.log(error);
}
}
}
var count = 0;
function gd_indicator() {
count = count + 1;
$('#gd_index_indicator').get(0).innerText = count;
$('#gd_index_indicator').get(0).style.display = 'inline';
$('.gd_settings_icon').tooltip('Relatórios indexados: ' + count);
}
function viewTownIntel(xhr) {
try {
if (!!xhr.responseText) {
var town_id = xhr.responseText.match(/\[town\].*?(?=\[)/g)[0];
town_id = town_id.substring(6);
// Add intel button and handle click event
var button_style = 'float: right; bottom: 5px;';
try {
if (molehole_active) {
button_style = '';
}
} catch (e) {}
var intelBtn = '' +
'' +
'' +
'' + translate.VIEW + '';
$('.info_tab_content_' + town_id + ' > .game_inner_box > .game_border > ul.game_list > li.odd').filter(':first').append(intelBtn);
// Handle click: view intel
$('#gd_index_town_' + town_id).click(function () {
var town_name = town_id;
var player_name = '';
try {
panel_root = $('.info_tab_content_' + town_id).parent().parent().parent().get(0);
town_name = panel_root.getElementsByClassName('ui-dialog-title')[0].innerText;
player_name = panel_root.getElementsByClassName('gp_player_link')[0].innerText;
} catch (e) {
console.log(e);
}
//panel_root.getElementsByClassName('active')[0].classList.remove('active');
loadTownIntel(town_id, town_name, player_name);
});
}
if (gd_settings.stats === true) {
try {
// Add stats button to player name
var player_id = xhr.responseText.match(/player_id = [0-9]*,/g);
if (player_id != null && player_id.length > 0) {
player_id = player_id[0].substring(12, player_id[0].search(','));
var statsBtn = ' ';
$('.info_tab_content_' + town_id + ' > .game_inner_box > .game_border > ul.game_list > li.even > div.list_item_right').eq(1).append(statsBtn);
$('.info_tab_content_' + town_id + ' > .game_inner_box > .game_border > ul.game_list > li.even > div.list_item_right').css("min-width", "140px");
}
// Add stats button to ally name
var ally_id = xhr.responseText.match(/alliance_id = parseInt\([0-9]*, 10\),/g);
if (ally_id != null && ally_id.length > 0) {
ally_id = ally_id[0].substring(23, ally_id[0].search(','));
var statsBtn2 = ' ';
$('.info_tab_content_' + town_id + ' > .game_inner_box > .game_border > ul.game_list > li.odd > div.list_item_right').filter(':first').append(statsBtn2);
$('.info_tab_content_' + town_id + ' > .game_inner_box > .game_border > ul.game_list > li.odd > div.list_item_right').filter(':first').css("min-width", "140px");
}
} catch (e) {
console.log(e);
}
}
} catch (error) {
let town_bb = '';
if (!!xhr && 'responseText' in xhr) {
town_bb = xhr.responseText;
}
errorHandling(error, "enhanceTownInfoPanel", {town_bb: town_bb});
}
}
// Loads a list of report ids that have already been indexed by the current user or their allies.
var user_has_team = false;
function loadIndexHashlist(check_login = false, startup = false, is_retry_attempt = false) {
try {
if (verbose) {
console.log("Loading grepodata hashlist")
}
getAccessToken().then(access_token => {
if (access_token === false) {
if (startup === true) {
showLoginNotification();
}
} else {
$.ajax({
method: "get",
headers: {"access_token": access_token},
url: backend_url + "/indexer/v2/getlatest?world=" + Game.world_id
}).done(function (b) {
try {
var has_hashes = false;
var has_teams = false;
if (b['hashlist'] !== undefined) {
globals.reportsFound = [];
$.each(b['hashlist'], function (b, d) {
has_hashes = true;
globals.reportsFound.push(d)
});
}
if (b['active_teams'] !== undefined) {
globals.active_teams = [];
$.each(b['active_teams'], function (b, d) {
has_teams = true;
user_has_team = true;
globals.active_teams.push(d)
});
}
if (b['active_threads'] !== undefined) {
globals.active_threads = [];
$.each(b['active_threads'], function (b, d) {
globals.active_threads.push(d)
});
}
if (startup && has_hashes && !has_teams) {
// user has been using the indexer but is not part of a team on this world (only run this once at startup)
showNoTeamNotification();
}
} catch (u) {}
}).fail(function (error) {
console.log('Unable to get latest hashlist: ', error);
if (error.responseJSON.error_code
&& error.responseJSON.error_code === 3003
&& is_retry_attempt === false
) {
// invalid JWT (probably expired, not caught because local client time is out of sync)
// try to force refresh the access token
getAccessToken(true).then(access_token => {
if (access_token === false) {
// If the force refresh was not succesful, we need a new explicit login from the user
showLoginNotification();
} else {
// try again with new token
loadIndexHashlist(check_login, startup, true);
}
});
}
});
}
});
} catch (error) {
errorHandling(error, "loadIndexHashlist");
}
}
function getBrowser() {
var browser = 'unknown';
try {
var ua = navigator.userAgent,
tem,
M = ua.match(/(opera|maxthon|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
M[1] = 'IE';
M[2] = tem[1] || '';
}
if (M[1] === 'Chrome') {
tem = ua.match(/\bOPR\/(\d+)/);
if (tem !== null) {
M[1] = 'Opera';
M[2] = tem[1];
}
}
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
if ((tem = ua.match(/version\/(\d+)/i)) !== null) M.splice(1, 1, tem[1]);
browser = M.join(' ');
} catch (u) {console.error("unable to identify browser", u);}
return browser;
}
// Error Handling / Remote diagnosis / Bug reports
function errorHandling(e, fn, params = null) {
try {
if (verbose && e) {
HumanMessage.error("GD-ERROR: " + e.message);
} else if (!(fn in errorSubmissions) && gd_settings.bug_reports) {
errorSubmissions[fn] = true;
var data = {
error: fn,
params: params,
"function": fn,
browser: getBrowser(),
version: gd_version,
world: world
}
if (e && e.stack) {
console.log("GD-ERROR stack ", e.stack);
data.error = e.stack.replace(/'/g, '"')
}
$.ajax({
type: "POST",
url: "https://api.grepodata.com/indexer/v2/scripterror",
data: data,
success: function (r) {}
});
}
} catch (error) {
console.log("Error handling bug report", error);
}
}
}
function enableCityIndex(globals) {
if (globals.gdIndex === undefined) {
globals.gdIndex = 'enabled';
console.log('GrepoData city indexer V2 is running in primary mode.');
loadCityIndex(globals);
} else {
// Duplicate scripts installed.. stop execution
console.log('Duplicate indexer script. You only need to have the GrepoData userscript installed once for all worlds you play on.');
}
}
var gd_w = window;
if(gd_w.location.href.indexOf("grepodata.com") >= 0){
// Viewer (grepodata.com)
console.log("initiated grepodata.com viewer");
grepodataObserver('');
// Watch for angular app route changes
function grepodataObserver(path) {
var initWatcher = setInterval(function () {
// If route is one of the indexer routes AND path has changed
if ((
gd_w.location.pathname.indexOf("/profile") >= 0 ||
gd_w.location.pathname.indexOf("/intel") >= 0 ||
gd_w.location.pathname.indexOf("/points") >= 0
) && gd_w.location.pathname != path) {
// stop looking for route changes and start looking for update message
clearInterval(initWatcher);
messageObserver();
} else if (path != '' && gd_w.location.pathname != path) {
// there was a route change but not to an indexer route
path = '';
}
}, 500);
}
// Hide install message on grepodata.com/indexer
function messageObserver() {
var timeout = 20000;
var initWatcher = setInterval(function () {
timeout = timeout - 100;
if ($('#help_by_contributing').get(0)) {
clearInterval(initWatcher); // stop watching for update messages
// Hide install banner if script is already running
$('#help_by_contributing').get(0).style.display = 'none';
// Ingest version
if ($('#userscript_version').get(0)) {
$('#userscript_version').append('' + gd_version + '');
}
// Start looking for route changes
grepodataObserver(gd_w.location.pathname);
} else if (timeout <= 0) {
clearInterval(initWatcher); // stop watching for update messages
grepodataObserver(gd_w.location.pathname); // start looking for route changes
}
}, 100);
}
} else if((gd_w.location.pathname.indexOf("game") >= 0)){
// Indexer (in-game)
setTimeout(function () {
if (gd_w.f0969b2b439fdb38b3adade00a45c40e === undefined) {
gd_w.f0969b2b439fdb38b3adade00a45c40e = {};
}
enableCityIndex(gd_w.f0969b2b439fdb38b3adade00a45c40e);
}, 300);
}
} catch(error) { console.error("GrepoData City Indexer crashed (please report a screenshot of this error to admin@grepodata.com): ", error); }
})();