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@@
 |