Merge branch 'to-push' of github.com:nexushoratio/ingress-intel-total-conversion into to-push

Conflicts:
	plugins/portal-defense.user.js
	plugins/show-linked-portals.user.js
This commit is contained in:
Mike Castle
2013-12-02 02:18:03 -08:00
98 changed files with 11406 additions and 11373 deletions

View File

@ -171,7 +171,7 @@ window.artifact.updateLayer = function() {
artifact._layer.clearLayers();
$.each(artifact.portalInfo, function(guid,data) {
var latlng = L.latLng ([data._entityData.locationE6.latE6/1E6, data._entityData.locationE6.lngE6/1E6]);
var latlng = L.latLng ([data._entityData.latE6/1E6, data._entityData.lngE6/1E6]);
// jarvis shard icon
var iconUrl = undefined;
@ -235,8 +235,8 @@ window.artifact.showArtifactList = function() {
var sortVal = 0;
var onclick = 'zoomToAndShowPortal(\''+guid+'\',['+data._entityData.locationE6.latE6/1E6+','+data._entityData.locationE6.lngE6/1E6+'])';
var row = '<tr><td class="portal"><a onclick="'+onclick+'" title="'+escapeHtmlSpecialChars(data._entityData.portalV2.descriptiveText.ADDRESS||'')+'">'+escapeHtmlSpecialChars(data._entityData.portalV2.descriptiveText.TITLE)+'</a></td>';
var onclick = 'zoomToAndShowPortal(\''+guid+'\',['+data._entityData.latE6/1E6+','+data._entityData.lngE6/1E6+'])';
var row = '<tr><td class="portal"><a onclick="'+onclick+'">'+escapeHtmlSpecialChars(data._entityData.title)+'</a></td>';
row += '<td class="info">';

View File

@ -338,7 +338,6 @@ window.setMapBaseLayer = function() {
// included as inline script in the original site, the data is static
// and cannot be updated.
window.setupPlayerStat = function() {
PLAYER.guid = playerNameToGuid(PLAYER.nickname);
var level;
var ap = parseInt(PLAYER.ap);
for(level = 0; level < MIN_AP_FOR_LEVEL.length; level++) {
@ -531,7 +530,6 @@ function boot() {
window.setupTaphold();
window.setupStyles();
window.setupDialogs();
window.setupPlayerNameCache();
window.setupMap();
window.setupGeosearch();
window.setupRedeem();
@ -542,6 +540,7 @@ function boot() {
window.setupPlayerStat();
window.setupTooltips();
window.chat.setup();
window.portalDetail.setup();
window.setupQRLoadLib();
window.setupLayerChooserSelectOne();
window.setupLayerChooserStatusRecorder();

View File

@ -127,7 +127,7 @@ window.chat.requestFaction = function(getOlderMsgs, isRetry) {
var d = chat.genPostData(true, chat._faction, getOlderMsgs);
var r = window.postAjax(
'getPaginatedPlextsV2',
'getPaginatedPlexts',
d,
function(data, textStatus, jqXHR) { chat.handleFaction(data, getOlderMsgs); },
isRetry
@ -178,7 +178,7 @@ window.chat.requestPublic = function(getOlderMsgs, isRetry) {
var d = chat.genPostData(false, chat._public, getOlderMsgs);
var r = window.postAjax(
'getPaginatedPlextsV2',
'getPaginatedPlexts',
d,
function(data, textStatus, jqXHR) { chat.handlePublic(data, getOlderMsgs); },
isRetry
@ -284,11 +284,9 @@ window.chat.writeDataToHash = function(newData, storageHash, isPublicChannel, is
switch(markup[0]) {
case 'SENDER': // user generated messages
nick = markup[1].plain.slice(0, -2); // cut “: ” at end
pguid = markup[1].guid;
break;
case 'PLAYER': // automatically generated messages
pguid = markup[1].guid;
nick = markup[1].plain;
team = markup[1].team === 'RESISTANCE' ? TEAM_RES : TEAM_ENL;
if(ind > 0) msg += nick; // dont repeat nick directly
@ -351,13 +349,9 @@ window.chat.writeDataToHash = function(newData, storageHash, isPublicChannel, is
if ((!isPublicChannel) && (!isSecureMessage)) msg = '<span style="color: #ff6">[public]</span> ' + msg;
// format: timestamp, autogenerated, HTML message, player guid
storageHash.data[json[0]] = [json[1], auto, chat.renderMsg(msg, nick, time, team, msgToPlayer, systemNarrowcast), pguid];
// format: timestamp, autogenerated, HTML message
storageHash.data[json[0]] = [json[1], auto, chat.renderMsg(msg, nick, time, team, msgToPlayer, systemNarrowcast)];
// if we're processing older messages, we could be looking at pre-name change mentions or similar
// so in that case, flag it so we don't overwrite existing name cache entries.
// (it's not perfect - the initial request has the wrong value here)
window.setPlayerName(pguid, nick, isOlderMsgs); // free nick name resolves.
});
}

View File

@ -8,8 +8,14 @@
// given the entity detail data, returns the team the entity belongs
// to. Uses TEAM_* enum values.
window.getTeam = function(details) {
return teamStringToId(details.controllingTeam.team);
}
window.teamStringToId = function(teamStr) {
var team = TEAM_NONE;
if(details.controllingTeam.team === 'ALIENS' || details.controllingTeam.team === 'ENLIGHTENED') team = TEAM_ENL;
if(details.controllingTeam.team === 'RESISTANCE') team = TEAM_RES;
if(teamStr === 'ENLIGHTENED') team = TEAM_ENL;
if(teamStr === 'RESISTANCE') team = TEAM_RES;
return team;
}

View File

@ -48,7 +48,8 @@
// called after each map data request finished. Argument is
// {success: boolean} indicated the request success or fail.
// iitcLoaded: called after IITC and all plugins loaded
// portalDetailLoaded: called when a request to load full portal detail
// completes. guid, success, details parameters
window._hooks = {}
window.VALID_HOOKS = [
@ -58,7 +59,8 @@ window.VALID_HOOKS = [
'portalDetailsUpdated',
'publicChatDataAvailable', 'factionChatDataAvailable',
'requestFinished', 'nicknameClicked',
'geoSearch', 'iitcLoaded'];
'geoSearch', 'iitcLoaded',
'portalDetailLoaded'];
window.runHooks = function(event, data) {
if(VALID_HOOKS.indexOf(event) === -1) throw('Unknown event type: ' + event);

View File

@ -104,7 +104,6 @@ window.Render.prototype.processDeletedGameEntityGuids = function(deleted) {
}
window.Render.prototype.processGameEntities = function(entities) {
var portalGuids = [];
for (var i in entities) {
var ent = entities[i];
@ -112,14 +111,9 @@ window.Render.prototype.processGameEntities = function(entities) {
// don't create entities in the 'deleted' list
if (!(ent[0] in this.deletedGuid)) {
this.createEntity(ent);
if ('portalV2' in ent[2]) portalGuids.push(ent[0]);
}
}
// now reconstruct links 'optimised' out of the data from the portal link data
for (var i in portalGuids) {
this.createLinksFromPortalData(portalGuids[i]);
}
}
@ -130,6 +124,7 @@ window.Render.prototype.endRenderPass = function() {
// check to see if there are any entities we haven't seen. if so, delete them
for (var guid in window.portals) {
// special case for selected portal - it's kept even if not seen
// artifact (e.g. jarvis shard) portals are also kept - but they're always 'seen'
if (!(guid in this.seenPortalsGuid) && guid !== selectedPortal) {
this.deletePortalEntity(guid);
}
@ -214,22 +209,6 @@ window.Render.prototype.deleteFieldEntity = function(guid) {
var f = window.fields[guid];
var fd = f.options.details;
var deletePortalLinkedField = function(pguid) {
if (pguid in window.portals) {
var pd = window.portals[pguid].options.details;
if (pd.portalV2.linkedFields) {
var i = pd.portalV2.linkedFields.indexOf(guid);
if (i >= 0) {
pd.portalV2.linkedFields.splice(i,1);
}
}
}
}
deletePortalLinkedField (fd.capturedRegion.vertexA.guid);
deletePortalLinkedField (fd.capturedRegion.vertexB.guid);
deletePortalLinkedField (fd.capturedRegion.vertexC.guid);
fieldsFactionLayers[f.options.team].removeLayer(f);
delete window.fields[guid];
}
@ -244,23 +223,30 @@ window.Render.prototype.createEntity = function(ent) {
// logic on detecting entity type based on the stock site javascript.
if ("portalV2" in ent[2]) {
this.createPortalEntity(ent);
} else if ("capturedRegion" in ent[2]) {
this.createFieldEntity(ent);
} else if ("edge" in ent[2]) {
this.createLinkEntity(ent);
} else {
console.warn("Unknown entity found: "+JSON.stringify(ent));
}
switch (ent[2].type) {
case 'portal':
this.createPortalEntity(ent);
break;
case 'edge':
this.createLinkEntity(ent);
break;
case 'region':
this.createFieldEntity(ent);
break;
default:
console.warn('unknown entity found: '+JSON.stringify(ent));
break;
}
}
window.Render.prototype.createPortalEntity = function(ent) {
this.seenPortalsGuid[ent[0]] = true; // flag we've seen it
var previousDetails = undefined;
var previousData = undefined;
// check if entity already exists
if (ent[0] in window.portals) {
@ -275,21 +261,17 @@ window.Render.prototype.createPortalEntity = function(ent) {
// remember the old details, for the callback
previousDetails = p.options.details;
previousData = p.options.data;
this.deletePortalEntity(ent[0]);
}
var portalLevel = getPortalLevel(ent[2]);
var team = getTeam(ent[2]);
var portalLevel = parseInt(ent[2].level);
var team = teamStringToId(ent[2].team);
// the data returns unclaimed portals as level 1 - but IITC wants them treated as level 0
if (team == TEAM_NONE) portalLevel = 0;
var latlng = L.latLng(ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6);
//TODO: move marker creation, style setting, etc into a separate class
//(as it's called from elsewhere - e.g. selecting/deselecting portals)
//ALSO: change API for highlighters - make them return the updated style rather than directly calling setStyle on the portal marker
//(can this be done in a backwardly-compatible way??)
var latlng = L.latLng(ent[2].latE6/1E6, ent[2].lngE6/1E6);
var dataOptions = {
level: portalLevel,
@ -297,32 +279,16 @@ window.Render.prototype.createPortalEntity = function(ent) {
ent: ent, // LEGACY - TO BE REMOVED AT SOME POINT! use .guid, .timestamp and .details instead
guid: ent[0],
timestamp: ent[1],
details: ent[2]
data: ent[2]
};
// Javascript uses references for objects. For now, at least, we need to modify the data within
// the options.details.portalV2 (to add in linkedFields). To avoid tainting the original data (which may be cached)
// we'll shallow-copy these items
dataOptions.details = $.extend({}, dataOptions.details);
dataOptions.details.portalV2 = $.extend({}, dataOptions.details.portalV2);
// create a linkedFields entry and add it to details - various bits of code assumes it will exist
for (var fguid in window.fields) {
var fd = window.fields[fguid].options.details;
if ( fd.capturedRegion.vertexA.guid == ent[0] || fd.capturedRegion.vertexB.guid == ent[0] || fd.capturedRegion.vertexC.guid == ent[0]) {
if (!dataOptions.details.portalV2.linkedFields) dataOptions.details.portalV2.linkedFields = [];
dataOptions.details.portalV2.linkedFields.push(fguid);
}
}
var marker = createMarker(latlng, dataOptions);
marker.on('click', function() { window.renderPortalDetails(ent[0]); });
marker.on('dblclick', function() { window.renderPortalDetails(ent[0]); window.map.setView(latlng, 17); });
window.runHooks('portalAdded', {portal: marker, previousDetails: previousDetails});
window.runHooks('portalAdded', {portal: marker, previousData: previousData});
window.portals[ent[0]] = marker;
@ -370,12 +336,11 @@ window.Render.prototype.createFieldEntity = function(ent) {
this.deleteFieldEntity(ent[0]); // option 2, for now
}
var team = getTeam(ent[2]);
var reg = ent[2].capturedRegion;
var team = teamStringToId(ent[2].team);
var latlngs = [
L.latLng(reg.vertexA.location.latE6/1E6, reg.vertexA.location.lngE6/1E6),
L.latLng(reg.vertexB.location.latE6/1E6, reg.vertexB.location.lngE6/1E6),
L.latLng(reg.vertexC.location.latE6/1E6, reg.vertexC.location.lngE6/1E6)
L.latLng(ent[2].points[0].latE6/1E6, ent[2].points[0].lngE6/1E6),
L.latLng(ent[2].points[1].latE6/1E6, ent[2].points[1].lngE6/1E6),
L.latLng(ent[2].points[2].latE6/1E6, ent[2].points[2].lngE6/1E6)
];
var poly = L.geodesicPolygon(latlngs, {
@ -387,27 +352,9 @@ window.Render.prototype.createFieldEntity = function(ent) {
team: team,
guid: ent[0],
timestamp: ent[1],
details: ent[2],
// LEGACY FIELDS: these duplicate data available via .details, as IITC previously stored it in data and vertices
data: ent[2],
vertices: ent[2].capturedRegion
});
// now fill in any references portals linkedFields data
var addPortalLinkedField = function(pguid) {
if (pguid in window.portals) {
var pd = window.portals[pguid].options.details;
if (!pd.portalV2.linkedFields) pd.portalV2.linkedFields = [];
if (pd.portalV2.linkedFields.indexOf(pguid) <0 ) {
pd.portalV2.linkedFields.push (ent[0]);
}
}
}
addPortalLinkedField(ent[2].capturedRegion.vertexA.guid);
addPortalLinkedField(ent[2].capturedRegion.vertexB.guid);
addPortalLinkedField(ent[2].capturedRegion.vertexC.guid);
runHooks('fieldAdded',{field: poly});
window.fields[ent[0]] = poly;
@ -433,11 +380,10 @@ window.Render.prototype.createLinkEntity = function(ent,faked) {
this.deleteLinkEntity(ent[0]); // option 2 - for now
}
var team = getTeam(ent[2]);
var edge = ent[2].edge;
var team = teamStringToId(ent[2].team);
var latlngs = [
L.latLng(edge.originPortalLocation.latE6/1E6, edge.originPortalLocation.lngE6/1E6),
L.latLng(edge.destinationPortalLocation.latE6/1E6, edge.destinationPortalLocation.lngE6/1E6)
L.latLng(ent[2].oLatE6/1E6, ent[2].oLngE6/1E6),
L.latLng(ent[2].dLatE6/1E6, ent[2].dLngE6/1E6)
];
var poly = L.geodesicPolyline(latlngs, {
color: COLORS[team],
@ -448,8 +394,6 @@ window.Render.prototype.createLinkEntity = function(ent,faked) {
team: team,
guid: ent[0],
timestamp: ent[1],
details: ent[2],
// LEGACY FIELDS: these duplicate data available via .details, as IITC previously stored it in data and vertices
data: ent[2]
});
@ -465,56 +409,6 @@ window.Render.prototype.createLinkEntity = function(ent,faked) {
}
window.Render.prototype.createLinksFromPortalData = function(portalGuid) {
var sourcePortal = portals[portalGuid];
for (var sourceLinkIndex in sourcePortal.options.details.portalV2.linkedEdges||[]) {
var sourcePortalLinkInfo = sourcePortal.options.details.portalV2.linkedEdges[sourceLinkIndex];
// portals often contain details for edges that don't exist. so only consider faking an edge if this
// is the origin portal
if (sourcePortalLinkInfo.isOrigin) {
// ... and the other porta has matching link information.
if (sourcePortalLinkInfo.otherPortalGuid in portals) {
var targetPortal = portals[sourcePortalLinkInfo.otherPortalGuid];
for (var targetLinkIndex in targetPortal.options.details.portalV2.linkedEdges||[]) {
var targetPortalLinkInfo = targetPortal.options.details.portalV2.linkedEdges[targetLinkIndex];
if (targetPortalLinkInfo.edgeGuid == sourcePortalLinkInfo.edgeGuid) {
// yes - edge in both portals. create it
var fakeEnt = [
sourcePortalLinkInfo.edgeGuid,
0, // mtime for entity data - unknown when faking it, so zero will be the oldest possible
{
controllingTeam: sourcePortal.options.details.controllingTeam,
edge: {
originPortalGuid: portalGuid,
originPortalLocation: sourcePortal.options.details.locationE6,
destinationPortalGuid: sourcePortalLinkInfo.otherPortalGuid,
destinationPortalLocation: targetPortal.options.details.locationE6
}
}
];
this.createLinkEntity(fakeEnt,true);
}
}
}
}
}
}
window.Render.prototype.updateEntityVisibility = function() {
if (this.entityVisibilityZoom === undefined || this.entityVisibilityZoom != map.getZoom()) {

View File

@ -28,7 +28,7 @@ window.MapDataRequest = function() {
this.MIN_TILES_PER_REQUEST = 4;
// number of times to retry a tile after a 'bad' error (i.e. not a timeout)
this.MAX_TILE_RETRIES = 3;
this.MAX_TILE_RETRIES = 1;
// refresh timers
this.MOVE_REFRESH = 1; //time, after a map move (pan/zoom) before starting the refresh processing
@ -45,7 +45,7 @@ window.MapDataRequest = function() {
this.RUN_QUEUE_DELAY = 0.5;
// delay before requeuing tiles in failed requests
this.BAD_REQUEST_REQUEUE_DELAY = 5; // longer delay before retrying a completely failed request - as in this case the servers are struggling
this.BAD_REQUEST_REQUEUE_DELAY = 10; // longer delay before retrying a completely failed request - as in this case the servers are struggling
// a delay before processing the queue after requeuing tiles. this gives a chance for other requests to finish
// or other requeue actions to happen before the queue is processed, allowing better grouping of requests
@ -391,7 +391,7 @@ window.MapDataRequest.prototype.sendTileRequest = function(tiles) {
var savedThis = this;
// NOTE: don't add the request with window.request.add, as we don't want the abort handling to apply to map data any more
window.postAjax('getThinnedEntitiesV4', data,
window.postAjax('getThinnedEntities', data,
function(data, textStatus, jqXHR) { savedThis.handleResponse (data, tiles, true); }, // request successful callback
function() { savedThis.handleResponse (undefined, tiles, false); } // request failed callback
);

View File

@ -14,140 +14,52 @@
var requestParameterMunges = [
// obsolete munge sets (they don't have some of the new parameters) deleted
// set 7 - 2013-11-06
// set 10 - 2013-11-27
{
'dashboard.getArtifactInfo': 'artifacts', // GET_ARTIFACT_INFO: new (and not obfuscated?!)
'dashboard.getGameScore': 'yol4dxx5ufqolhk2', // GET_GAME_SCORE
'dashboard.getPaginatedPlextsV2': '7b83j2z81rtk6101', // GET_PAGINATED_PLEXTS
'dashboard.getThinnedEntitiesV4': '46su4lrisoq28gxh', // GET_THINNED_ENTITIES
'dashboard.getPlayersByGuids': 'wsc5puahrymtf1qh', // LOOKUP_PLAYERS
'dashboard.redeemReward': 'oo0n7pw2m0xufpzx', // REDEEM_REWARD
'dashboard.sendInviteEmail': 'bo1bp74rz8kbdjkb', // SEND_INVITE_EMAIL
'dashboard.sendPlext': 'q0f8o4v9t8pt91yv', // SEND_PLEXT
'dashboard.getArtifactInfo': 'artifacts', // GET_ARTIFACT_INFO
'dashboard.getGameScore': '4oid643d9zc168hs', // GET_GAME_SCORE
'dashboard.getPaginatedPlexts': 's1msyywq51ntudpe', // GET_PAGINATED_PLEXTS
'dashboard.getThinnedEntities': '4467ff9bgxxe4csa', // GET_THINNED_ENTITIES
'dashboard.getPortalDetails': 'c00thnhf1yp3z6mn', // GET_PORTAL_DETAILS
'dashboard.redeemReward': '66l9ivg39ygfqqjm', // REDEEM_REWARD
'dashboard.sendInviteEmail': 'cgb7hi5hglv0xx8k', // SEND_INVITE_EMAIL
'dashboard.sendPlext': 'etn9xq7brd6947kq', // SEND_PLEXT
// common parameters
method: 'imo60cdzkemxduub',
version: '54lh4o0q7nz7dao9', //guessed parameter name - only seen munged
version_parameter: '370c0b4e160ed26c8c4ce40f10f546545730e1ef', // passed as the value to the above parameter
method: 'yyngyttbmmbuvdpa',
version: 'avz401t36lzrapis',
version_parameter: 'c5d0a5d608f729a1232bebdc12fb86ba5fb6c43f',
// GET_THINNED_ENTITIES
quadKeys: 'iqy8e2d3zpne0cmh', //guessed parameter name - only seen munged
quadKeys: '1mpmxz2yun22rwnn',
// GET_PAGINATED_PLEXTS
desiredNumItems: 'chwe3yko3xy0qlk3',
minLatE6: 'f31z3x27ua8i05cf',
minLngE6: 't0rmob7f42c0w04r',
maxLatE6: 'ebwfvri5io9q0tvu',
maxLngE6: 'lfqzvpj92dp8uxo6',
minTimestampMs: '23a6djyyieeaeduu',
maxTimestampMs: 'zhjtsm2gw7w3b7mx',
chatTab: 'tak64gipm3hhqpnh', //guessed parameter name - only seen munged
ascendingTimestampOrder: 'v5rzzxtg5rmry3dx',
desiredNumItems: 'nzd23jqm9k1cnnij',
minLatE6: '0dod6onpa1s4fezp',
minLngE6: 'soass3t7mm7anneo',
maxLatE6: 'cvarmr3o00ngylo1',
maxLngE6: 'udzwnlx07hzd3bfo',
minTimestampMs: '9iiiks138gkf8xho',
maxTimestampMs: '94wm0u3sc3sgzq7x',
chatTab: 'tqfj4a3okzn5v5o1',
ascendingTimestampOrder: '5jv1m90sq35u6utq',
// SEND_PLEXT
message: 'onptntn3szan21lj',
latE6: '1jq9lgu3hjajrt7s',
lngE6: 'plbubiopnavbxxh6',
// chatTab: 'tak64gipm3hhqpnh', //guessed parameter name - only seen munged
// LOOKUP_PLAYERS
guids: '919p2cfpdo2wz03n',
// SEND_INVITE_EMAIL
inviteeEmailAddress: 'thpbnoyjx0antwm5',
},
// set 8 - 2013-11-07
{
'dashboard.getArtifactInfo': 'artifacts', // GET_ARTIFACT_INFO: new (and not obfuscated?!)
'dashboard.getGameScore': 'lls4clhel87apzpa', // GET_GAME_SCORE
'dashboard.getPaginatedPlextsV2': 'r6n2xgcd8wjsm4og', // GET_PAGINATED_PLEXTS
'dashboard.getThinnedEntitiesV4': '1ybigzcf2sifu34b', // GET_THINNED_ENTITIES
'dashboard.getPlayersByGuids': 'uig0xeb6trclqd2l', // LOOKUP_PLAYERS
'dashboard.redeemReward': '7dd7x64cc2lbutoq', // REDEEM_REWARD
'dashboard.sendInviteEmail': 'd8p6dvwilsr460u3', // SEND_INVITE_EMAIL
'dashboard.sendPlext': 'repg2orpg7htkoto', // SEND_PLEXT
// common parameters
method: '97aes4vnlvyhoxik',
version: 'an8mglz21qabq3wq', //guessed parameter name - only seen munged
version_parameter: 'b92c9d055fcdf715887b173c706e7a2c267e32c5', // passed as the value to the above parameter
// GET_THINNED_ENTITIES
quadKeys: 'mhjknavysslwfhk6', //guessed parameter name - only seen munged
// GET_PAGINATED_PLEXTS
desiredNumItems: 'l61g8u397alq3j1x',
minLatE6: 'wwsvpboc5bxd1s9q',
minLngE6: '48l4x7ngfsz47z3u',
maxLatE6: 'p3m1qg81uqldizu6',
maxLngE6: 'h4kv1eef878vfyk3',
minTimestampMs: 'uj1vcy9ufws24v2c',
maxTimestampMs: '8pt1x5nd9hk5vakv',
chatTab: 'zy1yc1rfczashshu', //guessed parameter name - only seen munged
ascendingTimestampOrder: 'duyuskmky68nl2ci',
// SEND_PLEXT
message: 'xktwjguq0nohzioa',
latE6: 'm4crflfaibmg9mdf',
lngE6: 'h6jfungrw5ii830r',
// chatTab: 'zy1yc1rfczashshu', //guessed parameter name - only seen munged
// LOOKUP_PLAYERS
guids: '3u9h9cpfh2yiy4fk',
// SEND_INVITE_EMAIL
inviteeEmailAddress: 'jpg3y4ax7t0w356j',
},
// set 9 - 2013-11-1
{
'dashboard.getArtifactInfo': 'artifacts', // GET_ARTIFACT_INFO: new (and not obfuscated?!)
'dashboard.getGameScore': '9w8phj2dccvns3t9', // GET_GAME_SCORE
'dashboard.getPaginatedPlextsV2': '3b1nc3ub0sd1704x', // GET_PAGINATED_PLEXTS
'dashboard.getThinnedEntitiesV4': '2xa55qj41qrhfhas', // GET_THINNED_ENTITIES
'dashboard.getPlayersByGuids': '734hxjh89d53clqq', // LOOKUP_PLAYERS
'dashboard.redeemReward': 'k3hwg41wf112gjjh', // REDEEM_REWARD
'dashboard.sendInviteEmail': 'uwizjeb18xmcesa0', // SEND_INVITE_EMAIL
'dashboard.sendPlext': '5au1m1hut1gyvnix', // SEND_PLEXT
// common parameters
method: '3sld77nsm0tjmkvi',
version: 'xz7q6r3aja5ttvoo', //guessed parameter name - only seen munged
version_parameter: 'b121024077de2a0dc6b34119e4440785c9ea5e64', // passed as the value to the above parameter
// GET_THINNED_ENTITIES
quadKeys: '0o6bkrbwevwn6bg1', //guessed parameter name - only seen munged
// GET_PAGINATED_PLEXTS
desiredNumItems: '3fketl1tv01q7vxu',
minLatE6: '5i6jhgbv3aq3c4qz',
minLngE6: 'pe2io3r932qysg4u',
maxLatE6: 'plzyuy89bnlb3pth',
maxLngE6: 'q0qq1ooc7sxpynth',
minTimestampMs: 'nc282s8hdklv21mw',
maxTimestampMs: 'ezrljj0l71gpelpu',
chatTab: 'efaznrayv5n3jxs0', //guessed parameter name - only seen munged
ascendingTimestampOrder: 'fcmlcb8ya0oa1clk',
// SEND_PLEXT
message: 'jg4ms2i14rgzi02n',
latE6: 'nkf3evzpkxkq8l2q',
lngE6: '7xoz0xl8se4d1j53',
message: '8exta9k7y8huhqmc',
latE6: 'kqek161gza3kjcry',
lngE6: '3dlxsqrjj2vcmhbc',
// chatTab: 'efaznrayv5n3jxs0', //guessed parameter name - only seen munged
// LOOKUP_PLAYERS
guids: 'm4dcrdltldigfo94',
// GET_PORTAL_DETAILS
guid: 'seg6ohxgnqf9xu9w',
// SEND_INVITE_EMAIL
inviteeEmailAddress: 'rye9be4um2t1z5ts',
inviteeEmailAddress: '8exta9k7y8huhqmc',
},
];
var activeRequestMungeSet = undefined;
// in the recent stock site updates, their javascript code has been less obfuscated, but also the munge parameters
// change on every release. I can only assume it's now an integrated step in the build/release system, rather
@ -158,14 +70,15 @@ function extractMungeFromStock() {
var foundMunges = {};
// these are easy - directly available in variables
foundMunges['dashboard.getArtifactInfo'] = nemesis.dashboard.requests.MethodName.GET_ARTIFACT_INFO;
foundMunges['dashboard.getGameScore'] = nemesis.dashboard.requests.MethodName.GET_GAME_SCORE;
foundMunges['dashboard.getPaginatedPlextsV2'] = nemesis.dashboard.requests.MethodName.GET_PAGINATED_PLEXTS;
foundMunges['dashboard.getThinnedEntitiesV4'] = nemesis.dashboard.requests.MethodName.GET_THINNED_ENTITIES;
foundMunges['dashboard.getPlayersByGuids'] = nemesis.dashboard.requests.MethodName.LOOKUP_PLAYERS;
foundMunges['dashboard.redeemReward'] = nemesis.dashboard.requests.MethodName.REDEEM_REWARD;
foundMunges['dashboard.sendInviteEmail'] = nemesis.dashboard.requests.MethodName.SEND_INVITE_EMAIL;
foundMunges['dashboard.sendPlext'] = nemesis.dashboard.requests.MethodName.SEND_PLEXT;
// NOTE: the .toString() is there so missing variables throw an exception, rather than storing 'undefined'
foundMunges['dashboard.getArtifactInfo'] = nemesis.dashboard.requests.MethodName.GET_ARTIFACT_INFO.toString();
foundMunges['dashboard.getGameScore'] = nemesis.dashboard.requests.MethodName.GET_GAME_SCORE.toString();
foundMunges['dashboard.getPaginatedPlexts'] = nemesis.dashboard.requests.MethodName.GET_PAGINATED_PLEXTS.toString();
foundMunges['dashboard.getThinnedEntities'] = nemesis.dashboard.requests.MethodName.GET_THINNED_ENTITIES.toString();
foundMunges['dashboard.getPortalDetails'] = nemesis.dashboard.requests.MethodName.GET_PORTAL_DETAILS.toString();
foundMunges['dashboard.redeemReward'] = nemesis.dashboard.requests.MethodName.REDEEM_REWARD.toString();
foundMunges['dashboard.sendInviteEmail'] = nemesis.dashboard.requests.MethodName.SEND_INVITE_EMAIL.toString();
foundMunges['dashboard.sendPlext'] = nemesis.dashboard.requests.MethodName.SEND_PLEXT.toString();
// the rest are trickier - we need to parse the functions of the stock site. these break very often
// on site updates
@ -218,11 +131,11 @@ function extractMungeFromStock() {
var chatTab = result[7] || result[8];
if (chatTab != foundMunges.chatTab) throw 'Error: inconsistent munge parsing for chatTab';
// LOOKUP_PLAYERS
var reg = new RegExp('LOOKUP_PLAYERS, {'+mungeRegExpLit+'a}');
var result = reg.exec(nemesis.dashboard.network.DataFetcher.prototype.lookupPlayersByGuids.toString());
// GET_PORTAL_DETAILS
var reg = new RegExp('GET_PORTAL_DETAILS, {'+mungeRegExpLit+'a}');
var result = reg.exec(nemesis.dashboard.network.DataFetcher.prototype.getPortalDetails.toString());
foundMunges.guids = result[1] || result[2];
foundMunges.guid = result[1] || result[2];
// SEND_INVITE_EMAIL
var reg = new RegExp('SEND_INVITE_EMAIL, {'+mungeRegExpLit+'b}');
@ -244,44 +157,32 @@ window.detectActiveMungeSet = function() {
// first, try and parse the stock functions and extract the munges directly
activeMunge = extractMungeFromStock();
if (activeMunge) {
console.log('IITC: Successfully extracted munges from stock javascript');
return;
}
// try and find the stock page functions
// FIXME? revert to searching through all the code? is that practical?
var stockFunc;
try {
stockFunc = nemesis.dashboard.network.XhrController.prototype.doSendRequest_.toString();
} catch(e) {
try {
stockFunc = nemesis.dashboard.network.XhrController.prototype.sendRequest.toString();
} catch(e) {
try {
stockFunc = nemesis.dashboard.network.DataFetcher.prototype.sendRequest_.toString();
} catch(e) {
console.warn('Failed to find a relevant function in the stock site');
}
}
}
if(stockFunc) {
for (var i in requestParameterMunges) {
if (stockFunc.indexOf (requestParameterMunges[i]['method']) >= 0) {
console.log('IITC: found request munge set index '+i+' in stock intel site');
activeRequestMungeSet = i;
}
}
console.log('IITC: Successfully extracted munges from stock javascript - excellent work!');
} else {
console.error('IITC: failed to find the stock site function for detecting munge set');
console.warn('IITC: failed to detect a munge set from the code - searching our list...');
// try to find a matching munge set from the pre-defined ones. this code remains as in the case of
// things breaking it can be quicker to update the table than to fix the regular expressions used
// above
try {
for (var i in requestParameterMunges) {
if (requestParameterMunges[i]['dashboard.getThinnedEntities'] == nemesis.dashboard.requests.MethodName.GET_THINNED_ENTITIES) {
console.log('IITC: found a match with munge set index '+i);
activeMunge = requestParameterMunges[i];
break;
}
}
} catch(e) {
console.warn('IITC: failed to find matching munge set from supplied list');
}
}
if (activeRequestMungeSet===undefined) {
console.error('IITC: failed to find request munge set - IITC will likely fail');
activeRequestMungeSet = 0;
if (!activeMunge) {
console.warn('IITC: Error!! failed to find a parameter munge set - neither extracting from stock, or searching through table. IITC CANNOT WORK');
throw {error:'Failed to find a munge set'};
}
activeMunge = requestParameterMunges[activeRequestMungeSet];
}

View File

@ -1,152 +1,18 @@
// PLAYER NAMES //////////////////////////////////////////////////////
// Player names are cached in sessionStorage. There is no GUI
// element from within the total conversion to clean them, but you
// can run sessionStorage.clean() to reset it.
window.setupPlayerNameCache = function() {
// IITC used to store player names in localStorage rather than sessionStorage. lets clear out any cached
// names from session storage
var matchPlayerGuid = new RegExp ('^[0-9a-f]{32}\\.c$');
$.each(Object.keys(localStorage), function(ind,key) {
if ( matchPlayerGuid.test(key) ) {
// copy from localStorage to sessionStorage if not already there
if (!sessionStorage[key]) sessionStorage[key] = localStorage[key];
// then clear from localStorage
localStorage.removeItem(key);
}
});
}
// retrieves player name by GUID. If the name is not yet available, it
// will be added to a global list of GUIDs that need to be resolved.
// The resolve method is not called automatically.
window.getPlayerName = function(guid) {
if(sessionStorage[guid]) return sessionStorage[guid];
// only add to queue if it isnt already
if(playersToResolve.indexOf(guid) === -1 && playersInResolving.indexOf(guid) === -1) {
playersToResolve.push(guid);
}
return '{'+guid.slice(0, 12)+'}';
}
window._playerNameToGuidCache = {};
window.playerNameToGuid = function(playerName) {
var cachedGuid = window._playerNameToGuidCache[playerName];
if (cachedGuid !== undefined) return cachedGuid;
// IITC needs our own player GUID, from a lookup by name. so we retrieve this from localstorage (if available)
if (playerName == PLAYER.nickname) {
cachedGuid = localStorage['PLAYER-'+PLAYER.nickname];
if (cachedGuid !== undefined) return cachedGuid;
}
var guid = null;
$.each(Object.keys(sessionStorage), function(ind,key) {
if(playerName === sessionStorage[key]) {
guid = key;
window._playerNameToGuidCache[playerName] = guid;
return false; //break from $.each
}
});
return guid;
}
// resolves all player GUIDs that have been added to the list. Reruns
// renderPortalDetails when finished, so that then-unresolved names
// get replaced by their correct versions.
window.resolvePlayerNames = function() {
if(window.playersToResolve.length === 0) return;
//limit per request. stock site is never more than 13 (8 res, 4 mods, owner)
//testing shows 15 works and 20 fails
var MAX_RESOLVE_PLAYERS_PER_REQUEST = 15;
var MAX_RESOLVE_REQUESTS = 8;
if (window.playersToResolve.length > MAX_RESOLVE_PLAYERS_PER_REQUEST*MAX_RESOLVE_REQUESTS) {
console.log('Warning: player name resolve queue had '+window.playersToResolve.length+' entries. Limiting to the first '+MAX_RESOLVE_PLAYERS_PER_REQUEST*MAX_RESOLVE_REQUESTS+' to prevent excessive requests');
window.playersToResolve = playersToResolve.slice(0,MAX_RESOLVE_PLAYERS_PER_REQUEST*MAX_RESOLVE_REQUESTS);
}
var p = window.playersToResolve.slice(0,MAX_RESOLVE_PLAYERS_PER_REQUEST);
window.playersToResolve = playersToResolve.slice(MAX_RESOLVE_PLAYERS_PER_REQUEST);
var d = {guids: p};
window.playersInResolving = window.playersInResolving.concat(p);
postAjax('getPlayersByGuids', d, function(dat) {
var resolvedName = {};
if(dat.result) {
$.each(dat.result, function(ind, player) {
window.setPlayerName(player.guid, player.nickname);
resolvedName[player.guid] = player.nickname;
// remove from array
window.playersInResolving.splice(window.playersInResolving.indexOf(player.guid), 1);
});
} else {
//no 'result' - a successful http request, but the returned result was an error of some kind
console.warn('getplayers problem - no result in response: '+dat);
//likely to be some kind of 'bad request' (e.g. too many names at once, or otherwise badly formatted data.
//therefore, not a good idea to automatically retry by adding back to the playersToResolve list
}
// Run hook 'playerNameResolved' with the resolved player names
window.runHooks('playerNameResolved', {names: resolvedName});
//TODO: have an event triggered for this instead of hard-coded single function call
if(window.selectedPortal)
window.renderPortalDetails(window.selectedPortal);
//if more to do, run again
if(window.playersToResolve.length>0) resolvePlayerNames();
},
function() {
// append failed resolves to the list again
console.warn('resolving player guids failed: ' + p.join(', '));
window.playersToResolve.concat(p);
});
}
window.setPlayerName = function(guid, nick, uncertain) {
// the 'uncertain' flag is set when we're scrolling back through chat. it's possible in this case
// to come across a message from before a name change. these should be ignored if existing cache entries exist
if(uncertain && guid in sessionStorage) return;
if($.trim(('' + nick)).slice(0, 5) === '{"L":' && !window.alertFor37WasShown) {
window.alertFor37WasShown = true;
alert('You have run into bug #37. Please help me solve it!\nCopy and paste this text and post it here:\nhttps://github.com/breunigs/ingress-intel-total-conversion/issues/37\nIf copy & pasting doesnt work, make a screenshot instead.\n\n\n' + window.debug.printStackTrace() + '\n\n\n' + JSON.stringify(nick));
}
sessionStorage[guid] = nick;
// IITC needs our own player ID early on in startup. the only way we can find this is by something else
// doing a guid->name lookup for our own name. as this doesn't always happen - and likely won't happen when needed
// we'll store our own name->guid lookup in localStorage
if (nick == PLAYER.nickname) {
localStorage['PLAYER-'+PLAYER.nickname] = guid;
PLAYER.guid = guid; // set it in PLAYER in case it wasn't already done
}
}
// test to see if a specific player GUID is a special system account (e.g. __JARVIS__, __ADA__) that shouldn't
// be listed as a player
window.isSystemPlayer = function(guid) {
window.isSystemPlayer = function(name) {
switch (guid) {
case '00000000000000000000000000000001.c':
case '00000000000000000000000000000002.c':
switch (name) {
case '__ADA__':
case '__JARVIS__':
return true;
default:
return false;
}
}

81
code/portal_data.js Normal file
View File

@ -0,0 +1,81 @@
/// PORTAL DATA TOOLS ///////////////////////////////////////////////////
// misc functions to get portal info
// search through the links data for all that link from or to a portal. returns an object with separate lists of in
// and out links. may or may not be as accurate as the portal details, depending on how much data the API returns
window.getPortalLinks = function(guid) {
var links = { in: [], out: [] };
$.each(window.links, function(g,l) {
var d = l.options.data;
if (d.oGuid == guid) {
links.out.push(g);
}
if (d.dGuid == guid) {
links.in.push(g);
}
});
return links;
}
// search through the fields for all that reference a portal
window.getPortalFields = function(guid) {
var fields = [];
$.each(window.fields, function(g,f) {
var d = f.options.data;
if ( d.points[0].guid == guid
|| d.points[1].guid == guid
|| d.points[2].guid == guid ) {
fields.push(g);
}
});
return fields;
}
// find the lat/lon for a portal, using any and all available data
// (we have the list of portals, the cached portal details, plus links and fields as sources of portal locations)
window.findPortalLatLng = function(guid) {
if (window.portals[guid]) {
return window.portals[guid].getLatLng();
}
// not found in portals - try the cached (and possibly stale) details - good enough for location
var details = portalDetail.get(guid);
if (details) {
return L.latLng (details.locationE6.latE6/1E6, details.locationE6.lngE6/1E6);
}
// now try searching through fields
for (var fguid in window.fields) {
var f = window.fields[fguid].options.data;
for (var i in f.points) {
if (f.points[i].guid == guid) {
return L.latLng (f.points[i].latE6/1E6, f.points[i].lngE6/1E6);
}
}
}
// and finally search through links
for (var lguid in window.links) {
var l = window.links[lguid].options.data;
if (l.oGuid == guid) {
return L.latLng (l.oLatE6/1E6, l.oLngE6/1E6);
}
if (l.dGuid == guid) {
return L.latLng (l.dLatE6/1E6, l.dLngE6/1E6);
}
}
// no luck finding portal lat/lng
return undefined;
}

59
code/portal_detail.js Normal file
View File

@ -0,0 +1,59 @@
/// PORTAL DETAIL //////////////////////////////////////
// code to retrieve the new potal detail data from the servers
// NOTE: the API for portal detailed information is NOT FINAL
// this is a temporary measure to get things working again after a major change to the intel map
// API. expect things to change here
// anonymous function wrapper for the code - any variables/functions not placed into 'window' will be private
(function(){
var cache;
window.portalDetail = function() {};
window.portalDetail.setup = function() {
cache = new DataCache();
cache.startExpireInterval(20);
}
window.portalDetail.get = function(guid) {
return cache.get(guid);
}
window.portalDetail.isFresh = function(guid) {
return cache.isFresh(guid);
}
var handleResponse = function(guid, data, success) {
if (success) {
cache.store(guid,data);
//FIXME..? better way of handling sidebar refreshing...
if (guid == selectedPortal) {
renderPortalDetails(guid);
}
}
window.runHooks ('portalDetailLoaded', {guid:guid, success:success, details:data});
}
window.portalDetail.request = function(guid) {
window.postAjax('getPortalDetails', {guid:guid},
function(data,textStatus,jqXHR) { handleResponse(guid, data, true); },
function() { handleResponse(guid, undefined, false); }
);
}
})(); // anonumous wrapper function end

View File

@ -5,79 +5,45 @@
window.renderPortalDetails = function(guid) {
selectPortal(window.portals[guid] ? guid : null);
if (!portalDetail.isFresh(guid)) {
portalDetail.request(guid);
}
// TODO? handle the case where we request data for a particular portal GUID, but it *isn't* in
// window.portals....
if(!window.portals[guid]) {
urlPortal = guid;
$('#portaldetails').html('');
if(isSmartphone()) {
$('.fullimg').remove();
$('#mobileinfo').html('');
$('#mobileinfo').html('<div style="text-align: center"><b>tap here for info screen</b></div>');
}
return;
}
var d = window.portals[guid].options.details;
// collect some random data thats not worth to put in an own method
var links = {incoming: 0, outgoing: 0};
if(d.portalV2.linkedEdges) $.each(d.portalV2.linkedEdges, function(ind, link) {
links[link.isOrigin ? 'outgoing' : 'incoming']++;
});
function linkExpl(t) { return '<tt title="↳ incoming links\n↴ outgoing links\n• is the portal">'+t+'</tt>'; }
var linksText = [linkExpl('links'), linkExpl(' ↳ ' + links.incoming+'&nbsp;&nbsp;•&nbsp;&nbsp;'+links.outgoing+' ↴')];
var player = d.captured && d.captured.capturingPlayerId
? '<span class="nickname">' + getPlayerName(d.captured.capturingPlayerId) + '</span>'
: null;
var playerText = player ? ['owner', player] : null;
var time = d.captured
? '<span title="' + unixTimeToDateTimeString(d.captured.capturedTime, false) + '\n'
+ formatInterval(Math.floor((Date.now()-d.captured.capturedTime)/1000), 2) + ' ago">'
+ unixTimeToString(d.captured.capturedTime) + '</span>'
: null;
var sinceText = time ? ['since', time] : null;
var linkedFields = ['fields', d.portalV2.linkedFields ? d.portalV2.linkedFields.length : 0];
// collect and html-ify random data
var randDetails = [
playerText, sinceText,
getRangeText(d), getEnergyText(d),
linksText, getAvgResoDistText(d),
linkedFields, getAttackApGainText(d),
getHackDetailsText(d), getMitigationText(d)
];
// artifact details
//niantic hard-code the fact it's just jarvis shards/targets - so until more examples exist, we'll do the same
//(at some future point we can iterate through all the artifact types and add rows as needed)
var jarvisArtifact = artifact.getPortalData (guid, 'jarvis');
if (jarvisArtifact) {
// the genFourColumnTable function below doesn't handle cases where one column is null and the other isn't - so default to *something* in both columns
var target = ['',''], shards = ['shards','(none)'];
if (jarvisArtifact.target) {
target = ['target', '<span class="'+TEAM_TO_CSS[jarvisArtifact.target]+'">'+(jarvisArtifact.target==TEAM_RES?'Resistance':'Enlightened')+'</span>'];
}
if (jarvisArtifact.fragments) {
shards = [jarvisArtifact.fragments.length>1?'shards':'shard', '#'+jarvisArtifact.fragments.join(', #')];
}
randDetails.push (target, shards);
}
var portal = window.portals[guid];
var data = portal.options.data;
var details = portalDetail.get(guid);
randDetails = '<table id="randdetails">' + genFourColumnTable(randDetails) + '</table>';
var modDetails = details ? '<div class="mods">'+getModDetails(details)+'</div>' : '';
var miscDetails = details ? getPortalMiscDetails(guid,details) : '';
var resoDetails = details ? getResonatorDetails(details) : '';
var resoDetails = '<table id="resodetails">' + getResonatorDetails(d) + '</table>';
//TODO? other status details...
var statusDetails = details ? '' : '<div id="portalStatus">Loading details...</div>';
var img = getPortalImageUrl(d);
var lat = d.locationE6.latE6/1E6;
var lng = d.locationE6.lngE6/1E6;
var perma = '/intel?ll='+lat+','+lng+'&z=17&pll='+lat+','+lng;
var imgTitle = 'title="'+getPortalDescriptionFromDetails(d)+'\n\nClick to show full image."';
var poslinks = 'window.showPortalPosLinks('+lat+','+lng+',\''+escapeJavascriptString(d.portalV2.descriptiveText.TITLE)+'\')';
var portalDetailObj = window.getPortalDescriptionFromDetailsExtended(d);
var img = fixPortalImageUrl(details ? details.imageByUrl && details.imageByUrl.imageUrl : data.image);
var title = details ? details.portalV2.descriptiveText.TITLE : data.title;
var lat = data.latE6/1E6;
var lng = data.lngE6/1E6;
var imgTitle = details ? getPortalDescriptionFromDetails(details) : data.title;
imgTitle += '\n\nClick to show full image.';
var portalDetailObj = details ? window.getPortalDescriptionFromDetailsExtended(details) : undefined;
var portalDetailedDescription = '';
@ -111,74 +77,184 @@ window.renderPortalDetails = function(guid) {
portalDetailedDescription += '</table>';
}
var levelDetails = getPortalLevel(d);
if(levelDetails != 8) {
if(levelDetails==Math.ceil(levelDetails))
levelDetails += "\n8";
else
levelDetails += "\n" + (Math.ceil(levelDetails) - levelDetails)*8;
levelDetails += " resonator level(s) needed for next portal level";
} else {
levelDetails += "\nfully upgraded";
// portal level. start with basic data - then extend with fractional info in tooltip if available
var levelInt = data ? data.level : getPortalLevel(details);
var levelDetails = data.level;
if (details) {
levelDetails = getPortalLevel(details);
if(levelDetails != 8) {
if(levelDetails==Math.ceil(levelDetails))
levelDetails += "\n8";
else
levelDetails += "\n" + (Math.ceil(levelDetails) - levelDetails)*8;
levelDetails += " resonator level(s) needed for next portal level";
} else {
levelDetails += "\nfully upgraded";
}
}
levelDetails = "Level " + levelDetails;
var linkDetails = [];
var posOnClick = 'window.showPortalPosLinks('+lat+','+lng+',\''+escapeJavascriptString(title)+'\')';
var permalinkUrl = '/intel?ll='+lat+','+lng+'&z=17&pll='+lat+','+lng;
if (typeof android !== 'undefined' && android && android.intentPosLink) {
// android devices. one share link option - and the android app provides an interface to share the URL,
// share as a geo: intent (navigation via google maps), etc
var shareLink = $('<div>').html( $('<a>').attr({onclick:posOnClick}).text('Share portal') ).html();
linkDetails.push('<aside>'+shareLink+'</aside>');
} else {
// non-android - a permalink for the portal
var permaHtml = $('<div>').html( $('<a>').attr({href:permalinkUrl, target:'_blank', title:'Create a URL link to this portal'}).text('Portal link') ).html();
linkDetails.push ( '<aside>'+permaHtml+'</aside>' );
// and a map link popup dialog
var mapHtml = $('<div>').html( $('<a>').attr({onclick:posOnClick, title:'Link to alternative maps (Google, etc)'}).text('Map links') ).html();
linkDetails.push('<aside>'+mapHtml+'</aside>');
}
$('#portaldetails')
.attr('class', TEAM_TO_CSS[getTeam(d)])
.html(''
+ '<h3 class="title">'+escapeHtmlSpecialChars(d.portalV2.descriptiveText.TITLE)+'</h3>'
+ '<span class="close" onclick="renderPortalDetails(null); if(isSmartphone()) show(\'map\');" title="Close">X</span>'
.html('') //to ensure it's clear
.attr('class', TEAM_TO_CSS[portal.options.team])
.append(
$('<h3>').attr({class:'title'}).text(data.title),
$('<span>').attr({class:'close', onclick:'renderPortalDetails(null); if(isSmartphone()) show("map");',title:'Close'}).text('X'),
// help cursor via ".imgpreview img"
+ '<div class="imgpreview" '+imgTitle+' style="background-image: url('+img+')">'
+ '<span id="level" title="'+levelDetails+'">'+Math.floor(getPortalLevel(d))+'</span>'
+ '<div class="portalDetails">'+ portalDetailedDescription + '</div>'
+ '<img class="hide" src="'+img+'"/></div>'
+ '</div>'
+ '<div class="mods">'+getModDetails(d)+'</div>'
+ randDetails
+ resoDetails
+ '<div class="linkdetails">'
+ (
typeof android !== 'undefined' && android && android.intentPosLink // Android handles both links via a dialog
? '<aside><a onclick="'+poslinks+'" title="Create a URL link to this portal" >Portal link</a></aside>'
: '<aside><a href="'+perma+'" onclick="return androidCopy(this.href)" title="Create a URL link to this portal" >Portal link</a></aside>'
+ '<aside><a onclick="'+poslinks+'" title="Link to alternative maps (Google, etc)">Map links</a></aside>'
)
+ '</div>'
$('<div>')
.attr({class:'imgpreview', title:imgTitle, style:"background-image: url('"+img+"')"})
.append(
$('<span>').attr({id:'level', title: levelDetails}).text(levelInt),
$('<div>').attr({class:'portalDetails'}).html(portalDetailedDescription),
$('<img>').attr({class:'hide', src:img})
),
modDetails,
miscDetails,
resoDetails,
statusDetails,
'<div class="linkdetails">' + linkDetails.join('') + '</div>'
);
// try to resolve names that were required for above functions, but
// weren't available yet.
resolvePlayerNames();
runHooks('portalDetailsUpdated', {portalDetails: d});
// only run the hooks when we have a portalDetails object - most plugins rely on the extended data
// TODO? another hook to call always, for any plugins that can work with less data?
if (details) {
runHooks('portalDetailsUpdated', {guid: guid, portal: portal, portalDetails: details, portalData: data});
}
}
window.getPortalMiscDetails = function(guid,d) {
var randDetails;
if (d) {
// collect some random data thats not worth to put in an own method
var links = {incoming: 0, outgoing: 0};
$.each(d.portalV2.linkedEdges||[], function(ind, link) {
links[link.isOrigin ? 'outgoing' : 'incoming']++;
});
function linkExpl(t) { return '<tt title="↳ incoming links\n↴ outgoing links\n• is the portal">'+t+'</tt>'; }
var linksText = [linkExpl('links'), linkExpl(' ↳ ' + links.incoming+'&nbsp;&nbsp;•&nbsp;&nbsp;'+links.outgoing+' ↴')];
var player = d.captured && d.captured.capturingPlayerId
? '<span class="nickname">' + d.captured.capturingPlayerId + '</span>'
: null;
var playerText = player ? ['owner', player] : null;
var time = d.captured
? '<span title="' + unixTimeToDateTimeString(d.captured.capturedTime, false) + '\n'
+ formatInterval(Math.floor((Date.now()-d.captured.capturedTime)/1000), 2) + ' ago">'
+ unixTimeToString(d.captured.capturedTime) + '</span>'
: null;
var sinceText = time ? ['since', time] : null;
var linkedFields = ['fields', d.portalV2.linkedFields ? d.portalV2.linkedFields.length : 0];
// collect and html-ify random data
var randDetailsData = [];
if (playerText && sinceText) {
randDetailsData.push (playerText, sinceText);
}
randDetailsData.push (
getRangeText(d), getEnergyText(d),
linksText, getAvgResoDistText(d),
linkedFields, getAttackApGainText(d),
getHackDetailsText(d), getMitigationText(d)
);
// artifact details
//niantic hard-code the fact it's just jarvis shards/targets - so until more examples exist, we'll do the same
//(at some future point we can iterate through all the artifact types and add rows as needed)
var jarvisArtifact = artifact.getPortalData (guid, 'jarvis');
if (jarvisArtifact) {
// the genFourColumnTable function below doesn't handle cases where one column is null and the other isn't - so default to *something* in both columns
var target = ['',''], shards = ['shards','(none)'];
if (jarvisArtifact.target) {
target = ['target', '<span class="'+TEAM_TO_CSS[jarvisArtifact.target]+'">'+(jarvisArtifact.target==TEAM_RES?'Resistance':'Enlightened')+'</span>'];
}
if (jarvisArtifact.fragments) {
shards = [jarvisArtifact.fragments.length>1?'shards':'shard', '#'+jarvisArtifact.fragments.join(', #')];
}
randDetailsData.push (target, shards);
}
randDetails = '<table id="randdetails">' + genFourColumnTable(randDetailsData) + '</table>';
}
return randDetails;
}
// draws link-range and hack-range circles around the portal with the
// given details. Clear them if parameter 'd' is null.
window.setPortalIndicators = function(d) {
window.setPortalIndicators = function(p) {
if(portalRangeIndicator) map.removeLayer(portalRangeIndicator);
portalRangeIndicator = null;
if(portalAccessIndicator) map.removeLayer(portalAccessIndicator);
portalAccessIndicator = null;
if(d === null) return;
// if we have a portal...
var range = getPortalRange(d);
var coord = [d.locationE6.latE6/1E6, d.locationE6.lngE6/1E6];
portalRangeIndicator = (range.range > 0
? L.geodesicCircle(coord, range.range, {
fill: false,
color: RANGE_INDICATOR_COLOR,
weight: 3,
dashArray: range.isLinkable ? undefined : "10,10",
clickable: false })
: L.circle(coord, range.range, { fill: false, stroke: false, clickable: false })
if(p) {
var coord = p.getLatLng();
// range is only known for sure if we have portal details
// TODO? render a min range guess until details are loaded..?
var d = portalDetail.get(p.options.guid);
if (d) {
var range = getPortalRange(d);
portalRangeIndicator = (range.range > 0
? L.geodesicCircle(coord, range.range, {
fill: false,
color: RANGE_INDICATOR_COLOR,
weight: 3,
dashArray: range.isLinkable ? undefined : "10,10",
clickable: false })
: L.circle(coord, range.range, { fill: false, stroke: false, clickable: false })
).addTo(map);
}
portalAccessIndicator = L.circle(coord, HACK_RANGE,
{ fill: false, color: ACCESS_INDICATOR_COLOR, weight: 2, clickable: false }
).addTo(map);
}
portalAccessIndicator = L.circle(coord, HACK_RANGE,
{ fill: false, color: ACCESS_INDICATOR_COLOR, weight: 2, clickable: false }
).addTo(map);
}
// highlights portal with given GUID. Automatically clears highlights
@ -205,7 +281,7 @@ window.selectPortal = function(guid) {
}
}
setPortalIndicators(newPortal ? newPortal.options.details : null);
setPortalIndicators(newPortal);
runHooks('portalSelected', {selectedPortalGuid: guid, unselectedPortalGuid: oldPortalGuid});
return update;

View File

@ -29,8 +29,8 @@ window.getPortalDescriptionFromDetails = function(details) {
var desc = descObj.TITLE;
if(descObj.ADDRESS)
desc += '\n' + descObj.ADDRESS;
if(descObj.ATTRIBUTION)
desc += '\nby '+descObj.ATTRIBUTION+' ('+descObj.ATTRIBUTION_LINK+')';
// if(descObj.ATTRIBUTION)
// desc += '\nby '+descObj.ATTRIBUTION+' ('+descObj.ATTRIBUTION_LINK+')';
return desc;
}
@ -102,7 +102,7 @@ window.getModDetails = function(d) {
modTooltip = modName + '\n';
if (mod.installingUser) {
modTooltip += 'Installed by: '+ getPlayerName(mod.installingUser) + '\n';
modTooltip += 'Installed by: '+ mod.installingUser + '\n';
}
if (mod.stats) {
@ -176,7 +176,7 @@ window.getResonatorDetails = function(d) {
var l = parseInt(reso.level);
var v = parseInt(reso.energyTotal);
var nick = window.getPlayerName(reso.ownerGuid);
var nick = reso.ownerGuid;
var dist = reso.distanceToPortal;
// if array order and slot order drift apart, at least the octant
// naming will still be correct.
@ -184,7 +184,8 @@ window.getResonatorDetails = function(d) {
resoDetails.push(renderResonatorDetails(slot, l, v, dist, nick));
});
return genFourColumnTable(resoDetails);
return '<table id="resodetails">' + genFourColumnTable(resoDetails) + '</table>';
}
// helper function that renders the HTML for a given resonator. Does

View File

@ -112,7 +112,8 @@ window.getAttackApGain = function(d) {
return true;
resoCount += 1;
var reslevel=parseInt(reso.level);
if(reso.ownerGuid === PLAYER.guid) {
// NOTE: reso.ownerGuid is actually the name - no player GUIDs are visible in the protocol any more
if(reso.ownerGuid === PLAYER.nickname) {
if(maxResonators[reslevel] > 0) {
maxResonators[reslevel] -= 1;
}
@ -167,7 +168,8 @@ window.potentialPortalLevel = function(d) {
player_resontators[i] = i > PLAYER.level ? 0 : MAX_RESO_PER_PLAYER[i];
}
$.each(resonators_on_portal, function(ind, reso) {
if(reso !== null && reso.ownerGuid === window.PLAYER.guid) {
// NOTE: reso.ownerGuid is actually the player name - GUIDs are not in the protocol any more
if(reso !== null && reso.ownerGuid === window.PLAYER.nickname) {
player_resontators[reso.level]--;
}
resonator_levels.push(reso === null ? 0 : reso.level);
@ -194,10 +196,8 @@ window.potentialPortalLevel = function(d) {
}
window.getPortalImageUrl = function(d) {
if (d.imageByUrl && d.imageByUrl.imageUrl) {
url = d.imageByUrl.imageUrl;
window.fixPortalImageUrl = function(url) {
if (url) {
if (window.location.protocol === 'https:') {
url = url.indexOf('www.panoramio.com') !== -1
? url.replace(/^http:\/\/www/, 'https://ssl').replace('small', 'medium')

View File

@ -281,7 +281,7 @@ window.getMinPortalLevelForZoom = function(z) {
// these values are from the stock intel map. however, they're too detailed for reasonable speed, and increasing
// detail at a higher zoom level shows enough detail still, AND speeds up IITC considerably
//var ZOOM_TO_LEVEL = [8, 8, 8, 8, 7, 7, 6, 6, 5, 4, 4, 3, 3, 2, 2, 1, 1];
var ZOOM_TO_LEVEL = [8, 8, 8, 8, 8, 8, 7, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1];
var ZOOM_TO_LEVEL = [8, 8, 8, 8, 8, 7, 7, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1];
var l = ZOOM_TO_LEVEL[z] || 0;
return l;