414 lines
14 KiB
JavaScript
414 lines
14 KiB
JavaScript
// ==UserScript==
|
|
// @id iitc-plugin-portals-list@teo96
|
|
// @name IITC plugin: show list of portals
|
|
// @category Info
|
|
// @version 0.2.1.@@DATETIMEVERSION@@
|
|
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
|
// @updateURL @@UPDATEURL@@
|
|
// @downloadURL @@DOWNLOADURL@@
|
|
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Display a sortable list of all visible portals with full details about the team, resonators, links, etc.
|
|
// @include https://*.ingress.com/intel*
|
|
// @include http://*.ingress.com/intel*
|
|
// @match https://*.ingress.com/intel*
|
|
// @match http://*.ingress.com/intel*
|
|
// @include https://*.ingress.com/mission/*
|
|
// @include http://*.ingress.com/mission/*
|
|
// @match https://*.ingress.com/mission/*
|
|
// @match http://*.ingress.com/mission/*
|
|
// @grant none
|
|
// ==/UserScript==
|
|
|
|
@@PLUGINSTART@@
|
|
|
|
// PLUGIN START ////////////////////////////////////////////////////////
|
|
|
|
// use own namespace for plugin
|
|
window.plugin.portalslist = function() {};
|
|
|
|
window.plugin.portalslist.listPortals = [];
|
|
window.plugin.portalslist.sortBy = 1; // second column: level
|
|
window.plugin.portalslist.sortOrder = -1;
|
|
window.plugin.portalslist.enlP = 0;
|
|
window.plugin.portalslist.resP = 0;
|
|
window.plugin.portalslist.neuP = 0;
|
|
window.plugin.portalslist.filter = 0;
|
|
|
|
/*
|
|
* plugins may add fields by appending their specifiation to the following list. The following members are supported:
|
|
* title: String
|
|
* Name of the column. Required.
|
|
* value: function(portal)
|
|
* The raw value of this field. Can by anything. Required, but can be dummy implementation if sortValue and format
|
|
* are implemented.
|
|
* sortValue: function(value, portal)
|
|
* The value to sort by. Optional, uses value if omitted. The raw value is passed as first argument.
|
|
* sort: function(valueA, valueB, portalA, portalB)
|
|
* Custom sorting function. See Array.sort() for details on return value. Both the raw values and the portal objects
|
|
* are passed as arguments. Optional. Set to null to disable sorting
|
|
* format: function(cell, portal, value)
|
|
* Used to fill and format the cell, which is given as a DOM node. If omitted, the raw value is put in the cell.
|
|
* defaultOrder: -1|1
|
|
* Which order should by default be used for this column. -1 means descending. Default: 1
|
|
*/
|
|
|
|
|
|
window.plugin.portalslist.fields = [
|
|
{
|
|
title: "Portal Name",
|
|
value: function(portal) { return portal.options.data.title; },
|
|
sortValue: function(value, portal) { return value.toLowerCase(); },
|
|
format: function(cell, portal, value) {
|
|
$(cell)
|
|
.append(plugin.portalslist.getPortalLink(portal))
|
|
.addClass("portalTitle");
|
|
}
|
|
},
|
|
{
|
|
title: "Level",
|
|
value: function(portal) { return portal.options.data.level; },
|
|
format: function(cell, portal, value) {
|
|
$(cell)
|
|
.css('background-color', COLORS_LVL[value])
|
|
.text('L' + value);
|
|
},
|
|
defaultOrder: -1,
|
|
},
|
|
{
|
|
title: "Team",
|
|
value: function(portal) { return portal.options.team; },
|
|
format: function(cell, portal, value) {
|
|
$(cell).text(['NEU', 'RES', 'ENL'][value]);
|
|
}
|
|
},
|
|
{
|
|
title: "Health",
|
|
value: function(portal) { return portal.options.data.health; },
|
|
sortValue: function(value, portal) { return portal.options.team===TEAM_NONE ? -1 : value; },
|
|
format: function(cell, portal, value) {
|
|
$(cell)
|
|
.addClass("alignR")
|
|
.text(portal.options.team===TEAM_NONE ? '-' : value+'%');
|
|
},
|
|
defaultOrder: -1,
|
|
},
|
|
{
|
|
title: "Res",
|
|
value: function(portal) { return portal.options.data.resCount; },
|
|
format: function(cell, portal, value) {
|
|
$(cell)
|
|
.addClass("alignR")
|
|
.text(value);
|
|
},
|
|
defaultOrder: -1,
|
|
},
|
|
{
|
|
title: "Links",
|
|
value: function(portal) { return window.getPortalLinks(portal.options.guid); },
|
|
sortValue: function(value, portal) { return value.in.length + value.out.length; },
|
|
format: function(cell, portal, value) {
|
|
$(cell)
|
|
.addClass("alignR")
|
|
.addClass('help')
|
|
.attr('title', 'In:\t' + value.in.length + '\nOut:\t' + value.out.length)
|
|
.text(value.in.length+value.out.length);
|
|
},
|
|
defaultOrder: -1,
|
|
},
|
|
{
|
|
title: "Fields",
|
|
value: function(portal) { return getPortalFieldsCount(portal.options.guid) },
|
|
format: function(cell, portal, value) {
|
|
$(cell)
|
|
.addClass("alignR")
|
|
.text(value);
|
|
},
|
|
defaultOrder: -1,
|
|
},
|
|
{
|
|
title: "AP",
|
|
value: function(portal) {
|
|
var links = window.getPortalLinks(portal.options.guid);
|
|
var fields = getPortalFieldsCount(portal.options.guid);
|
|
return portalApGainMaths(portal.options.data.resCount, links.in.length+links.out.length, fields);
|
|
},
|
|
sortValue: function(value, portal) { return value.enemyAp; },
|
|
format: function(cell, portal, value) {
|
|
var title = '';
|
|
if (teamStringToId(PLAYER.team) == portal.options.team) {
|
|
title += 'Friendly AP:\t'+value.friendlyAp+'\n'
|
|
+ '- deploy '+(8-portal.options.data.resCount)+' resonator(s)\n'
|
|
+ '- upgrades/mods unknown\n';
|
|
}
|
|
title += 'Enemy AP:\t'+value.enemyAp+'\n'
|
|
+ '- Destroy AP:\t'+value.destroyAp+'\n'
|
|
+ '- Capture AP:\t'+value.captureAp;
|
|
|
|
$(cell)
|
|
.addClass("alignR")
|
|
.addClass('help')
|
|
.prop('title', title)
|
|
.html(digits(value.enemyAp));
|
|
},
|
|
defaultOrder: -1,
|
|
},
|
|
];
|
|
|
|
//fill the listPortals array with portals avaliable on the map (level filtered portals will not appear in the table)
|
|
window.plugin.portalslist.getPortals = function() {
|
|
//filter : 0 = All, 1 = Neutral, 2 = Res, 3 = Enl, -x = all but x
|
|
var retval=false;
|
|
|
|
var displayBounds = map.getBounds();
|
|
|
|
window.plugin.portalslist.listPortals = [];
|
|
$.each(window.portals, function(i, portal) {
|
|
// eliminate offscreen portals (selected, and in padding)
|
|
if(!displayBounds.contains(portal.getLatLng())) return true;
|
|
|
|
retval=true;
|
|
|
|
switch (portal.options.team) {
|
|
case TEAM_RES:
|
|
window.plugin.portalslist.resP++;
|
|
break;
|
|
case TEAM_ENL:
|
|
window.plugin.portalslist.enlP++;
|
|
break;
|
|
default:
|
|
window.plugin.portalslist.neuP++;
|
|
}
|
|
|
|
// cache values and DOM nodes
|
|
var obj = { portal: portal, values: [], sortValues: [] };
|
|
|
|
var row = document.createElement('tr');
|
|
row.className = TEAM_TO_CSS[portal.options.team];
|
|
obj.row = row;
|
|
|
|
var cell = row.insertCell(-1);
|
|
cell.className = 'alignR';
|
|
|
|
window.plugin.portalslist.fields.forEach(function(field, i) {
|
|
cell = row.insertCell(-1);
|
|
|
|
var value = field.value(portal);
|
|
obj.values.push(value);
|
|
|
|
obj.sortValues.push(field.sortValue ? field.sortValue(value, portal) : value);
|
|
|
|
if(field.format) {
|
|
field.format(cell, portal, value);
|
|
} else {
|
|
cell.textContent = value;
|
|
}
|
|
});
|
|
|
|
window.plugin.portalslist.listPortals.push(obj);
|
|
});
|
|
|
|
return retval;
|
|
}
|
|
|
|
window.plugin.portalslist.displayPL = function() {
|
|
var list;
|
|
// plugins (e.g. bookmarks) can insert fields before the standard ones - so we need to search for the 'level' column
|
|
window.plugin.portalslist.sortBy = window.plugin.portalslist.fields.map(function(f){return f.title;}).indexOf('Level');
|
|
window.plugin.portalslist.sortOrder = -1;
|
|
window.plugin.portalslist.enlP = 0;
|
|
window.plugin.portalslist.resP = 0;
|
|
window.plugin.portalslist.neuP = 0;
|
|
window.plugin.portalslist.filter = 0;
|
|
|
|
if (window.plugin.portalslist.getPortals()) {
|
|
list = window.plugin.portalslist.portalTable(window.plugin.portalslist.sortBy, window.plugin.portalslist.sortOrder,window.plugin.portalslist.filter);
|
|
} else {
|
|
list = $('<table class="noPortals"><tr><td>Nothing to show!</td></tr></table>');
|
|
};
|
|
|
|
if(window.useAndroidPanes()) {
|
|
$('<div id="portalslist" class="mobile">').append(list).appendTo(document.body);
|
|
} else {
|
|
dialog({
|
|
html: $('<div id="portalslist">').append(list),
|
|
dialogClass: 'ui-dialog-portalslist',
|
|
title: 'Portal list: ' + window.plugin.portalslist.listPortals.length + ' ' + (window.plugin.portalslist.listPortals.length == 1 ? 'portal' : 'portals'),
|
|
id: 'portal-list',
|
|
width: 700
|
|
});
|
|
}
|
|
}
|
|
|
|
window.plugin.portalslist.portalTable = function(sortBy, sortOrder, filter) {
|
|
// save the sortBy/sortOrder/filter
|
|
window.plugin.portalslist.sortBy = sortBy;
|
|
window.plugin.portalslist.sortOrder = sortOrder;
|
|
window.plugin.portalslist.filter = filter;
|
|
|
|
var portals = window.plugin.portalslist.listPortals;
|
|
var sortField = window.plugin.portalslist.fields[sortBy];
|
|
|
|
portals.sort(function(a, b) {
|
|
var valueA = a.sortValues[sortBy];
|
|
var valueB = b.sortValues[sortBy];
|
|
|
|
if(sortField.sort) {
|
|
return sortOrder * sortField.sort(valueA, valueB, a.portal, b.portal);
|
|
}
|
|
|
|
//FIXME: sort isn't stable, so re-sorting identical values can change the order of the list.
|
|
//fall back to something constant (e.g. portal name?, portal GUID?),
|
|
//or switch to a stable sort so order of equal items doesn't change
|
|
return sortOrder *
|
|
(valueA < valueB ? -1 :
|
|
valueA > valueB ? 1 :
|
|
0);
|
|
});
|
|
|
|
if(filter !== 0) {
|
|
portals = portals.filter(function(obj) {
|
|
return filter < 0
|
|
? obj.portal.options.team+1 != -filter
|
|
: obj.portal.options.team+1 == filter;
|
|
});
|
|
}
|
|
|
|
var table, row, cell;
|
|
var container = $('<div>');
|
|
|
|
table = document.createElement('table');
|
|
table.className = 'filter';
|
|
container.append(table);
|
|
|
|
row = table.insertRow(-1);
|
|
|
|
var length = window.plugin.portalslist.listPortals.length;
|
|
|
|
["All", "Neutral", "Resistance", "Enlightened"].forEach(function(label, i) {
|
|
cell = row.appendChild(document.createElement('th'));
|
|
cell.className = 'filter' + label.substr(0, 3);
|
|
cell.textContent = label+':';
|
|
cell.title = 'Show only portals of this color';
|
|
$(cell).click(function() {
|
|
$('#portalslist').empty().append(window.plugin.portalslist.portalTable(sortBy, sortOrder, i));
|
|
});
|
|
|
|
|
|
cell = row.insertCell(-1);
|
|
cell.className = 'filter' + label.substr(0, 3);
|
|
if(i != 0) cell.title = 'Hide portals of this color';
|
|
$(cell).click(function() {
|
|
$('#portalslist').empty().append(window.plugin.portalslist.portalTable(sortBy, sortOrder, -i));
|
|
});
|
|
|
|
switch(i-1) {
|
|
case -1:
|
|
cell.textContent = length;
|
|
break;
|
|
case 0:
|
|
cell.textContent = window.plugin.portalslist.neuP + ' (' + Math.round(window.plugin.portalslist.neuP/length*100) + '%)';
|
|
break;
|
|
case 1:
|
|
cell.textContent = window.plugin.portalslist.resP + ' (' + Math.round(window.plugin.portalslist.resP/length*100) + '%)';
|
|
break;
|
|
case 2:
|
|
cell.textContent = window.plugin.portalslist.enlP + ' (' + Math.round(window.plugin.portalslist.enlP/length*100) + '%)';
|
|
}
|
|
});
|
|
|
|
table = document.createElement('table');
|
|
table.className = 'portals';
|
|
container.append(table);
|
|
|
|
var thead = table.appendChild(document.createElement('thead'));
|
|
row = thead.insertRow(-1);
|
|
|
|
cell = row.appendChild(document.createElement('th'));
|
|
cell.textContent = '#';
|
|
|
|
window.plugin.portalslist.fields.forEach(function(field, i) {
|
|
cell = row.appendChild(document.createElement('th'));
|
|
cell.textContent = field.title;
|
|
if(field.sort !== null) {
|
|
cell.classList.add("sortable");
|
|
if(i == window.plugin.portalslist.sortBy) {
|
|
cell.classList.add("sorted");
|
|
}
|
|
|
|
$(cell).click(function() {
|
|
var order;
|
|
if(i == sortBy) {
|
|
order = -sortOrder;
|
|
} else {
|
|
order = field.defaultOrder < 0 ? -1 : 1;
|
|
}
|
|
|
|
$('#portalslist').empty().append(window.plugin.portalslist.portalTable(i, order, filter));
|
|
});
|
|
}
|
|
});
|
|
|
|
portals.forEach(function(obj, i) {
|
|
var row = obj.row
|
|
if(row.parentNode) row.parentNode.removeChild(row);
|
|
|
|
row.cells[0].textContent = i+1;
|
|
|
|
table.appendChild(row);
|
|
});
|
|
|
|
container.append('<div class="disclaimer">Click on portals table headers to sort by that column. '
|
|
+ 'Click on <b>All, Neutral, Resistance, Enlightened</b> to only show portals owner by that faction or on the number behind the factions to show all but those portals.</div>');
|
|
|
|
return container;
|
|
}
|
|
|
|
// portal link - single click: select portal
|
|
// double click: zoom to and select portal
|
|
// code from getPortalLink function by xelio from iitc: AP List - https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/ap-list.user.js
|
|
window.plugin.portalslist.getPortalLink = function(portal) {
|
|
var coord = portal.getLatLng();
|
|
var perma = '/intel?ll='+coord.lat+','+coord.lng+'&z=17&pll='+coord.lat+','+coord.lng;
|
|
|
|
// jQuery's event handlers seem to be removed when the nodes are remove from the DOM
|
|
var link = document.createElement("a");
|
|
link.textContent = portal.options.data.title;
|
|
link.href = perma;
|
|
link.addEventListener("click", function(ev) {
|
|
renderPortalDetails(portal.options.guid);
|
|
ev.preventDefault();
|
|
return false;
|
|
}, false);
|
|
link.addEventListener("dblclick", function(ev) {
|
|
zoomToAndShowPortal(portal.options.guid, [coord.lat, coord.lng]);
|
|
ev.preventDefault();
|
|
return false;
|
|
});
|
|
return link;
|
|
}
|
|
|
|
window.plugin.portalslist.onPaneChanged = function(pane) {
|
|
if(pane == "plugin-portalslist")
|
|
window.plugin.portalslist.displayPL();
|
|
else
|
|
$("#portalslist").remove()
|
|
};
|
|
|
|
var setup = function() {
|
|
if(window.useAndroidPanes()) {
|
|
android.addPane("plugin-portalslist", "Portals list", "ic_action_paste");
|
|
addHook("paneChanged", window.plugin.portalslist.onPaneChanged);
|
|
} else {
|
|
$('#toolbox').append('<a onclick="window.plugin.portalslist.displayPL()" title="Display a list of portals in the current view [t]" accesskey="t">Portals list</a>');
|
|
}
|
|
|
|
$("<style>")
|
|
.prop("type", "text/css")
|
|
.html("@@INCLUDESTRING:plugins/portals-list.css@@")
|
|
.appendTo("head");
|
|
|
|
}
|
|
|
|
// PLUGIN END //////////////////////////////////////////////////////////
|
|
|
|
@@PLUGINEND@@
|