Advertisement
Guest User

Ingress IITC iitc-plugin-portals-list script with export

a guest
Jan 7th, 2018
1,175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @id             iitc-plugin-portals-list@teo96
  3. // @name           IITC plugin: show list of portals // modified with export button
  4. // @category       Info
  5. // @version        0.2.1.20170108.21732
  6. // @namespace      https://github.com/jonatkins/ingress-intel-total-conversion
  7. // @updateURL      https://static.iitc.me/build/release/plugins/portals-list.meta.js
  8. // @downloadURL    https://static.iitc.me/build/release/plugins/portals-list.user.js
  9. // @description    [iitc-2017-01-08-021732] Display a sortable list of all visible portals with full details about the team, resonators, links, etc.
  10. // @include        https://*.ingress.com/intel*
  11. // @include        http://*.ingress.com/intel*
  12. // @match          https://*.ingress.com/intel*
  13. // @match          http://*.ingress.com/intel*
  14. // @include        https://*.ingress.com/mission/*
  15. // @include        http://*.ingress.com/mission/*
  16. // @match          https://*.ingress.com/mission/*
  17. // @match          http://*.ingress.com/mission/*
  18. // @grant          none
  19. // ==/UserScript==
  20.  
  21.  
  22. function wrapper(plugin_info) {
  23. // ensure plugin framework is there, even if iitc is not yet loaded
  24. if(typeof window.plugin !== 'function') window.plugin = function() {};
  25.  
  26. //PLUGIN AUTHORS: writing a plugin outside of the IITC build environment? if so, delete these lines!!
  27. //(leaving them in place might break the 'About IITC' page or break update checks)
  28. plugin_info.buildName = 'iitc';
  29. plugin_info.dateTimeVersion = '20170108.21732';
  30. plugin_info.pluginId = 'portals-list';
  31. //END PLUGIN AUTHORS NOTE
  32.  
  33.  
  34.  
  35. // PLUGIN START ////////////////////////////////////////////////////////
  36.  
  37. // use own namespace for plugin
  38. window.plugin.portalslist = function() {};
  39.  
  40. window.plugin.portalslist.listPortals = [];
  41. window.plugin.portalslist.sortBy = 1; // second column: level
  42. window.plugin.portalslist.sortOrder = -1;
  43. window.plugin.portalslist.enlP = 0;
  44. window.plugin.portalslist.resP = 0;
  45. window.plugin.portalslist.neuP = 0;
  46. window.plugin.portalslist.filter = 0;
  47.  
  48. /*
  49.  * plugins may add fields by appending their specifiation to the following list. The following members are supported:
  50.  * title: String
  51.  *     Name of the column. Required.
  52.  * value: function(portal)
  53.  *     The raw value of this field. Can by anything. Required, but can be dummy implementation if sortValue and format
  54.  *     are implemented.
  55.  * sortValue: function(value, portal)
  56.  *     The value to sort by. Optional, uses value if omitted. The raw value is passed as first argument.
  57.  * sort: function(valueA, valueB, portalA, portalB)
  58.  *     Custom sorting function. See Array.sort() for details on return value. Both the raw values and the portal objects
  59.  *     are passed as arguments. Optional. Set to null to disable sorting
  60.  * format: function(cell, portal, value)
  61.  *     Used to fill and format the cell, which is given as a DOM node. If omitted, the raw value is put in the cell.
  62.  * defaultOrder: -1|1
  63.  *     Which order should by default be used for this column. -1 means descending. Default: 1
  64.  */
  65.  
  66.  
  67. window.plugin.portalslist.fields = [
  68.   {
  69.     title: "Portal Name",
  70.     value: function(portal) { return portal.options.data.title; },
  71.     sortValue: function(value, portal) { return value.toLowerCase(); },
  72.     format: function(cell, portal, value) {
  73.       $(cell)
  74.         .append(plugin.portalslist.getPortalLink(portal))
  75.         .addClass("portalTitle");
  76.     }
  77.   },
  78.   {
  79.     title: "Latitude",
  80.     value: function(portal) { return portal.getLatLng().lat; },
  81.     format: function(cell, portal, value) {
  82.       $(cell).text(value);
  83.     }
  84.   },
  85.   {
  86.     title: "Longitude",
  87.     value: function(portal) { return portal.getLatLng().lng; },
  88.     format: function(cell, portal, value) {
  89.       $(cell).text(value);
  90.     }
  91.   },
  92.   {
  93.     title: "Level",
  94.     value: function(portal) { return portal.options.data.level; },
  95.     format: function(cell, portal, value) {
  96.       $(cell)
  97.         .css('background-color', COLORS_LVL[value])
  98.         .text('L' + value);
  99.     },
  100.     defaultOrder: -1,
  101.   },
  102.   {
  103.     title: "Team",
  104.     value: function(portal) { return portal.options.team; },
  105.     format: function(cell, portal, value) {
  106.       $(cell).text(['NEU', 'RES', 'ENL'][value]);
  107.     }
  108.   },
  109.   {
  110.     title: "Health",
  111.     value: function(portal) { return portal.options.data.health; },
  112.     sortValue: function(value, portal) { return portal.options.team===TEAM_NONE ? -1 : value; },
  113.     format: function(cell, portal, value) {
  114.       $(cell)
  115.         .addClass("alignR")
  116.         .text(portal.options.team===TEAM_NONE ? '-' : value+'%');
  117.     },
  118.     defaultOrder: -1,
  119.   },
  120.   {
  121.     title: "Res",
  122.     value: function(portal) { return portal.options.data.resCount; },
  123.     format: function(cell, portal, value) {
  124.       $(cell)
  125.         .addClass("alignR")
  126.         .text(value);
  127.     },
  128.     defaultOrder: -1,
  129.   },
  130.   {
  131.     title: "Links",
  132.     value: function(portal) { return window.getPortalLinks(portal.options.guid); },
  133.     sortValue: function(value, portal) { return value.in.length + value.out.length; },
  134.     format: function(cell, portal, value) {
  135.       $(cell)
  136.         .addClass("alignR")
  137.         .addClass('help')
  138.         .attr('title', 'In:\t' + value.in.length + '\nOut:\t' + value.out.length)
  139.         .text(value.in.length+value.out.length);
  140.     },
  141.     defaultOrder: -1,
  142.   },
  143.   {
  144.     title: "Fields",
  145.     value: function(portal) { return getPortalFieldsCount(portal.options.guid) },
  146.     format: function(cell, portal, value) {
  147.       $(cell)
  148.         .addClass("alignR")
  149.         .text(value);
  150.     },
  151.     defaultOrder: -1,
  152.   },
  153.   {
  154.     title: "AP",
  155.     value: function(portal) {
  156.       var links = window.getPortalLinks(portal.options.guid);
  157.       var fields = getPortalFieldsCount(portal.options.guid);
  158.       return portalApGainMaths(portal.options.data.resCount, links.in.length+links.out.length, fields);
  159.     },
  160.     sortValue: function(value, portal) { return value.enemyAp; },
  161.     format: function(cell, portal, value) {
  162.       var title = '';
  163.       if (teamStringToId(PLAYER.team) == portal.options.team) {
  164.         title += 'Friendly AP:\t'+value.friendlyAp+'\n'
  165.                + '- deploy '+(8-portal.options.data.resCount)+' resonator(s)\n'
  166.                + '- upgrades/mods unknown\n';
  167.       }
  168.       title += 'Enemy AP:\t'+value.enemyAp+'\n'
  169.              + '- Destroy AP:\t'+value.destroyAp+'\n'
  170.              + '- Capture AP:\t'+value.captureAp;
  171.  
  172.       $(cell)
  173.         .addClass("alignR")
  174.         .addClass('help')
  175.         .prop('title', title)
  176.         .html(digits(value.enemyAp));
  177.     },
  178.     defaultOrder: -1,
  179.   },
  180. ];
  181.  
  182. //fill the listPortals array with portals avaliable on the map (level filtered portals will not appear in the table)
  183. window.plugin.portalslist.getPortals = function() {
  184.   //filter : 0 = All, 1 = Neutral, 2 = Res, 3 = Enl, -x = all but x
  185.   var retval=false;
  186.  
  187.   var displayBounds = map.getBounds();
  188.  
  189.   window.plugin.portalslist.listPortals = [];
  190.   $.each(window.portals, function(i, portal) {
  191.     // eliminate offscreen portals (selected, and in padding)
  192.     if(!displayBounds.contains(portal.getLatLng())) return true;
  193.  
  194.     retval=true;
  195.  
  196.     switch (portal.options.team) {
  197.       case TEAM_RES:
  198.         window.plugin.portalslist.resP++;
  199.         break;
  200.       case TEAM_ENL:
  201.         window.plugin.portalslist.enlP++;
  202.         break;
  203.       default:
  204.         window.plugin.portalslist.neuP++;
  205.     }
  206.  
  207.     // cache values and DOM nodes
  208.     var obj = { portal: portal, values: [], sortValues: [] };
  209.  
  210.     var row = document.createElement('tr');
  211.     row.className = TEAM_TO_CSS[portal.options.team];
  212.     obj.row = row;
  213.  
  214.     var cell = row.insertCell(-1);
  215.     cell.className = 'alignR';
  216.  
  217.     window.plugin.portalslist.fields.forEach(function(field, i) {
  218.       cell = row.insertCell(-1);
  219.  
  220.       var value = field.value(portal);
  221.       obj.values.push(value);
  222.  
  223.       //console.log('DEBUG2 ' + value);
  224.       obj.sortValues.push(field.sortValue ? field.sortValue(value, portal) : value);
  225.  
  226.       if(field.format) {
  227.         field.format(cell, portal, value);
  228.       } else {
  229.         cell.textContent = value;
  230.       }
  231.     });
  232.  
  233.     window.plugin.portalslist.listPortals.push(obj);
  234.   });
  235.  
  236.   return retval;
  237. }
  238.  
  239. window.plugin.portalslist.displayPL = function() {
  240.   var list;
  241.   // plugins (e.g. bookmarks) can insert fields before the standard ones - so we need to search for the 'level' column
  242.   window.plugin.portalslist.sortBy = window.plugin.portalslist.fields.map(function(f){return f.title;}).indexOf('Level');
  243.   window.plugin.portalslist.sortOrder = -1;
  244.   window.plugin.portalslist.enlP = 0;
  245.   window.plugin.portalslist.resP = 0;
  246.   window.plugin.portalslist.neuP = 0;
  247.   window.plugin.portalslist.filter = 0;
  248.  
  249.   if (window.plugin.portalslist.getPortals()) {
  250.     list = window.plugin.portalslist.portalTable(window.plugin.portalslist.sortBy, window.plugin.portalslist.sortOrder,window.plugin.portalslist.filter);
  251.   } else {
  252.     list = $('<table class="noPortals"><tr><td>Nothing to show!</td></tr></table>');
  253.   };
  254.  
  255.   if(window.useAndroidPanes()) {
  256.     $('<div id="portalslist" class="mobile">').append(list).appendTo(document.body);
  257.   } else {
  258.     dialog({
  259.       html: $('<div id="portalslist">').append(list),
  260.       dialogClass: 'ui-dialog-portalslist',
  261.       title: 'Portal list: ' + window.plugin.portalslist.listPortals.length + ' ' + (window.plugin.portalslist.listPortals.length == 1 ? 'portal' : 'portals'),
  262.       id: 'portal-list',
  263.       width: 700
  264.     });
  265.   }
  266. }
  267.  
  268. window.plugin.portalslist.portalTable = function(sortBy, sortOrder, filter) {
  269.   // save the sortBy/sortOrder/filter
  270.   window.plugin.portalslist.sortBy = sortBy;
  271.   window.plugin.portalslist.sortOrder = sortOrder;
  272.   window.plugin.portalslist.filter = filter;
  273.  
  274.   var portals = window.plugin.portalslist.listPortals;
  275.   var sortField = window.plugin.portalslist.fields[sortBy];
  276.  
  277.   portals.sort(function(a, b) {
  278.     var valueA = a.sortValues[sortBy];
  279.     var valueB = b.sortValues[sortBy];
  280.  
  281.     if(sortField.sort) {
  282.       return sortOrder * sortField.sort(valueA, valueB, a.portal, b.portal);
  283.     }
  284.  
  285. //FIXME: sort isn't stable, so re-sorting identical values can change the order of the list.
  286. //fall back to something constant (e.g. portal name?, portal GUID?),
  287. //or switch to a stable sort so order of equal items doesn't change
  288.     return sortOrder *
  289.       (valueA < valueB ? -1 :
  290.       valueA > valueB ?  1 :
  291.       0);
  292.   });
  293.  
  294.   if(filter !== 0) {
  295.     portals = portals.filter(function(obj) {
  296.       return filter < 0
  297.         ? obj.portal.options.team+1 != -filter
  298.         : obj.portal.options.team+1 == filter;
  299.     });
  300.   }
  301.  
  302.   var table, row, cell;
  303.   var container = $('<div>');
  304.  
  305.   table = document.createElement('table');
  306.   table.className = 'filter';
  307.   container.append(table);
  308.  
  309.   row = table.insertRow(-1);
  310.  
  311.   var length = window.plugin.portalslist.listPortals.length;
  312.  
  313.   ["All", "Neutral", "Resistance", "Enlightened"].forEach(function(label, i) {
  314.     cell = row.appendChild(document.createElement('th'));
  315.     cell.className = 'filter' + label.substr(0, 3);
  316.     cell.textContent = label+':';
  317.     cell.title = 'Show only portals of this color';
  318.     $(cell).click(function() {
  319.       $('#portalslist').empty().append(window.plugin.portalslist.portalTable(sortBy, sortOrder, i));
  320.     });
  321.  
  322.  
  323.     cell = row.insertCell(-1);
  324.     cell.className = 'filter' + label.substr(0, 3);
  325.     if(i != 0) cell.title = 'Hide portals of this color';
  326.     $(cell).click(function() {
  327.       $('#portalslist').empty().append(window.plugin.portalslist.portalTable(sortBy, sortOrder, -i));
  328.     });
  329.  
  330.     switch(i-1) {
  331.       case -1:
  332.         cell.textContent = length;
  333.         break;
  334.       case 0:
  335.         cell.textContent = window.plugin.portalslist.neuP + ' (' + Math.round(window.plugin.portalslist.neuP/length*100) + '%)';
  336.         break;
  337.       case 1:
  338.         cell.textContent = window.plugin.portalslist.resP + ' (' + Math.round(window.plugin.portalslist.resP/length*100) + '%)';
  339.         break;
  340.       case 2:
  341.         cell.textContent = window.plugin.portalslist.enlP + ' (' + Math.round(window.plugin.portalslist.enlP/length*100) + '%)';
  342.     }
  343.   });
  344.  
  345.   table = document.createElement('table');
  346.   table.className = 'portals';
  347.   container.append(table);
  348.  
  349.   var thead = table.appendChild(document.createElement('thead'));
  350.   row = thead.insertRow(-1);
  351.  
  352.   cell = row.appendChild(document.createElement('th'));
  353.   cell.textContent = '#';
  354.  
  355.   window.plugin.portalslist.fields.forEach(function(field, i) {
  356.     cell = row.appendChild(document.createElement('th'));
  357.     cell.textContent = field.title;
  358.     if(field.sort !== null) {
  359.       cell.classList.add("sortable");
  360.       if(i == window.plugin.portalslist.sortBy) {
  361.         cell.classList.add("sorted");
  362.       }
  363.  
  364.       $(cell).click(function() {
  365.         var order;
  366.         if(i == sortBy) {
  367.           order = -sortOrder;
  368.         } else {
  369.           order = field.defaultOrder < 0 ? -1 : 1;
  370.         }
  371.  
  372.         $('#portalslist').empty().append(window.plugin.portalslist.portalTable(i, order, filter));
  373.       });
  374.     }
  375.   });
  376.  
  377.   portals.forEach(function(obj, i) {
  378.     var row = obj.row
  379.     if(row.parentNode) row.parentNode.removeChild(row);
  380.  
  381.     row.cells[0].textContent = i+1;
  382.  
  383.     table.appendChild(row);
  384.   });
  385.  
  386.   container.append('<div class="disclaimer">Click on portals table headers to sort by that column. '
  387.     + '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>');
  388.  
  389.   container.append('<div class="disclaimer"><a onclick="window.plugin.portalslist.exportPortals()" title="Export a list of portals in the current view [e]" accesskey="e">Export portals</a>.</div>');
  390.  
  391.   return container;
  392. }
  393.  
  394. window.plugin.portalslist.exportPortals = function() {
  395.   var portals = window.plugin.portalslist.listPortals;
  396.  
  397.   var s = '';
  398.  
  399.   portals.forEach(function(obj, i) {
  400.     s = s + '"' + portals[i].values[0] + '",' + portals[i].values[1] + ',' + portals[i].values[2] + '\n';
  401.   });
  402.  
  403.   var a = window.document.createElement('a');
  404.   a.href = window.URL.createObjectURL(new Blob([s], {type: 'text/csv'}));
  405.   a.download = 'export.csv';
  406.  
  407.   // Append anchor to body.
  408.   document.body.appendChild(a);
  409.   a.click();
  410.  
  411.   // Remove anchor from body
  412.   document.body.removeChild(a);
  413. }
  414.  
  415. // portal link - single click: select portal
  416. //               double click: zoom to and select portal
  417. // 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
  418. window.plugin.portalslist.getPortalLink = function(portal) {
  419.   var coord = portal.getLatLng();
  420.   var perma = '/intel?ll='+coord.lat+','+coord.lng+'&z=17&pll='+coord.lat+','+coord.lng;
  421.  
  422.   // jQuery's event handlers seem to be removed when the nodes are remove from the DOM
  423.   var link = document.createElement("a");
  424.   link.textContent = portal.options.data.title;
  425.   link.href = perma;
  426.   link.addEventListener("click", function(ev) {
  427.     renderPortalDetails(portal.options.guid);
  428.     ev.preventDefault();
  429.     return false;
  430.   }, false);
  431.   link.addEventListener("dblclick", function(ev) {
  432.     zoomToAndShowPortal(portal.options.guid, [coord.lat, coord.lng]);
  433.     ev.preventDefault();
  434.     return false;
  435.   });
  436.   return link;
  437. }
  438.  
  439. window.plugin.portalslist.onPaneChanged = function(pane) {
  440.   if(pane == "plugin-portalslist")
  441.     window.plugin.portalslist.displayPL();
  442.   else
  443.     $("#portalslist").remove()
  444. };
  445.  
  446. var setup =  function() {
  447.   if(window.useAndroidPanes()) {
  448.     android.addPane("plugin-portalslist", "Portals list", "ic_action_paste");
  449.     addHook("paneChanged", window.plugin.portalslist.onPaneChanged);
  450.   } else {
  451.     $('#toolbox').append('<a onclick="window.plugin.portalslist.displayPL()" title="Display a list of portals in the current view [t]" accesskey="t">Portals list</a>');
  452.   }
  453.  
  454.   $("<style>")
  455.     .prop("type", "text/css")
  456.     .html("#portalslist.mobile {\n  background: transparent;\n  border: 0 none !important;\n  height: 100% !important;\n  width: 100% !important;\n  left: 0 !important;\n  top: 0 !important;\n  position: absolute;\n  overflow: auto;\n}\n\n#portalslist table {\n  margin-top: 5px;\n  border-collapse: collapse;\n  empty-cells: show;\n  width: 100%;\n  clear: both;\n}\n\n#portalslist table td, #portalslist table th {\n  background-color: #1b415e;\n  border-bottom: 1px solid #0b314e;\n  color: white;\n  padding: 3px;\n}\n\n#portalslist table th {\n  text-align: center;\n}\n\n#portalslist table .alignR {\n  text-align: right;\n}\n\n#portalslist table.portals td {\n  white-space: nowrap;\n}\n\n#portalslist table th.sortable {\n  cursor: pointer;\n}\n\n#portalslist table .portalTitle {\n  min-width: 120px !important;\n  max-width: 240px !important;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n#portalslist .sorted {\n  color: #FFCE00;\n}\n\n#portalslist table.filter {\n  table-layout: fixed;\n  cursor: pointer;\n  border-collapse: separate;\n  border-spacing: 1px;\n}\n\n#portalslist table.filter th {\n  text-align: left;\n  padding-left: 0.3em;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#portalslist table.filter td {\n  text-align: right;\n  padding-right: 0.3em;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n#portalslist .filterNeu {\n  background-color: #666;\n}\n\n#portalslist table tr.res td, #portalslist .filterRes {\n  background-color: #005684;\n}\n\n#portalslist table tr.enl td, #portalslist .filterEnl {\n  background-color: #017f01;\n}\n\n#portalslist table tr.none td {\n  background-color: #000;\n}\n\n#portalslist .disclaimer {\n  margin-top: 10px;\n  font-size: 10px;\n}\n\n#portalslist.mobile table.filter tr {\n  display: block;\n  text-align: center;\n}\n#portalslist.mobile table.filter th, #portalslist.mobile table.filter td {\n  display: inline-block;\n  width: 22%;\n}\n\n")
  457.     .appendTo("head");
  458.  
  459. }
  460.  
  461. // PLUGIN END //////////////////////////////////////////////////////////
  462.  
  463.  
  464. setup.info = plugin_info; //add the script info data to the function as a property
  465. if(!window.bootPlugins) window.bootPlugins = [];
  466. window.bootPlugins.push(setup);
  467. // if IITC has already booted, immediately run the 'setup' function
  468. if(window.iitcLoaded && typeof setup === 'function') setup();
  469. } // wrapper end
  470. // inject code into site context
  471. var script = document.createElement('script');
  472. var info = {};
  473. if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description };
  474. script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');'));
  475. (document.body || document.head || document.documentElement).appendChild(script);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement