diff --git a/plugins/portals-list.css b/plugins/portals-list.css
new file mode 100644
index 00000000..2abbc70e
--- /dev/null
+++ b/plugins/portals-list.css
@@ -0,0 +1,96 @@
+#portalslist.mobile {
+ background: transparent;
+ border: 0 none !important;
+ height: 100% !important;
+ width: 100% !important;
+ left: 0 !important;
+ top: 0 !important;
+ position: absolute;
+ overflow: auto;
+}
+
+#portalslist table {
+ margin-top: 5px;
+ border-collapse: collapse;
+ empty-cells: show;
+ width: 100%;
+ clear: both;
+}
+
+#portalslist table td, #portalslist table th {
+ background-color: #1b415e;
+ border-bottom: 1px solid #0b314e;
+ color: white;
+ padding: 3px;
+}
+
+#portalslist table th {
+ text-align: center;
+}
+
+#portalslist table .alignR {
+ text-align: right;
+}
+
+#portalslist table.portals td {
+ white-space: nowrap;
+}
+
+#portalslist table th.sortable {
+ cursor: pointer;
+}
+
+#portalslist table .portalTitle {
+ min-width: 120px !important;
+ max-width: 240px !important;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+#portalslist .sorted {
+ color: #FFCE00;
+}
+
+#portalslist table.filter {
+ table-layout: fixed;
+ cursor: pointer;
+ border-collapse: separate;
+ border-spacing: 1px;
+}
+
+#portalslist table.filter th {
+ text-align: left;
+ padding-left: 0.3em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#portalslist table.filter td {
+ text-align: right;
+ padding-right: 0.3em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#portalslist .filterNeu {
+ background-color: #666;
+}
+
+#portalslist table tr.res td, #portalslist .filterRes {
+ background-color: #005684;
+}
+
+#portalslist table tr.enl td, #portalslist .filterEnl {
+ background-color: #017f01;
+}
+
+#portalslist table tr.none td {
+ background-color: #000;
+}
+
+#portalslist .disclaimer {
+ margin-top: 10px;
+ font-size: 10px;
+}
+
diff --git a/plugins/portals-list.user.js b/plugins/portals-list.user.js
index 5116102f..85ab67ef 100644
--- a/plugins/portals-list.user.js
+++ b/plugins/portals-list.user.js
@@ -2,7 +2,7 @@
// @id iitc-plugin-portals-list@teo96
// @name IITC plugin: show list of portals
// @category Info
-// @version 0.1.2.@@DATETIMEVERSION@@
+// @version 0.2.0.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@
@@ -18,42 +18,133 @@
// PLUGIN START ////////////////////////////////////////////////////////
- /* whatsnew
- * 0.1.0 : Using the new data format
- * 0.0.15: Add 'age' column to display how long each portal has been controlled by its current owner.
- * 0.0.14: Add support to new mods (S:Shield - T:Turret - LA:Link Amp - H:Heat-sink - M:Multi-hack - FA:Force Amp)
- * 0.0.12: Use dialog() instead of alert so the user can drag the box around
- * 0.0.11: Add nominal energy column and # links, fix sort bug when opened even amounts of times, nits
- * 0.0.10: Fixed persistent css problem with alert
- * 0.0.9 : bugs hunt
- * 0.0.8 : Aborted to avoid problems with Niantic (export portals informations as csv or kml file)
- * 0.0.7 : more informations available via tooltips (who deployed, energy, ...), new E/AP column
- * 0.0.6 : Add power charge information into a new column + bugfix
- * 0.0.5 : Filter portals by clicking on 'All portals', 'Res Portals' or 'Enl Portals'
- * 0.0.4 : Add link to portals name, one click to display full information in portal panel, double click to zoom on portal, hover to show address
- * 0.0.3 : sorting ascending/descending and add numbers of portals by faction on top on table
- * 0.0.2 : add sorting feature when click on header column
- * 0.0.1 : initial release, show list of portals with level, team, resonators and shield information
- *
- * Display code inspired from @vita10gy's scoreboard plugin : iitc-plugin-scoreboard@vita10gy - https://github.com/breunigs/ingress-intel-total-conversion
- * Portal link code from xelio - iitc: AP List - https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/ap-list.user.js
- *
- * todo : export as GPX, Open in Google Maps, more statistics in the header, what else ?
- */
-
// use own namespace for plugin
window.plugin.portalslist = function() {};
window.plugin.portalslist.listPortals = [];
-window.plugin.portalslist.sortBy = 'level';
+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.
+ * 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+'%');
+ }
+ },
+ {
+ title: "Res",
+ value: function(portal) { return portal.options.data.resCount; },
+ format: function(cell, portal, value) {
+ $(cell)
+ .addClass("alignR")
+ .text(value);
+ }
+ },
+ {
+ 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);
+ }
+ },
+ {
+ title: "Fields",
+ value: function(portal) { return getPortalFieldsCount(portal.options.guid) },
+ format: function(cell, portal, value) {
+ $(cell)
+ .addClass("alignR")
+ .text(value);
+ }
+ },
+ {
+ 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 (PLAYER.team == portal.options.data.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));
+ }
+ },
+];
+
//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 = Res, 2 = Enl
+ //filter : 0 = All, 1 = Neutral, 2 = Res, 3 = Enl, -x = all but x
var retval=false;
var displayBounds = map.getBounds();
@@ -64,64 +155,69 @@ window.plugin.portalslist.getPortals = function() {
if(!displayBounds.contains(portal.getLatLng())) return true;
retval=true;
- var d = portal.options.data;
- var teamN = portal.options.team;
- switch (teamN) {
+ 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++;
}
- var l = window.getPortalLinks(i);
- var f = window.getPortalFields(i);
- var ap = portalApGainMaths(d.resCount, l.in.length+l.out.length, f.length);
- var thisPortal = {
- 'portal': portal,
- 'guid': i,
- 'teamN': teamN, // TEAM_NONE, TEAM_RES or TEAM_ENL
- 'team': d.team, // "NEUTRAL", "RESISTANCE" or "ENLIGHTENED"
- 'name': d.title || '(untitled)',
- 'nameLower': d.title && d.title.toLowerCase(),
- 'level': portal.options.level,
- 'health': d.health,
- 'resCount': d.resCount,
- 'img': d.img,
- 'linkCount': l.in.length + l.out.length,
- 'link' : l,
- 'fieldCount': f.length,
- 'field' : f,
- 'enemyAp': ap.enemyAp,
- 'ap': ap,
- };
- window.plugin.portalslist.listPortals.push(thisPortal);
+ // 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 html = '';
- window.plugin.portalslist.sortBy = 'level';
+ var list;
+ window.plugin.portalslist.sortBy = 1;
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()) {
- html += window.plugin.portalslist.portalTable(window.plugin.portalslist.sortBy, window.plugin.portalslist.sortOrder,window.plugin.portalslist.filter);
+ list = window.plugin.portalslist.portalTable(window.plugin.portalslist.sortBy, window.plugin.portalslist.sortOrder,window.plugin.portalslist.filter);
} else {
- html = '
';
+ list = $('');
};
if(window.useAndroidPanes()) {
- $('' + html + '
').appendTo(document.body);
+ $('').append(list).appendTo(document.body);
} else {
dialog({
- html: '
' + html + '
',
+ html: $('
').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',
@@ -136,124 +232,142 @@ window.plugin.portalslist.portalTable = function(sortBy, sortOrder, filter) {
window.plugin.portalslist.sortOrder = sortOrder;
window.plugin.portalslist.filter = filter;
- var portals=window.plugin.portalslist.listPortals;
+ var portals = window.plugin.portalslist.listPortals;
+ var sortField = window.plugin.portalslist.fields[sortBy];
- //Array sort
- window.plugin.portalslist.listPortals.sort(function(a, b) {
- var retVal = 0;
+ portals.sort(function(a, b) {
+ var valueA = a.sortValues[sortBy];
+ var valueB = b.sortValues[sortBy];
- var aComp = a[sortBy];
- var bComp = b[sortBy];
-
- if (aComp < bComp) {
- retVal = -1;
- } else if (aComp > bComp) {
- retVal = 1;
- } else {
- // equal - compare GUIDs to ensure consistent (but arbitrary) order
- retVal = a.guid < b.guid ? -1 : 1;
+ if(sortField.sort) {
+ return sortOrder * sortField.sort(valueA, valueB, a.portal, b.portal);
}
- // sortOrder is 1 (normal) or -1 (reversed)
- retVal = retVal * sortOrder;
- return retVal;
+ return sortOrder *
+ (valueA < valueB ? -1 :
+ valueA > valueB ? 1 :
+ 0);
});
- var sortAttr = window.plugin.portalslist.portalTableHeaderSortAttr;
- var html = window.plugin.portalslist.stats();
- html += '
'
- + '\n';
+ if(filter !== 0) {
+ portals = portals.filter(function(obj) {
+ return filter < 0
+ ? obj.portal.options.team+1 != -filter
+ : obj.portal.options.team+1 == filter;
+ });
+ }
- var rowNum = 1;
+ var table, row, cell;
+ var container = $('');
- $.each(portals, function(ind, portal) {
- if (filter === TEAM_NONE || filter === portal.teamN) {
+ table = document.createElement('table');
+ table.className = 'filter';
+ container.append(table);
- html += '
'
- + ''+rowNum+' | '
- + '' + window.plugin.portalslist.getPortalLink(portal, portal.guid) + ' | '
- + '' + portal.level + ' | '
- + '' + portal.team.substr(0,3) + ' | ';
+ row = table.insertRow(-1);
- html += '' + (portal.teamN!=TEAM_NONE?portal.health+'%':'-') + ' | '
- + '' + portal.resCount + ' | '
- + '' + (portal.linkCount?portal.linkCount:'-') + ' | '
- + '' + (portal.fieldCount?portal.fieldCount:'-') + ' | ';
+ var length = window.plugin.portalslist.listPortals.length;
- var apTitle = '';
- if (PLAYER.team == portal.team) {
- apTitle += 'Friendly AP:\t'+portal.ap.friendlyAp+'\n'
- + '- deploy '+(8-portal.resCount)+' resonator(s)\n'
- + '- upgrades/mods unknown\n';
+ ["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");
}
- apTitle += 'Enemy AP:\t'+portal.ap.enemyAp+'\n'
- + '- Destroy AP:\t'+portal.ap.destroyAp+'\n'
- + '- Capture AP:\t'+portal.ap.captureAp;
- html += '' + digits(portal.ap.enemyAp) + ' | ';
+ $(cell).click(function() {
+ var order;
+ if(i == sortBy) {
+ order = -sortOrder;
+ } else {
+ order = field.defaultOrder < 0 ? -1 : 1;
+ }
- html+= '
';
-
- rowNum++;
+ $('#portalslist').empty().append(window.plugin.portalslist.portalTable(i, order, filter));
+ });
}
});
- html += '
';
- html += '
Click on portals table headers to sort by that column. '
- + 'Click on All Portals, Resistance Portals, Enlightened Portals to filter
';
+ portals.forEach(function(obj, i) {
+ var row = obj.row
+ if(row.parentNode) row.parentNode.removeChild(row);
- return html;
+ row.cells[0].textContent = i+1;
+
+ table.appendChild(row);
+ });
+
+ container.append('
Click on portals table headers to sort by that column. '
+ + 'Click on All, Neutral, Resistance, Enlightened to only show portals owner by that faction or on the number behind the factions to show all but those portals.
');
+
+ return container;
}
-window.plugin.portalslist.stats = function(sortBy) {
- var html = '
'
- + 'All Portals : (click to filter) | ' + window.plugin.portalslist.listPortals.length + ' | '
- + 'Resistance Portals : | ' + window.plugin.portalslist.resP +' (' + Math.floor(window.plugin.portalslist.resP/window.plugin.portalslist.listPortals.length*100) + '%) | '
- + 'Enlightened Portals : | '+ window.plugin.portalslist.enlP +' (' + Math.floor(window.plugin.portalslist.enlP/window.plugin.portalslist.listPortals.length*100) + '%) | '
- + '
'
- + '
';
- return html;
-}
-
-// A little helper function so the above isn't so messy
-window.plugin.portalslist.portalTableHeaderSortAttr = function(name, by, defOrder, extraClass) {
- // data-sort attr: used by jquery .data('sort') below
- var retVal = 'data-sort="'+name+'" data-defaultorder="'+defOrder+'" class="'+(extraClass?extraClass+' ':'')+'sortable'+(name==by?' sorted':'')+'"';
-
- return retVal;
-};
-
// portal link - single click: select portal
// double click: zoom to and select portal
-// hover: show address
// 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,guid) {
- var coord = portal.portal.getLatLng();
- var latlng = [coord.lat, coord.lng].join();
- var jsSingleClick = 'window.renderPortalDetails(\''+guid+'\');return false';
- var jsDoubleClick = 'window.zoomToAndShowPortal(\''+guid+'\', ['+latlng+']);return false';
+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;
- //Use Jquery to create the link, which escape characters in TITLE and ADDRESS of portal
- var a = $('
',{
- text: portal.name,
-// title: portal.name,
- href: perma,
- onClick: jsSingleClick,
- onDblClick: jsDoubleClick
- })[0].outerHTML;
-
- return a;
+ //Use Jquery to create the link, which escape characters in TITLE of portal
+ return $('')
+ .text(portal.options.data.title)
+ .attr('href', perma)
+ .click(function(ev) {
+ renderPortalDetails(portal.options.guid);
+ ev.preventDefault();
+ return false;
+ })
+ .dblclick(function(ev) {
+ zoomToAndShowPortal(portal.options.guid, [coord.lat, coord.lng]);
+ ev.preventDefault();
+ return false;
+ });
}
window.plugin.portalslist.onPaneChanged = function(pane) {
@@ -271,45 +385,11 @@ var setup = function() {
$('#toolbox').append(' Portals list');
}
- $('head').append('');
+ $("