// ==UserScript==
// @author EisFrei - fork by DanielOnDiordna
// @name Live Inventory
// @category Info
// @version 0.0.17.20210724.002500
// @homepageURL https://github.com/EisFrei/IngressLiveInventory
// @updateURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/DanielOnDiordna/liveInventory.meta.js
// @downloadURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/DanielOnDiordna/liveInventory.user.js
// @description [EisFrei-0.0.17.20210724.002500] Show current in-game inventory. Requires CORE subscription. Fork by DanielOnDiordna https://github.com/DanielOndiordna/IngressLiveInventory extras: menu buttons, refresh feedback, portals list key count, capsule view, keys in capsule view on portals, zoom keys, draw bookmarks.
// @id liveInventory@DanielOnDiordna
// @namespace https://softspot.nl/ingress/
// @match https://intel.ingress.com/*
// @grant none
// ==/UserScript==
function wrapper(plugin_info) {
// ensure plugin framework is there, even if iitc is not yet loaded
if(typeof window.plugin !== 'function') window.plugin = function() {};
// use own namespace for plugin
window.plugin.LiveInventory = function() {};
var self = window.plugin.LiveInventory;
self.id = 'LiveInventory';
self.title = 'Live Inventory';
self.version = '0.0.17.20210724.002500';
self.author = 'EisFrei - fork by DanielOnDiordna';
self.changelog = `
Changelog:
version 0.0.12.20210313.211400
- fork from github version 0.0.12
- replaced tab indent with 4 spaces
- fixed plugin script formatting to make it work on IITC.me 0.26
- replace variable thisPlugin by self
- split menu into 3 submenus
- autosave and apply settings changes
- changed colomn order, count first
- added inventory refresh button
- added alerts and console messages when refreshing inventory with a nice time string
- renew menu when refreshing inventory
- added portals list plugin column with key count
version 0.0.12.20210415.160000
- IITC me compatibility fix for getMapZoomTileParameters
version 0.0.12.20210421.190200
- minor fix for IITC CE where runHooks iitcLoaded is executed before addHook is defined in this plugin
version 0.0.12.20210524233900
- added runhooks pluginLiveInventoryUpdated to return an object of itemCount, keykeyCount and keyMap objects
- added runhooks pluginLiveInventorySubscription to return subscription data
- added the display of items/keys count inside every capsule, in table and in portal details
- added a capsules tab with overview of items/keys counts
- scrollable list with fixed header/buttons at the top (TO DO: needs a fix for table header alignment > DONE 0.0.15.20210613.235900)
- added refresh anyway button, to override the 10min interval
version 0.0.13.20210528210100
- changed icon text color to bright yellow (instead of soft yellow)
- changed icon text size to 11px (instead of 10px)
- added icon display settings option to display count of keys outside capsules and keys in capsules (between brackets)
version 0.0.14.20210607.004200
- update portal counts on portals after inventory refresh
- rearranged and changed ajax queries with better callback handling
version 0.0.15.20210613.235900
- fixed table header alignment
- added setting to show detailed keys in capsules for selected portal
- applied internal changes to create HTML with DOM functions
version 0.0.15.20210614.235900
- applied even more internal changes to create HTML with DOM functions
version 0.0.16.20210617.001900
- added clickable capsule names to show capsule contents
- added capsule rename option
- added changelog button and author information on settings menu
version 0.0.17.20210706.224100
version 0.0.17.20210706.225200
version 0.0.17.20210706.230300
version 0.0.17.20210706.231300
version 0.0.17.20210707.094700
version 0.0.17.20210707.122800
- moved Copy Items and Copy Keys buttons to settings menu
- moved Settings menu button from top to bottom of the dialog
- added keys filter to see all, inventory or keys in capsules
- added dialog title key totals
- added zoom all for keys, also for capsules
- added draw bookmarks button for all keys, keys in inventory or capsules (only if bookmarks plugin is enabled)
- added colors for Quantum Capsule count: red if 100, orange if >= 95
- added portal count for keys in capsules
- applied auto sizing dialogs
version 0.0.17.20210724.002500
- prevent double plugin setup on hook iitcLoaded
`;
self.namespace = 'window.plugin.' + self.id + '.';
self.pluginname = 'plugin-' + self.id;
self.lastmenu = undefined;
self.lastsortcolumn = {Items:'Type',Keys:'Portal',Capsules:'Name'};
self.lastsortdirection = {
Items:{Count:true,Type:true,Rarity:true,Capsules:true},
Keys:{Count:true,Portal:true,Distance:true,Capsules:true},
Capsules:{Capsule:true,Name:true,Count:true,Items:true,Keys:true,Type:true},
};
self.lastsortkeys = 'name';
self.lastsortkeysdirection = 1;
self.lastsortcapsule = 'name';
self.lastsortcapsuledirection = 1;
self.keyCount = [];
self.itemCount = [];
self.capsuleCount = [];
self.keyGuidCount = {};
self.keyIcon = '';
self.inventoryexpires = 0;
const KEY_SETTINGS = "plugin-live-inventory";
self.settings = {
displayMode: 'icon',
capsuleNames: '',
hideemptycapsules: false,
selectedportalcapsulekeys: false,
filterkeys: 'All'
};
const translations = {
BOOSTED_POWER_CUBE: 'Hypercube', // resourceType
CAPSULE: 'Capsule', // resourceType
DRONE: 'Drone', // resourceType
INTEREST_CAPSULE: 'Quantum Capsule', // resourceType
KEY_CAPSULE: 'Key Capsule', // resourceType
KINETIC_CAPSULE: 'Kinetic Capsule', // resourceType
PLAYER_POWERUP: 'Apex', // resourceType
PORTAL_LINK_KEY: 'Key', // resourceType
PORTAL_POWERUP: 'Fracker', // resourceType
EMITTER_A: 'Resonator',
EMP_BURSTER: 'XMP',
EXTRA_SHIELD: 'Aegis Shield',
FLIP_CARD: 'Virus',
FORCE_AMP: 'Force Amp',
HEATSINK: 'HS',
LINK_AMPLIFIER: 'LA',
MEDIA: 'Media',
MULTIHACK: 'Multi-Hack',
POWER_CUBE: 'PC',
RES_SHIELD: 'Shield',
TRANSMUTER_ATTACK: 'ITO -',
TRANSMUTER_DEFENSE: 'ITO +',
TURRET: 'Turret',
ULTRA_LINK_AMP: 'Ultra-Link',
ULTRA_STRIKE: 'US',
};
function getSubscription(callback,errorcallback,retrycnt) {
retrycnt = retrycnt || 0;
window.postAjax('getHasActiveSubscription', {
}, (data, textStatus, jqXHR) => {
if (!data || !(data instanceof Object) || data.result !== true) {
retrycnt--;
if (retrycnt >= 0)
setTimeout(function() { getSubscription(callback,errorcallback,retrycnt); },500);
else if (typeof errorcallback == 'function')
errorcallback(textStatus);
} else if (typeof callback == 'function')
callback(data);
}, (jqXHR, textStatus, errorThrown) => {
retrycnt--;
if (retrycnt >= 0)
setTimeout(function() { getSubscription(callback,errorcallback,retrycnt); },500);
else if (typeof errorcallback == 'function')
errorcallback(textStatus);
});
}
function getInventory(callback,errorcallback,retrycnt) {
retrycnt = retrycnt || 0;
window.postAjax('getInventory', {
'lastQueryTimestamp': 0
}, (data, textStatus, jqXHR) => {
if (!data || !(data instanceof Object) || !(data.result instanceof Object)) {
retrycnt--;
if (retrycnt >= 0)
setTimeout(function() { getInventory(callback,errorcallback,retrycnt); },500);
else if (typeof errorcallback == 'function')
errorcallback(textStatus);
} else if (typeof callback == 'function')
callback(data);
}, (jqXHR, textStatus, errorThrown) => {
retrycnt--;
if (retrycnt >= 0)
setTimeout(function() { getInventory(callback,errorcallback,retrycnt); },500);
else if (typeof errorcallback == 'function')
errorcallback(textStatus);
});
}
function parseCapsuleNames(str) {
const reg = new RegExp(/^([0-9a-f]{8}):(.+)$/, 'i');
str = str || '';
const map = {};
const rows = str.split('\n')
.map(e => reg.exec(e))
.filter(e => e && e.length === 3)
.forEach(e => map[e[1]] = e[2]);
return map;
}
function svgToIcon(str, s) {
const url = ("data:image/svg+xml," + encodeURIComponent(str)).replace(/#/g, '%23');
return new L.Icon({
iconUrl: url,
iconSize: [s, s],
iconAnchor: [s / 2, s / 2],
className: 'no-pointer-events', //allows users to click on portal under the unique marker
})
}
function createIcons() {
self.keyIcon = svgToIcon(``, 15);
}
function addItemToCount(item, countMap, incBy, moniker) {
let key;
let type;
let flipCardType;
let newCountMap;
let resourceRarity;
if (item[2] && item[2].resource && item[2].timedPowerupResource) {
key = `${item[2].resource.resourceType} ${item[2].timedPowerupResource.designation}`;
type = `Powerup ${translations[item[2].timedPowerupResource.designation] || item[2].timedPowerupResource.designation}`;
newCountMap = item[2].resource;
} else if (item[2] && item[2].resource && item[2].flipCard) {
key = `${item[2].resource.resourceType} ${item[2].flipCard.flipCardType}`;
type = `${translations[item[2].resource.resourceType]} ${item[2].flipCard.flipCardType}`;
newCountMap = item[2].resource;
flipCardType = item[2].flipCard.flipCardType;
} else if (item[2] && item[2].resource) {
key = `${item[2].resource.resourceType} ${item[2].resource.resourceRarity}`;
type = `${translations[item[2].resource.resourceType]}`;
newCountMap = item[2].resource;
} else if (item[2] && item[2].resourceWithLevels) {
key = `${item[2].resourceWithLevels.resourceType} ${item[2].resourceWithLevels.level}`;
type = `${translations[item[2].resourceWithLevels.resourceType]} ${item[2].resourceWithLevels.level}`;
newCountMap = item[2].resourceWithLevels;
resourceRarity = 'COMMON';
} else if (item[2] && item[2].modResource) {
key = `${item[2].modResource.resourceType} ${item[2].modResource.rarity}`;
type = `${translations[item[2].modResource.resourceType]}`;
newCountMap = item[2].modResource;
resourceRarity = item[2].modResource.rarity;
} else {
console.log(item);
}
if (key) {
if (!countMap[key]) {
countMap[key] = newCountMap;
countMap[key].count = 0;
countMap[key].type = type;
countMap[key].capsules = [];
countMap[key].capsuleCounts = {};
}
if (flipCardType) countMap[key].flipCardType = flipCardType;
if (resourceRarity) countMap[key].resourceRarity = resourceRarity;
if (moniker && countMap[key].capsules.indexOf(moniker) === -1) {
countMap[key].capsules.push(moniker);
countMap[key].capsuleCounts[moniker] = 0;
}
if (moniker) countMap[key].capsuleCounts[moniker] += incBy;
countMap[key].count += incBy;
}
}
function prepareItemCounts(data) {
if (!data || !data.result) {
return [];
}
const countMap = {};
data.result.forEach((item) => {
addItemToCount(item, countMap, 1);
if (item[2].container) {
item[2].container.stackableItems.forEach((item2) => {
addItemToCount(item2.exampleGameEntity, countMap, item2.itemGuids.length, item[2].moniker.differentiator);
});
}
});
const countList = Object.values(countMap);
countList.sort((a, b) => {
if (a.type === b.type) {
return 0;
}
return a.type > b.type ? 1 : -1;
});
return countList;
}
function HexToSignedFloat(num) {
let int = parseInt(num, 16);
if ((int & 0x80000000) === -0x80000000) {
int = -1 * (int ^ 0xffffffff) + 1;
}
return int / 10e5;
}
function addKeyToCount(item, countMap, incBy, moniker) {
if (item[2] && item[2].resource && item[2].resource.resourceType && item[2].resource.resourceType === 'PORTAL_LINK_KEY') {
const key = `${item[2].portalCoupler.portalGuid}`;
if (!countMap[key]) {
countMap[key] = item[2];
countMap[key].count = 0;
countMap[key].capsules = [];
countMap[key].capsuleCounts = {};
}
if (moniker && countMap[key].capsules.indexOf(moniker) === -1) {
countMap[key].capsules.push(moniker);
countMap[key].capsuleCounts[moniker] = 0;
}
if (moniker) countMap[key].capsuleCounts[moniker] = incBy;
countMap[key].count += incBy;
}
}
function prepareKeyCounts(data) {
if (!data || !data.result) {
return [];
}
const countMap = {};
data.result.forEach((item) => {
addKeyToCount(item, countMap, 1);
if (item[2].container) {
item[2].container.stackableItems.forEach((item2) => {
addKeyToCount(item2.exampleGameEntity, countMap, item2.itemGuids.length, item[2].moniker.differentiator);
});
}
});
const countList = Object.values(countMap);
countList.sort((a, b) => {
if (a.portalCoupler.portalTitle === b.portalCoupler.portalTitle) {
return 0;
}
return a.portalCoupler.portalTitle.toLowerCase() > b.portalCoupler.portalTitle.toLowerCase() ? 1 : -1;
});
return countList;
}
function prepareCapsuleCounts(data) {
if (!data || !data.result) {
return [];
}
const countMap = {};
data.result.forEach((item) => {
if (item[2].container && item[2].moniker) {
let id = item[2].moniker.differentiator;
countMap[id] = {
id: id,
count: item[2].container.currentCount,
itemscount: 0,
keyscount: 0,
type: translations[item[2].resource.resourceType] || item[2].resource.resourceType,
items: [],
keys: []
}
}
});
for (let cnt = 0; cnt < self.keyCount.length; cnt++) {
for (let cnt2 = 0; cnt2 < self.keyCount[cnt].capsules.length; cnt2++) {
let id = self.keyCount[cnt].capsules[cnt2];
countMap[id].keys = countMap[id].keys.concat(self.keyCount[cnt]);
countMap[id].keyscount += self.keyCount[cnt].capsuleCounts[id];
}
}
for (let cnt = 0; cnt < self.itemCount.length; cnt++) {
if (self.itemCount[cnt].resourceType != "PORTAL_LINK_KEY") {
for (let cnt2 = 0; cnt2 < self.itemCount[cnt].capsules.length; cnt2++) {
let id = self.itemCount[cnt].capsules[cnt2];
countMap[id].items = countMap[id].items.concat(self.itemCount[cnt]);
countMap[id].itemscount += self.itemCount[cnt].capsuleCounts[id];
}
}
}
const countList = Object.values(countMap);
countList.sort((a, b) => {
if (a.type === b.type) {
return 0;
}
return a.type > b.type ? 1 : -1;
});
return countList;
}
function setCapsuleLinks(elementcapsules,capsuleCounts,capsulescell) {
if (elementcapsules.length == 0) return;
const capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
let capsules = [];
for (let cnt = 0; cnt < elementcapsules.length; cnt++) {
let capsuleid = elementcapsules[cnt];
capsules.push({
id: capsuleid,
name: (capsuleNames[capsuleid] || ''),
count: capsuleCounts[capsuleid]
});
}
capsules.sort(function(a, b) {
if (a.name + a.id === b.name + a.id) {
return 0;
}
return (a.name + a.id > b.name + a.id ? 1 : -1);
});
if (capsules.length == 1) {
let capsulelink = capsulescell.appendChild(document.createElement('a'));
capsulelink.style.display = 'block';
capsulelink.textContent = (capsules[0].name || capsules[0].id) + ' (' + capsules[0].count + ')';
capsulelink.addEventListener('click', function(e) {
e.preventDefault();
showCapsuleContent(capsules[0].id);
}, false);
} else if (capsules.length > 1) {
let capsuleslist = capsulescell.appendChild(document.createElement('select'));
capsules.map(function(capsule) {
let option = capsuleslist.appendChild(document.createElement('option'));
option.value = capsule.id;
option.textContent = (capsule.name || capsule.id) + ' (' + capsule.count + ')';
});
let capsulebutton = capsulescell.appendChild(document.createElement('input'));
capsulebutton.type = 'button';
capsulebutton.value = 'O';
capsulebutton.addEventListener('click', function(e) {
e.preventDefault();
showCapsuleContent(capsuleslist.value);
}, false);
}
};
function setKeyTableBody(keys,tablebody,orderBy, direction,filtercapsuleid) {
while (tablebody.rows.length > 0) {
tablebody.deleteRow(0);
}
const sortFunctions = {
Count: (a, b) => (a.count - b.count) * (direction ? 1 : -1),
Portal: (a, b) => {
if (a.portalCoupler.portalTitle === b.portalCoupler.portalTitle) {
return 0;
}
return (a.portalCoupler.portalTitle.toLowerCase() > b.portalCoupler.portalTitle.toLowerCase() ? 1 : -1) * (direction ? 1 : -1);
},
Distance: (a, b) => (a._distance - b._distance) * (direction ? 1 : -1),
Capsules: (a, b) => {
const sA = a.capsules.join(', ').toLowerCase();
const sB = b.capsules.join(', ').toLowerCase();
if (sA === sB) {
return 0;
}
return (sA > sB ? 1 : -1) * (direction ? 1 : -1);
}
};
keys.sort(sortFunctions[orderBy]);
let countkeys = 0;
let countportals = 0;
keys.map((el) => {
let keysincapsules = Object.values(el.capsuleCounts).reduce((a, b) => a + b, 0);
if (filtercapsuleid ||
self.settings.filterkeys == 'All' ||
(self.settings.filterkeys == 'Inventory' && el.count > keysincapsules) ||
(self.settings.filterkeys == 'Capsules' && keysincapsules > 0)) {
let displaycount = el.count;
if (filtercapsuleid) {
displaycount = el.capsuleCounts[filtercapsuleid];
} else {
switch (self.settings.filterkeys) {
case 'All':
displaycount = el.count;
break;
case 'Inventory':
displaycount = el.count - keysincapsules;
break;
case 'Capsules':
displaycount = keysincapsules;
break;
}
}
countkeys += displaycount;
countportals++; // rows
let row = tablebody.appendChild(document.createElement('tr'));
let countcell = row.appendChild(document.createElement('td'));
countcell.align = 'right';
countcell.textContent = displaycount;
let portalcell = row.appendChild(document.createElement('td'));
portalcell.style.whiteSpace = 'nowrap';
let portallink = portalcell.appendChild(document.createElement('a'));
portallink.textContent = el.portalCoupler.portalTitle;
portallink.href = "//intel.ingress.com/?pll=" + el._latlng.lat + "," + el._latlng.lng;
portallink.addEventListener('click', function(e) {
e.preventDefault();
window.zoomToAndShowPortal(el.portalCoupler.portalGuid,[el._latlng.lat,el._latlng.lng]);
},false);
let distancecell = row.appendChild(document.createElement('td'));
distancecell.style.whiteSpace = 'nowrap';
distancecell.align = 'right';
distancecell.textContent = el._formattedDistance;
if (!filtercapsuleid) {
let capsulescell = row.appendChild(document.createElement('td'));
capsulescell.style.whiteSpace = 'nowrap';
setCapsuleLinks(el.capsules,el.capsuleCounts,capsulescell);
}
}
});
return {countkeys:countkeys,countportals:countportals};
}
function setItemTableBody(items,tablebody,orderBy, direction,filtercapsuleid) {
while (tablebody.rows.length > 0) {
tablebody.deleteRow(0);
}
const sortFunctions = {
Type: (a, b) => {
if (a.type === b.type) {
return 0;
}
return (a.type.toLowerCase() > b.type.toLowerCase() ? 1 : -1) * (direction ? 1 : -1);
},
Rarity: (a, b) => {
if (a.resourceRarity === b.resourceRarity) {
return 0;
}
return (a.resourceRarity.toLowerCase() > b.resourceRarity.toLowerCase() ? 1 : -1) * (direction ? 1 : -1);
},
Count: (a, b) => (a.count - b.count) * (direction ? 1 : -1),
Capsules: (a, b) => {
const sA = a.capsules.join(', ').toLowerCase();
const sB = b.capsules.join(', ').toLowerCase();
if (sA === sB) {
return 0;
}
return (sA > sB ? 1 : -1) * (direction ? 1 : -1);
}
};
items.sort(sortFunctions[orderBy]);
items.map((el) => {
let row = tablebody.appendChild(document.createElement('tr'));
let countcell = row.appendChild(document.createElement('td'));
countcell.align = 'right';
countcell.textContent = (filtercapsuleid ? el.capsuleCounts[filtercapsuleid] : el.count);
let typecell = row.appendChild(document.createElement('td'));
typecell.textContent = el.type;
let raritycell = row.appendChild(document.createElement('td'));
raritycell.textContent = (el.resourceRarity || '');
if (!filtercapsuleid) {
let capsulescell = row.appendChild(document.createElement('td'));
capsulescell.style.whiteSpace = 'nowrap';
setCapsuleLinks(el.capsules,el.capsuleCounts,capsulescell);
}
});
}
function setCapsulesTableBody(capsules,tablebody,orderBy, direction) {
const capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
const sortFunctions = {
Capsule: (a, b) => {
if (a.id === b.id) {
return 0;
}
return (a.id > b.id ? 1 : -1) * (direction ? 1 : -1);
},
Name: (a, b) => {
let namea = (capsuleNames[a.id] || a.id).toLowerCase();
let nameb = (capsuleNames[b.id] || b.id).toLowerCase();
if (namea === nameb) {
return 0;
}
return (namea > nameb ? 1 : -1) * (direction ? 1 : -1);
},
Count: (a, b) => (a.count - b.count) * (direction ? 1 : -1),
Items: (a, b) => (a.itemscount - b.itemscount) * (direction ? 1 : -1),
Keys: (a, b) => (a.keyscount - b.keyscount) * (direction ? 1 : -1),
Type: (a, b) => {
if (a.type === b.type) {
return 0;
}
return (a.type > b.type ? 1 : -1) * (direction ? 1 : -1);
}
};
while (tablebody.rows.length > 0) {
tablebody.deleteRow(0);
}
capsules.sort(sortFunctions[orderBy]);
capsules.map((el) => {
if (!(self.settings.hideemptycapsules && el.count == 0)) {
let row = tablebody.appendChild(document.createElement('tr'));
let capsulecell = row.appendChild(document.createElement('td'));
let capsulelink = capsulecell.appendChild(document.createElement('a'));
capsulelink.style.display = 'block';
capsulelink.textContent = el.id;
capsulelink.addEventListener('click', function(e) {
e.preventDefault();
showCapsuleContent(el.id);
}, false);
let namecell = row.appendChild(document.createElement('td'));
namecell.textContent = (capsuleNames[el.id] || '');
let countcell = row.appendChild(document.createElement('td'));
countcell.align = 'right';
countcell.textContent = (el.count || '');
countcell.style.paddingRight = '5px';
if (el.type == 'Quantum Capsule' && el.count >= 95) {
if (el.count == 100) {
countcell.style.backgroundColor = 'red';
} else {
countcell.style.backgroundColor = 'darkorange';
countcell.style.color = 'black';
}
}
let itemscountcell = row.appendChild(document.createElement('td'));
itemscountcell.align = 'right';
itemscountcell.style.paddingRight = '5px';
itemscountcell.textContent = (el.itemscount || '');
let keyscountcell = row.appendChild(document.createElement('td'));
keyscountcell.align = 'right';
keyscountcell.style.paddingRight = '5px';
keyscountcell.textContent = (el.keyscount || '');
let typecell = row.appendChild(document.createElement('td'));
typecell.textContent = el.type;
}
});
}
function exportItems() {
const capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
function getCapsulesNameCount(el) {
let capsules = [];
for (let cnt = 0; cnt < el.capsules.length; cnt++) {
let capsuleid = el.capsules[cnt];
let capsulename = (capsuleNames[capsuleid] || capsuleid);
capsules.push(capsulename + ' (' + el.capsuleCounts[capsuleid] + ')');
}
return capsules.sort();
}
const str = ['Type\tRarity\tCount', ...self.itemCount.map((i) => [i.type, i.resourceRarity, i.count, getCapsulesNameCount(i).join(',')].join('\t'))].join('\n');
navigator.clipboard.writeText(str);
alert('Items are copied to your clipboard');
}
function exportKeys() {
const capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
function getCapsulesNameCount(el) {
let capsules = [];
for (let cnt = 0; cnt < el.capsules.length; cnt++) {
let capsuleid = el.capsules[cnt];
let capsulename = (capsuleNames[capsuleid] || capsuleid);
capsules.push(capsulename + ' (' + el.capsuleCounts[capsuleid] + ')');
}
return capsules.sort();
}
const str = ['Name\tLink\tGUID\tKeys\tCapsules', ...self.keyCount.map((el) => [el.portalCoupler.portalTitle, `https//intel.ingress.com/?pll=${el._latlng.lat},${el._latlng.lng}`, el.portalCoupler.portalGuid, el.count, getCapsulesNameCount(el).join(',')].join('\t'))].join('\n');
navigator.clipboard.writeText(str);
alert('Keys are copied to your clipboard');
}
self.updateMenu = function() {
// update menu, if visible
if (document.getElementById('dialog-' + self.id))
self.menu();
};
self.menu = function(submenu) {
if (typeof submenu != 'string') submenu = self.lastmenu;
submenu = (submenu || 'Items');
self.lastmenu = submenu;
let container = document.createElement('div');
let buttonarea = container.appendChild(document.createElement('form'));
let menuitemsbutton = buttonarea.appendChild(document.createElement('input'));
menuitemsbutton.type = 'button';
menuitemsbutton.value = 'Items';
menuitemsbutton.style.marginRight = '5px';
menuitemsbutton.addEventListener('click', function(e) {
e.preventDefault();
self.menu('Items');
},false);
let menukeysbutton = buttonarea.appendChild(document.createElement('input'));
menukeysbutton.type = 'button';
menukeysbutton.value = 'Keys';
menukeysbutton.style.marginRight = '5px';
menukeysbutton.addEventListener('click', function(e) {
e.preventDefault();
self.menu('Keys');
},false);
let menucapsulesbutton = buttonarea.appendChild(document.createElement('input'));
menucapsulesbutton.type = 'button';
menucapsulesbutton.value = 'Capsules';
menucapsulesbutton.style.marginRight = '5px';
menucapsulesbutton.addEventListener('click', function(e) {
e.preventDefault();
self.menu('Capsules');
},false);
let inventoryarea = container.appendChild(document.createElement('div'));
inventoryarea.id = 'live-inventory';
let buttons = {};
let counts = undefined;
if (submenu == 'Items') {
let tablearea = inventoryarea.appendChild(document.createElement('div'));
let sortabletable = tablearea.appendChild(document.createElement('table'));
let tableheader = sortabletable.appendChild(document.createElement('thead'));
let headerrow = tableheader.appendChild(document.createElement('tr'));
let tablebody = sortabletable.appendChild(document.createElement('tbody'));
setItemTableBody(self.itemCount, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
['Count','Type','Rarity','Capsules'].map(function(column) {
let header = headerrow.appendChild(document.createElement('th'));
header.textContent = column;
header.addEventListener('click', function(e) {
e.preventDefault();
if (self.lastsortcolumn[submenu] == this.textContent)
self.lastsortdirection[submenu][self.lastsortcolumn[submenu]] = !self.lastsortdirection[submenu][self.lastsortcolumn[submenu]];
else
self.lastsortcolumn[submenu] = this.textContent;
setItemTableBody(self.itemCount, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
},false);
});
} else if (submenu == 'Keys') {
let tablearea = inventoryarea.appendChild(document.createElement('div'));
let sortabletable = tablearea.appendChild(document.createElement('table'));
let tableheader = sortabletable.appendChild(document.createElement('thead'));
let headerrow = tableheader.appendChild(document.createElement('tr'));
let tablebody = sortabletable.appendChild(document.createElement('tbody'));
counts = setKeyTableBody(self.keyCount, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
['Count','Portal','Distance','Capsules'].map(function(column) {
let header = headerrow.appendChild(document.createElement('th'));
header.textContent = column;
header.addEventListener('click', function(e) {
e.preventDefault();
if (self.lastsortcolumn[submenu] == this.textContent)
self.lastsortdirection[submenu][self.lastsortcolumn[submenu]] = !self.lastsortdirection[submenu][self.lastsortcolumn[submenu]];
else
self.lastsortcolumn[submenu] = this.textContent;
setKeyTableBody(self.keyCount, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
},false);
});
buttonarea.appendChild(document.createTextNode('Filter: '));
let filterselectarea = buttonarea.appendChild(document.createElement('select'));
['All','Inventory','Capsules'].map(function(text) {
let filteroption = filterselectarea.appendChild(document.createElement('option'));
filteroption.value = text;
filteroption.textContent = text;
filteroption.selected = (self.settings.filterkeys == text);
});
filterselectarea.addEventListener('change', function(e) {
e.preventDefault();
self.settings.filterkeys = this.value;
self.saveSettings();
let counts = setKeyTableBody(self.keyCount, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
$('#dialog-LiveInventory').dialog({title:self.title + ' - Keys (' + counts.countkeys + ' keys / ' + counts.countportals + ' portals)'});
},false);
buttons['Zoom all'] = function() {
let keys = [];
self.keyCount.map((el) => {
let keysincapsules = Object.values(el.capsuleCounts).reduce((a, b) => a + b, 0);
if (self.settings.filterkeys == 'All' ||
(self.settings.filterkeys == 'Inventory' && el.count > keysincapsules) ||
(self.settings.filterkeys == 'Capsules' && keysincapsules > 0)) {
keys.push(el);
}
});
if (keys.length > 0)
self.zoomallkeys(keys);
};
if ('bookmarks' in window.plugin) {
buttons['Draw bookmarks'] = function() {
let keys = [];
self.keyCount.map((el) => {
let keysincapsules = Object.values(el.capsuleCounts).reduce((a, b) => a + b, 0);
if (self.settings.filterkeys == 'All' ||
(self.settings.filterkeys == 'Inventory' && el.count > keysincapsules) ||
(self.settings.filterkeys == 'Capsules' && keysincapsules > 0)) {
keys.push(el);
}
});
if (keys.length > 0) {
let bookmarkfolder = self.settings.filterkeys + ' Keys';
if (confirm('This will draw a bookmark for every key (' + keys.length + ') in folder:\n' + bookmarkfolder + '\n\nAre you sure you want to continue?')) {
self.drawbookmarks(keys,bookmarkfolder);
}
} else {
alert('There are no keys to draw bookmarks for');
}
};
}
} else if (submenu == 'Capsules') {
let togglebutton = inventoryarea.appendChild(document.createElement('input'));
togglebutton.type = 'button';
togglebutton.value = 'Edit capsule names';
let hideemptycheckboxarea = inventoryarea.appendChild(document.createElement('label'));
let hideemptycheckbox = hideemptycheckboxarea.appendChild(document.createElement('input'));
hideemptycheckbox.type = 'checkbox';
hideemptycheckbox.checked = self.settings.hideemptycapsules;
hideemptycheckbox.style.userSelect = 'none';
hideemptycheckboxarea.appendChild(document.createTextNode('Hide empty capsules'));
let editorarea = inventoryarea.appendChild(document.createElement('div'));
editorarea.style.display = 'none';
let capsuletextarea = editorarea.appendChild(document.createElement('textarea'));
capsuletextarea.placeholder = "CAPSULEID:Display name";
capsuletextarea.value = (self.settings.capsuleNames || '');
capsuletextarea.style.height = '200px';
capsuletextarea.style.minWidth = '400px';
capsuletextarea.style.resize = 'none';
editorarea.appendChild(document.createElement('br'));
editorarea.appendChild(document.createTextNode('Formatting (one on each row): CAPSULEID:Display name'));
editorarea.appendChild(document.createElement('br'));
let savebutton = editorarea.appendChild(document.createElement('input'));
savebutton.type = 'button';
savebutton.value = 'Save names';
let tablearea = inventoryarea.appendChild(document.createElement('div'));
let sortabletable = tablearea.appendChild(document.createElement('table'));
let tableheader = sortabletable.appendChild(document.createElement('thead'));
let headerrow = tableheader.appendChild(document.createElement('tr'));
let tablebody = sortabletable.appendChild(document.createElement('tbody'));
setCapsulesTableBody(self.capsuleCount,tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
togglebutton.addEventListener('click', function(e) {
e.preventDefault();
if (editorarea.style.display == 'none') {
editorarea.style.display = 'block';
} else {
editorarea.style.display = 'none';
}
// fit table inside dialog
container.getElementsByTagName('table')[0].style.maxHeight = $('#dialog-' + self.id).height() - ($(sortabletable).offset().top - $(container).offset().top) + 'px';
},false);
savebutton.addEventListener('click', function(e) {
e.preventDefault();
self.settings.capsuleNames = capsuletextarea.value;
self.saveSettings();
if (window.selectedPortal) {
portalDetailsUpdated({guid:window.selectedPortal});
showSelectedPortalCapsuleKeys({selectedPortalGuid: window.selectedPortal, unselectedPortalGuid: undefined});
}
setCapsulesTableBody(self.capsuleCount,tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
},false);
hideemptycheckbox.addEventListener('change', function(e) {
e.preventDefault();
self.settings.hideemptycapsules = this.checked;
self.saveSettings();
setCapsulesTableBody(self.capsuleCount,tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
},false);
['Capsule','Name','Count','Items','Keys','Type'].map(function(column) {
let header = headerrow.appendChild(document.createElement('th'));
header.textContent = column;
header.addEventListener('click', function(e) {
e.preventDefault();
if (self.lastsortcolumn[submenu] == this.textContent)
self.lastsortdirection[submenu][self.lastsortcolumn[submenu]] = !self.lastsortdirection[submenu][self.lastsortcolumn[submenu]];
else
self.lastsortcolumn[submenu] = this.textContent;
setCapsulesTableBody(self.capsuleCount,tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]]);
},false);
});
} else if (submenu == 'Settings') {
inventoryarea.appendChild(document.createTextNode('Keys Layer display mode:'));
inventoryarea.appendChild(document.createElement('br'));
let modeselector = container.appendChild(document.createElement('select'));
modeselector.id = 'live-inventory-settings--mode';
let optionKeyIcon = modeselector.appendChild(document.createElement('option'));
optionKeyIcon.value = 'icon';
optionKeyIcon.textContent = 'Key icon';
optionKeyIcon.selected = (self.settings.displayMode == 'icon');
let optionKeyCount = modeselector.appendChild(document.createElement('option'));
optionKeyCount.value = 'count';
optionKeyCount.textContent = 'Number of keys';
optionKeyCount.selected = (self.settings.displayMode == 'count');
let optionKeyCapsules = modeselector.appendChild(document.createElement('option'));
optionKeyCapsules.value = 'capsules';
optionKeyCapsules.textContent = 'Keys (+ keys in capsules)';
optionKeyCapsules.selected = (self.settings.displayMode == 'capsules');
container.appendChild(document.createElement('br'));
let selectedportalcheckboxarea = container.appendChild(document.createElement('label'));
selectedportalcheckboxarea.style.display = 'block';
let selectedportalcheckbox = selectedportalcheckboxarea.appendChild(document.createElement('input'));
selectedportalcheckbox.type = 'checkbox';
selectedportalcheckbox.style.userSelect = 'none';
selectedportalcheckbox.checked = self.settings.selectedportalcapsulekeys;
selectedportalcheckbox.disabled = (self.settings.displayMode == 'icon');
selectedportalcheckboxarea.appendChild(document.createTextNode('Show detailed keys in capsules for selected portal'));
let copyitemsbutton = container.appendChild(document.createElement('input'));
copyitemsbutton.type = 'button';
copyitemsbutton.value = 'Copy Items to clipboard';
copyitemsbutton.style.display = 'block';
copyitemsbutton.style.width = '240px';
copyitemsbutton.addEventListener('click', function(e) {
e.preventDefault();
exportItems();
},false);
let copykeysbutton = container.appendChild(document.createElement('input'));
copykeysbutton.type = 'button';
copykeysbutton.value = 'Copy Keys to clipboard';
copykeysbutton.style.display = 'block';
copykeysbutton.style.width = '240px';
copykeysbutton.addEventListener('click', function(e) {
e.preventDefault();
exportKeys();
},false);
buttons['Changelog'] = function() {
alert(self.changelog);
};
let author = container.appendChild(document.createElement('div'));
author.className = self.id + 'author';
author.textContent = self.title + ' version ' + self.version + ' by ' + self.author;
modeselector.addEventListener('change', function(e) {
e.preventDefault();
self.settings.displayMode = this.value;
selectedportalcheckbox.disabled = (self.settings.displayMode == 'icon');
self.saveSettings();
self.removeAllIcons();
self.checkShowAllIcons();
if (window.selectedPortal) portalDetailsUpdated({guid:window.selectedPortal});
},false);
selectedportalcheckbox.addEventListener('change', function(e) {
e.preventDefault();
self.settings.selectedportalcapsulekeys = this.checked;
self.saveSettings();
if (window.selectedPortal) {
if (self.settings.selectedportalcapsulekeys)
showSelectedPortalCapsuleKeys({selectedPortalGuid: window.selectedPortal, unselectedPortalGuid: undefined});
else {
removeKeyFromLayer({portal:window.portals[window.selectedPortal]});
addKeyToLayer({portal:window.portals[window.selectedPortal]});
}
}
},false);
}
let menudialog = window.dialog({
html: container,
title: self.title + ' - ' + submenu + (counts ? ' (' + counts.countkeys + ' keys / ' + counts.countportals + ' portals)' : ''),
id: self.id,
width: 'auto',
height: 'auto' //(isSmartphone() || submenu == 'Settings' ? 'auto' : window.innerHeight - 100)
}).dialog('option', 'buttons', { ...buttons,
'Refresh': refreshInventory,
'Settings': function() {
self.menu('Settings');
},
'Close': function() {
$(this).dialog('close');
},
});
// fit table inside dialog
container.getElementsByTagName('table')[0].style.maxHeight = menudialog.height() - ($(container.getElementsByTagName('table')[0]).offset().top - $(container).offset().top) + 'px';
};
function preparePortalKeyMap() {
const keyMap = {};
self.keyCount.forEach((k) => {
keyMap[k.portalCoupler.portalGuid] = k;
});
return keyMap;
}
function formatDistance(dist) {
if (dist >= 10000) {
dist = Math.round(dist / 1000) + ' km';
} else if (dist >= 1000) {
dist = Math.round(dist / 100) / 10 + ' km';
} else {
dist = Math.round(dist) + ' m';
}
return dist;
}
function updateDistances() {
const center = window.map.getCenter();
self.keyCount.forEach((k) => {
if (!k._latlng) {
k._latlng = L.latLng.apply(L, k.portalCoupler.portalLocation.split(',').map(e => {
return HexToSignedFloat(e);
}));
}
k._distance = k._latlng.distanceTo(center);
k._formattedDistance = formatDistance(k._distance);
});
}
function decodeInventoryData(data) {
self.itemCount = prepareItemCounts(data);
self.keyCount = prepareKeyCounts(data);
self.keyMap = preparePortalKeyMap();
for (const guid in self.keyMap) {
self.keyMap[guid].totalincapsules = 0;
for (const capsuleid in self.keyMap[guid].capsuleCounts) {
self.keyMap[guid].totalincapsules += self.keyMap[guid].capsuleCounts[capsuleid];
}
self.keyMap[guid].totaloutcapsules = self.keyMap[guid].count - self.keyMap[guid].totalincapsules;
}
self.keyGuidCount = {};
for (let cnt = 0; cnt < self.keyCount.length; cnt++) {
self.keyGuidCount[self.keyCount[cnt].portalCoupler.portalGuid] = self.keyCount[cnt].count;
}
self.capsuleCount = prepareCapsuleCounts(data);
updateDistances();
self.updateMenu();
}
function nicetimestring(milliseconds) {
let str;
let seconds = Math.floor(milliseconds / 1000);
if (seconds < 60)
str = seconds + ' seconds';
else {
let minutes = Math.floor(seconds / 60);
seconds = seconds % 60;
if (minutes > 5)
str = minutes + ' minutes';
else
str = minutes + ':' + (seconds<10?'0':'') + seconds + ' minutes';
}
return str;
};
function loadInventory(silent,retry) {
try {
let localData = localStorage[KEY_SETTINGS];
if (!localData || localData == "") return;
localData = JSON.parse(localData);
if (!(localData instanceof Object)) return;
if ('settings' in localData && localData.settings instanceof Object) {
for (const key in self.settings) {
if (key in localData.settings && typeof self.settings[key] == typeof localData.settings[key]) {
self.settings[key] = localData.settings[key];
}
}
}
if ('data' in localData && localData.data instanceof Object) {
decodeInventoryData(localData.data);
}
if ('expires' in localData && typeof localData.expires == 'number') {
self.inventoryexpires = localData.expires;
}
} catch (e) {
console.log('loadInventory error',e);
}
};
function refreshInventory(silent,retrysubscription,retryinventory) {
if (self.inventoryexpires > Date.now()) { // do not update
if (silent === true)
console.log(self.title + ' - Inventory was recently updated, wait ' + nicetimestring(self.inventoryexpires - Date.now()));
else {
let container = document.createElement('div');
let text = container.appendChild(document.createElement('p'));
text.textContent = 'Inventory was recently updated, wait ' + nicetimestring(self.inventoryexpires - Date.now());
let refreshbutton = container.appendChild(document.createElement('button'));
refreshbutton.textContent = 'Refresh anyway';
refreshbutton.addEventListener('click', function(e) {
e.preventDefault();
refreshbutton.disabled = true;
refreshbutton.textContent = 'Refreshing now';
self.saveExpireValue(Date.now() - 1 * 60); // expire
refreshInventory();
}, false);
window.dialog({
html: container,
title: self.title + ' - Refresh',
id: self.id,
}).dialog('option', 'buttons', {
'< Main menu': self.menu,
'Close': function () {
$(this).dialog('close');
},
});
}
return;
}
console.log(self.title + ' - Checking subscription status...');
getSubscription(
function(data) {
console.log(self.title + ' - Player has active subscription');
console.log(self.title + ' - Updating inventory...');
getInventory(
function(data) {
console.log(self.title + " - Inventory data received");
self.storeInventoryData(data);
decodeInventoryData(data);
window.runHooks('pluginLiveInventoryUpdated', {
itemCount: self.itemCount,
keyCount: self.keyCount,
keyMap: self.keyMap
});
// update keys on map
self.removeAllIcons();
self.checkShowAllIcons();
// update menu, if visible
self.updateMenu();
if (window.selectedPortal) portalDetailsUpdated({guid:window.selectedPortal});
if (silent !== true)
alert("OKAY - Inventory was updated succesfuly");
},
function(error) {
console.error(self.title + " - Failed to get inventory after 2 retries");
if (silent !== true) {
alert("FAILED - Inventory update failed");
}
},
2);
},
function(error) {
console.error(self.title + " - Failed to get subscription after 2 retries");
if (silent !== true) {
alert("FAILED - Subscription check failed, inventory update skipped");
}
},
2);
};
self.storeInventoryData = function(data) {
self.inventoryexpires = Date.now() + 10 * 60 * 1000; // request data only once per 10 minutes, or we might hit a rate limit
localStorage[KEY_SETTINGS] = JSON.stringify({
data: data,
expires: self.inventoryexpires,
settings: self.settings
});
};
self.saveExpireValue = function(expires) {
self.inventoryexpires = expires;
const ls = {};
try {
const localData = JSON.parse(localStorage[KEY_SETTINGS]);
ls.data = localData.data;
ls.settings = localData.settings;
} catch (e) {}
ls.expires = expires;
localStorage[KEY_SETTINGS] = JSON.stringify(ls);
};
self.saveSettings = function() {
const ls = {};
try {
const localData = JSON.parse(localStorage[KEY_SETTINGS]);
ls.data = localData.data;
ls.expires = self.inventoryexpires;
} catch (e) {}
ls.settings = self.settings;
localStorage[KEY_SETTINGS] = JSON.stringify(ls);
};
self.drawbookmarks = function(keys,bookmarksfolder) {
function getBookmarkFolderID(bookmarksfolder) {
if (!bookmarksfolder) bookmarksfolder = "Others"; // default
let folderid = undefined;
for (const ID in window.plugin.bookmarks.bkmrksObj.portals) {
if (window.plugin.bookmarks.bkmrksObj.portals[ID].label == bookmarksfolder) {
folderid = ID;
break;
}
}
return folderid;
}
function createNewBookmarksFolder(bookmarksfolder) {
if (!bookmarksfolder) bookmarksfolder = "Others"; // default
let folderid = getBookmarkFolderID(bookmarksfolder);
if (!folderid) {
folderid = window.plugin.bookmarks.generateID();
window.plugin.bookmarks.bkmrksObj.portals[folderid] = {"label":bookmarksfolder,"state":1,"bkmrk":{}};
window.plugin.bookmarks.saveStorage();
window.plugin.bookmarks.refreshBkmrks();
window.runHooks('pluginBkmrksEdit', {"target": 'portals', "action": "add", "id": folderid});
}
return folderid;
};
function getFolderIDforBookmark(portalguid) {
let folderid = undefined;
for (const ID in window.plugin.bookmarks.bkmrksObj.portals) {
for (const bkmrkID in window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk) {
if (window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk[bkmrkID].guid == portalguid) {
folderid = ID;
break;
}
}
}
return folderid;
}
function getBookmarkID(portalguid) {
let bookmarkid = undefined;
for (const ID in window.plugin.bookmarks.bkmrksObj.portals) {
for (const bkmrkID in window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk) {
if (window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk[bkmrkID].guid == portalguid) {
bookmarkid = bkmrkID;
break;
}
}
}
return bookmarkid;
}
function createNewBookMarkInFolder(portalguid,latlng,label,bookmarksfolder) {
// check if bookmark already exists
let bookmarkid = getBookmarkID(portalguid);
let oldfolderid = getFolderIDforBookmark(portalguid);
// create new folder if needed
let folderid = createNewBookmarksFolder(bookmarksfolder);
if (!bookmarkid) { // create new bookmark if not already exists
if (!(portalguid in window.portals)) { // create placeholder for non existing portals to prevent errors from bookmarks.editStar
window.mapDataRequest.render.createPlaceholderPortalEntity(portalguid,latlng.lat * 1E6,latlng.lng * 1E6,"N");
}
window.plugin.bookmarks.addPortalBookmark(portalguid,latlng.lat + ',' + latlng.lng,label);
bookmarkid = getBookmarkID(portalguid);
oldfolderid = getFolderIDforBookmark(portalguid); // bookmarks are added to idOthers by default, so it needs to be moved
}
let bookmarkmoved = 0;
if (folderid != oldfolderid) { // move bookmark to new folder if needed
let Bkmrk = window.plugin.bookmarks.bkmrksObj.portals[oldfolderid].bkmrk[bookmarkid];
delete window.plugin.bookmarks.bkmrksObj.portals[oldfolderid].bkmrk[bookmarkid];
window.plugin.bookmarks.bkmrksObj.portals[folderid].bkmrk[bookmarkid] = Bkmrk;
bookmarkmoved = 1;
}
return bookmarkmoved;
}
let bookmarksqueue = [];
let timerid = 0;
let bookmarksmoved = 0;
function clearbookmarksqueue() {
bookmarksqueue = [];
if (timerid) window.clearTimeout(timerid);
timerid = 0;
bookmarksmoved = 0;
$('#dialog-' + self.id + 'bookmarks').dialog('close');
}
function createnextbookmark(bookmarkscountobject) {
if (bookmarksqueue.length > 0) {
let newbookmarkdata = bookmarksqueue.shift();
bookmarksmoved += createNewBookMarkInFolder(newbookmarkdata.portalguid,newbookmarkdata.latlng,newbookmarkdata.label,newbookmarkdata.bookmarksfolder);
bookmarkscountobject.textContent = bookmarksqueue.length;
timerid = setTimeout(function() { createnextbookmark(bookmarkscountobject); },0);
} else {
if (bookmarksmoved > 0) {
window.plugin.bookmarks.saveStorage();
window.plugin.bookmarks.refreshBkmrks();
window.runHooks('pluginBkmrksEdit', {"target": "bookmarks", "action": "sort"});
}
clearbookmarksqueue();
}
}
clearbookmarksqueue();
// if number of new bookmarks is large, it gets very slow... use a queue, a timer and a dialog:
for (let cnt in keys) {
let portalguid = keys[cnt].portalCoupler.portalGuid;
let latlng = keys[cnt]._latlng;
let label = keys[cnt].portalCoupler.portalTitle;
let bookmarkid = getBookmarkID(portalguid);
if (!bookmarkid) {
bookmarksqueue.push({
portalguid:portalguid,
latlng:latlng,
label:label,
bookmarksfolder:bookmarksfolder
});
} else {
bookmarksmoved += createNewBookMarkInFolder(portalguid,latlng,label,bookmarksfolder);
}
}
let container = document.createElement('div');
container.appendChild(document.createTextNode('Remaining '));
let bookmarkscount = container.appendChild(document.createElement('span'));
bookmarkscount.textContent = bookmarksqueue.length;
container.appendChild(document.createTextNode('/' + bookmarksqueue.length + ' bookmarks. Please be patient...'));
container.appendChild(document.createElement('br'));
container.appendChild(document.createTextNode('Bookmark folder: ' + bookmarksfolder));
if (bookmarksqueue.length > 20) // only show a dialog when drawing bookmarks may take a long time
window.dialog({
html: container,
id: self.id + 'bookmarks',
title: self.title + ' - Bookmarks',
width: 'auto',
closeCallback: function() {
clearbookmarksqueue();
}
}).dialog('option', 'buttons', {
'Cancel': function() { $(this).dialog('close'); },
});
createnextbookmark(bookmarkscount);
};
self.zoomallkeys = function(keys) {
let keyslayer = new L.FeatureGroup();
for (let cnt in keys) {
L.marker(keys[cnt]._latlng, {
icon: self.keyIcon,
interactive: false,
keyboard: false,
width: '35px'
}).addTo(keyslayer);
}
window.map.fitBounds(keyslayer.getBounds());
};
function showCapsuleContent(capsuleid,submenu) {
const capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
let capsulename = capsuleNames[capsuleid];
let capsule = undefined;
for (let cnt = 0; cnt < self.capsuleCount.length; cnt++) {
if (self.capsuleCount[cnt].id == capsuleid) {
capsule = self.capsuleCount[cnt];
break;
}
}
if (!capsule) {
alert('Capsule not found: ' + capsuleid);
return;
}
if (typeof submenu != 'string') submenu = 'Items';
submenu = (submenu || 'Items');
if (capsule.count == 0) {
alert(capsule.type + ' is empty' + (capsulename ? ' (' + capsuleid + '): ' + capsulename : ': ' + capsuleid));
return;
}
if (submenu == 'Items' && capsule.itemscount == 0) {
submenu = 'Keys';
} else if (submenu == 'Keys' && capsule.keyscount == 0) {
submenu = 'Items';
}
let container = document.createElement('div');
let buttonarea = container.appendChild(document.createElement('form'));
let menuitemsbutton = buttonarea.appendChild(document.createElement('input'));
menuitemsbutton.type = 'button';
menuitemsbutton.value = 'Items';
menuitemsbutton.style.marginRight = '5px';
menuitemsbutton.disabled = (capsule.itemscount == 0);
menuitemsbutton.addEventListener('click', function(e) {
e.preventDefault();
showCapsuleContent(capsuleid,'Items');
},false);
let menukeysbutton = buttonarea.appendChild(document.createElement('input'));
menukeysbutton.type = 'button';
menukeysbutton.value = 'Keys';
menukeysbutton.style.marginRight = '5px';
menukeysbutton.disabled = (capsule.keyscount == 0);
menukeysbutton.addEventListener('click', function(e) {
e.preventDefault();
showCapsuleContent(capsuleid,'Keys');
},false);
buttonarea.appendChild(document.createTextNode('Items: ' + capsule.itemscount + ' Keys: ' + capsule.keyscount + '/' + capsule.keys.length + ' portals Total: ' + capsule.count));
let inventoryarea = container.appendChild(document.createElement('div'));
inventoryarea.id = 'live-inventory';
let buttons = {};
let counts = undefined;
if (submenu == 'Items') {
let tablearea = inventoryarea.appendChild(document.createElement('div'));
let sortabletable = tablearea.appendChild(document.createElement('table'));
sortabletable.style.maxHeight = ((isSmartphone() ? document.body.clientHeight : window.innerHeight - 100) - 105) + 'px';
let tableheader = sortabletable.appendChild(document.createElement('thead'));
let headerrow = tableheader.appendChild(document.createElement('tr'));
let tablebody = sortabletable.appendChild(document.createElement('tbody'));
setItemTableBody(capsule.items, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]],capsuleid);
['Count','Type','Rarity'].map(function(column) {
let header = headerrow.appendChild(document.createElement('th'));
header.textContent = column;
header.addEventListener('click', function(e) {
e.preventDefault();
if (self.lastsortcolumn[submenu] == this.textContent)
self.lastsortdirection[submenu][self.lastsortcolumn[submenu]] = !self.lastsortdirection[submenu][self.lastsortcolumn[submenu]];
else
self.lastsortcolumn[submenu] = this.textContent;
setItemTableBody(capsule.items, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]],capsuleid);
},false);
});
} else if (submenu == 'Keys') {
let tablearea = inventoryarea.appendChild(document.createElement('div'));
let sortabletable = tablearea.appendChild(document.createElement('table'));
sortabletable.style.maxHeight = ((isSmartphone() ? document.body.clientHeight : window.innerHeight - 100) - 105) + 'px';
let tableheader = sortabletable.appendChild(document.createElement('thead'));
let headerrow = tableheader.appendChild(document.createElement('tr'));
let tablebody = sortabletable.appendChild(document.createElement('tbody'));
counts = setKeyTableBody(capsule.keys, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]],capsuleid);
['Count','Portal','Distance'].map(function(column) {
let header = headerrow.appendChild(document.createElement('th'));
header.textContent = column;
header.addEventListener('click', function(e) {
e.preventDefault();
if (self.lastsortcolumn[submenu] == this.textContent)
self.lastsortdirection[submenu][self.lastsortcolumn[submenu]] = !self.lastsortdirection[submenu][self.lastsortcolumn[submenu]];
else
self.lastsortcolumn[submenu] = this.textContent;
setKeyTableBody(capsule.keys, tablebody, self.lastsortcolumn[submenu], self.lastsortdirection[submenu][self.lastsortcolumn[submenu]],capsuleid);
},false);
});
buttons['Zoom all'] = function() {
self.zoomallkeys(capsule.keys);
};
if ('bookmarks' in window.plugin) {
buttons['Draw bookmarks'] = function() {
let bookmarkfolder = capsule.type + (capsulename ? ' (' + capsuleid + '): ' + capsulename : ': ' + capsuleid);
if (confirm('This will draw a bookmark for every key (' + capsule.keys.length + ') in folder:\n' + bookmarkfolder + '\n\nAre you sure you want to continue?')) {
self.drawbookmarks(capsule.keys,bookmarkfolder);
}
};
}
}
let dialogheight = 140 + 17 * (submenu == 'Keys' ? capsule.keys.length : capsule.items.length);
if (dialogheight > window.innerHeight - 100) dialogheight = window.innerHeight - 100;
window.dialog({
html: container,
title: self.title + ' - ' + capsule.type + (capsulename ? ' (' + capsuleid + '): ' + capsulename : ': ' + capsuleid) + (counts ? ' (' + counts.countkeys + ' keys / ' + counts.countportals + ' portals)' : ''),
id: capsuleid,
width: 'auto',
height: (isSmartphone() ? 'auto' : dialogheight)
}).dialog('option', 'buttons', { ...buttons,
'Rename': function () {
let newname = prompt('Enter new capsule name (' + capsuleid + '):',capsulename);
if (newname == null || newname == capsulename) return;
let capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
capsuleNames[capsuleid] = newname;
let capsuleNamesList = [];
for (const id in capsuleNames) {
capsuleNamesList.push(id + ':' + capsuleNames[id]);
}
self.settings.capsuleNames = capsuleNamesList.join("\n");
self.saveSettings();
if (window.selectedPortal) {
portalDetailsUpdated({guid:window.selectedPortal});
showSelectedPortalCapsuleKeys({selectedPortalGuid: window.selectedPortal, unselectedPortalGuid: undefined});
}
self.updateMenu();
showCapsuleContent(capsuleid,submenu);
},
'Close': function () {
$(this).dialog('close');
},
});
}
function portalDetailsUpdated(p) { // {guid: guid, portal: portal, portalDetails: details, portalData: data}
$('.randdetails-keys').remove();
if (!self.keyMap) {
return;
}
const countData = self.keyMap[p.guid];
if (!countData) {
return;
}
const capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
let capsules = [];
for (let cnt = 0; cnt < countData.capsules.length; cnt++) {
let capsuleid = countData.capsules[cnt];
capsules.push({
id: capsuleid,
name: (capsuleNames[capsuleid] || ''),
count: countData.capsuleCounts[capsuleid]
});
}
capsules.sort(function(a, b) {
if (a.name + a.id === b.name + a.id) {
return 0;
}
return (a.name + a.id > b.name + a.id ? 1 : -1);
});
let keysrow = document.createElement('tr');
keysrow.className = 'randdetails-keys';
let keyscell = keysrow.appendChild(document.createElement('td'));
if (self.settings.displayMode == 'capsules') {
let totalstext = [];
if (countData.totaloutcapsules > 0) totalstext.push(countData.totaloutcapsules);
if (countData.totalincapsules > 0) totalstext.push('(' + countData.totalincapsules + ')');
keyscell.textContent = totalstext.join(' ');
} else {
keyscell.textContent = countData.count;
}
let keysheadercell = keysrow.appendChild(document.createElement('th'));
keysheadercell.textContent = 'Keys';
let capsulesheadercell = keysrow.appendChild(document.createElement('th'));
capsulesheadercell.textContent = (capsules.length?'Capsules':'');
let capsulescell = keysrow.appendChild(document.createElement('th'));
capsulescell.className = 'randdetails-capsules';
capsules.map(function(c) {
let capsulelink = capsulescell.appendChild(document.createElement('a'));
capsulelink.style.display = 'block';
capsulelink.textContent = (c.name || c.id) + ' (' + c.count + ')';
capsulelink.addEventListener('click', function(e) {
e.preventDefault();
showCapsuleContent(c.id);
}, false);
});
$(keysrow).appendTo($('#randdetails tbody'));
}
function addKeyToLayer(data) { // {portal: marker, previousData: previousData}
const tileParams = window.getCurrentZoomTileParameters ? window.getCurrentZoomTileParameters() : window.getMapZoomTileParameters(window.getDataZoomForMapZoom(window.map.getZoom()));
if (tileParams.level !== 0) {
return;
}
if (self.keyMap && data.portal.options.guid in self.keyMap && !data.portal._keyMarker) {
let icontext;
if (self.settings.displayMode === 'count') {
icontext = self.keyMap[data.portal.options.guid].count;
} else if (self.settings.displayMode === 'capsules') {
let totalstext = [];
if (self.keyMap[data.portal.options.guid].totaloutcapsules > 0) totalstext.push(self.keyMap[data.portal.options.guid].totaloutcapsules);
if (self.keyMap[data.portal.options.guid].totalincapsules > 0) totalstext.push('(' + self.keyMap[data.portal.options.guid].totalincapsules + ')');
icontext = totalstext.join(' ');
}
let icon;
if (self.settings.displayMode === 'icon') {
icon = self.keyIcon;
} else {
if (self.settings.selectedportalcapsulekeys && window.selectedPortal == data.portal.options.guid && self.keyMap[data.portal.options.guid].totalincapsules > 0) {
const countData = self.keyMap[data.portal.options.guid];
const capsuleNames = parseCapsuleNames(self.settings.capsuleNames);
let capsules = [];
for (let cnt = 0; cnt < countData.capsules.length; cnt++) {
let capsuleid = countData.capsules[cnt];
let capsulename = (capsuleNames[capsuleid] || capsuleid);
capsules.push(capsulename + ' (' + countData.capsuleCounts[capsuleid] + ')');
}
capsules.sort();
icontext += '
' + capsules.join('
');
}
icon = new L.DivIcon({
html: icontext,
className: 'plugin-live-inventory-count'
});
}
data.portal._keyMarker = L.marker(data.portal._latlng, {
icon: icon,
interactive: false,
keyboard: false,
width: '35px'
}).addTo(self.layerGroup);
}
}
function removeKeyFromLayer(data) { // {portal: p, data: p.options.data }
if (data.portal._keyMarker) {
self.layerGroup.removeLayer(data.portal._keyMarker);
delete data.portal._keyMarker;
}
}
function showSelectedPortalCapsuleKeys(data) { // {selectedPortalGuid: guid, unselectedPortalGuid: oldPortalGuid}
if (!self.settings.selectedportalcapsulekeys) return;
if (data.unselectedPortalGuid && data.unselectedPortalGuid in window.portals) {
removeKeyFromLayer({portal:window.portals[data.unselectedPortalGuid]});
addKeyToLayer({portal:window.portals[data.unselectedPortalGuid]});
}
if (data.selectedPortalGuid && data.selectedPortalGuid in window.portals) {
removeKeyFromLayer({portal:window.portals[data.selectedPortalGuid]});
addKeyToLayer({portal:window.portals[data.selectedPortalGuid]});
}
}
self.removeAllIcons = function() {
self.layerGroup.clearLayers();
for (let id in window.portals) {
delete window.portals[id]._keyMarker;
}
};
self.checkShowAllIcons = function() {
const tileParams = window.getCurrentZoomTileParameters ? window.getCurrentZoomTileParameters() : window.getMapZoomTileParameters(window.getDataZoomForMapZoom(window.map.getZoom()));
if (tileParams.level !== 0) {
self.removeAllIcons();
} else {
for (let id in window.portals) {
addKeyToLayer({
portal: window.portals[id]
});
}
}
};
self.setupPortalsList = function() {
if (!window.plugin.portalslist) return;
let colpos = 0;
for (colpos = 0; colpos < window.plugin.portalslist.fields.length; colpos++) { // find column Portal Name
if (window.plugin.portalslist.fields[colpos].title == 'Portal Name') {
break;
}
}
if (colpos >= window.plugin.portalslist.fields.length) colpos = 0; // default first colum if column name not found
// insert extra column at colpos:
window.plugin.portalslist.fields.splice(colpos,0,{
title: 'Keys',
value: function(portal) { return self.keyGuidCount[portal.options.guid]; },
sortValue: function(value, portal) { return (self.keyGuidCount[portal.options.guid] > 0 ? self.keyGuidCount[portal.options.guid] : 0); },
format: function(cell, portal, value) {
$(cell)
.addClass("alignR")
.append($('')
.html(self.keyGuidCount[portal.options.guid] > 0 ? self.keyGuidCount[portal.options.guid] : '')
);
},
defaultOrder: -1 // descending
});
};
self.setup = function() {
if ('pluginloaded' in self) {
console.log('IITC plugin already loaded: ' + self.title + ' version ' + self.version);
return;
} else {
self.pluginloaded = true;
}
self.layerGroup = new L.LayerGroup();
window.addLayerGroup('Portal keys', self.layerGroup, false);
createIcons();
self.setupPortalsList();
$('')
.text('Inventory')
.click(self.menu)
.appendTo($('#toolbox'));
window.pluginCreateHook('pluginLiveInventorySubscription'); // pluginCreateHook for IITC-me compatibility
window.pluginCreateHook('pluginLiveInventoryUpdated'); // pluginCreateHook for IITC-me compatibility
window.addHook('portalDetailsUpdated', portalDetailsUpdated);
window.addHook('portalAdded', addKeyToLayer);
window.addHook('portalRemoved', removeKeyFromLayer);
window.addHook('portalSelected', showSelectedPortalCapsuleKeys);
window.map.on('zoom', self.checkShowAllIcons);
window.map.on('moveend', updateDistances);
$("