// ==UserScript==
// @name CONfetti
// @namespace https://www.conflictnations.com/
// @version 0.9.1
// @description Improve the Conflict Of Nations UI experience.
// @author Taviandir
// @match https://www.conflictnations.com/*
// @icon https://www.google.com/s2/favicons?domain=tampermonkey.net
// @license MIT
// @grant none
// ==/UserScript==
(function () {
'use strict';
if (!inIframe()) return;
log('INIT');
let correctUrl = document.location.href.includes('con-client');
console.log('CORRECT URL?', { correctUrl, href: document.location.href });
if (!correctUrl) return;
// Determine when it's loaded by observing changes to splash screen attributes. When it is finished loading, style="display: none;" is added to this element.
var splashScreen = document.getElementById('splashScreenContainer');
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.type === 'attributes') {
if (!__loaded) {
log('INIT SCRIPT');
initExtensionPlay();
__loaded = true;
log('INIT SCRIPT - DONE');
}
}
});
});
observer.observe(splashScreen, {
attributes: true, //configure it to listen to attribute changes
});
})();
var __loaded = false;
function initExtensionPlay() {
initEventWindow();
// hideGoldMarketing();
hideTutorialAdvisor();
initExtensionMenuRow();
initDiplomacyWindow();
}
function initDiplomacyWindow() {
var dipBtn = document.getElementById('func_btn_diplomacy');
dipBtn.addEventListener('click', onOpenDiplomacyWindow);
}
function onOpenDiplomacyWindow() {
log('on diplomacy open');
setTimeout(() => {
// delay setting to give it time to load the UI
var messageTabEl = document.getElementById('func_tab_messages');
messageTabEl.addEventListener('click', onClickDiplomacyMessagesTab);
}, 2000);
}
var __enabledCtrlEnterSend;
function onClickDiplomacyMessagesTab() {
// enable CTRL-Enter to send messages
if (!__enabledCtrlEnterSend) {
setTimeout(() => {
var textAreaEl = document.getElementById('func_create_message_body');
if (textAreaEl) {
textAreaEl.addEventListener('keydown', function (e) {
if (e.ctrlKey && (e.keyCode == 10 || e.keyCode == 13)) {
// Ctrl-Enter pressed
document.getElementById('func_send_message').click();
}
});
__enabledCtrlEnterSend = true;
}
}, 2000);
}
}
/**************************************** MENU ROW FEATURES ****************************************/
function initExtensionMenuRow() {
var refElement = document.getElementById('menuContainer');
var menuWrapper = document.createElement('div');
menuWrapper.id = 'ExtMenu';
menuWrapper.style = 'position: absolute; bottom: -80px; left: 0; width: 315px; z-index: 10; color: white; margin-left: 13px;';
var ulEl = document.createElement('ul');
ulEl.classList.add('mainmenu');
ulEl.style = 'display: grid; grid-template-columns: repeat(5, 1fr)';
menuWrapper.appendChild(ulEl);
insertAfter(menuWrapper, refElement);
// UNIT B-LVL BUTTON
var ubLvlButtonEl = addButtonToMenu(ulEl);
ubLvlButtonEl.style.fill = 'white';
var svgWrapper = document.createElement('div');
svgWrapper.innerHTML = _buildingIconSvg;
svgWrapper.style = 'width: 100%; margin: 0.25rem';
ubLvlButtonEl.appendChild(svgWrapper);
ubLvlButtonEl.addEventListener('click', onClickMenuUnitBuildingLevel);
// NOTES BUTTON
var notesButtonEl = addButtonToMenu(ulEl);
notesButtonEl.innerText = 'NOTES';
notesButtonEl.addEventListener('click', onClickMenuItemNotes);
}
function addButtonToMenu(menuEl) {
var liEl = document.createElement('li');
liEl.classList.add('con_button');
liEl.style = 'display: inline-flex; align-items: center; justify-content: center; font-weight: bold;';
menuEl.appendChild(liEl);
return liEl;
}
function createPopupCloseButton() {
var div = document.createElement('div');
div.innerHTML = `<div class="close_button_s"><div>x</div></div>`;
var child = div.firstChild;
child.parent = null;
div.remove();
return child;
}
/*** UNIT BUILDING LVL MATRIX ***/
function onClickMenuUnitBuildingLevel() {
log('UNIT BUILDING-LEVEL ITEM CLICKED');
var popupEl = document.createElement('div');
popupEl.id = 'ExtNotesUbLvl';
popupEl.style =
'min-width: 500px; background: #51666d; color: white; border: 1px solid #ccc; position: absolute; left: 2%; top: 25%; display: flex; flex-direction: column; padding: 0.25rem;';
popupEl.appendChild(initUbPopupStyles());
popupEl.appendChild(initUbLvlHeader());
var tableWrapper = document.createElement('div');
popupEl.appendChild(tableWrapper);
tableWrapper.innerHTML = _unitBuildlingLevelTableTemplate;
// inject data into table
var data = parseUnitBuildingLevelsData();
var tableEl = tableWrapper.firstChild;
for (let key in data) {
let obj = data[key];
for (let lvl = 1; lvl <= 5; lvl++) {
let arr = obj[lvl];
if (arr.length) {
let tdHtml = ubArrayToHtml(arr);
var cellId = getUbTableCellId(key, lvl);
var tdMatch = tableEl.querySelectorAll('#' + cellId);
if (tdMatch.length) {
tdMatch[0].innerHTML = tdHtml;
}
}
}
}
document.getElementById('s1914').appendChild(popupEl);
}
function initUbPopupStyles() {
var styles = document.createElement('style');
styles.innerHTML = _unitBuildlingPopupCss;
return styles;
}
function ubArrayToHtml(arr) {
let html = '';
for (let x of arr) {
html += '<div>' + x + '</div>';
}
return html;
}
function getUbTableCellId(building, lvl) {
let key = '';
if (building === 'Army Base') {
key = 'army';
} else if (building === 'Air Base') {
key = 'air';
} else if (building === 'Naval Base') {
key = 'naval';
}
return key + '_' + lvl;
}
function initUbLvlHeader() {
var headerWrapper = document.createElement('div');
headerWrapper.style = 'display: flex; align-items: space-between';
var closeButton = createPopupCloseButton();
closeButton.addEventListener('click', onClickCloseUbLvlWindow);
var headerEl = document.createElement('h1');
headerEl.innerText = 'Units by Building Levels';
headerEl.style = 'margin-bottom: 0.5rem';
headerWrapper.appendChild(headerEl);
headerWrapper.appendChild(closeButton);
return headerWrapper;
}
function onClickCloseUbLvlWindow() {
document.getElementById('ExtNotesUbLvl').remove();
}
function parseUnitBuildingLevelsData() {
var rows = _unitBuildingLevelsData.split('\n');
let dict = {};
let currentObj = null;
for (let row of rows) {
let cols = row.split('\t');
// check for new category
if (cols[0] != '') {
currentObj = {};
initLevelsInBuildingObj(currentObj);
var key = cols[0] + ' Base';
dict[key] = currentObj;
}
// add units to its level
for (let lvl = 1; lvl <= 5; lvl++) {
if (!!cols[lvl]) {
currentObj[lvl].push(cols[lvl]);
}
}
}
return dict;
}
function initLevelsInBuildingObj(obj) {
for (let i = 0; i < 5; i++) {
obj[i + 1] = [];
}
}
/*** NOTES ***/
function onClickMenuItemNotes() {
log('NOTES MENU ITEM CLICKED');
var popupEl = document.createElement('div');
popupEl.id = 'ExtNotesPopup';
popupEl.style =
'min-width: 500px; background: #eee; color: black; border: 1px solid #ccc; position: absolute; left: 2%; top: 25%; display: flex; flex-direction: column; padding: 0.25rem;';
var headerEl = document.createElement('h1');
headerEl.innerText = 'Notes';
headerEl.style = 'margin-bottom: 0.5rem';
popupEl.appendChild(headerEl);
var gameId = getGameId();
var textEl = document.createElement('textarea');
textEl.id = 'ExtNoteInput';
textEl.value = loadGameNote(gameId);
textEl.setAttribute('rows', 10);
popupEl.appendChild(textEl);
// buttons container
var buttonDiv = document.createElement('div');
buttonDiv.style = 'display: flex; margin-top: 0.5rem; justify-content: flex-end';
popupEl.appendChild(buttonDiv);
// cancel button
var cancelEl = document.createElement('button');
cancelEl.id = 'ExtNoteCancel';
cancelEl.innerText = 'Cancel';
cancelEl.className = 'con_button large_button uppercase';
cancelEl.style = 'margin-right: 0.5rem;';
cancelEl.addEventListener('click', onClickCancelNote);
buttonDiv.appendChild(cancelEl);
// save button
var saveEl = document.createElement('button');
saveEl.id = 'ExtNoteSave';
saveEl.innerText = 'Save';
saveEl.className = 'con_button large_button uppercase';
saveEl.addEventListener('click', onClickSaveNote);
buttonDiv.appendChild(saveEl);
document.getElementById('s1914').appendChild(popupEl);
}
function onClickCancelNote() {
closeNoteWindow();
}
function onClickSaveNote() {
// log('save click');
var textValue = document.getElementById('ExtNoteInput').value;
log(textValue);
saveGameNote(getGameId(), textValue);
// log('NOTE SAVED');
closeNoteWindow();
}
function closeNoteWindow() {
document.getElementById('ExtNotesPopup').remove();
}
function loadGameNote(gameId) {
return localStorage.getItem('ext_note_' + gameId);
}
function saveGameNote(gameId, text) {
localStorage.setItem('ext_note_' + gameId, text);
}
function hideTutorialAdvisor() {
var element = document.getElementById('tutorialAdviceTextContainer');
if (element) {
element.remove();
}
}
function hideGoldMarketing() {
var element = document.getElementById('marketingPopupContainer');
let m = document.querySelectorAll('#marketingPopupContainer .func_close_button');
console.log('GOLD match', m);
if (m.length) {
m.click();
}
}
var _unreadEvents = 0;
function initEventWindow() {
var eventButton = document.getElementById('func_btn_events');
// extract the "Unread Events" value from the red circle
var unreadEventsElem = document.getElementById('func_events_unread');
if (unreadEventsElem) {
log('UNREAD EVENTS: ' + unreadEventsElem.innerText);
if (unreadEventsElem.innerText !== '') {
var unreadValue = parseInt(unreadEventsElem.innerText);
if (+unreadValue) {
_unreadEvents = +unreadValue;
}
console.log('unread as number', unreadValue, _unreadEvents);
}
}
eventButton.addEventListener('click', (event) => {
initOptionsInEventWindow();
markUnreadEvents();
addUnitTypeToResearchEvents();
enhanceAgentEvents();
});
}
function initOptionsInEventWindow() {
log('init event window!');
var eventContentElem = document.querySelector('#eventsContainer .content .overview');
if (eventContentElem) {
// create a parent wrapper object for all filters
let wrapper = document.createElement('div');
wrapper.id = 'confetti-event-filters';
wrapper.style = 'display: flex; padding: 1rem;';
eventContentElem.prepend(wrapper);
addTypeFilterSelect(wrapper);
addCountryFilterSelect(wrapper);
}
}
function markUnreadEvents() {
console.log('Mark Unread Events', _unreadEvents);
let childrenOfUl = document.querySelector('#eventsContainer .content .overview ul').children;
//console.log("children of ul", childrenOfUl, unreadEvents);
for (var i = 0; i < _unreadEvents; i++) {
var liElem = childrenOfUl[i];
// console.log("SET UNREAD: li elem", liElem);
liElem.style.borderLeft = '4px solid yellow';
}
}
function enhanceAgentEvents() {
log('Enhance Agent Events');
let childrenOfUl = document.querySelector('#eventsContainer .content .overview ul').children;
//console.log("children of ul", childrenOfUl, unreadEvents);
// console.log("AGENT childrenoful", childrenOfUl);
for (var i = 0; i < childrenOfUl.length; i++) {
var evEl = childrenOfUl[i];
var desc = evEl.querySelector('.event-description');
// console.log("event innertext", evEl, desc, desc.innerText);
if (desc.innerText.indexOf('Our agent') >= 0) {
// console.log("ENHANCE - our agent event", desc.innerText);
var headerEl = evEl.querySelector('.event-time');
if (desc.innerText.indexOf('have intercepted') >= 0) {
// this is one of the "done to us"
} else if (desc.innerText.indexOf('has been captured') >= 0) {
// mission failed
headerEl.innerText = '👎 ' + headerEl.innerText;
headerEl.style = 'color: yellow;';
} else if (desc.innerText.indexOf('sabotaged buildings') >= 0 || desc.innerText.indexOf('destroyed resources') >= 0) {
// mission successful
headerEl.innerText = '👍 ' + headerEl.innerText;
headerEl.style = 'color: #0f0;';
}
// TODO : agent missions done on us
}
}
}
function addUnitTypeToResearchEvents() {
setTimeout(() => {
// console.log('addUnitTypeToResearchEvents()');
var eventElems = document.querySelectorAll('#eventsContainer .content .overview ul li');
// console.log('RES event elems', eventElems);
for (var i = 0; i < eventElems.length; i++) {
var evEl = eventElems[i];
var desc = evEl.querySelector('.event-description');
var content = '';
if (desc) {
content = desc.innerText;
} else {
continue;
}
if (content.includes('Research Completed')) {
// parse out research name from event text
let prefix = 'Research for ';
let suffix = ' has been completed';
let idxStart = content.lastIndexOf(prefix) + prefix.length;
let idxEnd = content.lastIndexOf(suffix);
let researchName = content.substring(idxStart, idxEnd);
let researchNameWithoutParanthesis = researchName.substring(0, researchName.indexOf(' ('));
// find match
let unitTypeMatch = tryMatchUnitType(researchNameWithoutParanthesis);
// let unitTypeMatch = tryMatchUnitType('Benjamin Franklin Class');
console.log('Unit type match?', { unitTypeMatch, researchNameWithoutParanthesis, researchName });
if (unitTypeMatch) {
// set the new innerText on the content div
//var newContent = content.substring(0, idxStart) + researchName + ' [' + unitTypeMatch + ']' + content.substring(idxEnd);
setNewResearchContent(content, idxStart, idxEnd, researchName, unitTypeMatch, desc);
} else {
// perhaps a soft upgrade, try to match for it instead
var softUpgrades = parseSoftUnitUpgradesData();
var lvl = extractResearchUpgradeLevel(researchName);
// console.log('research softs', { researchName, researchNameWithoutParanthesis, lvl });
if (lvl > 1) {
var unitTypes = tryMatchSoftUpgrade(researchNameWithoutParanthesis, lvl);
// console.log("soft upgr result", { unitTypes });
// if more than 3 hits, then dont write anything (too common upgrade, e.g. "Engine Upgrade (Lvl 2)"
if (unitTypes.length && unitTypes.length <= 3) {
var softMatchStr = unitTypes.join(' / ');
setNewResearchContent(content, idxStart, idxEnd, researchName, softMatchStr, desc);
}
}
}
}
}
}, 1000);
}
function setNewResearchContent(content, idxStart, idxEnd, researchName, matchName, el) {
var style = 'text-decoration: underline';
var newContent =
content.substring(0, idxStart) +
researchName +
' <span style="' +
style +
'">[ ' +
matchName +
' ]</span>' +
content.substring(idxEnd);
// console.log("NEW RESEARCH CONTENT", newContent);
el.innerHTML = newContent;
}
function extractResearchUpgradeLevel(researchName) {
var x = /(\d\))$/.exec(researchName)[0].replace(')', '');
if (!isNaN(x)) {
return parseInt(x);
} else {
return null;
}
}
function tryMatchSoftUpgrade(researchName, lvl) {
let data = parseSoftUnitUpgradesData();
var idx = lvl - 1;
var matches = [];
for (let key in data) {
if (data.hasOwnProperty(key)) {
try {
if (data[key][idx] === researchName) {
matches.push(key);
}
} catch (error) {}
}
}
return matches;
}
function tryMatchUnitType(researchName) {
let data = parseUnitDoctrineData();
for (let key in data) {
if (data.hasOwnProperty(key)) {
if (data[key].some((str) => str == researchName)) {
// console.log("MATCH FOUND!", { type: key, needle: researchName });
return key;
}
}
}
return null;
}
// format: { [key: string]: string[] }
// where key = unit type, and array = unit type names in the doctrines
// e.g. { 'Attack Submarine': ['Los Angeles Class', ...]
var _parsedUnitDoctrineData = null;
function parseUnitDoctrineData() {
if (_parsedUnitDoctrineData) {
return _parsedUnitDoctrineData;
}
let rowSplit = _unitDoctrineData.split('\n');
var result = {};
for (let i = 0; i < rowSplit.length; i++) {
let rawRow = rowSplit[i];
let cols = rawRow.split('\t');
let values = cols.filter((x) => !!x && x.length);
let unitType = values.shift();
result[unitType] = values;
}
_parsedUnitDoctrineData = result;
return _parsedUnitDoctrineData;
}
var _parsedSoftUpgradesData = null;
function parseSoftUnitUpgradesData() {
if (_parsedSoftUpgradesData) {
return _parsedSoftUpgradesData;
}
let rowSplit = _unitSoftUpgradesData.split('\n');
var result = {};
for (let i = 0; i < rowSplit.length; i++) {
let rawRow = rowSplit[i];
let cols = rawRow.split('\t');
// let values = cols.filter(x => !!x && x.length);
let values = cols;
let unitType = values.shift();
result[unitType] = values;
}
_parsedSoftUpgradesData = result;
return _parsedSoftUpgradesData;
}
/****************************** EVENT FILTERS ******************************/
function onChangeFilters() {
console.log('onChangeFilters()');
var typeFilterValue = getEventFilterTypeValue();
var countryFilterValue = getEventFilterCountryValue();
// console.log("FILTER VALUES", { typeFilterValue, countryFilterValue });
var eventElems = document.querySelectorAll('#eventsContainer .content .overview ul li');
// console.log('eventElems', { eventElems });
for (var i = 0; i < eventElems.length; i++) {
var evEl = eventElems[i];
var typeOk = evalFilterType(evEl, typeFilterValue);
var countryOk = evalFilterCountry(evEl, countryFilterValue);
// console.log('EV FILTER RESULT', { evEl, typeOk, countryOk, typeFilterValue, countryFilterValue });
var show = typeOk && countryOk;
if (show) {
evEl.removeAttribute('hidden');
} else {
evEl.setAttribute('hidden', '');
}
}
}
function addTypeFilterSelect(elem) {
log('addTypeFilterSelect()');
let wrapper = document.createElement('div');
wrapper.id = 'confetti-event-wrapper-type';
wrapper.style.marginRight = '1rem';
// add a label for type select
let filterLabel = document.createElement('span');
filterLabel.innerText = 'Type: ';
wrapper.appendChild(filterLabel);
// add type select w/ options
let filterSelect = document.createElement('select');
filterSelect.id = _eventFilterTypeId;
wrapper.appendChild(filterSelect);
filterSelect.style.padding = '0.5rem';
addOptionToParent('All', 'ALL', filterSelect);
addOptionToParent('Combat', 'COM', filterSelect);
addOptionToParent('Territories', 'TER', filterSelect);
addOptionToParent('Agents', 'AGE', filterSelect);
addOptionToParent('Research', 'RES', filterSelect);
addOptionToParent('City Production', 'CIT', filterSelect);
addOptionToParent('Diplomacy', 'DIP', filterSelect);
filterSelect.addEventListener('change', onChangeFilters);
elem.append(wrapper);
}
var _eventFilterTypeId = 'confetti-filter-type-select';
function getEventFilterTypeValue() {
return document.getElementById('' + _eventFilterTypeId).value;
}
var _eventFilterCountryId = 'confetti-filter-country-select';
function getEventFilterCountryValue() {
return document.getElementById('' + _eventFilterCountryId).value;
}
function evalFilterType(evEl, filter) {
if (!filter || filter == '' || filter == 'ALL') return true;
var desc = evEl.querySelector('.event-description');
var content = '';
// console.log('evalFilterType', { desc, filter });
if (desc) {
content = desc.innerText;
} else {
return true;
}
var show = true;
var keywordsToSearchFor;
if (filter === 'COM') {
keywordsToSearchFor = ['Enemy Defeated', 'Fighting.', 'Friendly Unit Lost', 'Civilian Casualties'];
} else if (filter === 'TER') {
keywordsToSearchFor = ['Province Entered', 'Territory Lost', 'Territory Conquered'];
} else if (filter === 'AGE') {
keywordsToSearchFor = ['Agent'];
} else if (filter === 'RES') {
keywordsToSearchFor = ['Research Completed'];
} else if (filter === 'CIT') {
keywordsToSearchFor = ['built in', 'mobilized'];
} else if (filter === 'DIP') {
keywordsToSearchFor = ['New Article Published', 'Message Received', 'Diplomatic Status Changed', 'the coalition'];
}
if (keywordsToSearchFor && keywordsToSearchFor.length) {
show = keywordsToSearchFor.map((x) => content.includes(x)).some((match) => match === true);
}
return show;
}
function evalFilterCountry(evEl, filter) {
if (!filter || filter == '') return true;
var attr = evEl.getAttribute('data-country');
return attr === filter;
}
function addCountryFilterSelect(elem) {
log('addCountryFilterSelect()');
var countries = detectCountriesInEvents();
// console.log("COUNTRIES", countries);
let wrapper = document.createElement('div');
wrapper.id = 'confetti-event-wrapper-country';
wrapper.style.marginRight = '1rem';
// add a label for type select
let filterLabel = document.createElement('span');
filterLabel.innerText = 'Country: ';
wrapper.appendChild(filterLabel);
// add type select w/ options
let filterSelect = document.createElement('select');
filterSelect.id = _eventFilterCountryId;
wrapper.appendChild(filterSelect);
filterSelect.style.padding = '0.5rem';
// add options
addOptionToParent('All', '', filterSelect);
for (var i = 0; i < countries.length; i++) {
var c = countries[i];
addOptionToParent(c.value, c.key, filterSelect);
}
filterSelect.addEventListener('change', onChangeFilters);
elem.append(wrapper);
}
function detectCountriesInEvents() {
// NOTE : THIS METHOD BOTH DETECTS COUNTRIES AND SETS [data-country] ATTR ON EVENT
var eventElems = document.querySelectorAll('#eventsContainer .content .overview ul li');
// var filter = event.target.value;
var countriesLower = [];
for (var i = 0; i < eventElems.length; i++) {
var evEl = eventElems[i];
var finds = evEl.querySelectorAll('.small_flag_container img');
if (finds.length === 0) continue;
var el = finds[0];
var imgSrc = el.getAttribute('src');
var countryName = imgSrc.split('small_')[1].split('.png')[0];
// set data-country attr on event elem
evEl.setAttribute('data-country', countryName);
// add to list of possible countries, if not there already
if (countriesLower.indexOf(countryName) === -1) {
countriesLower.push(countryName);
}
}
var result = countriesLower.map((s) => {
if (_flagCountryNameDict[s]) {
return { key: s, value: _flagCountryNameDict[s] };
} else {
return { key: s, value: toUpperCaseFirst(s) };
}
});
// finally, sort
console.log('>>>>>> countries result', { result });
result.sort((a, b) => (a.value < b.value ? -1 : 1));
return result;
}
/************************ MISC METHODS *******************************/
function getGameId() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('gameID');
}
function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function addOptionToParent(displayName, value, parentElem) {
let node = document.createElement('option');
node.value = value;
node.innerText = displayName;
parentElem.appendChild(node);
}
function log(msg) {
if (typeof msg === 'string') {
console.log('[CONfetti] ' + msg);
} else {
console.log('[CONfetti] ', msg);
}
}
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
function toUpperCaseFirst(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
/************************ STATIC DATA *******************************/
const _flagCountryNameDict = {
thechosen: 'The Chosen',
roguestate: 'Rogue State',
};
const _unitDoctrineData = `Motorized Infantry Basic Infantry Advanced Infantry Modern Infantry
Mechanized Infantry Basic Mechanized Advanced Mechanized Modern Mechanized
Naval Infantry Basic Marines Advanced Marines Modern Marines
Airborne Infantry Basic Airborne Advanced Airborne Modern Airborne
Special Forces Basic Rangers Advanced Rangers Modern Rangers Basic SAS Advanced SAS Modern SAS Basic Spetsnaz Advanced Spetsnaz Modern Spetsnaz
National Guard Basic National Guard Advanced National Guard Modern National Guard
Combat Recon Vehicle M113 Recon M1117 RSTA LAV-25 Fox FV721 VEC-M1 Griffon VBMR BRDM-1 BRDM-2 BRDM-3
Armored Fighting Vehicle M551 Sheridan M2 Bradley M3 Bradley Scorpion FV Warrior Puma BMP-2 BMP-3 Dragon T-15
Amphibious Combat Vehicle LVTP-7 AAVP-7A1 ACV 1.1 Fuchs Piranha VCBI II BTR-80 BTR-90 Bumerang
Main Battle Tank M1A1 Abrams M1A2 Abrams M1A3 Abrams Leopard 2 Challenger 2 Leopard 2A7+ T-80 T-90 T-14 Armata
Tank Destroyer M56 Scorpion M901 ITV M1134 Stryker ATGM Kanonenjagdpanzer AMX-10 RC Centauro 2S25 Sprut-SD BMPT Terminator BMPT-72 Terminator 2
Towed Artillery M198 Howitzer M119 Howitzer M777 Howitzer FH70 TRF1 155 GH 52 APU D-30 Howitzer 2A36 Giatsint-B 2A Msta-B
Mobile Artillery M110 Howitzer M109 Howitzer M1203 NLOS GCT 155mm AS-90 Braveheart Panzerhaubitzer 2000 2S3 Akatsiya 2S19 Msta-S 2S35 Koalitsiya-SV
Multiple Rocket Launcher M270 MLRS M270A1 MLRS M142 HIMARS Teruel M270 B1 LRSVM Morava BM-21 Grad BM-30 Smerch 9A52-4 Tornado
Mobile Anti-Air Vehicle M163 VADS M247 Sergeant York LAV-AD Air Defense Gepard Otomatic Marksman AZU-57-2 ZSU-23-4 Shilka 2K22 Tunguska
Mobile SAM Launcher MIM-23 Hawk MIM-72 Chaparral AN/TWQ-1 Avenger Ozelot Crotale Stormer HVM 9K35 Strela-10 BUK M1 Pantsir-S1
Theater Defense System MIM-14 Nike MIM-104 Patriot THAAD Missile Defence Bloodhound MEADS SAMP/T S-125 Neva S-300 S-400 Triumf
Mobile Radar LCM RADAR ELEC EQ-36 PATRIOT AN/MPQ-53 UNIMOG SCB MARS-L Ground Master 400 1L121-E KASTA Nebo-M
Helicopter Gunship Kiowa UH-1Y Venom Armed Black Hawk Gazelle Super Puma NH-90 Mi-8 TVK Mi-24 Hind Mi-35M
Attack Helicopter AH-1G Cobra AH-1Z Viper AH-64D Apache Longbow A129 Mangusta AW Apache AH64D Tiger Ka-50 Black Shark Ka-52 Alligator Mi-28 Havoc
ASW Helicopter SH-3 Sea King SH-2 Super Seasprite MH-60R Seahawk AB 212ASW Panther AW159 Wildcat Ka-25 Mi-14 Haze Ka-27 Helix
Air Superiority Fighter F-5 Tiger F-16A Fighting Falcon F-16V Viper J 35A Draken Mirage F1 Typhoon MiG-23 Flogger MiG-29 Fulcrum MiG-35 Super Fulcrum
Naval Air Superiority Fighter F-4 Phantom II F-14A Tomcat F-14D Super Tomcat Étendard IVM Jaguar M Rafale M Yak-141 Su-33 Flanker D MiG-29K
Stealth Air Superiority Fighter F-22 Raptor MBB Firefly Su-47 Berkut
Strike Fighter F-111 Aardvark F-15 Strike Eagle F-15 Silent Eagle Mirage Delta 2000 Tornado JAS 39 Gripen Su-24 Fencer Su-27 Flanker Su-35 Super Flanker
Naval Strike Fighter A-6 Intruder A-7 Corsair II F-18 Super Hornet Harrier Super Étendard Harrier II Plus Yak-38 Su-27K Su-35K
Stealth Strike Fighter F-35 Lightning II F-117 Nighthawk Su-T50 PakFa
UAV MQ1-Predator RQ-9 Global Hawk X-47B Super Heron MQ9-Reaper NEUROn ZOND II United 40 B5 MIG SKAT
Naval Patrol Aircraft P-3 Orion CP-140 Aurora P-8 Poseidon Nimrod CN-235 CASA C295 Persuader Tu-142 Bear Il-38 Dolphin A-40 Albatros
AWACS EC-121 Warning Star E-3 Sentry E-8 Joint STARS EC-121 Warning Star E-3 Sentry E-8 Joint STARS Tu-126 A-50 Mainstay A-100
Naval AWACS E-2 Hawkeye Bombardier Globaleye Tu-126XXXXX
Heavy Bomber B-47 Stratojet B-52 Stratofortress B-1 Lancer Valiant Victor Vulcan Tu-95 Bear Tu-22M Backfire Tu-160 White Swan
Stealth Bomber B-2 Spirit SR71 Blackbird Tu-PakDa
Corvette Hamilton Class Cyclone Class Freedom Class LCS Descubierta Class Göteborg Class Braunschweig Class Albatros Class Steregushchiy Class Gremyashchiy Class
Frigate Garcia Class Knox Class Perry Class Duke Class Bremen Class Horizon Class Krivak Class Neutrashimy Class Admiral Gorshkov Class
Destroyer Farragut Class Spruance Class Arleigh Burke Class Hamburg Class Gloucester Class Daring Class Kashin Class Sovremennyy Class Lider Class
Cruiser California Class Virginia Class Ticonderoga Class Tiger Class Vittorio Veneto Class Absalon Class Kresta II Class Kara Class Slava Class
Aircraft Carrier Kitty Hawk Class Nimitz Class Gerald R. Ford Class Giuseppe Garibaldi Class Charles de Gaulle Class Queen Elizabeth Class Kiev Class Kuznetsov Class Ulyanovsk Class
Attack Submarine Los Angeles Class Seawolf Class Virginia Class Swiftsure Class Rubis Class Astute Class Viktor Class Akula Class Yasen Class
Ballistic Missile Submarine Benjamin Franklin Class Ohio Class Columbia Class Resolution Class Vanguard Class Triomphant Class Delta Class Typhoon Class Borey Class
ICBM Minuteman III GBSD M51.1 M51.2 RT-2PM Topol RS-26 Rubezh
Ballistic Missile Pershing I Pershing II Pershing III PGM-17 Thor SSBS S3 J-600T Scud SS-20 Saber 9K720 Iskander
Cruise Missile Gryphon Tomahawk LRSO RBS-15 KEPD 350 Storm Shadow P-500 Bazalt Kh-55 3M-54 Klub`;
const _unitSoftUpgradesData = `Motorized Infantry Engine Upgrade I Man Portable Air Defense Engine Upgrade II Personal Armor
Mechanized Infantry Engine Upgrade NBC Protection Reinforced Armor
Naval Infantry Engine Upgrade Portable Air Defense NBC Protection
Airborne Infantry Jungle Warfare Training Rapid Deployment Training Woodland Warfare Training Advanced Ballistic Armor
Special Forces Portable Air Defense Amphibious Warfare Training
National Guard Personal Armor Rapid Deployment Training I Rapid Deployment Training II Streamlined Mobilization
Combat Recon Vehicle Engine Upgrade Air Assault NBC Protection Reinforced Armor
Armored Fighting Vehicle Ground-to-Air Armament Upgrade Reinforced Armor NBC Protection Urban Survival Kit
Amphibious Combat Vehicle
Main Battle Tank Reinforced Armor Engine Upgrade NBC Protection Urban Survival Kit
Tank Destroyer Anti Personnel Ammunition Engine Upgrade Air Assault Reinforced Armor
Towed Artillery Rocket Assisted Projectiles Air Assault Enhanced Optical Sights Extended Barrel Upgrade
Mobile Artillery Rocket Assisted Projectiles Reinforced Armor NBC Protection
Multiple Rocket Launcher Improved Rocket Range Engine Upgrade
Mobile Anti-Air Vehicle Reinforced Armor Engine Upgrade Air Assault Ground-to-Air Armament Upgrade
Mobile SAM Launcher Improved Missile Range Engine Upgrade Air Assault
Theater Defense System Improved Missile Range Survivability Kit Stealth Locating System
Mobile Radar Advanced Sensors Array Engine Upgrade Stealth Locating System
Helicopter Gunship Bulletproofing Engine Upgrade AT Missile Pods Fuel Optimization Measures
Attack Helicopter Bulletproofing Fuel Optimization Measures Engine Upgrade Streamlined Mobilization
ASW Helicopter Fuel Optimization Measures Advanced Sensors Array Anti-Surface Warfare Kit
Air Superiority Fighter Reinforced Airframe Engine Replacement Fuel Optimization Measures Streamlined Mobilization
Naval Air Superiority Fighter
Stealth Air Superiority Fighter
Strike Fighter Reinforced Airframe Air-to-Air Armament Upgrade Fuel Optimization Measures Streamlined Mobilization
Naval Strike Fighter
Stealth Strike Fighter
UAV Fuel Optimization Measures Engine Replacement Reinforced Airframe
Naval Patrol Aircraft Advanced Sensor Array Cruise Missile Hardpoints
AWACS Reinforced Airframe Advanced Sensor Array Stealth Locating System
Naval AWACS
Heavy Bomber Reinforced Airframe Fuel Optimization Measures Increased Missile Hardpoints Bunker Busting Ordnance
Stealth Bomber
Corvette Survivability Refit Streamlined Mobilization Engine Overhaul Air Defense Upgrade
Frigate AA Envelope Expansion Point-Defense Upgrade Engine Overhaul Stealth Locating System
Destroyer Engine Overhaul Air Defense Upgrade Survivability Refit
Cruiser Survivability Refit Expanded Missile Magazine
Aircraft Carrier Air Defense Upgrade Point-Defense Upgrade
Attack Submarine Survivability Refit Nuclear Reactor Refit Expanded Missile Magazine
Ballistic Missile Submarine Nuclear Reactor Refit Cruise Missile Launch System Improved Reloading System Expanded Missile Magazine
ICBM Fuel Improvement Warhead Shielding
Ballistic Missile Fuel Improvement Booster Upgrade Warhead Shielding
Cruise Missile Booster Upgrade Fuel Improvement Warhead Shielding `;
const _unitBuildingLevelsData = `Army Motorized Infantry Mechanized Infantry Special Forces Multiple Rocket Launcher Theater Defense System
National Guard Naval Infantry Mobile Artillery
Combat Recon Vehicle Airmobile Infantry Mobile SAM Launcher
Mobile Anti-Air Vehicle Armored Fighting Vehicle Tank Commander
Infantry Officer Amphibious Combat Vehicle
Airborne Officer Main Battle Tank
Tank Destroyer
Towed Artillery
Mobile Radar
Air Helicopter Gunship Attack Helicopter Naval Strike Fighter AWACS Stealth Air Superiority Fighter
Air Superiority Fighter ASW Helicopter Naval Patrol Aircraft Naval AWACS Stealth Strike Fighter
UAV Naval Air Superiority Fighter Heavy Bomber Stealth Bomber
Rotary Wing Officer Strike Fighter
Naval Corvette Destroyer Cruiser Aircraft Carrier
Frigate Attack Submarine Ballistic Missile Submarine
Naval Officer Submarine Commander `;
const _unitBuildlingPopupCss = `
thead.ext-ub-head {
border-bottom: 1px solid #fff;
}
td.ext-ub-cell-category {
border-right: 1px solid #fff;
}
#ExtUbTable tbody td {
padding: 1rem;
vertical-align: unset;
}
`;
const _unitBuildlingLevelTableTemplate = `<table id="ExtUbTable">
<thead class="ext-ub-head">
<tr>
<th>Building</th>
<th>Level 1</th>
<th>Level 2</th>
<th>Level 3</th>
<th>Level 4</th>
<th>Level 5</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ext-ub-cell-category">Army Base</td>
<td id="army_1"></td>
<td id="army_2"></td>
<td id="army_3"></td>
<td id="army_4"></td>
<td id="army_5"></td>
</tr>
<tr>
<td class="ext-ub-cell-category">Air Base</td>
<td id="air_1"></td>
<td id="air_2"></td>
<td id="air_3"></td>
<td id="air_4"></td>
<td id="air_5"></td>
</tr>
<tr>
<td class="ext-ub-cell-category">Naval Base</td>
<td id="naval_1"></td>
<td id="naval_2"></td>
<td id="naval_3"></td>
<td id="naval_4"></td>
<td id="naval_5"></td>
</tr>
</tbody>
</table>`;
// NOTE : temp until code for 'native' icons are in place
const _buildingIconSvg = `<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 442 442" style="enable-background:new 0 0 442 442;" xml:space="preserve">
<g>
<path d="M382,0H60c-5.523,0-10,4.477-10,10v422c0,5.523,4.477,10,10,10h322c5.523,0,10-4.477,10-10V10C392,4.477,387.523,0,382,0z
M295,422h-55V279h55V422z M372,422h-57V269c0-5.523-4.477-10-10-10h-75c-5.523,0-10,4.477-10,10v153H70V20h302V422z"/>
<path d="M103,128h50c5.523,0,10-4.477,10-10V53c0-5.523-4.477-10-10-10h-50c-5.523,0-10,4.477-10,10v65
C93,123.523,97.477,128,103,128z M113,63h30v45h-30V63z"/>
<path d="M196,128h50c5.523,0,10-4.477,10-10V53c0-5.523-4.477-10-10-10h-50c-5.523,0-10,4.477-10,10v65
C186,123.523,190.477,128,196,128z M206,63h30v45h-30V63z"/>
<path d="M289,128h50c5.523,0,10-4.477,10-10V53c0-5.523-4.477-10-10-10h-50c-5.523,0-10,4.477-10,10v65
C279,123.523,283.477,128,289,128z M299,63h30v45h-30V63z"/>
<path d="M103,236h50c5.523,0,10-4.477,10-10v-65c0-5.523-4.477-10-10-10h-50c-5.523,0-10,4.477-10,10v65
C93,231.523,97.477,236,103,236z M113,171h30v45h-30V171z"/>
<path d="M196,236h50c5.523,0,10-4.477,10-10v-65c0-5.523-4.477-10-10-10h-50c-5.523,0-10,4.477-10,10v65
C186,231.523,190.477,236,196,236z M206,171h30v45h-30V171z"/>
<path d="M289,236h50c5.523,0,10-4.477,10-10v-65c0-5.523-4.477-10-10-10h-50c-5.523,0-10,4.477-10,10v65
C279,231.523,283.477,236,289,236z M299,171h30v45h-30V171z"/>
<path d="M103,344h50c5.523,0,10-4.477,10-10v-65c0-5.523-4.477-10-10-10h-50c-5.523,0-10,4.477-10,10v65
C93,339.523,97.477,344,103,344z M113,279h30v45h-30V279z"/>
</g></svg>`;