278
code/artifact.js
278
code/artifact.js
@ -35,7 +35,7 @@ window.artifact.requestData = function() {
|
||||
if (isIdle()) {
|
||||
artifact.idle = true;
|
||||
} else {
|
||||
window.postAjax('artifacts', {}, artifact.handleSuccess, artifact.handleError);
|
||||
window.postAjax('getArtifactPortals', {}, artifact.handleSuccess, artifact.handleError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,34 +65,16 @@ window.artifact.handleFailure = function(data) {
|
||||
|
||||
window.artifact.processData = function(data) {
|
||||
|
||||
if (data.error || !data.artifacts) {
|
||||
console.warn('Failed to find artifacts in artifact response');
|
||||
if (data.error || !data.result) {
|
||||
console.warn('Failed to find result in getArtifactPortals response');
|
||||
return;
|
||||
}
|
||||
|
||||
var oldArtifacts = artifact.entities;
|
||||
artifact.clearData();
|
||||
|
||||
$.each (data.artifacts, function(i,artData) {
|
||||
// if we have no descriptions for a type, we don't know about it
|
||||
if (!artifact.getArtifactDescriptions(artData.artifactId)) {
|
||||
// jarvis and amar artifacts - fragmentInfos and targetInfos
|
||||
// (future types? completely unknown at this time!)
|
||||
console.warn('Note: unknown artifactId '+artData.artifactId+' - guessing how to handle it');
|
||||
}
|
||||
|
||||
artifact.artifactTypes[artData.artifactId] = artData.artifactId;
|
||||
|
||||
if (artData.fragmentInfos) {
|
||||
artifact.processFragmentInfos (artData.artifactId, artData.fragmentInfos);
|
||||
}
|
||||
|
||||
if (artData.targetInfos) {
|
||||
artifact.processTargetInfos (artData.artifactId, artData.targetInfos);
|
||||
}
|
||||
|
||||
// other data in future? completely unknown!
|
||||
});
|
||||
|
||||
artifact.processResult(data.result);
|
||||
runHooks('artifactsUpdated', {old: oldArtifacts, 'new': artifact.entities});
|
||||
|
||||
// redraw the artifact layer
|
||||
artifact.updateLayer();
|
||||
@ -101,38 +83,52 @@ window.artifact.processData = function(data) {
|
||||
|
||||
|
||||
window.artifact.clearData = function() {
|
||||
|
||||
artifact.portalInfo = {};
|
||||
artifact.artifactTypes = {};
|
||||
|
||||
artifact.entities = [];
|
||||
}
|
||||
|
||||
window.artifact.processFragmentInfos = function (id, fragments) {
|
||||
$.each(fragments, function(i, fragment) {
|
||||
if (!artifact.portalInfo[fragment.portalGuid]) {
|
||||
artifact.portalInfo[fragment.portalGuid] = { _entityData: fragment.portalInfo };
|
||||
|
||||
window.artifact.processResult = function (portals) {
|
||||
// portals is an object, keyed from the portal GUID, containing the portal entity array
|
||||
|
||||
for (var guid in portals) {
|
||||
var ent = portals[guid];
|
||||
var data = decodeArray.portalSummary(ent);
|
||||
|
||||
// we no longer know the faction for the target portals, and we don't know which fragment numbers are at the portals
|
||||
// all we know, from the portal summary data, for each type of artifact, is that each artifact portal is
|
||||
// - a target portal or not - no idea for which faction
|
||||
// - has one (or more) fragments, or not
|
||||
|
||||
if (!artifact.portalInfo[guid]) artifact.portalInfo[guid] = {};
|
||||
|
||||
// store the decoded data - needed for lat/lng for layer markers
|
||||
artifact.portalInfo[guid]._data = data;
|
||||
|
||||
for(var type in data.artifactBrief.target) {
|
||||
if (!artifact.artifactTypes[type]) artifact.artifactTypes[type] = {};
|
||||
|
||||
if (!artifact.portalInfo[guid][type]) artifact.portalInfo[guid][type] = {};
|
||||
|
||||
artifact.portalInfo[guid][type].target = TEAM_NONE; // as we no longer know the team...
|
||||
}
|
||||
|
||||
if (!artifact.portalInfo[fragment.portalGuid][id]) artifact.portalInfo[fragment.portalGuid][id] = {};
|
||||
for(var type in data.artifactBrief.fragment) {
|
||||
if (!artifact.artifactTypes[type]) artifact.artifactTypes[type] = {};
|
||||
|
||||
if (!artifact.portalInfo[fragment.portalGuid][id].fragments) artifact.portalInfo[fragment.portalGuid][id].fragments = [];
|
||||
if (!artifact.portalInfo[guid][type]) artifact.portalInfo[guid][type] = {};
|
||||
|
||||
$.each(fragment.fragments, function(i,f) {
|
||||
artifact.portalInfo[fragment.portalGuid][id].fragments.push(f);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
window.artifact.processTargetInfos = function (id, targets) {
|
||||
$.each(targets, function(i, target) {
|
||||
if (!artifact.portalInfo[target.portalGuid]) {
|
||||
artifact.portalInfo[target.portalGuid] = { _entityData: target.portalInfo };
|
||||
artifact.portalInfo[guid][type].fragments = true; //as we no longer have a list of the fragments there
|
||||
}
|
||||
|
||||
if (!artifact.portalInfo[target.portalGuid][id]) artifact.portalInfo[target.portalGuid][id] = {};
|
||||
|
||||
artifact.portalInfo[target.portalGuid][id].target = target.team === 'RESISTANCE' ? TEAM_RES : TEAM_ENL;
|
||||
});
|
||||
// let's pre-generate the entities needed to render the map - array of [guid, timestamp, ent_array]
|
||||
artifact.entities.push ( [guid, data.timestamp, ent] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
window.artifact.getArtifactTypes = function() {
|
||||
@ -143,30 +139,9 @@ window.artifact.isArtifact = function(type) {
|
||||
return type in artifact.artifactTypes;
|
||||
}
|
||||
|
||||
window.artifact.getArtifactDescriptions = function(type) {
|
||||
var descriptions = {
|
||||
'jarvis': { 'title': "Jarvis Shards", 'fragmentName': "shards" },
|
||||
'amar': { 'title': "Amar Artifacts", 'fragmentName': "artifacts" },
|
||||
'helios': { 'title': "Helios Artifacts", 'fragmentName': "artifacts" },
|
||||
'shonin': { 'title': "Sh\u014Dnin Shards", 'fragmentName': "shards" },
|
||||
'lightman': { 'title': "Lightman Shards", 'fragmentName': "shards" },
|
||||
};
|
||||
|
||||
return descriptions[type];
|
||||
}
|
||||
|
||||
// used to render portals that would otherwise be below the visible level
|
||||
window.artifact.getArtifactEntities = function() {
|
||||
var entities = [];
|
||||
|
||||
// create fake entities from the artifact data
|
||||
$.each (artifact.portalInfo, function(guid,data) {
|
||||
var timestamp = 0; // we don't have a valid timestamp - so let's use 0
|
||||
var ent = [ guid, timestamp, data._entityData ];
|
||||
entities.push(ent);
|
||||
});
|
||||
|
||||
return entities;
|
||||
return artifact.entities;
|
||||
}
|
||||
|
||||
window.artifact.getInterestingPortals = function() {
|
||||
@ -187,99 +162,53 @@ window.artifact.updateLayer = function() {
|
||||
artifact._layer.clearLayers();
|
||||
|
||||
$.each(artifact.portalInfo, function(guid,data) {
|
||||
var latlng = L.latLng ([data._entityData[2]/1E6, data._entityData[3]/1E6]);
|
||||
var latlng = L.latLng ([data._data.latE6/1E6, data._data.lngE6/1E6]);
|
||||
|
||||
// jarvis shard icon
|
||||
var iconUrl = undefined;
|
||||
var iconSize = 0;
|
||||
var opacity = 1.0;
|
||||
$.each(data, function(type,detail) {
|
||||
|
||||
// redundant as of 2014-02-05 - jarvis shards removed
|
||||
if (data.jarvis) {
|
||||
if (data.jarvis.target) {
|
||||
// target portal - show the target marker. use the count of fragments at the target to pick the right icon - it has segments that fill up
|
||||
// we'll construct the URL form the type - stock seems to do that now
|
||||
|
||||
var count = data.jarvis.fragments ? data.jarvis.fragments.length : 0;
|
||||
var iconUrl;
|
||||
if (data[type].target !== undefined) {
|
||||
// target portal
|
||||
var iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/'+type+'_shard_target.png'
|
||||
var iconSize = 100/2;
|
||||
var opacity = 1.0;
|
||||
|
||||
var icon = L.icon({
|
||||
iconUrl: iconUrl,
|
||||
iconSize: [iconSize,iconSize],
|
||||
iconAnchor: [iconSize/2,iconSize/2],
|
||||
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
|
||||
});
|
||||
|
||||
var marker = L.marker (latlng, {icon: icon, clickable: false, keyboard: false, opacity: opacity });
|
||||
|
||||
artifact._layer.addLayer(marker);
|
||||
|
||||
} else if (data[type].fragments) {
|
||||
// fragment(s) at portal
|
||||
|
||||
var iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/'+type+'_shard.png'
|
||||
var iconSize = 60/2;
|
||||
var opacity = 0.6;
|
||||
|
||||
var icon = L.icon({
|
||||
iconUrl: iconUrl,
|
||||
iconSize: [iconSize,iconSize],
|
||||
iconAnchor: [iconSize/2,iconSize/2],
|
||||
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
|
||||
});
|
||||
|
||||
var marker = L.marker (latlng, {icon: icon, clickable: false, keyboard: false, opacity: opacity });
|
||||
|
||||
artifact._layer.addLayer(marker);
|
||||
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/jarvis_shard_target_'+count+'.png';
|
||||
iconSize = 100/2; // 100 pixels - half that size works better
|
||||
} else if (data.jarvis.fragments) {
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/jarvis_shard.png';
|
||||
iconSize = 60/2; // 60 pixels - half that size works better
|
||||
opacity = 0.6; // these often hide portals - let's make them semi transparent
|
||||
}
|
||||
|
||||
}
|
||||
// 2014-02-06: a guess at whats needed for the new artifacts
|
||||
if (data.amar) {
|
||||
if (data.amar.target) {
|
||||
// target portal - show the target marker. use the count of fragments at the target to pick the right icon - it has segments that fill up
|
||||
}); //end $.each(data, function(type,detail)
|
||||
|
||||
var count = data.amar.fragments ? data.amar.fragments.length : 0;
|
||||
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/amar_shard_target_'+count+'.png';
|
||||
iconSize = 100/2; // 100 pixels - half that size works better
|
||||
} else if (data.amar.fragments) {
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/amar_shard.png';
|
||||
iconSize = 60/2; // 60 pixels - half that size works better
|
||||
opacity = 0.6; // these often hide portals - let's make them semi transparent
|
||||
}
|
||||
}
|
||||
|
||||
// 2014-08-09 - helios artifacts. original guess was slightly wrong
|
||||
if (data.helios) {
|
||||
if (data.helios.target) {
|
||||
// target portal - show the target marker. helios target marker doesn't fill like the earlier jarvis/amar targets
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/helios_shard_target.png';
|
||||
iconSize = 100/2; // 100 pixels - half that size works better
|
||||
} else if (data.helios.fragments) {
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/helios_shard.png';
|
||||
iconSize = 60/2; // 60 pixels - half that size works better
|
||||
opacity = 0.6; // these often hide portals - let's make them semi transparent
|
||||
}
|
||||
}
|
||||
|
||||
// 2015-03-05 - shonin shards
|
||||
if (data.shonin) {
|
||||
if (data.shonin.target) {
|
||||
// target portal - show the target marker.
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/shonin_shard_target.png';
|
||||
iconSize = 100/2; // 100 pixels - half that size works better
|
||||
} else if (data.shonin.fragments) {
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/shonin_shard.png';
|
||||
iconSize = 60/2; // 60 pixels - half that size works better
|
||||
opacity = 0.6; // these often hide portals - let's make them semi transparent
|
||||
}
|
||||
}
|
||||
|
||||
// 2015-04-22 - lightman fragments (guessed)
|
||||
if (data.lightman) {
|
||||
if (data.lightman.target) {
|
||||
// target portal - show the target marker.
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/lightman_shard_target.png';
|
||||
iconSize = 100/2; // 100 pixels - half that size works better
|
||||
} else if (data.lightman.fragments) {
|
||||
iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/lightman_shard.png';
|
||||
iconSize = 60/2; // 60 pixels - half that size works better
|
||||
opacity = 0.6; // these often hide portals - let's make them semi transparent
|
||||
}
|
||||
}
|
||||
|
||||
if (iconUrl) {
|
||||
var icon = L.icon({
|
||||
iconUrl: iconUrl,
|
||||
iconSize: [iconSize,iconSize],
|
||||
iconAnchor: [iconSize/2,iconSize/2],
|
||||
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
|
||||
});
|
||||
|
||||
var marker = L.marker (latlng, {icon: icon, clickable: false, keyboard: false, opacity: opacity });
|
||||
|
||||
artifact._layer.addLayer(marker);
|
||||
} else {
|
||||
console.warn('Oops! no URL for artifact portal icon?!');
|
||||
}
|
||||
});
|
||||
}); //end $.each(artifact.portalInfo, function(guid,data)
|
||||
|
||||
}
|
||||
|
||||
@ -293,9 +222,9 @@ window.artifact.showArtifactList = function() {
|
||||
|
||||
var first = true;
|
||||
$.each(artifact.artifactTypes, function(type,type2) {
|
||||
var description = artifact.getArtifactDescriptions(type);
|
||||
|
||||
var name = description ? description.title : ('unknown artifact type: '+type);
|
||||
// no nice way to convert the Niantic internal name into the correct display name
|
||||
// (we do get the description string once a portal with that shard type is selected - could cache that somewhere?)
|
||||
var name = type.capitalize() + ' shards';
|
||||
|
||||
if (!first) html += '<hr>';
|
||||
first = false;
|
||||
@ -310,29 +239,33 @@ window.artifact.showArtifactList = function() {
|
||||
if (type in data) {
|
||||
// this portal has data for this artifact type - add it to the table
|
||||
|
||||
var sortVal = 0;
|
||||
|
||||
var onclick = 'zoomToAndShowPortal(\''+guid+'\',['+data._entityData[2]/1E6+','+data._entityData[3]/1E6+'])';
|
||||
var row = '<tr><td class="portal"><a onclick="'+onclick+'">'+escapeHtmlSpecialChars(data._entityData[8])+'</a></td>';
|
||||
var onclick = 'zoomToAndShowPortal(\''+guid+'\',['+data._data.latE6/1E6+','+data._data.lngE6/1E6+'])';
|
||||
var row = '<tr><td class="portal"><a onclick="'+onclick+'">'+escapeHtmlSpecialChars(data._data.title)+'</a></td>';
|
||||
|
||||
row += '<td class="info">';
|
||||
|
||||
if (data[type].target) {
|
||||
row += '<span class="target '+TEAM_TO_CSS[data[type].target]+'">'+(data[type].target==TEAM_RES?'Resistance':'Enlightened')+' target</span> ';
|
||||
sortVal = 100000+data[type].target;
|
||||
if (data[type].target !== undefined) {
|
||||
if (data[type].target == TEAM_NONE) {
|
||||
row += '<span class="target">Target Portal</span> ';
|
||||
} else {
|
||||
row += '<span class="target '+TEAM_TO_CSS[data[type].target]+'">'+(data[type].target==TEAM_RES?'Resistance':'Enlightened')+' target</span> ';
|
||||
}
|
||||
}
|
||||
|
||||
if (data[type].fragments) {
|
||||
if (data[type].target) {
|
||||
if (data[type].target !== undefined) {
|
||||
row += '<br>';
|
||||
}
|
||||
var fragmentName = description ? description.fragmentName : 'fragment';
|
||||
row += '<span class="fragments'+(data[type].target?' '+TEAM_TO_CSS[data[type].target]:'')+'">'+fragmentName+': #'+data[type].fragments.join(', #')+'</span> ';
|
||||
sortVal = Math.min.apply(null, data[type].fragments); // use min shard number at portal as sort key
|
||||
var fragmentName = 'shard';
|
||||
// row += '<span class="fragments'+(data[type].target?' '+TEAM_TO_CSS[data[type].target]:'')+'">'+fragmentName+': #'+data[type].fragments.join(', #')+'</span> ';
|
||||
row += '<span class="fragments'+(data[type].target?' '+TEAM_TO_CSS[data[type].target]:'')+'">'+fragmentName+': yes</span> ';
|
||||
}
|
||||
|
||||
row += '</td></tr>';
|
||||
|
||||
// sort by target portals first, then by portal GUID
|
||||
var sortVal = (data[type].target !== undefined ? 'A' : 'Z') + guid;
|
||||
|
||||
tableRows.push ( [sortVal, row] );
|
||||
}
|
||||
});
|
||||
@ -344,7 +277,9 @@ window.artifact.showArtifactList = function() {
|
||||
|
||||
// sort the rows
|
||||
tableRows.sort(function(a,b) {
|
||||
return a[0]-b[0];
|
||||
if (a[0] == b[0]) return 0;
|
||||
else if (a[0] < b[0]) return -1;
|
||||
else return 1;
|
||||
});
|
||||
|
||||
// and add them to the table
|
||||
@ -355,6 +290,13 @@ window.artifact.showArtifactList = function() {
|
||||
});
|
||||
|
||||
|
||||
html += "<hr />"
|
||||
+ "<p>In Summer 2015, Niantic changed the data format for artifact portals. We no longer know:</p>"
|
||||
+ "<ul><li>Which team each target portal is for - only that it is a target</li>"
|
||||
+ "<li>Which shards are at each portal, just that it has one or more shards</li></ul>"
|
||||
+ "<p>You can select a portal and the detailed data contains the list of shard numbers, but there's still no"
|
||||
+ " more information on targets.</p>";
|
||||
|
||||
dialog({
|
||||
title: 'Artifacts',
|
||||
html: html,
|
||||
|
28
code/boot.js
28
code/boot.js
@ -519,7 +519,11 @@ window.setupLayerChooserApi = function() {
|
||||
var baseLayersJSON = JSON.stringify(baseLayers);
|
||||
|
||||
if (typeof android !== 'undefined' && android && android.setLayers) {
|
||||
android.setLayers(baseLayersJSON, overlayLayersJSON);
|
||||
if(this.androidTimer) clearTimeout(this.androidTimer);
|
||||
this.androidTimer = setTimeout(function() {
|
||||
this.androidTimer = null;
|
||||
android.setLayers(baseLayersJSON, overlayLayersJSON);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -561,6 +565,27 @@ window.setupLayerChooserApi = function() {
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var _update = window.layerChooser._update;
|
||||
window.layerChooser._update = function() {
|
||||
// update layer menu in IITCm
|
||||
try {
|
||||
if(typeof android != 'undefined')
|
||||
window.layerChooser.getLayers();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
// call through
|
||||
return _update.apply(this, arguments);
|
||||
}
|
||||
// as this setupLayerChooserApi function is called after the layer menu is populated, we need to also get they layers once
|
||||
// so they're passed through to the android app
|
||||
try {
|
||||
if(typeof android != 'undefined')
|
||||
window.layerChooser.getLayers();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,7 +611,6 @@ function boot() {
|
||||
}});
|
||||
|
||||
window.extractFromStock();
|
||||
window.iitc_bg.init(); //NOTE: needs to be early (before any requests sent), but after extractFromStock()
|
||||
window.setupIdle();
|
||||
window.setupTaphold();
|
||||
window.setupStyles();
|
||||
|
@ -1,326 +0,0 @@
|
||||
// interface to the use of the google 'botguard' javascript added to the intel site
|
||||
|
||||
|
||||
iitc_bg = Object();
|
||||
|
||||
iitc_bg.DISABLED = false; //if set, botguard is disabld. no b/c params sent in requests, no processing of responses
|
||||
|
||||
iitc_bg.init = function() {
|
||||
if (iitc_bg.DISABLED) return;
|
||||
|
||||
// stock site - 'ad.e()' constructor
|
||||
//function Ad() {
|
||||
// this.Eb = {}; // a map, indexed by 'group-[ab]-actions', each entry containing an array of 'yd' objects (simple object, with 'gf' and 'cb' members). a queue of data to process?
|
||||
// this.Oa = {}; // a map, indexed by 'group-[ab]-actions', each entry containing an object with an 'invoke' method
|
||||
// this.Zc = {}; // a map, indexed by group, witn constructors for botguard
|
||||
// this.eb = ""; // the 'key' - B
|
||||
// this.Kh = e; // e is defined in the main web page as "var e = function(w) {eval(w);};"
|
||||
//}
|
||||
|
||||
var botguard_eval = e;
|
||||
|
||||
iitc_bg.data_queue = {}; //.lb - indexed by group
|
||||
iitc_bg.instance_queue = {}; //.ya - indexed by group
|
||||
iitc_bg.key = ""; //.cb
|
||||
iitc_bg.botguard = {}; //.qc - indexed by key
|
||||
iitc_bg.evalFunc = botguard_eval;
|
||||
|
||||
// stock site code
|
||||
//Ad.prototype.U = function(a, b) {
|
||||
// Bd(this, a);
|
||||
// for (var c in b) if ("group-b-actions" == c || "group-a-actions" == c) {
|
||||
// for (var d = 0; d < b[c].length; ++d) Dd(this, c, new Ed(b[c][d], a));
|
||||
// Fd(this, c);
|
||||
// }
|
||||
//};
|
||||
// and.. Ed - a simple object that holds it's parameters and the timestamp it was created
|
||||
//function Ed(a, b) {
|
||||
// var c = w();
|
||||
// this.mg = a;
|
||||
// this.eb = b;
|
||||
// this.Ki = c;
|
||||
//}
|
||||
|
||||
|
||||
// to initialise, we need four things
|
||||
// B - a key(?). set in the main web page HTML, name isn't changed on site updates
|
||||
// CS - initialisation data for botguard - again in the main page, again name is constant
|
||||
|
||||
|
||||
var botguard_key = B;
|
||||
var botguard_data = CS;
|
||||
|
||||
iitc_bg.process_key(botguard_key);
|
||||
|
||||
for (var group in botguard_data) {
|
||||
// TODO? filter this loop by group-[ab]-actions only? the stock site does, but this seems unnecessary
|
||||
|
||||
// the stock site has code to create the emtpy arrays with the group index as and when needed
|
||||
// however, it makes more sense to do it here just once, rather than testing every time
|
||||
iitc_bg.data_queue[group] = [];
|
||||
iitc_bg.instance_queue[group] = [];
|
||||
|
||||
for (var i=0; i < botguard_data[group].length; i++) {
|
||||
iitc_bg.push_queue(group, botguard_data[group][i], botguard_key);
|
||||
}
|
||||
|
||||
iitc_bg.process_queue(group);
|
||||
}
|
||||
};
|
||||
|
||||
//TODO: better name - will do for now...
|
||||
iitc_bg.push_queue = function(group, data, key) {
|
||||
//stock site code
|
||||
//a=='this', b==group-[ab]-actions, c==object with .mg==data, .eb==key, .Ki==current timestamp
|
||||
//function Dd(a, b, c) {
|
||||
// var d = c.eb && a.Zc[c.eb];
|
||||
// if ("dkxm" == c.mg || d) a.Eb[b] || (a.Eb[b] = []), a.Eb[b].push(c);
|
||||
//}
|
||||
|
||||
// Niantic have changed how the server returns data items to the client a few times, which cause
|
||||
// bad non-string items to come into here. this breaks things badly
|
||||
if (typeof data !== "string") throw "Error: iitc_bg.process_queue got dodgy data - expected a string";
|
||||
|
||||
var botguard_instance = iitc_bg.key && iitc_bg.botguard[iitc_bg.key];
|
||||
|
||||
if (data == "dkxm" || botguard_instance) {
|
||||
// NOTE: we pre-create empty per-group arrays on init
|
||||
iitc_bg.data_queue[group].push( {data: data, key: key, time: Date.now()} );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// called both on initialisation and on processing responses from the server
|
||||
//
|
||||
iitc_bg.process_key = function(key,serverEval) {
|
||||
if (iitc_bg.DISABLED) return;
|
||||
|
||||
|
||||
// stock site code
|
||||
//function Bd(a, b, c) {
|
||||
// if (a.Zc[b]) a.eb = b; else {
|
||||
// var d = !0;
|
||||
// if (c) try {
|
||||
// a.Kh(c);
|
||||
// } catch (f) {
|
||||
// d = !1;
|
||||
// }
|
||||
// d && (a.Zc[b] = botguard.bg, a.eb = b);
|
||||
// }
|
||||
//}
|
||||
|
||||
if (iitc_bg.botguard[key]) {
|
||||
iitc_bg.key = key;
|
||||
} else {
|
||||
var noCatch = true;
|
||||
|
||||
if (serverEval) {
|
||||
// server wants us to eval some code! risky, and impossible to be certain we can do it safely
|
||||
// seems to always be the same javascript as already found in the web page source.
|
||||
try {
|
||||
console.warn('botguard: Server-generated javascript eval requested:\n'+serverEval);
|
||||
iitc_bg.evalFunc(serverEval);
|
||||
console.log('botguard: Server-generated javascript ran OK');
|
||||
} catch(e) {
|
||||
console.warn('botguard: Server-generated javascript - threw an exception');
|
||||
console.warn(e);
|
||||
noCatch = false;
|
||||
}
|
||||
}
|
||||
if (noCatch) {
|
||||
iitc_bg.botguard[key] = botguard.bg;
|
||||
iitc_bg.key = key;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//convert a method name to the group-[ab]-actions value, or return 'undefined' for no group
|
||||
//NOTE: the stock code separates the 'in any group' and 'which group' test, but it's cleaner to combine them
|
||||
//UPDATE: the 'not in any group' case was removed from the stock site logic
|
||||
iitc_bg.get_method_group = function(method) {
|
||||
//stock site
|
||||
//function Cd(a) {
|
||||
// return -1 != ig.indexOf(a) ? "group-a-actions" : "group-b-actions";
|
||||
//}
|
||||
|
||||
if (window.niantic_params.botguard_method_group_flag[method] === undefined) {
|
||||
throw 'Error: method '+method+' not found in the botguard_method_group_flag object';
|
||||
}
|
||||
|
||||
if (window.niantic_params.botguard_method_group_flag[method]) {
|
||||
return "ingress-a-actions";
|
||||
} else {
|
||||
return "ingress-b-actions";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// returns the extra parameters to add to any JSON request
|
||||
iitc_bg.extra_request_params = function(method) {
|
||||
if (iitc_bg.DISABLED) return {};
|
||||
|
||||
|
||||
var extra = {};
|
||||
extra.b = iitc_bg.key;
|
||||
extra.c = iitc_bg.get_request_data(method);
|
||||
|
||||
return extra;
|
||||
};
|
||||
|
||||
iitc_bg.get_request_data = function(method) {
|
||||
//function Id(a, b) {
|
||||
// var c = "mxkd", d = Cd(b);
|
||||
// a.Oa[d] && 0 < a.Oa[d].length && a.Oa[d].shift().invoke(function(a) {
|
||||
// c = a;
|
||||
// });
|
||||
// Fd(a, d);
|
||||
// return c;
|
||||
//}
|
||||
|
||||
|
||||
var group = iitc_bg.get_method_group(method);
|
||||
if (!group) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// this seems to be some kind of 'flag' string - and is either "mxkd" or "dkxm". it can be returned from the
|
||||
// server, so we stick with the same string rather than something more sensibly named
|
||||
var data = "mxkd";
|
||||
|
||||
if (iitc_bg.instance_queue[group] && iitc_bg.instance_queue[group].length > 0) {
|
||||
var instance = iitc_bg.instance_queue[group].shift();
|
||||
instance.invoke(function(a) { data=a; });
|
||||
};
|
||||
|
||||
iitc_bg.process_queue(group);
|
||||
|
||||
if (data.indexOf('undefined is not a function') != -1) {
|
||||
// there's been cases of something going iffy in the botguard code, or IITC's interface to it. in this case,
|
||||
// instead of the correct encoded string, the data contains an error message along the lines of
|
||||
// "E:undefined is not a function:TypeError: undefined is not a function"[...]
|
||||
// in this case, better to just stop with some kind of error than send the string to the server
|
||||
debugger;
|
||||
throw ('Error: iitc_bg.get_request_data got bad data - cannot safely continue');
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
// stock site - 'dummy' botguard object
|
||||
//function Sf() {}
|
||||
//Sf.prototype.invoke = function(a) {
|
||||
// a("dkxm");
|
||||
//};
|
||||
|
||||
iitc_bg.dummy_botguard = function() {};
|
||||
iitc_bg.dummy_botguard.prototype.invoke = function(callback) {
|
||||
callback("dkxm");
|
||||
};
|
||||
|
||||
|
||||
iitc_bg.process_queue = function(group) {
|
||||
//stock site
|
||||
//function Fd(a, b) {
|
||||
// if (a.Eb[b]) for (; 0 < a.Eb[b].length; ) {
|
||||
// var c = a.Eb[b].shift(), d = c.mg, f = c.Ki + 717e4, g;
|
||||
// "dkxm" == d ? g = new jg : w() < f && (g = new a.Zc[c.eb](d));
|
||||
// if (g) {
|
||||
// c = a;
|
||||
// d = b;
|
||||
// c.Oa[d] || (c.Oa[d] = []);
|
||||
// c.Oa[d].push(g);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// processes an entry in the queue for the specified group
|
||||
|
||||
while (iitc_bg.data_queue[group] && iitc_bg.data_queue[group].length > 0) {
|
||||
var item = iitc_bg.data_queue[group].shift();
|
||||
var obj = undefined;
|
||||
|
||||
if (item.data == "dkxm") {
|
||||
obj = new iitc_bg.dummy_botguard;
|
||||
} else if (Date.now() < item.time + 7170000) {
|
||||
obj = new iitc_bg.botguard[item.key](item.data);
|
||||
}
|
||||
|
||||
// note: we pre-create empty per-group arrays on init
|
||||
if (obj) {
|
||||
iitc_bg.instance_queue[group].push(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
iitc_bg.process_response_params = function(method,data) {
|
||||
if (iitc_bg.DISABLED) {
|
||||
// the rest of IITC won't expect these strange a/b/c params in the response data
|
||||
// (e.g. it's pointless to keep in the cache, etc), so clear them
|
||||
delete data.a;
|
||||
delete data.b;
|
||||
delete data.c;
|
||||
return;
|
||||
}
|
||||
|
||||
// stock site: response processing
|
||||
//yd.prototype.vi = function(a, b) {
|
||||
// var c = b.target;
|
||||
// if (cd(c)) {
|
||||
// this.Ib.reset();
|
||||
// var d = a.hg, f = JSON.parse(ed(c));
|
||||
// if (f.c && 0 < f.c.length) {
|
||||
// var g = Ad.e(), h = a.getMethodName(), l = f.a, m = f.b, r = f.c[0];
|
||||
// m && l && Bd(g, m, l);
|
||||
// h = Cd(h);
|
||||
// if (r && m && 0 < r.length) for (l = 0; l < r.length; ++l) Dd(g, h, new Ed(r[l], m));
|
||||
// g.Oa[h] && 0 != g.Oa[h].length || Fd(g, h);
|
||||
// }
|
||||
// "error" in f && "out of date" == f.error ? (d = rd.e(), Gd(!1), d.ie = !0, Hd("Please refresh for the latest version.")) : "error" in f && "RETRY" == f.error ? (this.Da.ja(1, a), td(this.Ib)) : n.setTimeout(pa(d, f), 0);
|
||||
// } else this.Ib.Ec = !0, d = a.cg, ha(d) && (f = {
|
||||
// error: dd(c) || "unknown",
|
||||
// respStatus: c.getStatus()
|
||||
// }, n.setTimeout(pa(d, f), 0));
|
||||
// d = this.Te;
|
||||
// d.Aa.remove(c) && d.zc(c);
|
||||
//};
|
||||
|
||||
|
||||
if (data.c && data.c.length > 0) {
|
||||
|
||||
if (data.b && data.a) {
|
||||
// in this case, we *EVAL* the 'data.a' string from the server!
|
||||
// however, it's not a case that's been ever triggered in normal use, as far as I know
|
||||
iitc_bg.process_key(data.b, data.a);
|
||||
}
|
||||
|
||||
var group = iitc_bg.get_method_group(method);
|
||||
|
||||
//NOTE: I missed a change here a while ago. originally data.c was a single-level array of items to push on the queue,
|
||||
//but now it's a two-dimensional array, and it's only index zero that's used!
|
||||
var data_items = data.c[0];
|
||||
|
||||
if (data_items && data.b && data_items.length > 0) {
|
||||
for (var i=0; i<data_items.length; i++) {
|
||||
iitc_bg.push_queue(group, data_items[i], data.b);
|
||||
}
|
||||
}
|
||||
|
||||
if (iitc_bg.instance_queue[group] && iitc_bg.instance_queue[group].length == 0) {
|
||||
iitc_bg.process_queue(group);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// finally, the rest of IITC won't expect these strange a/b/c params in the response data
|
||||
// (e.g. it's pointless to keep in the cache, etc), so clear them
|
||||
delete data.a;
|
||||
delete data.b;
|
||||
delete data.c;
|
||||
};
|
@ -51,10 +51,6 @@ window.DIALOG_SLIDE_DURATION = 100;
|
||||
window.dialog = function(options) {
|
||||
// Override for smartphones. Preserve default behavior and create a modal dialog.
|
||||
options = options || {};
|
||||
if(isSmartphone()) {
|
||||
options.modal = true;
|
||||
options.width = 'auto';
|
||||
}
|
||||
|
||||
// Build an identifier for this dialog
|
||||
var id = 'dialog-' + (options.modal ? 'modal' : (options.id ? options.id : 'anon-' + window.DIALOG_ID++));
|
||||
@ -78,7 +74,7 @@ window.dialog = function(options) {
|
||||
|
||||
// Modal dialogs should not be draggable
|
||||
if(options.modal) {
|
||||
options.dialogClass = 'ui-dialog-modal';
|
||||
options.dialogClass = (options.dialogClass ? options.dialogClass + ' ' : '') + 'ui-dialog-modal';
|
||||
options.draggable = false;
|
||||
}
|
||||
|
||||
@ -93,6 +89,15 @@ window.dialog = function(options) {
|
||||
}
|
||||
}
|
||||
|
||||
// there seems to be a bug where width/height are set to a fixed value after moving a dialog
|
||||
function sizeFix() {
|
||||
if(dialog.data('collapsed')) return;
|
||||
|
||||
var options = dialog.dialog('option');
|
||||
dialog.dialog('option', 'height', options.height);
|
||||
dialog.dialog('option', 'width', options.width);
|
||||
}
|
||||
|
||||
// Create the window, appending a div to the body
|
||||
$('body').append('<div id="' + id + '"></div>');
|
||||
var dialog = $(jqID).dialog($.extend(true, {
|
||||
@ -140,7 +145,8 @@ window.dialog = function(options) {
|
||||
var button = dialog.find('.ui-dialog-titlebar-button-collapse');
|
||||
|
||||
// Slide toggle
|
||||
$(selector).slideToggle({duration: window.DIALOG_SLIDE_DURATION});
|
||||
$(this).css('height', '');
|
||||
$(selector).slideToggle({duration: window.DIALOG_SLIDE_DURATION, complete: sizeFix});
|
||||
|
||||
if(collapsed) {
|
||||
$(button).removeClass('ui-dialog-titlebar-button-collapse-collapsed');
|
||||
@ -214,6 +220,8 @@ window.dialog = function(options) {
|
||||
}
|
||||
}, options));
|
||||
|
||||
dialog.on('dialogdragstop dialogresizestop', sizeFix);
|
||||
|
||||
// Set HTML and IDs
|
||||
dialog.html(html);
|
||||
dialog.data('id', id);
|
||||
|
@ -25,17 +25,59 @@
|
||||
energy: arr[2],
|
||||
};
|
||||
}
|
||||
function parseArtifactBrief(arr) {
|
||||
if (arr === null) return null;
|
||||
|
||||
// array index 0 is for fragments at the portal. index 1 is for target portals
|
||||
// each of those is two dimensional - not sure why. part of this is to allow for multiple types of artifacts,
|
||||
// with their own targets, active at once - but one level for the array is enough for that
|
||||
|
||||
// making a guess - first level is for different artifact types, second index would allow for
|
||||
// extra data for that artifact type
|
||||
|
||||
function decodeArtifactArray(arr) {
|
||||
var result = {};
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
// we'll use the type as the key - and store any additional array values as the value
|
||||
// that will be an empty array for now, so only object keys are useful data
|
||||
result[arr[i][0]] = arr[i].slice(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
fragment: decodeArtifactArray(arr[0]),
|
||||
target: decodeArtifactArray(arr[1]),
|
||||
};
|
||||
}
|
||||
|
||||
function parseArtifactDetail(arr) {
|
||||
if (arr == null) { return null; }
|
||||
// empty artifact data is pointless - ignore it
|
||||
if (arr.length == 3 && arr[0] == "" && arr[1] == "" && arr[2].length == 0) { return null; }
|
||||
return {
|
||||
type: arr[0],
|
||||
displayName: arr[1],
|
||||
fragments: arr[2],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var summaryArrayLength = undefined;
|
||||
//there's also a 'placeholder' portal - generated from the data in links/fields. only has team/lat/lng
|
||||
|
||||
|
||||
function basePortalData(a) {
|
||||
var CORE_PORTA_DATA_LENGTH = 4;
|
||||
function corePortalData(a) {
|
||||
return {
|
||||
// a[0] == type (always 'p')
|
||||
team: a[1],
|
||||
latE6: a[2],
|
||||
lngE6: a[3],
|
||||
lngE6: a[3]
|
||||
}
|
||||
};
|
||||
|
||||
var SUMMARY_PORTAL_DATA_LENGTH = 14;
|
||||
function summaryPortalData(a) {
|
||||
return {
|
||||
level: a[4],
|
||||
health: a[5],
|
||||
resCount: a[6],
|
||||
@ -43,19 +85,32 @@
|
||||
title: a[8],
|
||||
ornaments: a[9],
|
||||
mission: a[10],
|
||||
mission50plus: a[11]
|
||||
mission50plus: a[11],
|
||||
artifactBrief: parseArtifactBrief(a[12]),
|
||||
timestamp: a[13]
|
||||
};
|
||||
};
|
||||
|
||||
var DETAILED_PORTAL_DATA_LENGTH = SUMMARY_PORTAL_DATA_LENGTH+4;
|
||||
|
||||
|
||||
window.decodeArray.portalSummary = function(a) {
|
||||
if (!a) return undefined;
|
||||
|
||||
if (a[0] != 'p') throw 'Error: decodeArray.portalSUmmary - not a portal';
|
||||
|
||||
if (summaryArrayLength===undefined) summaryArrayLength = a.length;
|
||||
if (summaryArrayLength!=a.length) console.warn('decodeArray.portalSUmmary: inconsistant map data portal array lengths');
|
||||
if (a.length == CORE_PORTA_DATA_LENGTH) {
|
||||
return corePortalData(a);
|
||||
}
|
||||
|
||||
return basePortalData(a);
|
||||
// NOTE: allow for either summary or detailed portal data to be passed in here, as details are sometimes
|
||||
// passed into code only expecting summaries
|
||||
if (a.length != SUMMARY_PORTAL_DATA_LENGTH && a.length != DETAILED_PORTAL_DATA_LENGTH) {
|
||||
console.warn('Portal summary length changed - portal details likely broken!');
|
||||
debugger;
|
||||
}
|
||||
|
||||
return $.extend(corePortalData(a), summaryPortalData(a));
|
||||
}
|
||||
|
||||
window.decodeArray.portalDetail = function(a) {
|
||||
@ -63,15 +118,22 @@
|
||||
|
||||
if (a[0] != 'p') throw 'Error: decodeArray.portalDetail - not a portal';
|
||||
|
||||
if (summaryArrayLength===undefined) throw 'Error: decodeArray.portalDetail - not yet seen any portal summary data - cannot decode!';
|
||||
if (a.length != DETAILED_PORTAL_DATA_LENGTH) {
|
||||
console.warn('Portal detail length changed - portal details may be wrong');
|
||||
debugger;
|
||||
}
|
||||
|
||||
//TODO look at the array values, make a better guess as to which index the mods start at, rather than using the hard-coded SUMMARY_PORTAL_DATA_LENGTH constant
|
||||
|
||||
|
||||
// the portal details array is just an extension of the portal summary array
|
||||
// to allow for niantic adding new items into the array before the extended details start,
|
||||
// use the length of the summary array
|
||||
return $.extend(basePortalData(a),{
|
||||
mods: a[summaryArrayLength+0].map(parseMod),
|
||||
resonators:a[summaryArrayLength+1].map(parseResonator),
|
||||
owner: a[summaryArrayLength+2]
|
||||
return $.extend(corePortalData(a), summaryPortalData(a),{
|
||||
mods: a[SUMMARY_PORTAL_DATA_LENGTH+0].map(parseMod),
|
||||
resonators:a[SUMMARY_PORTAL_DATA_LENGTH+1].map(parseResonator),
|
||||
owner: a[SUMMARY_PORTAL_DATA_LENGTH+2],
|
||||
artifactDetail: parseArtifactDetail(a[SUMMARY_PORTAL_DATA_LENGTH+3]),
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -5,29 +5,10 @@
|
||||
window.extractFromStock = function() {
|
||||
window.niantic_params = {}
|
||||
|
||||
window.niantic_params.botguard_method_group_flag = {};
|
||||
|
||||
// extract the former nemesis.dashboard.config.CURRENT_VERSION from the code
|
||||
var reVersion = new RegExp('[a-z]=[a-z].getData\\(\\);[a-z].v="([a-f0-9]{40})";');
|
||||
var reVersion = new RegExp('"X-CSRFToken".*[a-z].v="([a-f0-9]{40})";');
|
||||
|
||||
var minified = new RegExp('^[a-zA-Z$][a-zA-Z$0-9]$');
|
||||
|
||||
// required for botguard
|
||||
var requestPrototype = (function() {
|
||||
for(var topLevel in window) {
|
||||
if(!window[topLevel]) continue;
|
||||
// need an example for a request object
|
||||
for(var property in window[topLevel]) {
|
||||
try {
|
||||
if(window[topLevel][property] == "getRegionScoreDetails") {
|
||||
return Object.getPrototypeOf(window[topLevel]);
|
||||
}
|
||||
} catch(e) { // might throw SecurityError or others (noticed on top.opener, which might be cross-origin)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
var minified = new RegExp('^[a-zA-Z$][a-zA-Z$0-9]?$');
|
||||
|
||||
for (var topLevel in window) {
|
||||
if (minified.test(topLevel)) {
|
||||
@ -69,10 +50,10 @@ window.extractFromStock = function() {
|
||||
}
|
||||
if (justInts) {
|
||||
|
||||
// current lengths are: 17: ZOOM_TO_LEVEL, 16: TILES_PER_EDGE
|
||||
// current lengths are: 17: ZOOM_TO_LEVEL, 14: TILES_PER_EDGE
|
||||
// however, slightly longer or shorter are a possibility in the future
|
||||
|
||||
if (topObject.length >= 15 && topObject.length <= 18) {
|
||||
if (topObject.length >= 12 && topObject.length <= 18) {
|
||||
// a reasonable array length for tile parameters
|
||||
// need to find two types:
|
||||
// a. portal level limits. decreasing numbers, starting at 8
|
||||
@ -93,7 +74,8 @@ window.extractFromStock = function() {
|
||||
}
|
||||
} // end if (topObject[0] == 8)
|
||||
|
||||
if (topObject[topObject.length-1] == 36000 || topObject[topObject.length-1] == 18000 || topObject[topObject.length-1] == 9000) {
|
||||
// 2015-06-25 - changed to top value of 64000, then to 32000 - allow for them to restore it just in case
|
||||
if (topObject[topObject.length-1] >= 9000 && topObject[topObject.length-1] <= 64000) {
|
||||
var increasing = true;
|
||||
for (var i=1; i<topObject.length; i++) {
|
||||
if (topObject[i-1] > topObject[i]) {
|
||||
@ -114,25 +96,11 @@ window.extractFromStock = function() {
|
||||
}
|
||||
|
||||
|
||||
// finding the required method names for the botguard interface code
|
||||
if(topObject && typeof topObject == "object" && Object.getPrototypeOf(topObject) == requestPrototype) {
|
||||
var methodKey = Object
|
||||
.keys(topObject)
|
||||
.filter(function(key) { return typeof key == "string"; })[0];
|
||||
|
||||
for(var secLevel in topObject) {
|
||||
if(typeof topObject[secLevel] == "boolean") {
|
||||
window.niantic_params.botguard_method_group_flag[topObject[methodKey]] = topObject[secLevel];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (niantic_params.CURRENT_VERSION === undefined || Object.keys(window.niantic_params.botguard_method_group_flag).length == 0) {
|
||||
if (niantic_params.CURRENT_VERSION === undefined) {
|
||||
dialog({
|
||||
title: 'IITC Broken',
|
||||
html: '<p>IITC failed to extract the required parameters from the intel site</p>'
|
||||
|
@ -18,6 +18,7 @@
|
||||
// portalSelected: called when portal on map is selected/unselected.
|
||||
// Provide guid of selected and unselected portal.
|
||||
// mapDataRefreshStart: called when we start refreshing map data
|
||||
// mapDataEntityInject: called just as we start to render data. has callback to inject cached entities into the map render
|
||||
// mapDataRefreshEnd: called when we complete the map data load
|
||||
// portalAdded: called when a portal has been received and is about to
|
||||
// be added to its layer group. Note that this does NOT
|
||||
@ -51,11 +52,13 @@
|
||||
// this only selects the current chat pane; on mobile, it
|
||||
// also switches between map, info and other panes defined
|
||||
// by plugins
|
||||
// artifactsUpdated: called when the set of artifacts (including targets)
|
||||
// has changed. Parameters names are old, new.
|
||||
|
||||
window._hooks = {}
|
||||
window.VALID_HOOKS = [
|
||||
'portalSelected', 'portalDetailsUpdated',
|
||||
'mapDataRefreshStart', 'mapDataRefreshEnd',
|
||||
'portalSelected', 'portalDetailsUpdated', 'artifactsUpdated',
|
||||
'mapDataRefreshStart', 'mapDataEntityInject', 'mapDataRefreshEnd',
|
||||
'portalAdded', 'linkAdded', 'fieldAdded',
|
||||
'publicChatDataAvailable', 'factionChatDataAvailable',
|
||||
'requestFinished', 'nicknameClicked',
|
||||
@ -103,3 +106,16 @@ window.addHook = function(event, callback) {
|
||||
else
|
||||
_hooks[event].push(callback);
|
||||
}
|
||||
|
||||
// callback must the SAME function to be unregistered.
|
||||
window.removeHook = function(event, callback) {
|
||||
if (typeof callback !== 'function') throw('Callback must be a function.');
|
||||
|
||||
if (_hooks[event]) {
|
||||
var index = _hooks[event].indexOf(callback);
|
||||
if(index == -1)
|
||||
console.warn('Callback wasn\'t registered for this event.');
|
||||
else
|
||||
_hooks[event].splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,18 @@
|
||||
|
||||
window.setupDataTileParams = function() {
|
||||
// default values - used to fall back to if we can't detect those used in stock intel
|
||||
var DEFAULT_ZOOM_TO_TILES_PER_EDGE = [256, 256, 256, 256, 512, 512, 512, 2048, 2048, 2048, 4096, 4096, 6500, 6500, 6500, 18e3, 18e3, 36e3];
|
||||
var DEFAULT_ZOOM_TO_LEVEL = [ 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 3, 2, 2, 1, 1 ];
|
||||
var DEFAULT_ZOOM_TO_TILES_PER_EDGE = [1,1,1,40,40,80,80,320,1000,2000,2000,4000,8000,16000,16000,32000];
|
||||
var DEFAULT_ZOOM_TO_LEVEL = [8,8,8,8,7,7,7,6,6,5,4,4,3,2,2,1,1];
|
||||
|
||||
// stock intel doesn't have this array (they use a switch statement instead), but this is far neater
|
||||
var DEFAULT_ZOOM_TO_LINK_LENGTH = [200000,200000,200000,200000,200000,60000,60000,10000,5000,2500,2500,800,300,0,0];
|
||||
|
||||
window.TILE_PARAMS = {};
|
||||
|
||||
// not in stock to detect - we'll have to assume the above values...
|
||||
window.TILE_PARAMS.ZOOM_TO_LINK_LENGTH = DEFAULT_ZOOM_TO_LINK_LENGTH;
|
||||
|
||||
|
||||
if (niantic_params.ZOOM_TO_LEVEL && niantic_params.TILES_PER_EDGE) {
|
||||
window.TILE_PARAMS.ZOOM_TO_LEVEL = niantic_params.ZOOM_TO_LEVEL;
|
||||
window.TILE_PARAMS.TILES_PER_EDGE = niantic_params.TILES_PER_EDGE;
|
||||
@ -25,22 +31,56 @@ window.setupDataTileParams = function() {
|
||||
|
||||
// lazy numerical array comparison
|
||||
if ( JSON.stringify(niantic_params.ZOOM_TO_LEVEL) != JSON.stringify(DEFAULT_ZOOM_TO_LEVEL)) {
|
||||
console.warn('Tile parameter ZOOM_TO_LEVEL have changed in stock intel. Detectec correct values, but code should be updated');
|
||||
console.warn('Tile parameter ZOOM_TO_LEVEL have changed in stock intel. Detected correct values, but code should be updated');
|
||||
debugger;
|
||||
}
|
||||
if ( JSON.stringify(niantic_params.TILES_PER_EDGE) != JSON.stringify(DEFAULT_ZOOM_TO_TILES_PER_EDGE)) {
|
||||
console.warn('Tile parameter ZOOM_TO_LEVEL have changed in stock intel. Detectec correct values, but code should be updated');
|
||||
console.warn('Tile parameter TILES_PER_EDGE have changed in stock intel. Detected correct values, but code should be updated');
|
||||
debugger;
|
||||
}
|
||||
|
||||
} else {
|
||||
console.warn('Failed to detect both ZOOM_TO_LEVEL and TILES_PER_EDGE in the stock intel site - using internal defaults');
|
||||
debugger;
|
||||
dialog({
|
||||
title: 'IITC Warning',
|
||||
html: "<p>IITC failed to detect the ZOOM_TO_LEVEL and/or TILES_PER_EDGE settings from the stock intel site.</p>"
|
||||
+"<p>IITC is now using fallback default values. However, if detection has failed it's likely the values have changed."
|
||||
+" IITC may not load the map if these default values are wrong.</p>",
|
||||
});
|
||||
|
||||
window.TILE_PARAMS.ZOOM_TO_LEVEL = DEFAULT_ZOOM_TO_LEVEL;
|
||||
window.TILE_PARAMS.TILES_PER_EDGE = DEFAULT_ZOOM_TO_TILES_PER_EDGE;
|
||||
}
|
||||
|
||||
// 2015-07-01: niantic added code to the stock site that overrides the min zoom level for unclaimed portals to 15 and above
|
||||
// instead of updating the zoom-to-level array. makes no sense really....
|
||||
// we'll just chop off the array at that point, so the code defaults to level 0 (unclaimed) everywhere...
|
||||
window.TILE_PARAMS.ZOOM_TO_LEVEL = window.TILE_PARAMS.ZOOM_TO_LEVEL.slice(0,15);
|
||||
|
||||
}
|
||||
|
||||
|
||||
window.debugMapZoomParameters = function() {
|
||||
|
||||
//for debug purposes, log the tile params used for each zoom level
|
||||
console.log('DEBUG: Map Zoom Parameters');
|
||||
var doneZooms = {};
|
||||
for (var z=MIN_ZOOM; z<=21; z++) {
|
||||
var ourZoom = getDataZoomForMapZoom(z);
|
||||
console.log('DEBUG: map zoom '+z+': IITC requests '+ourZoom+(ourZoom!=z?' instead':''));
|
||||
if (!doneZooms[ourZoom]) {
|
||||
var params = getMapZoomTileParameters(ourZoom);
|
||||
var msg = 'DEBUG: data zoom '+ourZoom;
|
||||
if (params.hasPortals) {
|
||||
msg += ' has portals, L'+params.level+'+';
|
||||
} else {
|
||||
msg += ' NO portals (was L'+params.level+'+)';
|
||||
}
|
||||
msg += ', minLinkLength='+params.minLinkLength;
|
||||
msg += ', tiles per edge='+params.tilesPerEdge;
|
||||
console.log(msg);
|
||||
doneZooms[ourZoom] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,12 +94,12 @@ window.getMapZoomTileParameters = function(zoom) {
|
||||
|
||||
var level = window.TILE_PARAMS.ZOOM_TO_LEVEL[zoom] || 0; // default to level 0 (all portals) if not in array
|
||||
|
||||
if (window.CONFIG_ZOOM_SHOW_LESS_PORTALS_ZOOMED_OUT) {
|
||||
if (level <= 7 && level >= 4) {
|
||||
// reduce portal detail level by one - helps reduce clutter
|
||||
level = level+1;
|
||||
}
|
||||
}
|
||||
// if (window.CONFIG_ZOOM_SHOW_LESS_PORTALS_ZOOMED_OUT) {
|
||||
// if (level <= 7 && level >= 4) {
|
||||
// // reduce portal detail level by one - helps reduce clutter
|
||||
// level = level+1;
|
||||
// }
|
||||
// }
|
||||
|
||||
var maxTilesPerEdge = window.TILE_PARAMS.TILES_PER_EDGE[window.TILE_PARAMS.TILES_PER_EDGE.length-1];
|
||||
|
||||
@ -67,6 +107,8 @@ window.getMapZoomTileParameters = function(zoom) {
|
||||
level: level,
|
||||
maxLevel: window.TILE_PARAMS.ZOOM_TO_LEVEL[zoom] || 0, // for reference, for log purposes, etc
|
||||
tilesPerEdge: window.TILE_PARAMS.TILES_PER_EDGE[zoom] || maxTilesPerEdge,
|
||||
minLinkLength: window.TILE_PARAMS.ZOOM_TO_LINK_LENGTH[zoom] || 0,
|
||||
hasPortals: zoom >= window.TILE_PARAMS.ZOOM_TO_LINK_LENGTH.length, // no portals returned at all when link length limits things
|
||||
zoom: zoom // include the zoom level, for reference
|
||||
};
|
||||
}
|
||||
@ -83,16 +125,6 @@ window.getDataZoomForMapZoom = function(zoom) {
|
||||
zoom = 21;
|
||||
}
|
||||
|
||||
if (window.CONFIG_ZOOM_SHOW_MORE_PORTALS) {
|
||||
// slightly unfriendly to the servers, requesting more, but smaller, tiles, for the 'unclaimed' level of detail
|
||||
// however, server load issues are all related to the map area in view, with no real issues related to detail level
|
||||
// therefore, I believel we can get away with these smaller tiles for one or two further zoom levels without issues
|
||||
|
||||
if (zoom == 16) {
|
||||
zoom = 17;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!window.CONFIG_ZOOM_DEFAULT_DETAIL_LEVEL) {
|
||||
|
||||
@ -104,7 +136,11 @@ window.getDataZoomForMapZoom = function(zoom) {
|
||||
|
||||
while (zoom > MIN_ZOOM) {
|
||||
var newTileParams = getMapZoomTileParameters(zoom-1);
|
||||
if (newTileParams.tilesPerEdge != origTileParams.tilesPerEdge || newTileParams.level != origTileParams.level) {
|
||||
|
||||
if ( newTileParams.tilesPerEdge != origTileParams.tilesPerEdge
|
||||
|| newTileParams.hasPortals != origTileParams.hasPortals
|
||||
|| newTileParams.level*newTileParams.hasPortals != origTileParams.level*origTileParams.hasPortals // multiply by 'hasPortals' bool - so comparison does not matter when no portals available
|
||||
) {
|
||||
// switching to zoom-1 would result in a different detail level - so we abort changing things
|
||||
break;
|
||||
} else {
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
|
||||
window.Render = function() {
|
||||
|
||||
this.portalMarkerScale = undefined;
|
||||
}
|
||||
|
||||
@ -25,7 +24,7 @@ window.Render.prototype.startRenderPass = function(level,bounds) {
|
||||
// this will just avoid a few entity removals at start of render when they'll just be added again
|
||||
var paddedBounds = bounds.pad(0.1);
|
||||
|
||||
this.clearPortalsBelowLevelOrOutsideBounds(level,paddedBounds);
|
||||
this.clearPortalsOutsideBounds(paddedBounds);
|
||||
|
||||
this.clearLinksOutsideBounds(paddedBounds);
|
||||
this.clearFieldsOutsideBounds(paddedBounds);
|
||||
@ -34,12 +33,12 @@ window.Render.prototype.startRenderPass = function(level,bounds) {
|
||||
this.rescalePortalMarkers();
|
||||
}
|
||||
|
||||
window.Render.prototype.clearPortalsBelowLevelOrOutsideBounds = function(level,bounds) {
|
||||
window.Render.prototype.clearPortalsOutsideBounds = function(bounds) {
|
||||
var count = 0;
|
||||
for (var guid in window.portals) {
|
||||
var p = portals[guid];
|
||||
// clear portals below specified level - unless it's the selected portal, or it's relevant to artifacts
|
||||
if ((parseInt(p.options.level) < level || !bounds.contains(p.getLatLng())) && guid !== selectedPortal && !artifact.isInterestingPortal(guid)) {
|
||||
// clear portals outside visible bounds - unless it's the selected portal, or it's relevant to artifacts
|
||||
if (!bounds.contains(p.getLatLng()) && guid !== selectedPortal && !artifact.isInterestingPortal(guid)) {
|
||||
this.deletePortalEntity(guid);
|
||||
count++;
|
||||
}
|
||||
@ -110,7 +109,7 @@ window.Render.prototype.processDeletedGameEntityGuids = function(deleted) {
|
||||
|
||||
}
|
||||
|
||||
window.Render.prototype.processGameEntities = function(entities,ignoreLevel) {
|
||||
window.Render.prototype.processGameEntities = function(entities) {
|
||||
|
||||
// we loop through the entities three times - for fields, links and portals separately
|
||||
// this is a reasonably efficient work-around for leafletjs limitations on svg render order
|
||||
@ -131,27 +130,13 @@ window.Render.prototype.processGameEntities = function(entities,ignoreLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2015-03-12 - Niantic have been returning all mission portals to the client, ignoring portal level
|
||||
// and density filtering usually in use. this makes things unusable when viewing the global view, so we
|
||||
// filter these out
|
||||
var minLevel = ignoreLevel ? 0 : this.level;
|
||||
var ignoredCount = 0;
|
||||
|
||||
for (var i in entities) {
|
||||
var ent = entities[i];
|
||||
|
||||
if (ent[2][0] == 'p' && !(ent[0] in this.deletedGuid)) {
|
||||
var portalLevel = ent[2][1] == 'N' ? 0 : parseInt(ent[2][4]);
|
||||
if (portalLevel >= minLevel) {
|
||||
this.createPortalEntity(ent);
|
||||
} else {
|
||||
ignoredCount++;
|
||||
}
|
||||
|
||||
this.createPortalEntity(ent);
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoredCount) console.log('Render: ignored '+ignoredCount+' portals below the level requested from the server');
|
||||
}
|
||||
|
||||
|
||||
@ -263,6 +248,39 @@ window.Render.prototype.deleteFieldEntity = function(guid) {
|
||||
}
|
||||
|
||||
|
||||
window.Render.prototype.createPlaceholderPortalEntity = function(guid,latE6,lngE6,team) {
|
||||
// intel no longer returns portals at anything but the closest zoom
|
||||
// stock intel creates 'placeholder' portals from the data in links/fields - IITC needs to do the same
|
||||
// we only have the portal guid, lat/lng coords, and the faction - no other data
|
||||
// having the guid, at least, allows the portal details to be loaded once it's selected. however,
|
||||
// no highlighters, portal level numbers, portal names, useful counts of portals, etc are possible
|
||||
|
||||
|
||||
var ent = [
|
||||
guid, //ent[0] = guid
|
||||
0, //ent[1] = timestamp - zero will mean any other source of portal data will have a higher timestamp
|
||||
//ent[2] = an array with the entity data
|
||||
[ 'p', //0 - a portal
|
||||
team, //1 - team
|
||||
latE6, //2 - lat
|
||||
lngE6 //3 - lng
|
||||
]
|
||||
];
|
||||
|
||||
// placeholder portals don't have a useful timestamp value - so the standard code that checks for updated
|
||||
// portal details doesn't apply
|
||||
// so, check that the basic details are valid and delete the existing portal if out of date
|
||||
if (guid in window.portals) {
|
||||
var p = window.portals[guid];
|
||||
if (team != p.options.data.team || latE6 != p.options.data.latE6 || lngE6 != p.options.data.lngE6) {
|
||||
// team or location have changed - delete existing portal
|
||||
this.deletePortalEntity(guid);
|
||||
}
|
||||
}
|
||||
|
||||
this.createPortalEntity(ent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
window.Render.prototype.createPortalEntity = function(ent) {
|
||||
@ -288,7 +306,7 @@ window.Render.prototype.createPortalEntity = function(ent) {
|
||||
this.deletePortalEntity(ent[0]);
|
||||
}
|
||||
|
||||
var portalLevel = parseInt(ent[2][4]);
|
||||
var portalLevel = parseInt(ent[2][4])||0;
|
||||
var team = teamStringToId(ent[2][1]);
|
||||
// the data returns unclaimed portals as level 1 - but IITC wants them treated as level 0
|
||||
if (team == TEAM_NONE) portalLevel = 0;
|
||||
@ -350,6 +368,18 @@ window.Render.prototype.createPortalEntity = function(ent) {
|
||||
window.Render.prototype.createFieldEntity = function(ent) {
|
||||
this.seenFieldsGuid[ent[0]] = true; // flag we've seen it
|
||||
|
||||
var data = {
|
||||
// type: ent[2][0],
|
||||
team: ent[2][1],
|
||||
points: ent[2][2].map(function(arr) { return {guid: arr[0], latE6: arr[1], lngE6: arr[2] }; })
|
||||
};
|
||||
|
||||
//create placeholder portals for field corners. we already do links, but there are the odd case where this is useful
|
||||
for (var i=0; i<3; i++) {
|
||||
var p=data.points[i];
|
||||
this.createPlaceholderPortalEntity(p.guid, p.latE6, p.lngE6, data.team);
|
||||
}
|
||||
|
||||
// check if entity already exists
|
||||
if(ent[0] in window.fields) {
|
||||
// yes. in theory, we should never get updated data for an existing field. they're created, and they're destroyed - never changed
|
||||
@ -364,12 +394,6 @@ window.Render.prototype.createFieldEntity = function(ent) {
|
||||
this.deleteFieldEntity(ent[0]); // option 2, for now
|
||||
}
|
||||
|
||||
var data = {
|
||||
// type: ent[2][0],
|
||||
team: ent[2][1],
|
||||
points: ent[2][2].map(function(arr) { return {guid: arr[0], latE6: arr[1], lngE6: arr[2] }; })
|
||||
};
|
||||
|
||||
var team = teamStringToId(ent[2][1]);
|
||||
var latlngs = [
|
||||
L.latLng(data.points[0].latE6/1E6, data.points[0].lngE6/1E6),
|
||||
@ -399,8 +423,31 @@ window.Render.prototype.createFieldEntity = function(ent) {
|
||||
}
|
||||
|
||||
window.Render.prototype.createLinkEntity = function(ent,faked) {
|
||||
// Niantic have been faking link entities, based on data from fields
|
||||
// these faked links are sent along with the real portal links, causing duplicates
|
||||
// the faked ones all have longer GUIDs, based on the field GUID (with _ab, _ac, _bc appended)
|
||||
var fakedLink = new RegExp("^[0-9a-f]{32}\.b_[ab][bc]$"); //field GUIDs always end with ".b" - faked links append the edge identifier
|
||||
if (fakedLink.test(ent[0])) return;
|
||||
|
||||
|
||||
this.seenLinksGuid[ent[0]] = true; // flag we've seen it
|
||||
|
||||
var data = { // TODO add other properties and check correction direction
|
||||
// type: ent[2][0],
|
||||
team: ent[2][1],
|
||||
oGuid: ent[2][2],
|
||||
oLatE6: ent[2][3],
|
||||
oLngE6: ent[2][4],
|
||||
dGuid: ent[2][5],
|
||||
dLatE6: ent[2][6],
|
||||
dLngE6: ent[2][7]
|
||||
};
|
||||
|
||||
// create placeholder entities for link start and end points (before checking if the link itself already exists
|
||||
this.createPlaceholderPortalEntity(data.oGuid, data.oLatE6, data.oLngE6, data.team);
|
||||
this.createPlaceholderPortalEntity(data.dGuid, data.dLatE6, data.dLngE6, data.team);
|
||||
|
||||
|
||||
// check if entity already exists
|
||||
if (ent[0] in window.links) {
|
||||
// yes. now, as sometimes links are 'faked', they have incomplete data. if the data we have is better, replace the data
|
||||
@ -415,17 +462,6 @@ window.Render.prototype.createLinkEntity = function(ent,faked) {
|
||||
this.deleteLinkEntity(ent[0]); // option 2 - for now
|
||||
}
|
||||
|
||||
var data = { // TODO add other properties and check correction direction
|
||||
// type: ent[2][0],
|
||||
team: ent[2][1],
|
||||
oGuid: ent[2][2],
|
||||
oLatE6: ent[2][3],
|
||||
oLngE6: ent[2][4],
|
||||
dGuid: ent[2][5],
|
||||
dLatE6: ent[2][6],
|
||||
dLngE6: ent[2][7]
|
||||
};
|
||||
|
||||
var team = teamStringToId(ent[2][1]);
|
||||
var latlngs = [
|
||||
L.latLng(data.oLatE6/1E6, data.oLngE6/1E6),
|
||||
@ -469,12 +505,12 @@ window.Render.prototype.rescalePortalMarkers = function() {
|
||||
|
||||
// add the portal to the visible map layer
|
||||
window.Render.prototype.addPortalToMapLayer = function(portal) {
|
||||
portalsFactionLayers[parseInt(portal.options.level)][portal.options.team].addLayer(portal);
|
||||
portalsFactionLayers[parseInt(portal.options.level)||0][portal.options.team].addLayer(portal);
|
||||
}
|
||||
|
||||
window.Render.prototype.removePortalFromMapLayer = function(portal) {
|
||||
//remove it from the portalsLevels layer
|
||||
portalsFactionLayers[parseInt(portal.options.level)][portal.options.team].removeLayer(portal);
|
||||
portalsFactionLayers[parseInt(portal.options.level)||0][portal.options.team].removeLayer(portal);
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,7 +57,7 @@ window.MapDataRequest = function() {
|
||||
// render queue
|
||||
// number of items to process in each render pass. there are pros and cons to smaller and larger values
|
||||
// (however, if using leaflet canvas rendering, it makes sense to push as much as possible through every time)
|
||||
this.RENDER_BATCH_SIZE = L.Path.CANVAS ? 1E9 : 500;
|
||||
this.RENDER_BATCH_SIZE = L.Path.CANVAS ? 1E9 : 1500;
|
||||
|
||||
// delay before repeating the render loop. this gives a better chance for user interaction
|
||||
this.RENDER_PAUSE = (typeof android === 'undefined') ? 0.1 : 0.2; //100ms desktop, 200ms mobile
|
||||
@ -69,6 +69,13 @@ window.MapDataRequest = function() {
|
||||
|
||||
// ensure we have some initial map status
|
||||
this.setStatus ('startup', undefined, -1);
|
||||
|
||||
// add a portalDetailLoaded hook, so we can use the exteneed details to update portals on the map
|
||||
var _this = this;
|
||||
addHook('portalDetailLoaded',function(data){
|
||||
_this.render.processGameEntities([data.ent]);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -232,8 +239,11 @@ window.MapDataRequest.prototype.refresh = function() {
|
||||
|
||||
this.render.startRenderPass(tileParams.level, dataBounds);
|
||||
|
||||
var _render = this.render;
|
||||
window.runHooks ('mapDataEntityInject', {callback: function(ents) { _render.processGameEntities(ents);}});
|
||||
|
||||
this.render.processGameEntities(artifact.getArtifactEntities(),true);
|
||||
|
||||
this.render.processGameEntities(artifact.getArtifactEntities());
|
||||
|
||||
var logMessage = 'requesting data tiles at zoom '+dataZoom;
|
||||
if (tileParams.level != tileParams.maxLevel) {
|
||||
|
@ -33,16 +33,18 @@ window.ornaments.addPortal = function(portal) {
|
||||
var size = window.ornaments.OVERLAY_SIZE;
|
||||
var latlng = portal.getLatLng();
|
||||
|
||||
window.ornaments._portals[guid] = portal.options.data.ornaments.map(function(ornament) {
|
||||
var icon = L.icon({
|
||||
iconUrl: "//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/"+ornament+".png",
|
||||
iconSize: [size, size],
|
||||
iconAnchor: [size/2,size/2],
|
||||
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
|
||||
});
|
||||
if (portal.options.data.ornaments) {
|
||||
window.ornaments._portals[guid] = portal.options.data.ornaments.map(function(ornament) {
|
||||
var icon = L.icon({
|
||||
iconUrl: "//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/"+ornament+".png",
|
||||
iconSize: [size, size],
|
||||
iconAnchor: [size/2,size/2],
|
||||
className: 'no-pointer-events' // the clickable: false below still blocks events going through to the svg underneath
|
||||
});
|
||||
|
||||
return L.marker(latlng, {icon: icon, clickable: false, keyboard: false, opacity: window.ornaments.OVERLAY_OPACITY }).addTo(window.ornaments._layer);
|
||||
});
|
||||
return L.marker(latlng, {icon: icon, clickable: false, keyboard: false, opacity: window.ornaments.OVERLAY_OPACITY }).addTo(window.ornaments._layer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.ornaments.removePortal = function(portal) {
|
||||
|
@ -37,8 +37,12 @@ var handleResponse = function(guid, data, success) {
|
||||
}
|
||||
|
||||
if (success) {
|
||||
|
||||
var dict = decodeArray.portalDetail(data.result);
|
||||
|
||||
// entity format, as used in map data
|
||||
var ent = [guid,dict.timestamp,data.result];
|
||||
|
||||
cache.store(guid,dict);
|
||||
|
||||
//FIXME..? better way of handling sidebar refreshing...
|
||||
@ -47,7 +51,7 @@ var handleResponse = function(guid, data, success) {
|
||||
renderPortalDetails(guid);
|
||||
}
|
||||
|
||||
window.runHooks ('portalDetailLoaded', {guid:guid, success:success, details:dict});
|
||||
window.runHooks ('portalDetailLoaded', {guid:guid, success:success, details:dict, ent:ent});
|
||||
|
||||
} else {
|
||||
if (data && data.error == "RETRY") {
|
||||
|
@ -41,46 +41,13 @@ window.renderPortalDetails = function(guid) {
|
||||
|
||||
|
||||
var img = fixPortalImageUrl(details ? details.image : data.image);
|
||||
var title = data.title;
|
||||
var title = (details && details.title) || (data && data.title) || '(untitled)';
|
||||
|
||||
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 imgTitle = title+'\n\nClick to show full image.';
|
||||
|
||||
var portalDetailedDescription = '';
|
||||
|
||||
if(portalDetailObj) {
|
||||
portalDetailedDescription = '<table description="Portal Photo Details" class="portal_details">';
|
||||
|
||||
// TODO (once the data supports it) - portals can have multiple photos. display all, with navigation between them
|
||||
// (at this time the data isn't returned from the server - although a count of images IS returned!)
|
||||
|
||||
if(portalDetailObj.submitter.name.length > 0) {
|
||||
if(portalDetailObj.submitter.team) {
|
||||
submitterSpan = '<span class="' + (portalDetailObj.submitter.team === 'RESISTANCE' ? 'res' : 'enl') + ' nickname">';
|
||||
} else {
|
||||
submitterSpan = '<span class="none">';
|
||||
}
|
||||
portalDetailedDescription += '<tr><th>Photo by:</th><td>' + submitterSpan
|
||||
+ escapeHtmlSpecialChars(portalDetailObj.submitter.name) + '</span>'+(portalDetailObj.submitter.voteCount !== undefined ? ' (' + portalDetailObj.submitter.voteCount + ' votes)' : '')+'</td></tr>';
|
||||
}
|
||||
if(portalDetailObj.submitter.link.length > 0) {
|
||||
portalDetailedDescription += '<tr><th>Photo from:</th><td><a href="'
|
||||
+ escapeHtmlSpecialChars(portalDetailObj.submitter.link) + '">' + escapeHtmlSpecialChars(portalDetailObj.submitter.link) + '</a></td></tr>';
|
||||
}
|
||||
|
||||
if(portalDetailObj.description) {
|
||||
portalDetailedDescription += '<tr class="padding-top"><th>Description:</th><td>' + escapeHtmlSpecialChars(portalDetailObj.description) + '</td></tr>';
|
||||
}
|
||||
// if(d.descriptiveText.map.ADDRESS) {
|
||||
// portalDetailedDescription += '<tr><th>Address:</th><td>' + escapeHtmlSpecialChars(d.descriptiveText.map.ADDRESS) + '</td></tr>';
|
||||
// }
|
||||
|
||||
portalDetailedDescription += '</table>';
|
||||
}
|
||||
|
||||
// portal level. start with basic data - then extend with fractional info in tooltip if available
|
||||
var levelInt = (teamStringToId(data.team) == TEAM_NONE) ? 0 : data.level;
|
||||
@ -127,7 +94,7 @@ window.renderPortalDetails = function(guid) {
|
||||
.html('') //to ensure it's clear
|
||||
.attr('class', TEAM_TO_CSS[teamStringToId(data.team)])
|
||||
.append(
|
||||
$('<h3>').attr({class:'title'}).text(data.title),
|
||||
$('<h3>').attr({class:'title'}).text(title),
|
||||
|
||||
$('<span>').attr({
|
||||
class: 'close',
|
||||
@ -141,7 +108,6 @@ window.renderPortalDetails = function(guid) {
|
||||
.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})
|
||||
),
|
||||
|
||||
@ -169,11 +135,15 @@ window.getPortalMiscDetails = function(guid,d) {
|
||||
|
||||
// collect some random data that’s not worth to put in an own method
|
||||
var linkInfo = getPortalLinks(guid);
|
||||
var maxOutgoing = getMaxOutgoingLinks(d);
|
||||
var linkCount = linkInfo.in.length + linkInfo.out.length;
|
||||
var links = {incoming: linkInfo.in.length, outgoing: linkInfo.out.length};
|
||||
|
||||
function linkExpl(t) { return '<tt title="'+links.outgoing+' links out (8 max)\n'+links.incoming+' links in\n('+(links.outgoing+links.incoming)+' total)">'+t+'</tt>'; }
|
||||
var linksText = [linkExpl('links'), linkExpl(links.outgoing+' out / '+links.incoming+' in')];
|
||||
var title = 'at most ' + maxOutgoing + ' outgoing links\n' +
|
||||
links.outgoing + ' links out\n' +
|
||||
links.incoming + ' links in\n' +
|
||||
'(' + (links.outgoing+links.incoming) + ' total)'
|
||||
var linksText = ['links', links.outgoing+' out / '+links.incoming+' in', title];
|
||||
|
||||
var player = d.owner
|
||||
? '<span class="nickname">' + d.owner + '</span>'
|
||||
@ -214,32 +184,23 @@ window.getPortalMiscDetails = function(guid,d) {
|
||||
'<span title="force amplifier" class="text-overflow-ellipsis">force amplifier</span>',
|
||||
'×'+attackValues.force_amplifier]);
|
||||
|
||||
// artifact details
|
||||
|
||||
// 2014-02-06: stock site changed from supporting 'jarvis shards' to 'amar artifacts'(?) - so let's see what we can do to be generic...
|
||||
$.each(artifact.getArtifactTypes(),function(index,type) {
|
||||
var artdata = artifact.getPortalData (guid, type);
|
||||
if (artdata) {
|
||||
var details = artifact.getArtifactDescriptions(type);
|
||||
if (details) {
|
||||
// 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 = [details.fragmentName,'(none)'];
|
||||
if (artdata.target) {
|
||||
target = ['target', '<span class="'+TEAM_TO_CSS[artdata.target]+'">'+(artdata.target==TEAM_RES?'Resistance':'Enlightened')+'</span>'];
|
||||
}
|
||||
if (artdata.fragments) {
|
||||
shards = [details.fragmentName, '#'+artdata.fragments.join(', #')];
|
||||
}
|
||||
|
||||
randDetailsData.push (target, shards);
|
||||
} else {
|
||||
console.warn('Unknown artifact type '+type+': no names, so cannot display');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
randDetails = '<table id="randdetails">' + genFourColumnTable(randDetailsData) + '</table>';
|
||||
|
||||
|
||||
// artifacts - tacked on after (but not as part of) the 'randdetails' table
|
||||
// instead of using the existing columns....
|
||||
|
||||
if (d.artifactBrief && d.artifactBrief.target && Object.keys(d.artifactBrief.target).length > 0) {
|
||||
var targets = Object.keys(d.artifactBrief.target);
|
||||
//currently (2015-07-10) we no longer know the team each target portal is for - so we'll just show the artifact type(s)
|
||||
randDetails += '<div id="artifact_target">Target portal: '+targets.map(function(x) { return x.capitalize(); }).join(', ')+'</div>';
|
||||
}
|
||||
|
||||
// shards - taken directly from the portal details
|
||||
if (d.artifactDetail) {
|
||||
randDetails += '<div id="artifact_fragments">Shards: '+d.artifactDetail.displayName+' #'+d.artifactDetail.fragments.join(', ')+'</div>';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return randDetails;
|
||||
|
@ -12,67 +12,15 @@ window.getRangeText = function(d) {
|
||||
|
||||
if(!range.isLinkable) title += '\nPortal is missing resonators,\nno new links can be made';
|
||||
|
||||
return ['<span title="' + title + '">range</span>',
|
||||
return ['range',
|
||||
'<a onclick="window.rangeLinkClick()"'
|
||||
+ (range.isLinkable ? '' : ' style="text-decoration:line-through;"')
|
||||
+ ' title="'+title+'">'
|
||||
+ '>'
|
||||
+ (range.range > 1000
|
||||
? Math.floor(range.range/1000) + ' km'
|
||||
: Math.floor(range.range) + ' m')
|
||||
+ '</a>'];
|
||||
}
|
||||
|
||||
// generates description text from details for portal
|
||||
window.getPortalDescriptionFromDetails = function(details) {
|
||||
return details.title || '(untitled)';
|
||||
|
||||
// var descObj = details.descriptiveText.map;
|
||||
// // FIXME: also get real description?
|
||||
// var desc = descObj.TITLE;
|
||||
// if(descObj.ADDRESS)
|
||||
// desc += '\n' + descObj.ADDRESS;
|
||||
//// if(descObj.ATTRIBUTION)
|
||||
//// desc += '\nby '+descObj.ATTRIBUTION+' ('+descObj.ATTRIBUTION_LINK+')';
|
||||
// return desc;
|
||||
}
|
||||
|
||||
// Grabs more info, including the submitter name for the current main
|
||||
// portal image
|
||||
window.getPortalDescriptionFromDetailsExtended = function(details) {
|
||||
var descObj = details.title;
|
||||
var photoStreamObj = details.photoStreamInfo;
|
||||
|
||||
var submitterObj = new Object();
|
||||
submitterObj.type = "";
|
||||
submitterObj.name = "";
|
||||
submitterObj.team = "";
|
||||
submitterObj.link = "";
|
||||
submitterObj.voteCount = undefined;
|
||||
|
||||
if(photoStreamObj && photoStreamObj.hasOwnProperty("coverPhoto") && photoStreamObj.coverPhoto.hasOwnProperty("attributionMarkup")) {
|
||||
submitterObj.name = "Unknown";
|
||||
|
||||
var attribution = photoStreamObj.coverPhoto.attributionMarkup;
|
||||
submitterObj.type = attribution[0];
|
||||
if(attribution[1].hasOwnProperty("plain"))
|
||||
submitterObj.name = attribution[1].plain;
|
||||
if(attribution[1].hasOwnProperty("team"))
|
||||
submitterObj.team = attribution[1].team;
|
||||
if(attribution[1].hasOwnProperty("attributionLink"))
|
||||
submitterObj.link = attribution[1].attributionLink;
|
||||
if(photoStreamObj.coverPhoto.hasOwnProperty("voteCount"))
|
||||
submitterObj.voteCount = photoStreamObj.coverPhoto.voteCount;
|
||||
}
|
||||
|
||||
|
||||
var portalDetails = {
|
||||
title: descObj.TITLE,
|
||||
description: descObj.DESCRIPTION,
|
||||
address: descObj.ADDRESS,
|
||||
submitter: submitterObj
|
||||
};
|
||||
|
||||
return portalDetails;
|
||||
+ '</a>',
|
||||
title];
|
||||
}
|
||||
|
||||
|
||||
@ -115,6 +63,7 @@ window.getModDetails = function(d) {
|
||||
else if (key === 'ATTACK_FREQUENCY') val = (val/1000) +'x'; // 2000 = 2x
|
||||
else if (key === 'FORCE_AMPLIFIER') val = (val/1000) +'x'; // 2000 = 2x
|
||||
else if (key === 'LINK_RANGE_MULTIPLIER') val = (val/1000) +'x'; // 2000 = 2x
|
||||
else if (key === 'LINK_DEFENSE_BOOST') val = (val/1000) +'x'; // 1500 = 1.5x
|
||||
else if (key === 'REMOVAL_STICKINESS' && val > 100) val = (val/10000)+'%'; // an educated guess
|
||||
// else display unmodified. correct for shield mitigation and multihack - unknown for future/other mods
|
||||
|
||||
@ -150,9 +99,9 @@ window.getModDetails = function(d) {
|
||||
window.getEnergyText = function(d) {
|
||||
var currentNrg = getCurrentPortalEnergy(d);
|
||||
var totalNrg = getTotalPortalEnergy(d);
|
||||
var inf = currentNrg + ' / ' + totalNrg;
|
||||
var title = currentNrg + ' / ' + totalNrg;
|
||||
var fill = prettyEnergy(currentNrg) + ' / ' + prettyEnergy(totalNrg)
|
||||
return ['energy', '<tt title="'+inf+'">' + fill + '</tt>'];
|
||||
return ['energy', fill, title];
|
||||
}
|
||||
|
||||
|
||||
@ -236,22 +185,19 @@ window.getAttackApGainText = function(d,fieldCount,linkCount) {
|
||||
var breakdown = getAttackApGain(d,fieldCount,linkCount);
|
||||
var totalGain = breakdown.enemyAp;
|
||||
|
||||
function tt(text) {
|
||||
var t = '';
|
||||
if (teamStringToId(PLAYER.team) == teamStringToId(d.team)) {
|
||||
totalGain = breakdown.friendlyAp;
|
||||
t += 'Friendly AP:\t' + breakdown.friendlyAp + '\n';
|
||||
t += ' Deploy ' + breakdown.deployCount + ', ';
|
||||
t += 'Upgrade ' + breakdown.upgradeCount + '\n';
|
||||
t += '\n';
|
||||
}
|
||||
t += 'Enemy AP:\t' + breakdown.enemyAp + '\n';
|
||||
t += ' Destroy AP:\t' + breakdown.destroyAp + '\n';
|
||||
t += ' Capture AP:\t' + breakdown.captureAp + '\n';
|
||||
return '<tt title="' + t + '">' + text + '</tt>';
|
||||
var t = '';
|
||||
if (teamStringToId(PLAYER.team) == teamStringToId(d.team)) {
|
||||
totalGain = breakdown.friendlyAp;
|
||||
t += 'Friendly AP:\t' + breakdown.friendlyAp + '\n';
|
||||
t += ' Deploy ' + breakdown.deployCount + ', ';
|
||||
t += 'Upgrade ' + breakdown.upgradeCount + '\n';
|
||||
t += '\n';
|
||||
}
|
||||
t += 'Enemy AP:\t' + breakdown.enemyAp + '\n';
|
||||
t += ' Destroy AP:\t' + breakdown.destroyAp + '\n';
|
||||
t += ' Capture AP:\t' + breakdown.captureAp + '\n';
|
||||
|
||||
return [tt('AP Gain'), tt(digits(totalGain))];
|
||||
return ['AP Gain', digits(totalGain), t];
|
||||
}
|
||||
|
||||
|
||||
@ -260,16 +206,12 @@ window.getHackDetailsText = function(d) {
|
||||
|
||||
var shortHackInfo = hackDetails.hacks+' @ '+formatInterval(hackDetails.cooldown);
|
||||
|
||||
function tt(text) {
|
||||
var t = 'Hacks available every 4 hours\n';
|
||||
t += 'Hack count:\t'+hackDetails.hacks+'\n';
|
||||
t += 'Cooldown time:\t'+formatInterval(hackDetails.cooldown)+'\n';
|
||||
t += 'Burnout time:\t'+formatInterval(hackDetails.burnout)+'\n';
|
||||
var title = 'Hacks available every 4 hours\n'
|
||||
+ 'Hack count:\t'+hackDetails.hacks+'\n'
|
||||
+ 'Cooldown time:\t'+formatInterval(hackDetails.cooldown)+'\n'
|
||||
+ 'Burnout time:\t'+formatInterval(hackDetails.burnout);
|
||||
|
||||
return '<span title="'+t+'">'+text+'</span>';
|
||||
}
|
||||
|
||||
return [tt('hacks'), tt(shortHackInfo)];
|
||||
return ['hacks', shortHackInfo, title];
|
||||
}
|
||||
|
||||
|
||||
@ -279,16 +221,12 @@ window.getMitigationText = function(d,linkCount) {
|
||||
var mitigationShort = mitigationDetails.total;
|
||||
if (mitigationDetails.excess) mitigationShort += ' (+'+mitigationDetails.excess+')';
|
||||
|
||||
function tt(text) {
|
||||
var t = 'Total shielding:\t'+(mitigationDetails.shields+mitigationDetails.links)+'\n'
|
||||
+ '- active:\t'+mitigationDetails.total+'\n'
|
||||
+ '- excess:\t'+mitigationDetails.excess+'\n'
|
||||
+ 'From\n'
|
||||
+ '- shields:\t'+mitigationDetails.shields+'\n'
|
||||
+ '- links:\t'+mitigationDetails.links;
|
||||
var title = 'Total shielding:\t'+(mitigationDetails.shields+mitigationDetails.links)+'\n'
|
||||
+ '- active:\t'+mitigationDetails.total+'\n'
|
||||
+ '- excess:\t'+mitigationDetails.excess+'\n'
|
||||
+ 'From\n'
|
||||
+ '- shields:\t'+mitigationDetails.shields+'\n'
|
||||
+ '- links:\t'+mitigationDetails.links;
|
||||
|
||||
return '<span title="'+t+'">'+text+'</span>';
|
||||
}
|
||||
|
||||
return [tt('shielding'), tt(mitigationShort)];
|
||||
return ['shielding', mitigationShort, title];
|
||||
}
|
||||
|
@ -207,7 +207,8 @@ window.getPortalModsByType = function(d, type) {
|
||||
TURRET: 'HIT_BONUS', // and/or ATTACK_FREQUENCY??
|
||||
HEATSINK: 'HACK_SPEED',
|
||||
MULTIHACK: 'BURNOUT_INSULATION',
|
||||
LINK_AMPLIFIER: 'LINK_RANGE_MULTIPLIER'
|
||||
LINK_AMPLIFIER: 'LINK_RANGE_MULTIPLIER',
|
||||
ULTRA_LINK_AMP: 'OUTGOING_LINKS_BONUS', // and/or LINK_DEFENSE_BOOST??
|
||||
};
|
||||
|
||||
var stat = typeToStat[type];
|
||||
@ -257,6 +258,17 @@ window.getPortalMitigationDetails = function(d,linkCount) {
|
||||
return mitigation;
|
||||
}
|
||||
|
||||
window.getMaxOutgoingLinks = function(d) {
|
||||
var linkAmps = getPortalModsByType(d, 'ULTRA_LINK_AMP');
|
||||
|
||||
var links = 8;
|
||||
|
||||
linkAmps.forEach(function(mod, i) {
|
||||
links += parseInt(mod.stats.OUTGOING_LINKS_BONUS);
|
||||
});
|
||||
|
||||
return links;
|
||||
};
|
||||
|
||||
window.getPortalHackDetails = function(d) {
|
||||
|
||||
|
@ -47,11 +47,18 @@ window.getMarkerStyleOptions = function(details) {
|
||||
var LEVEL_TO_WEIGHT = [2, 2, 2, 2, 2, 3, 3, 4, 4];
|
||||
var LEVEL_TO_RADIUS = [7, 7, 7, 7, 8, 8, 9,10,11];
|
||||
|
||||
var level = Math.floor(details.level);
|
||||
var level = Math.floor(details.level||0);
|
||||
|
||||
var lvlWeight = LEVEL_TO_WEIGHT[level] * Math.sqrt(scale);
|
||||
var lvlRadius = LEVEL_TO_RADIUS[level] * scale;
|
||||
|
||||
var dashArray = null;
|
||||
// thinner and dashed outline for placeholder portals
|
||||
if (details.team != TEAM_NONE && level==0) {
|
||||
lvlWeight = 1;
|
||||
dashArray = [1,2];
|
||||
}
|
||||
|
||||
var options = {
|
||||
radius: lvlRadius,
|
||||
stroke: true,
|
||||
@ -61,7 +68,7 @@ window.getMarkerStyleOptions = function(details) {
|
||||
fill: true,
|
||||
fillColor: COLORS[details.team],
|
||||
fillOpacity: 0.5,
|
||||
dashArray: null
|
||||
dashArray: dashArray
|
||||
};
|
||||
|
||||
return options;
|
||||
|
@ -225,6 +225,8 @@ addHook('search', function(query) {
|
||||
|
||||
$.each(portals, function(guid, portal) {
|
||||
var data = portal.options.data;
|
||||
if(!data.title) return;
|
||||
|
||||
if(data.title.toLowerCase().indexOf(term) !== -1) {
|
||||
var team = portal.options.team;
|
||||
var color = team==TEAM_NONE ? '#CCC' : COLORS[team];
|
||||
|
@ -34,7 +34,6 @@ window.postAjax = function(action, data, successCallback, errorCallback) {
|
||||
|
||||
var onSuccess = function(data, textStatus, jqXHR) {
|
||||
window.requests.remove(jqXHR);
|
||||
iitc_bg.process_response_params(action,data);
|
||||
|
||||
// the Niantic server can return a HTTP success, but the JSON response contains an error. handle that sensibly
|
||||
if (data && data.error && data.error == 'out of date') {
|
||||
@ -65,7 +64,7 @@ window.postAjax = function(action, data, successCallback, errorCallback) {
|
||||
}
|
||||
|
||||
var versionStr = niantic_params.CURRENT_VERSION;
|
||||
var post_data = JSON.stringify($.extend({}, data, {v: versionStr}, iitc_bg.extra_request_params(action)));
|
||||
var post_data = JSON.stringify($.extend({}, data, {v: versionStr}));
|
||||
|
||||
var result = $.ajax({
|
||||
url: '/r/'+action,
|
||||
@ -96,7 +95,7 @@ window.outOfDateUserPrompt = function()
|
||||
|
||||
dialog({
|
||||
title: 'Reload IITC',
|
||||
html: '<p>IITC is using an outdated version code. This will happen when Niantic update the standard intel site.</p>'
|
||||
html: '<p>IITC is using an outdated version code. This will happen when Niantic updates the standard intel site.</p>'
|
||||
+'<p>You need to reload the page to get the updated changes.</p>'
|
||||
+'<p>If you have just reloaded the page, then an old version of the standard site script is cached somewhere.'
|
||||
+'In this case, try clearing your cache, or waiting 15-30 minutes for the stale data to expire.</p>',
|
||||
|
@ -7,15 +7,32 @@ window.renderUpdateStatusTimer_ = undefined;
|
||||
window.renderUpdateStatus = function() {
|
||||
var progress = 1;
|
||||
|
||||
// portal level display
|
||||
var t = '<span class="help portallevel" title="Indicates portal levels displayed. Zoom in to display lower level portals.">';
|
||||
if(!window.isSmartphone()) // space is valuable
|
||||
t += '<b>portals</b>: ';
|
||||
var minlvl = getMinPortalLevel();
|
||||
if(minlvl === 0)
|
||||
t+= '<span id="loadlevel">all</span>';
|
||||
else
|
||||
t+= '<span id="loadlevel" style="background:'+COLORS_LVL[minlvl]+'">L'+minlvl+(minlvl<8?'+':'') + '</span>';
|
||||
// portal/limk level display
|
||||
|
||||
var zoom = map.getZoom();
|
||||
zoom = getDataZoomForMapZoom(zoom);
|
||||
var tileParams = getMapZoomTileParameters(zoom);
|
||||
|
||||
var t = '<span class="help portallevel" title="Indicates portal levels/link lengths displayed. Zoom in to display more.">';
|
||||
|
||||
if (tileParams.hasPortals) {
|
||||
// zoom level includes portals (and also all links/fields)
|
||||
if(!window.isSmartphone()) // space is valuable
|
||||
t += '<b>portals</b>: ';
|
||||
if(tileParams.level === 0)
|
||||
t += '<span id="loadlevel">all</span>';
|
||||
else
|
||||
t += '<span id="loadlevel" style="background:'+COLORS_LVL[tileParams.level]+'">L'+tileParams.level+(tileParams.level<8?'+':'') + '</span>';
|
||||
} else {
|
||||
if(!window.isSmartphone()) // space is valuable
|
||||
t += '<b>links</b>: ';
|
||||
|
||||
if (tileParams.minLinkLength > 0)
|
||||
t += '<span id="loadlevel">>'+(tileParams.minLinkLength>1000?tileParams.minLinkLength/1000+'km':tileParams.minLinkLength+'m')+'</span>';
|
||||
else
|
||||
t += '<span id="loadlevel">all links</span>';
|
||||
}
|
||||
|
||||
t +='</span>';
|
||||
|
||||
|
||||
|
@ -335,10 +335,11 @@ window.uniqueArray = function(arr) {
|
||||
window.genFourColumnTable = function(blocks) {
|
||||
var t = $.map(blocks, function(detail, index) {
|
||||
if(!detail) return '';
|
||||
var title = detail[2] ? ' title="'+escapeHtmlSpecialChars(detail[2]) + '"' : '';
|
||||
if(index % 2 === 0)
|
||||
return '<tr><td>'+detail[1]+'</td><th>'+detail[0]+'</th>';
|
||||
return '<tr><td'+title+'>'+detail[1]+'</td><th'+title+'>'+detail[0]+'</th>';
|
||||
else
|
||||
return ' <th>'+detail[0]+'</th><td>'+detail[1]+'</td></tr>';
|
||||
return ' <th'+title+'>'+detail[0]+'</th><td'+title+'>'+detail[1]+'</td></tr>';
|
||||
}).join('');
|
||||
if(t.length % 2 === 1) t + '<td></td><td></td></tr>';
|
||||
return t;
|
||||
@ -413,6 +414,16 @@ window.addLayerGroup = function(name, layerGroup, defaultDisplay) {
|
||||
layerChooser.addOverlay(layerGroup, name);
|
||||
}
|
||||
|
||||
window.removeLayerGroup = function(layerGroup) {
|
||||
if(!layerChooser._layers[layerGroup._leaflet_id]) throw('Layer was not found');
|
||||
// removing the layer will set it's default visibility to false (store if layer gets added again)
|
||||
var name = layerChooser._layers[layerGroup._leaflet_id].name;
|
||||
var enabled = isLayerGroupDisplayed(name);
|
||||
map.removeLayer(layerGroup);
|
||||
layerChooser.removeLayer(layerGroup);
|
||||
updateDisplayedLayerGroup(name, enabled);
|
||||
};
|
||||
|
||||
window.clampLat = function(lat) {
|
||||
// the map projection used does not handle above approx +- 85 degrees north/south of the equator
|
||||
if (lat > 85.051128)
|
||||
|
2
external/s2geometry.js
vendored
2
external/s2geometry.js
vendored
@ -212,8 +212,6 @@ S2.S2Cell.FromLatLng = function(latLng,level) {
|
||||
var ij = STToIJ(st,level);
|
||||
|
||||
return S2.S2Cell.FromFaceIJ (faceuv[0], ij, level);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
S2.S2Cell.FromFaceIJ = function(face,ij,level) {
|
||||
|
24
main.js
24
main.js
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @id ingress-intel-total-conversion@jonatkins
|
||||
// @name IITC: Ingress intel map total conversion
|
||||
// @version 0.22.4.@@DATETIMEVERSION@@
|
||||
// @version 0.25.2.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -27,18 +27,11 @@ window.iitcBuildDate = '@@BUILDDATE@@';
|
||||
window.onload = function() {};
|
||||
document.body.onload = function() {};
|
||||
|
||||
// rescue user data from original page
|
||||
var scr = document.getElementsByTagName('script');
|
||||
for(var x in scr) {
|
||||
var s = scr[x];
|
||||
if(s.src) continue;
|
||||
if(s.type !== 'text/javascript') continue;
|
||||
var d = s.innerHTML.split('\n');
|
||||
break;
|
||||
}
|
||||
|
||||
//originally code here parsed the <Script> tags from the page to find the one that defined the PLAYER object
|
||||
//however, that's already been executed, so we can just access PLAYER - no messing around needed!
|
||||
|
||||
if(!d) {
|
||||
if (typeof(window.PLAYER)!="object" || typeof(window.PLAYER.nickname) != "string") {
|
||||
// page doesn’t have a script tag with player information.
|
||||
if(document.getElementById('header_email')) {
|
||||
// however, we are logged in.
|
||||
@ -53,11 +46,6 @@ if(!d) {
|
||||
}
|
||||
|
||||
|
||||
for(var i = 0; i < d.length; i++) {
|
||||
if(!d[i].match('var PLAYER = ')) continue;
|
||||
eval(d[i].match(/^var /, 'window.'));
|
||||
break;
|
||||
}
|
||||
// player information is now available in a hash like this:
|
||||
// window.PLAYER = {"ap": "123", "energy": 123, "available_invites": 123, "nickname": "somenick", "team": "ENLIGHTENED||RESISTANCE"};
|
||||
|
||||
@ -168,8 +156,8 @@ window.RANGE_INDICATOR_COLOR = 'red'
|
||||
window.MIN_ZOOM = 3;
|
||||
|
||||
window.DEFAULT_PORTAL_IMG = '//commondatastorage.googleapis.com/ingress.com/img/default-portal-image.png';
|
||||
//window.NOMINATIM = '//nominatim.openstreetmap.org/search?format=json&limit=1&q=';
|
||||
window.NOMINATIM = '//open.mapquestapi.com/nominatim/v1/search.php?format=json&polygon_geojson=1&q=';
|
||||
//window.NOMINATIM = '//open.mapquestapi.com/nominatim/v1/search.php?format=json&polygon_geojson=1&q=';
|
||||
window.NOMINATIM = '//nominatim.openstreetmap.org/search?format=json&polygon_geojson=1&q=';
|
||||
|
||||
// INGRESS CONSTANTS /////////////////////////////////////////////////
|
||||
// http://decodeingress.me/2012/11/18/ingress-portal-levels-and-link-range/
|
||||
|
@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.cradle.iitc_mobile"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="97"
|
||||
android:versionName="0.22.4">
|
||||
android:versionCode="104"
|
||||
android:versionName="0.25.2">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
|
@ -23,13 +23,13 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
private class HighlighterAdapter extends ArrayAdapter<String> {
|
||||
private final HighlighterComparator mComparator = new HighlighterComparator();
|
||||
|
||||
private HighlighterAdapter(int resource) {
|
||||
private HighlighterAdapter(final int resource) {
|
||||
super(mIitc, resource);
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String object) {
|
||||
public void add(final String object) {
|
||||
super.remove(object); // to avoid duplicates
|
||||
super.add(object);
|
||||
super.sort(mComparator);
|
||||
@ -44,7 +44,7 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
|
||||
private class HighlighterComparator implements Comparator<String> {
|
||||
@Override
|
||||
public int compare(String lhs, String rhs) {
|
||||
public int compare(final String lhs, final String rhs) {
|
||||
// Move "No Highlights" on top. Sort the rest alphabetically
|
||||
if (lhs.equals("No Highlights")) {
|
||||
return -1000;
|
||||
@ -68,14 +68,14 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
}
|
||||
|
||||
private class LayerAdapter extends ArrayAdapter<Layer> {
|
||||
public LayerAdapter(int resource) {
|
||||
public LayerAdapter(final int resource) {
|
||||
super(mIitc, resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
Layer item = getItem(position);
|
||||
View view = super.getView(position, convertView, parent);
|
||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
||||
final Layer item = getItem(position);
|
||||
final View view = super.getView(position, convertView, parent);
|
||||
|
||||
if (view instanceof CheckedTextView) {
|
||||
((CheckedTextView) view).setChecked(item.active);
|
||||
@ -98,8 +98,9 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
private int mActiveLayer;
|
||||
|
||||
private boolean mLoading = true;
|
||||
private boolean mDisableListeners = false;
|
||||
|
||||
public IITC_MapSettings(IITC_Mobile activity) {
|
||||
public IITC_MapSettings(final IITC_Mobile activity) {
|
||||
mIitc = activity;
|
||||
|
||||
mHighlighters = new HighlighterAdapter(R.layout.list_item_narrow);
|
||||
@ -109,8 +110,8 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
mHighlighters.setDropDownViewResource(R.layout.list_item_selectable);
|
||||
mBaseLayers.setDropDownViewResource(R.layout.list_item_selectable);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) mIitc.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View header = inflater.inflate(R.layout.map_options_header, null);
|
||||
final LayoutInflater inflater = (LayoutInflater) mIitc.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
final View header = inflater.inflate(R.layout.map_options_header, null);
|
||||
|
||||
mSpinnerHighlighter = (Spinner) header.findViewById(R.id.spinnerHighlighter);
|
||||
mSpinnerBaseMap = (Spinner) header.findViewById(R.id.spinnerBaseLayer);
|
||||
@ -128,14 +129,14 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
mListViewOverlayLayers.setOnItemLongClickListener(this);
|
||||
}
|
||||
|
||||
private void setLayer(Layer layer) {
|
||||
private void setLayer(final Layer layer) {
|
||||
if (!mLoading) {
|
||||
mIitc.getWebView().loadUrl(
|
||||
"javascript: window.layerChooser.showLayer(" + layer.id + "," + layer.active + ");");
|
||||
}
|
||||
}
|
||||
|
||||
public void addPortalHighlighter(String name) {
|
||||
public void addPortalHighlighter(final String name) {
|
||||
mHighlighters.add(name);
|
||||
|
||||
// to select active highlighter. must be called every time because of sorting
|
||||
@ -144,26 +145,27 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
|
||||
public void onBootFinished() {
|
||||
mLoading = false;
|
||||
updateLayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
public void onItemClick(final AdapterView<?> parent, final View view, int position, final long id) {
|
||||
if (mDisableListeners) return;
|
||||
position--; // The ListView header counts as an item as well.
|
||||
|
||||
Layer item = mOverlayLayers.getItem(position);
|
||||
final Layer item = mOverlayLayers.getItem(position);
|
||||
item.active = !item.active;
|
||||
setLayer(item);
|
||||
mOverlayLayers.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
public boolean onItemLongClick(final AdapterView<?> parent, final View view, int position, final long id) {
|
||||
if (mDisableListeners) return false;
|
||||
position--; // The ListView header counts as an item as well.
|
||||
boolean active = !mOverlayLayers.getItem(position).active;
|
||||
final boolean active = !mOverlayLayers.getItem(position).active;
|
||||
|
||||
for (int i = 0; i < mOverlayLayers.getCount(); i++) {
|
||||
Layer item = mOverlayLayers.getItem(i);
|
||||
final Layer item = mOverlayLayers.getItem(i);
|
||||
if (item.name.contains("DEBUG")) continue;
|
||||
if (active == item.active) continue; // no need to set same value again
|
||||
item.active = active;
|
||||
@ -176,23 +178,24 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
public void onItemSelected(final AdapterView<?> parent, final View view, final int position, final long id) {
|
||||
if (mLoading) return;
|
||||
if (mDisableListeners) return;
|
||||
|
||||
if (parent.equals(mSpinnerHighlighter)) {
|
||||
String name = mHighlighters.getItem(position);
|
||||
final String name = mHighlighters.getItem(position);
|
||||
mIitc.getWebView().loadUrl("javascript: window.changePortalHighlights('" + name + "')");
|
||||
} else if (parent.equals(mSpinnerBaseMap)) {
|
||||
mBaseLayers.getItem(mActiveLayer).active = false; // set old layer to hidden, but no need to really hide
|
||||
|
||||
Layer layer = mBaseLayers.getItem(position);
|
||||
final Layer layer = mBaseLayers.getItem(position);
|
||||
layer.active = true;
|
||||
setLayer(layer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
public void onNothingSelected(final AdapterView<?> parent) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@ -206,10 +209,10 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
mLoading = true;
|
||||
}
|
||||
|
||||
public void setActiveHighlighter(String name) {
|
||||
public void setActiveHighlighter(final String name) {
|
||||
mActiveHighlighter = name;
|
||||
|
||||
int position = mHighlighters.getPosition(mActiveHighlighter);
|
||||
final int position = mHighlighters.getPosition(mActiveHighlighter);
|
||||
if (position >= 0 && position < mHighlighters.getCount()) {
|
||||
mSpinnerHighlighter.setSelection(position);
|
||||
}
|
||||
@ -217,7 +220,7 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
mIitc.getNavigationHelper().setHighlighter(name);
|
||||
}
|
||||
|
||||
public void setLayers(String base_layer, String overlay_layer) {
|
||||
public void setLayers(final String base_layer, final String overlay_layer) {
|
||||
/*
|
||||
* the layer strings have a form like:
|
||||
* [{"layerId":27,"name":"MapQuest OSM","active":true},
|
||||
@ -230,18 +233,20 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
try {
|
||||
base_layers = new JSONArray(base_layer);
|
||||
overlay_layers = new JSONArray(overlay_layer);
|
||||
} catch (JSONException e) {
|
||||
} catch (final JSONException e) {
|
||||
Log.w(e);
|
||||
return;
|
||||
}
|
||||
|
||||
mDisableListeners = true;
|
||||
|
||||
mActiveLayer = 0;
|
||||
mBaseLayers.setNotifyOnChange(false);
|
||||
mBaseLayers.clear();
|
||||
for (int i = 0; i < base_layers.length(); i++) {
|
||||
try {
|
||||
JSONObject layerObj = base_layers.getJSONObject(i);
|
||||
Layer layer = new Layer();
|
||||
final JSONObject layerObj = base_layers.getJSONObject(i);
|
||||
final Layer layer = new Layer();
|
||||
|
||||
layer.id = layerObj.getInt("layerId");
|
||||
layer.name = layerObj.getString("name");
|
||||
@ -254,7 +259,7 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
}
|
||||
|
||||
mBaseLayers.add(layer);
|
||||
} catch (JSONException e) {
|
||||
} catch (final JSONException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
@ -265,24 +270,20 @@ public class IITC_MapSettings implements OnItemSelectedListener, OnItemClickList
|
||||
mOverlayLayers.clear();
|
||||
for (int i = 0; i < overlay_layers.length(); i++) {
|
||||
try {
|
||||
JSONObject layerObj = overlay_layers.getJSONObject(i);
|
||||
Layer layer = new Layer();
|
||||
final JSONObject layerObj = overlay_layers.getJSONObject(i);
|
||||
final Layer layer = new Layer();
|
||||
|
||||
layer.id = layerObj.getInt("layerId");
|
||||
layer.name = layerObj.getString("name");
|
||||
layer.active = layerObj.getBoolean("active");
|
||||
|
||||
mOverlayLayers.add(layer);
|
||||
} catch (JSONException e) {
|
||||
} catch (final JSONException e) {
|
||||
Log.w(e);
|
||||
}
|
||||
}
|
||||
mOverlayLayers.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void updateLayers() {
|
||||
if (!mLoading) {
|
||||
mIitc.getWebView().loadUrl("javascript: window.layerChooser.getLayers()");
|
||||
}
|
||||
mDisableListeners = false;
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +116,16 @@ public class IITC_Mobile extends Activity
|
||||
mViewDebug = findViewById(R.id.viewDebug);
|
||||
mBtnToggleMap = (ImageButton) findViewById(R.id.btnToggleMapVisibility);
|
||||
mEditCommand = (EditText) findViewById(R.id.editCommand);
|
||||
mEditCommand.setOnKeyListener(new View.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(final View v, final int keyCode, final KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && event.isCtrlPressed()) {
|
||||
onBtnRunCodeClick(v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
mEditCommand.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(final TextView v, final int actionId, final KeyEvent event) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-basemap-opencyclepam@jonatkins
|
||||
// @name IITC plugin: OpenCycleMap.org map tiles
|
||||
// @category Map Tiles
|
||||
// @version 0.1.1.@@DATETIMEVERSION@@
|
||||
// @version 0.2.0.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -24,21 +24,29 @@
|
||||
|
||||
|
||||
// use own namespace for plugin
|
||||
window.plugin.mapTileOpenCycleMap = function() {};
|
||||
window.plugin.mapTileOpenCycleMap = {
|
||||
addLayer: function() {
|
||||
//the Thunderforest (OpenCycleMap) tiles are free to use - http://www.thunderforest.com/terms/
|
||||
|
||||
window.plugin.mapTileOpenCycleMap.addLayer = function() {
|
||||
var ocmOpt = {
|
||||
attribution: 'Tiles © OpenCycleMap, Map data © OpenStreetMap',
|
||||
maxNativeZoom: 18,
|
||||
maxZoom: 21,
|
||||
};
|
||||
|
||||
//the Thunderforest (OpenCycleMap) tiles are free to use - http://www.thunderforest.com/terms/
|
||||
var layers = {
|
||||
'cycle': 'OpenCycleMap',
|
||||
'transport': 'Transport',
|
||||
'transport-dark': 'Transport Dark',
|
||||
'outdoors': 'Outdoors',
|
||||
'landscape': 'Landscape',
|
||||
};
|
||||
|
||||
osmAttribution = 'Map data © OpenStreetMap';
|
||||
var ocmOpt = {attribution: 'Tiles © OpenCycleMap, '+osmAttribution, maxNativeZoom: 18, maxZoom: 21};
|
||||
var ocmCycle = new L.TileLayer('http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', ocmOpt);
|
||||
var ocmTransport = new L.TileLayer('http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png', ocmOpt);
|
||||
var ocmLandscape = new L.TileLayer('http://{s}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png', ocmOpt);
|
||||
|
||||
layerChooser.addBaseLayer(ocmCycle, "Thunderforest OpenCycleMap");
|
||||
layerChooser.addBaseLayer(ocmTransport, "Thunderforest Transport");
|
||||
layerChooser.addBaseLayer(ocmLandscape, "Thunderforest Landscape");
|
||||
for(var i in layers) {
|
||||
var layer = new L.TileLayer('http://{s}.tile.thunderforest.com/' + i + '/{z}/{x}/{y}.png', ocmOpt);
|
||||
layerChooser.addBaseLayer(layer, 'Thunderforest ' + layers[i]);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var setup = window.plugin.mapTileOpenCycleMap.addLayer;
|
||||
|
@ -24,18 +24,27 @@
|
||||
|
||||
|
||||
// use own namespace for plugin
|
||||
window.plugin.mapTileOpenStreetMap = function() {};
|
||||
window.plugin.mapTileOpenStreetMap = {
|
||||
addLayer: function() {
|
||||
// OpenStreetMap tiles - we shouldn't use these by default - https://wiki.openstreetmap.org/wiki/Tile_usage_policy
|
||||
// "Heavy use (e.g. distributing an app that uses tiles from openstreetmap.org) is forbidden without prior permission from the System Administrators"
|
||||
|
||||
window.plugin.mapTileOpenStreetMap.addLayer = function() {
|
||||
var osmOpt = {
|
||||
attribution: 'Map data © OpenStreetMap contributors',
|
||||
maxNativeZoom: 18,
|
||||
maxZoom: 21,
|
||||
};
|
||||
|
||||
//OpenStreetMap tiles - we shouldn't use these by default - https://wiki.openstreetmap.org/wiki/Tile_usage_policy
|
||||
// "Heavy use (e.g. distributing an app that uses tiles from openstreetmap.org) is forbidden without prior permission from the System Administrators"
|
||||
var layers = {
|
||||
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png': 'OpenStreetMap',
|
||||
'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png': 'Humanitarian',
|
||||
};
|
||||
|
||||
osmAttribution = 'Map data © OpenStreetMap contributors';
|
||||
var osmOpt = {attribution: osmAttribution, maxNativeZoom: 18, maxZoom: 21};
|
||||
var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', osmOpt);
|
||||
|
||||
layerChooser.addBaseLayer(osm, "OpenStreetMap");
|
||||
for(var url in layers) {
|
||||
var layer = new L.TileLayer(url, osmOpt);
|
||||
layerChooser.addBaseLayer(layer, layers[url]);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var setup = window.plugin.mapTileOpenStreetMap.addLayer;
|
||||
|
@ -295,26 +295,36 @@
|
||||
}
|
||||
|
||||
// Append a 'star' flag in sidebar.
|
||||
window.plugin.bookmarks.onPortalSelectedPending = false;
|
||||
window.plugin.bookmarks.onPortalSelected = function() {
|
||||
$('.bkmrksStar').remove();
|
||||
|
||||
if(window.selectedPortal == null) return;
|
||||
|
||||
setTimeout(function() { // the sidebar is constructed after firing the hook
|
||||
if(typeof(Storage) === "undefined") {
|
||||
$('#portaldetails > .imgpreview').after(plugin.bookmarks.htmlDisabledMessage);
|
||||
return;
|
||||
}
|
||||
if (!window.plugin.bookmarks.onPortalSelectedPending) {
|
||||
window.plugin.bookmarks.onPortalSelectedPending = true;
|
||||
|
||||
// Prepend a star to mobile status-bar
|
||||
if(window.plugin.bookmarks.isSmart) {
|
||||
$('#updatestatus').prepend(plugin.bookmarks.htmlStar);
|
||||
$('#updatestatus .bkmrksStar').attr('title', '');
|
||||
}
|
||||
setTimeout(function() { // the sidebar is constructed after firing the hook
|
||||
window.plugin.bookmarks.onPortalSelectedPending = false;
|
||||
|
||||
$('.bkmrksStar').remove();
|
||||
|
||||
if(typeof(Storage) === "undefined") {
|
||||
$('#portaldetails > .imgpreview').after(plugin.bookmarks.htmlDisabledMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepend a star to mobile status-bar
|
||||
if(window.plugin.bookmarks.isSmart) {
|
||||
$('#updatestatus').prepend(plugin.bookmarks.htmlStar);
|
||||
$('#updatestatus .bkmrksStar').attr('title', '');
|
||||
}
|
||||
|
||||
$('#portaldetails > h3.title').before(plugin.bookmarks.htmlStar);
|
||||
window.plugin.bookmarks.updateStarPortal();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
$('#portaldetails > h3.title').before(plugin.bookmarks.htmlStar);
|
||||
window.plugin.bookmarks.updateStarPortal();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Update the status of the star (when a portal is selected from the map/bookmarks-list)
|
||||
|
62
plugins/cache-details-on-map.user.js
Normal file
62
plugins/cache-details-on-map.user.js
Normal file
@ -0,0 +1,62 @@
|
||||
// ==UserScript==
|
||||
// @id iitc-plugin-cache-details-on-map@jonatkins
|
||||
// @name IITC plugin: Cache viewed portal details and always show them on the map
|
||||
// @category Cache
|
||||
// @version 0.1.0.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Cache the details of recently viewed portals and use this to populate the map when possible
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
// @match http://www.ingress.com/intel*
|
||||
// @include https://www.ingress.com/mission/*
|
||||
// @include http://www.ingress.com/mission/*
|
||||
// @match https://www.ingress.com/mission/*
|
||||
// @match http://www.ingress.com/mission/*
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
|
||||
@@PLUGINSTART@@
|
||||
|
||||
// PLUGIN START ////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// use own namespace for plugin
|
||||
window.plugin.cachePortalDetailsOnMap = function() {};
|
||||
|
||||
window.plugin.cachePortalDetailsOnMap.MAX_AGE = 12*60*60; //12 hours max age for cached data
|
||||
|
||||
window.plugin.cachePortalDetailsOnMap.portalDetailLoaded = function(data) {
|
||||
window.plugin.cachePortalDetailsOnMap.cache[data.guid] = { loadtime: Date.now(), ent: data.ent };
|
||||
};
|
||||
|
||||
window.plugin.cachePortalDetailsOnMap.entityInject = function(data) {
|
||||
var maxAge = Date.now() - window.plugin.cachePortalDetailsOnMap.MAX_AGE*1000;
|
||||
|
||||
var ents = [];
|
||||
for (var guid in window.plugin.cachePortalDetailsOnMap.cache) {
|
||||
if (window.plugin.cachePortalDetailsOnMap.cache[guid].loadtime < maxAge) {
|
||||
delete window.plugin.cachePortalDetailsOnMap.cache[guid];
|
||||
} else {
|
||||
ents.push(window.plugin.cachePortalDetailsOnMap.cache[guid].ent);
|
||||
}
|
||||
}
|
||||
data.callback(ents);
|
||||
};
|
||||
|
||||
|
||||
window.plugin.cachePortalDetailsOnMap.setup = function() {
|
||||
|
||||
window.plugin.cachePortalDetailsOnMap.cache = {};
|
||||
|
||||
addHook('portalDetailLoaded', window.plugin.cachePortalDetailsOnMap.portalDetailLoaded);
|
||||
addHook('mapDataEntityInject', window.plugin.cachePortalDetailsOnMap.entityInject);
|
||||
};
|
||||
|
||||
var setup = window.plugin.cachePortalDetailsOnMap.setup;
|
||||
|
||||
// PLUGIN END //////////////////////////////////////////////////////////
|
||||
|
||||
@@PLUGINEND@@
|
@ -92,12 +92,16 @@ window.plugin.drawTools.setOptions = function() {
|
||||
window.plugin.drawTools.setDrawColor = function(color) {
|
||||
window.plugin.drawTools.currentColor = color;
|
||||
window.plugin.drawTools.currentMarker = window.plugin.drawTools.getMarkerIcon(color);
|
||||
|
||||
window.plugin.drawTools.drawControl.setDrawingOptions({
|
||||
'polygon': { 'shapeOptions': { color: color } },
|
||||
'polyline': { 'shapeOptions': { color: color } },
|
||||
'circle': { 'shapeOptions': { color: color } },
|
||||
'marker': { 'icon': window.plugin.drawTools.currentMarker },
|
||||
|
||||
window.plugin.drawTools.lineOptions.color = color;
|
||||
window.plugin.drawTools.polygonOptions.color = color;
|
||||
window.plugin.drawTools.markerOptions.icon = window.plugin.drawTools.currentMarker;
|
||||
|
||||
plugin.drawTools.drawControl.setDrawingOptions({
|
||||
polygon: { shapeOptions: plugin.drawTools.polygonOptions },
|
||||
polyline: { shapeOptions: plugin.drawTools.lineOptions },
|
||||
circle: { shapeOptions: plugin.drawTools.polygonOptions },
|
||||
marker: { icon: plugin.drawTools.markerOptions.icon },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,9 @@
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
}
|
||||
.plugin-mission-pane > button {
|
||||
padding: 0.3em 2em;
|
||||
}
|
||||
|
||||
.plugin-mission-summary {
|
||||
padding: 5px;
|
||||
@ -160,3 +163,11 @@
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.plugin-mission-search-result-desc {
|
||||
display: block;
|
||||
max-height: 2em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-missions@jonatkins
|
||||
// @name IITC plugin: Missions
|
||||
// @category Info
|
||||
// @version 0.1.1.@@DATETIMEVERSION@@
|
||||
// @version 0.1.2.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -79,6 +79,10 @@ window.plugin.missions = {
|
||||
// 3 weeks.
|
||||
portalMissionsCacheTime: 21 * 24 * 3600 * 1E3,
|
||||
|
||||
MISSION_COLOR: '#404000',
|
||||
MISSION_COLOR_ACTIVE: '#7f7f00',
|
||||
MISSION_COLOR_START: '#A6A600',
|
||||
|
||||
SYNC_DELAY: 5000,
|
||||
enableSync: false,
|
||||
|
||||
@ -113,22 +117,17 @@ window.plugin.missions = {
|
||||
},
|
||||
|
||||
openPortalMissions: function() {
|
||||
var me = this,
|
||||
portal = window.portals[window.selectedPortal];
|
||||
if (!portal) {
|
||||
return;
|
||||
}
|
||||
this.loadPortalMissions(window.selectedPortal, function(missions) {
|
||||
if (!missions.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (missions.length === 1) {
|
||||
me.loadMission(missions[0].guid, me.showMissionDialog.bind(me));
|
||||
this.loadMission(missions[0].guid, this.showMissionDialog.bind(this));
|
||||
} else {
|
||||
me.showMissionListDialog(missions);
|
||||
this.showMissionListDialog(missions);
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
openMission: function(guid) {
|
||||
@ -137,7 +136,7 @@ window.plugin.missions = {
|
||||
|
||||
showMissionDialog: function(mission) {
|
||||
var me = this;
|
||||
var markers = this.highlightMissionPortals(mission);
|
||||
var markers = this.drawMission(mission);
|
||||
var content = this.renderMission(mission);
|
||||
var id = mission.guid.replace(/\./g, '_'); // dots irritate the dialog framework and are not allowed in HTML IDs
|
||||
|
||||
@ -146,7 +145,18 @@ window.plugin.missions = {
|
||||
this.tabHeaders[id].parentNode.querySelector('.ui-icon-close').click();
|
||||
}
|
||||
|
||||
this.tabMarkers[id] = markers;
|
||||
|
||||
var button = content.insertBefore(document.createElement('button'), content.lastChild);
|
||||
button.textContent = 'Zoom to mission';
|
||||
button.addEventListener('click', function() {
|
||||
me.zoomToMission(mission);
|
||||
show('map');
|
||||
}, false);
|
||||
|
||||
var li = this.tabBar.appendChild(document.createElement('li'));
|
||||
li.dataset['mission_id'] = id;
|
||||
|
||||
var a = li.appendChild(document.createElement('a'));
|
||||
a.textContent = mission.title;
|
||||
a.href = '#mission_pane_'+id;
|
||||
@ -155,10 +165,11 @@ window.plugin.missions = {
|
||||
span.className = 'ui-icon ui-icon-close';
|
||||
span.textContent = 'Close mission';
|
||||
span.addEventListener('click', function() {
|
||||
this.unhighlightMissionPortals(markers);
|
||||
this.removeMissionLayers(markers);
|
||||
li.parentNode.removeChild(li);
|
||||
content.parentNode.removeChild(content);
|
||||
delete this.tabHeaders[id];
|
||||
delete this.tabMarkers[id];
|
||||
$(this.tabs)
|
||||
.tabs('refresh')
|
||||
.find('.ui-tabs-nav')
|
||||
@ -181,10 +192,13 @@ window.plugin.missions = {
|
||||
html: content,
|
||||
width: '450px',
|
||||
closeCallback: function() {
|
||||
me.unhighlightMissionPortals(markers);
|
||||
me.removeMissionLayers(markers);
|
||||
},
|
||||
collapseCallback: this.collapseFix,
|
||||
expandCallback: this.collapseFix,
|
||||
focus: function() {
|
||||
me.highlightMissionLayers(markers);
|
||||
}
|
||||
}).dialog('option', 'buttons', {
|
||||
'Zoom to mission': function() {
|
||||
me.zoomToMission(mission);
|
||||
@ -214,13 +228,17 @@ window.plugin.missions = {
|
||||
},
|
||||
|
||||
zoomToMission: function(mission) {
|
||||
map.fitBounds(this.getMissionBounds(mission), {maxZoom: 17});
|
||||
},
|
||||
|
||||
getMissionBounds: function(mission) {
|
||||
var latlngs = mission.waypoints.filter(function(waypoint) {
|
||||
return !!waypoint.portal;
|
||||
}).map(function(waypoint) {
|
||||
return [waypoint.portal.latE6/1E6, waypoint.portal.lngE6/1E6];
|
||||
});
|
||||
|
||||
map.fitBounds(L.latLngBounds(latlngs), {maxZoom: 17});
|
||||
return L.latLngBounds(latlngs);
|
||||
},
|
||||
|
||||
loadMissionsInBounds: function(bounds, callback, errorcallback) {
|
||||
@ -240,7 +258,7 @@ window.plugin.missions = {
|
||||
}
|
||||
callback(missions);
|
||||
}, function(error) {
|
||||
console.log('Error loading missions in bounds', arguments);
|
||||
console.error('Error loading missions in bounds', arguments);
|
||||
if (errorcallback) {
|
||||
errorcallback(error);
|
||||
}
|
||||
@ -255,7 +273,7 @@ window.plugin.missions = {
|
||||
return;
|
||||
}
|
||||
window.postAjax('getTopMissionsForPortal', {
|
||||
guid: window.selectedPortal
|
||||
guid: guid,
|
||||
}, function(data) {
|
||||
var missions = data.result.map(decodeMissionSummary);
|
||||
if (!missions) {
|
||||
@ -274,7 +292,7 @@ window.plugin.missions = {
|
||||
me.storeCache();
|
||||
callback(missions);
|
||||
}, function(error) {
|
||||
console.log('Error loading portal missions', arguments);
|
||||
console.error('Error loading portal missions', arguments);
|
||||
if (errorcallback) {
|
||||
errorcallback(error);
|
||||
}
|
||||
@ -575,7 +593,7 @@ window.plugin.missions = {
|
||||
else
|
||||
this.checkedWaypoints[mwpid] = true;
|
||||
|
||||
window.runHooks('plugin-missions-waypoint-changed', { mwpid: mwpid, });
|
||||
window.runHooks('plugin-missions-waypoint-changed', { mwpid: mwpid, local: true, });
|
||||
if (!dontsave) {
|
||||
this.checkedWaypointsUpdateQueue[mwpid] = true;
|
||||
this.storeLocal('checkedWaypoints');
|
||||
@ -606,7 +624,7 @@ window.plugin.missions = {
|
||||
else
|
||||
this.checkedMissions[mid] = true;
|
||||
|
||||
window.runHooks('plugin-missions-mission-changed', { mid: mid, });
|
||||
window.runHooks('plugin-missions-mission-changed', { mid: mid, local: true, });
|
||||
this.checkedMissionsUpdateQueue[mid] = true;
|
||||
this.storeLocal('checkedMissions');
|
||||
this.storeLocal('checkedMissionsUpdateQueue');
|
||||
@ -725,7 +743,7 @@ window.plugin.missions = {
|
||||
});
|
||||
},
|
||||
|
||||
highlightMissionPortals: function(mission) {
|
||||
drawMission: function(mission) {
|
||||
var markers = [];
|
||||
var latlngs = [];
|
||||
|
||||
@ -734,7 +752,7 @@ window.plugin.missions = {
|
||||
return;
|
||||
}
|
||||
|
||||
var radius = window.portals[waypoint.portal.guid] ? window.portals[waypoint.portal.guid].options.radius * 1.5 : 5;
|
||||
var radius = window.portals[waypoint.portal.guid] ? window.portals[waypoint.portal.guid].options.radius * 1.75 : 5;
|
||||
var ll = [waypoint.portal.latE6 / 1E6, waypoint.portal.lngE6 / 1E6];
|
||||
latlngs.push(ll);
|
||||
|
||||
@ -742,7 +760,7 @@ window.plugin.missions = {
|
||||
radius: radius,
|
||||
weight: 3,
|
||||
opacity: 1,
|
||||
color: '#222',
|
||||
color: this.MISSION_COLOR,
|
||||
fill: false,
|
||||
dashArray: null,
|
||||
clickable: false
|
||||
@ -753,7 +771,7 @@ window.plugin.missions = {
|
||||
}, this);
|
||||
|
||||
var line = L.geodesicPolyline(latlngs, {
|
||||
color: '#222',
|
||||
color: this.MISSION_COLOR,
|
||||
opacity: 1,
|
||||
weight: 2,
|
||||
clickable: false,
|
||||
@ -764,12 +782,25 @@ window.plugin.missions = {
|
||||
return markers;
|
||||
},
|
||||
|
||||
unhighlightMissionPortals: function(markers) {
|
||||
removeMissionLayers: function(markers) {
|
||||
markers.forEach(function(marker) {
|
||||
this.missionLayer.removeLayer(marker);
|
||||
}, this);
|
||||
},
|
||||
|
||||
highlightMissionLayers: function(markers) {
|
||||
// layer.bringToFront() will break if the layer is not visible
|
||||
var bringToFront = map.hasLayer(plugin.missions.missionLayer);
|
||||
|
||||
this.missionLayer.eachLayer(function(layer) {
|
||||
var active = (markers.indexOf(layer) !== -1);
|
||||
layer.setStyle({
|
||||
color: active ? this.MISSION_COLOR_ACTIVE : this.MISSION_COLOR,
|
||||
});
|
||||
if(active && bringToFront) layer.bringToFront();
|
||||
}, this);
|
||||
},
|
||||
|
||||
onPortalChanged: function(type, guid, oldval) {
|
||||
var portal;
|
||||
if (type === 'add' || type === 'update') {
|
||||
@ -787,7 +818,7 @@ window.plugin.missions = {
|
||||
radius: portal.options.radius + Math.ceil(portal.options.radius / 2),
|
||||
weight: 3,
|
||||
opacity: 1,
|
||||
color: '#555',
|
||||
color: this.MISSION_COLOR_START,
|
||||
fill: false,
|
||||
dashArray: null,
|
||||
clickable: false
|
||||
@ -830,10 +861,21 @@ window.plugin.missions = {
|
||||
},
|
||||
|
||||
// called after IITC and all plugin loaded
|
||||
registerFieldForSyncing: function() {
|
||||
if(!window.plugin.sync) return;
|
||||
window.plugin.sync.registerMapForSync('missions', 'checkedMissions', this.syncCallback.bind(this), this.syncInitialed.bind(this));
|
||||
window.plugin.sync.registerMapForSync('missions', 'checkedWaypoints', this.syncCallback.bind(this), this.syncInitialed.bind(this));
|
||||
onIITCLoaded: function() {
|
||||
var match = location.pathname.match(/\/mission\/([0-9a-z.]+)/);
|
||||
if(match && match[1]) {
|
||||
var mid = match[1];
|
||||
|
||||
this.loadMission(mid, function(mission) {
|
||||
this.openMission(mid);
|
||||
this.zoomToMission(mission);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
if(window.plugin.sync) {
|
||||
window.plugin.sync.registerMapForSync('missions', 'checkedMissions', this.syncCallback.bind(this), this.syncInitialed.bind(this));
|
||||
window.plugin.sync.registerMapForSync('missions', 'checkedWaypoints', this.syncCallback.bind(this), this.syncInitialed.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
// called after local or remote change uploaded
|
||||
@ -860,9 +902,9 @@ window.plugin.missions = {
|
||||
this.storeLocal(fieldName + 'UpdateQueue');
|
||||
|
||||
if(fieldName === 'checkedMissions') {
|
||||
window.runHooks('plugin-missions-mission-changed', { mid: e.property, });
|
||||
window.runHooks('plugin-missions-mission-changed', { mid: e.property, local: false, });
|
||||
} else if(fieldName === 'checkedWaypoints') {
|
||||
window.runHooks('plugin-missions-waypoint-changed', { mwpid: e.property, });
|
||||
window.runHooks('plugin-missions-waypoint-changed', { mwpid: e.property, local: false, });
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -883,6 +925,77 @@ window.plugin.missions = {
|
||||
}
|
||||
},
|
||||
|
||||
onSearch: function(query) {
|
||||
var self = this;
|
||||
|
||||
var bounds = window.map.getBounds();
|
||||
|
||||
if(query.confirmed) {
|
||||
this.loadMissionsInBounds(bounds, function(missions) {
|
||||
self.addMissionsToQuery(query, missions);
|
||||
});
|
||||
}
|
||||
|
||||
var cachedMissions = Object.keys(this.cacheByMissionGuid).map(function(guid) {
|
||||
return self.cacheByMissionGuid[guid].data;
|
||||
});
|
||||
|
||||
var cachedMissionsInView = cachedMissions.filter(function(mission) {
|
||||
return mission.waypoints && mission.waypoints.some(function(waypoint) {
|
||||
if(!waypoint) return false;
|
||||
if(!waypoint.portal) return false;
|
||||
return bounds.contains([waypoint.portal.latE6/1E6, waypoint.portal.lngE6/1E6]);
|
||||
});
|
||||
});
|
||||
|
||||
self.addMissionsToQuery(query, cachedMissionsInView);
|
||||
},
|
||||
|
||||
addMissionsToQuery: function(query, missions) {
|
||||
var term = query.term.toLowerCase();
|
||||
|
||||
missions.forEach(function(mission) {
|
||||
if(mission.title.toLowerCase().indexOf(term) === -1
|
||||
&& ((!mission.description) || mission.description.toLowerCase().indexOf(term) === -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(query.results.some(function(result) { return result.mission && (result.mission.guid == mission.guid); }))
|
||||
// mission already in list (a cached mission may be found again via missions in bounds)
|
||||
return;
|
||||
|
||||
var result = {
|
||||
title: escapeHtmlSpecialChars(mission.title),
|
||||
description: mission.description
|
||||
? 'Recently viewed mission: <small class="plugin-mission-search-result-desc">' + escapeHtmlSpecialChars(mission.description) + '</small>'
|
||||
: 'Mission in view',
|
||||
icon: 'https://commondatastorage.googleapis.com/ingress.com/img/tm_icons/tm_cyan.png',
|
||||
onSelected: this.onSearchResultSelected.bind(this),
|
||||
mission: mission,
|
||||
layer: null, // prevent a preview, we'll handle this
|
||||
};
|
||||
|
||||
// mission may be a cached mission or contain the full details
|
||||
if(mission.waypoints) {
|
||||
result.bounds = this.getMissionBounds(mission);
|
||||
}
|
||||
if(mission.typeNum) {
|
||||
result.icon = this.missionTypeImages[mission.typeNum] || this.missionTypeImages[0];
|
||||
}
|
||||
|
||||
query.addResult(result);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
onSearchResultSelected: function(result, event) {
|
||||
if(result.bounds) {
|
||||
map.fitBounds(result.bounds, {maxZoom: 17});
|
||||
}
|
||||
|
||||
this.openMission(result.mission.guid);
|
||||
return false;
|
||||
},
|
||||
|
||||
setup: function() {
|
||||
this.cacheByPortalGuid = {};
|
||||
this.cacheByMissionGuid = {};
|
||||
@ -906,9 +1019,18 @@ window.plugin.missions = {
|
||||
this.tabs = this.mobilePane.appendChild(document.createElement('div'));
|
||||
this.tabBar = this.tabs.appendChild(document.createElement('ul'));
|
||||
this.tabHeaders = {};
|
||||
this.tabMarkers = {};
|
||||
|
||||
$(this.tabs)
|
||||
.tabs()
|
||||
.tabs({
|
||||
activate: function(event, ui) {
|
||||
if(!ui.newTab) return;
|
||||
|
||||
var header = $(ui.newTab)[0];
|
||||
var id = header.dataset['mission_id'];
|
||||
this.highlightMissionLayers(this.tabMarkers[id]);
|
||||
}.bind(this),
|
||||
})
|
||||
.find('.ui-tabs-nav').sortable({
|
||||
axis: 'x',
|
||||
stop: function() {
|
||||
@ -923,6 +1045,8 @@ window.plugin.missions = {
|
||||
// window.addPortalHighlighter('Mission start point', this.highlight.bind(this));
|
||||
window.addHook('portalSelected', this.onPortalSelected.bind(this));
|
||||
|
||||
window.addHook('search', this.onSearch.bind(this));
|
||||
|
||||
/*
|
||||
I know iitc has portalAdded event but it is missing portalDeleted. So we have to resort to Object.observe
|
||||
*/
|
||||
@ -964,13 +1088,8 @@ window.plugin.missions = {
|
||||
window.addHook('plugin-missions-missions-refreshed', this.onMissionsRefreshed.bind(this));
|
||||
window.addHook('plugin-missions-waypoint-changed', this.onWaypointChanged.bind(this));
|
||||
window.addHook('plugin-missions-waypoints-refreshed', this.onWaypointsRefreshed.bind(this));
|
||||
window.addHook('iitcLoaded', this.registerFieldForSyncing.bind(this));
|
||||
|
||||
var match = location.pathname.match(/\/mission\/([0-9a-z.]+)/);
|
||||
if(match && match[1]) {
|
||||
var mid = match[1];
|
||||
this.openMission(mid);
|
||||
}
|
||||
window.addHook('iitcLoaded', this.onIITCLoaded.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,19 +27,22 @@ window.plugin.portalHighlighterInactive = function() {};
|
||||
|
||||
window.plugin.portalHighlighterInactive.highlight = function(data) {
|
||||
|
||||
var daysUnmodified = (new Date().getTime() - data.portal.options.timestamp) / (24*60*60*1000);
|
||||
if (data.portal.options.timestamp > 0) {
|
||||
|
||||
if (daysUnmodified >= 7) {
|
||||
var daysUnmodified = (new Date().getTime() - data.portal.options.timestamp) / (24*60*60*1000);
|
||||
|
||||
var fill_opacity = Math.min(1,((daysUnmodified-7)/24)*.85 + .15);
|
||||
if (daysUnmodified >= 7) {
|
||||
|
||||
var blue = Math.max(0,Math.min(255,Math.round((daysUnmodified-31)/62*255)));
|
||||
var fill_opacity = Math.min(1,((daysUnmodified-7)/24)*.85 + .15);
|
||||
|
||||
var colour = 'rgb(255,0,'+blue+')';
|
||||
var blue = Math.max(0,Math.min(255,Math.round((daysUnmodified-31)/62*255)));
|
||||
|
||||
var params = {fillColor: colour, fillOpacity: fill_opacity};
|
||||
var colour = 'rgb(255,0,'+blue+')';
|
||||
|
||||
data.portal.setStyle(params);
|
||||
var params = {fillColor: colour, fillOpacity: fill_opacity};
|
||||
|
||||
data.portal.setStyle(params);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,8 +27,10 @@ window.plugin.portalHighlighterPortalsLevelColor = function() {};
|
||||
|
||||
window.plugin.portalHighlighterPortalsLevelColor.colorLevel = function(data) {
|
||||
var portal_level = data.portal.options.data.level;
|
||||
var opacity = .6;
|
||||
data.portal.setStyle({fillColor: COLORS_LVL[portal_level], fillOpacity: opacity});
|
||||
if (portal_level !== undefined) {
|
||||
var opacity = .6;
|
||||
data.portal.setStyle({fillColor: COLORS_LVL[portal_level], fillOpacity: opacity});
|
||||
}
|
||||
}
|
||||
|
||||
var setup = function() {
|
||||
|
@ -30,7 +30,7 @@ window.plugin.portalsMissingResonators.highlight = function(data) {
|
||||
if(data.portal.options.team != TEAM_NONE) {
|
||||
var res_count = data.portal.options.data.resCount;
|
||||
|
||||
if(res_count < 8) {
|
||||
if(res_count !== undefined && res_count < 8) {
|
||||
var fill_opacity = ((8-res_count)/8)*.85 + .15;
|
||||
var color = 'red';
|
||||
var params = {fillColor: color, fillOpacity: fill_opacity};
|
||||
|
@ -29,7 +29,7 @@ window.plugin.portalHighlighterNeedsRecharge.highlight = function(data) {
|
||||
var d = data.portal.options.data;
|
||||
var health = d.health;
|
||||
|
||||
if(data.portal.options.team != TEAM_NONE && health < 100) {
|
||||
if(health !== undefined && data.portal.options.team != TEAM_NONE && health < 100) {
|
||||
var color,fill_opacity;
|
||||
if (health > 95) {
|
||||
color = 'yellow';
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-portal-level-numbers@rongou
|
||||
// @name IITC plugin: Portal Level Numbers
|
||||
// @category Layer
|
||||
// @version 0.1.4.@@DATETIMEVERSION@@
|
||||
// @version 0.1.5.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -88,7 +88,7 @@ window.plugin.portalLevelNumbers.updatePortalLabels = function() {
|
||||
|
||||
for (var guid in window.portals) {
|
||||
var p = window.portals[guid];
|
||||
if (p._map) { // only consider portals added to the map
|
||||
if (p._map && p.options.data.level !== undefined) { // only consider portals added to the map, and that have a level set
|
||||
var point = map.project(p.getLatLng());
|
||||
portalPoints[guid] = point;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-portal-names@zaso
|
||||
// @name IITC plugin: Portal Names
|
||||
// @category Layer
|
||||
// @version 0.1.5.@@DATETIMEVERSION@@
|
||||
// @version 0.1.6.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -93,7 +93,7 @@ window.plugin.portalNames.updatePortalLabels = function() {
|
||||
|
||||
for (var guid in window.portals) {
|
||||
var p = window.portals[guid];
|
||||
if (p._map) { // only consider portals added to the map
|
||||
if (p._map && p.options.data.title) { // only consider portals added to the map and with a title
|
||||
var point = map.project(p.getLatLng());
|
||||
portalPoints[guid] = point;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-show-linked-portals@fstopienski
|
||||
// @name IITC plugin: Show linked portals
|
||||
// @category Portal Info
|
||||
// @version 0.3.0.@@DATETIMEVERSION@@
|
||||
// @version 0.3.1.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -43,6 +43,8 @@ window.plugin.showLinkedPortal.portalDetail = function (data) {
|
||||
|
||||
var c = 1;
|
||||
|
||||
$('<div>',{id:'showLinkedPortalContainer'}).appendTo('#portaldetails');
|
||||
|
||||
function renderLinkedPortal(linkGuid) {
|
||||
if(c > 16) return;
|
||||
|
||||
@ -61,7 +63,7 @@ window.plugin.showLinkedPortal.portalDetail = function (data) {
|
||||
var title;
|
||||
|
||||
var data = (portals[guid] && portals[guid].options.data) || portalDetail.get(guid) || null;
|
||||
if(data) {
|
||||
if(data && data.title) {
|
||||
title = data.title;
|
||||
div.append($('<img/>').attr({
|
||||
'src': fixPortalImageUrl(data.image),
|
||||
@ -73,7 +75,7 @@ window.plugin.showLinkedPortal.portalDetail = function (data) {
|
||||
div
|
||||
.addClass('outOfRange')
|
||||
.append($('<span/>')
|
||||
.html('Portal out of range.<br>' + lengthShort));
|
||||
.html('Portal not loaded.<br>' + lengthShort));
|
||||
}
|
||||
|
||||
div
|
||||
@ -89,7 +91,7 @@ window.plugin.showLinkedPortal.portalDetail = function (data) {
|
||||
.append($('<span/>').html(lengthFull))
|
||||
.html(),
|
||||
})
|
||||
.appendTo('#portaldetails');
|
||||
.appendTo('#showLinkedPortalContainer');
|
||||
|
||||
c++;
|
||||
}
|
||||
@ -101,10 +103,10 @@ window.plugin.showLinkedPortal.portalDetail = function (data) {
|
||||
$('<div>')
|
||||
.addClass('showLinkedPortalLink showLinkedPortalOverflow')
|
||||
.text(length-16 + ' more')
|
||||
.appendTo('#portaldetails');
|
||||
.appendTo('#showLinkedPortalContainer');
|
||||
}
|
||||
|
||||
$('#portaldetails')
|
||||
$('#showLinkedPortalContainer')
|
||||
.on('click', '.showLinkedPortalLink', plugin.showLinkedPortal.onLinkedPortalClick)
|
||||
.on('mouseover', '.showLinkedPortalLink', plugin.showLinkedPortal.onLinkedPortalMouseOver)
|
||||
.on('mouseout', '.showLinkedPortalLink', plugin.showLinkedPortal.onLinkedPortalMouseOut);
|
||||
|
@ -1,12 +1,12 @@
|
||||
// ==UserScript==
|
||||
// @id iitc-plugin-show-more-portals@jonatkins
|
||||
// @name IITC plugin: Show more portals
|
||||
// @category Tweaks
|
||||
// @category Deleted
|
||||
// @version 0.2.1.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Boost the detail level of portals shown so that unclaimed portals are visible one level saooner.
|
||||
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Standard intel has changed to show all portals from zoom level 15, which is what this plugin used to force.
|
||||
// @include https://www.ingress.com/intel*
|
||||
// @include http://www.ingress.com/intel*
|
||||
// @match https://www.ingress.com/intel*
|
||||
@ -18,24 +18,3 @@
|
||||
// @grant none
|
||||
// ==/UserScript==
|
||||
|
||||
@@PLUGINSTART@@
|
||||
|
||||
// PLUGIN START ////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// use own namespace for plugin
|
||||
window.plugin.showMorePortals = function() {};
|
||||
|
||||
window.plugin.showMorePortals.setup = function() {
|
||||
|
||||
// NOTE: the logic required is closely tied to the IITC+stock map detail level code - so the logic is moved there now
|
||||
// and just enabled by this flag
|
||||
window.CONFIG_ZOOM_SHOW_MORE_PORTALS=true;
|
||||
|
||||
};
|
||||
|
||||
var setup = window.plugin.showMorePortals.setup;
|
||||
|
||||
// PLUGIN END //////////////////////////////////////////////////////////
|
||||
|
||||
@@PLUGINEND@@
|
||||
|
@ -27,7 +27,7 @@ window.plugin.portalWeakness = function() {};
|
||||
|
||||
window.plugin.portalWeakness.highlightWeakness = function(data) {
|
||||
|
||||
if(data.portal.options.team != TEAM_NONE) {
|
||||
if(data.portal.options.data.resCount !== undefined && data.portal.options.data.health !== undefined && data.portal.options.team != TEAM_NONE) {
|
||||
var res_count = data.portal.options.data.resCount;
|
||||
var portal_health = data.portal.options.data.health;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// @id iitc-plugin-uniques@3ch01c
|
||||
// @name IITC plugin: Uniques
|
||||
// @category Misc
|
||||
// @version 0.2.3.@@DATETIMEVERSION@@
|
||||
// @version 0.2.4.@@DATETIMEVERSION@@
|
||||
// @namespace https://github.com/3ch01c/ingress-intel-total-conversion
|
||||
// @updateURL @@UPDATEURL@@
|
||||
// @downloadURL @@DOWNLOADURL@@
|
||||
@ -99,6 +99,17 @@ window.plugin.uniques.onPublicChatDataAvailable = function(data) {
|
||||
&& markup[0][0] == 'PLAYER'
|
||||
&& markup[0][1].plain == nick
|
||||
&& markup[1][0] == 'TEXT'
|
||||
&& markup[1][1].plain == ' deployed a Resonator on '
|
||||
&& markup[2][0] == 'PORTAL') {
|
||||
// search for "x deployed a Resonator on z"
|
||||
var portal = markup[2][1];
|
||||
var guid = window.findPortalGuidByPositionE6(portal.latE6, portal.lngE6);
|
||||
if(guid) plugin.uniques.setPortalVisited(guid);
|
||||
} else if(plext.plextType == 'SYSTEM_BROADCAST'
|
||||
&& markup.length==3
|
||||
&& markup[0][0] == 'PLAYER'
|
||||
&& markup[0][1].plain == nick
|
||||
&& markup[1][0] == 'TEXT'
|
||||
&& markup[1][1].plain == ' captured '
|
||||
&& markup[2][0] == 'PORTAL') {
|
||||
// search for "x captured y"
|
||||
@ -188,6 +199,8 @@ window.plugin.uniques.updateCheckedAndHighlight = function(guid) {
|
||||
window.plugin.uniques.setPortalVisited = function(guid) {
|
||||
var uniqueInfo = plugin.uniques.uniques[guid];
|
||||
if (uniqueInfo) {
|
||||
if(uniqueInfo.visited) return;
|
||||
|
||||
uniqueInfo.visited = true;
|
||||
} else {
|
||||
plugin.uniques.uniques[guid] = {
|
||||
@ -203,6 +216,8 @@ window.plugin.uniques.setPortalVisited = function(guid) {
|
||||
window.plugin.uniques.setPortalCaptured = function(guid) {
|
||||
var uniqueInfo = plugin.uniques.uniques[guid];
|
||||
if (uniqueInfo) {
|
||||
if(uniqueInfo.visited && uniqueInfo.captured) return;
|
||||
|
||||
uniqueInfo.visited = true;
|
||||
uniqueInfo.captured = true;
|
||||
} else {
|
||||
@ -227,6 +242,8 @@ window.plugin.uniques.updateVisited = function(visited, guid) {
|
||||
};
|
||||
}
|
||||
|
||||
if(visited == uniqueInfo.visited) return;
|
||||
|
||||
if (visited) {
|
||||
uniqueInfo.visited = true;
|
||||
} else { // not visited --> not captured
|
||||
@ -249,6 +266,8 @@ window.plugin.uniques.updateCaptured = function(captured, guid) {
|
||||
};
|
||||
}
|
||||
|
||||
if(captured == uniqueInfo.captured) return;
|
||||
|
||||
if (captured) { // captured --> visited
|
||||
uniqueInfo.captured = true;
|
||||
uniqueInfo.visited = true;
|
||||
@ -489,19 +508,83 @@ window.plugin.uniques.setupPortalsList = function() {
|
||||
});
|
||||
}
|
||||
|
||||
window.plugin.uniques.onMissionChanged = function(data) {
|
||||
if(!data.local) return;
|
||||
|
||||
var mission = window.plugin.missions && window.plugin.missions.getMissionCache(data.mid, false);
|
||||
if(!mission) return;
|
||||
|
||||
window.plugin.uniques.checkMissionWaypoints(mission);
|
||||
};
|
||||
|
||||
window.plugin.uniques.onMissionLoaded = function(data) {
|
||||
// the mission has been loaded, but the dialog isn't visible yet.
|
||||
// we'll wait a moment so the mission dialog is opened behind the confirmation prompt
|
||||
setTimeout(function() {
|
||||
window.plugin.uniques.checkMissionWaypoints(data.mission);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
window.plugin.uniques.checkMissionWaypoints = function(mission) {
|
||||
if(!(window.plugin.missions && window.plugin.missions.checkedMissions[mission.guid])) return;
|
||||
|
||||
if(!mission.waypoints) return;
|
||||
|
||||
function isValidWaypoint(wp) {
|
||||
// might be hidden or field trip card
|
||||
if(!(wp && wp.portal && wp.portal.guid)) return false;
|
||||
|
||||
// only use hack, deploy, link, field and upgrade; ignore photo and passphrase
|
||||
if(wp.objectiveNum <= 0 || wp.objectiveNum > 5) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
function isVisited(wp) {
|
||||
var guid = wp.portal.guid,
|
||||
uniqueInfo = plugin.uniques.uniques[guid],
|
||||
visited = (uniqueInfo && uniqueInfo.visited) || false;
|
||||
|
||||
return visited;
|
||||
}
|
||||
|
||||
// check if all waypoints are already visited
|
||||
if(mission.waypoints.every(function(wp) {
|
||||
if(!isValidWaypoint(wp)) return true;
|
||||
return isVisited(wp);
|
||||
})) return;
|
||||
|
||||
if(!confirm('The mission ' + mission.title + ' contains waypoints not yet marked as visited.\n\n' +
|
||||
'Do you want to set them to \'visited\' now?'))
|
||||
return;
|
||||
|
||||
mission.waypoints.forEach(function(wp) {
|
||||
if(!isValidWaypoint(wp)) return;
|
||||
if(isVisited(wp)) return;
|
||||
|
||||
plugin.uniques.setPortalVisited(wp.portal.guid);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
var setup = function() {
|
||||
if($.inArray('pluginUniquesUpdateUniques', window.VALID_HOOKS) < 0)
|
||||
window.VALID_HOOKS.push('pluginUniquesUpdateUniques');
|
||||
if($.inArray('pluginUniquesRefreshAll', window.VALID_HOOKS) < 0)
|
||||
window.VALID_HOOKS.push('pluginUniquesRefreshAll');
|
||||
window.pluginCreateHook('pluginUniquesUpdateUniques');
|
||||
window.pluginCreateHook('pluginUniquesRefreshAll');
|
||||
|
||||
// to mark mission portals as visited
|
||||
window.pluginCreateHook('plugin-missions-mission-changed');
|
||||
window.pluginCreateHook('plugin-missions-loaded-mission');
|
||||
|
||||
window.plugin.uniques.setupCSS();
|
||||
window.plugin.uniques.setupContent();
|
||||
window.plugin.uniques.loadLocal('uniques');
|
||||
window.addPortalHighlighter('Uniques', window.plugin.uniques.highlighter);
|
||||
window.addHook('portalDetailsUpdated', window.plugin.uniques.onPortalDetailsUpdated);
|
||||
window.addHook('publicChatDataAvailable', window.plugin.uniques.onPublicChatDataAvailable);
|
||||
window.addHook('iitcLoaded', window.plugin.uniques.registerFieldForSyncing);
|
||||
window.addPortalHighlighter('Uniques', window.plugin.uniques.highlighter);
|
||||
|
||||
|
||||
window.addHook('plugin-missions-mission-changed', window.plugin.uniques.onMissionChanged);
|
||||
window.addHook('plugin-missions-loaded-mission', window.plugin.uniques.onMissionLoaded);
|
||||
|
||||
if(window.plugin.portalslist) {
|
||||
window.plugin.uniques.setupPortalsList();
|
||||
} else {
|
||||
|
11
style.css
11
style.css
@ -711,6 +711,15 @@ h3 {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
#artifact_target, #artifact_fragments {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
|
||||
/* resonators */
|
||||
#resodetails {
|
||||
margin-bottom: 0px;
|
||||
@ -957,9 +966,11 @@ h3 {
|
||||
}
|
||||
.ui-dialog-titlebar-button-close::after {
|
||||
transform: translateY(3.5px) rotate(45deg);
|
||||
-webkit-transform: translateY(3.5px) rotate(45deg);
|
||||
}
|
||||
.ui-dialog-titlebar-button-close::before {
|
||||
transform: translateY(3.5px) rotate(-45deg);
|
||||
-webkit-transform: translateY(3.5px) rotate(-45deg);
|
||||
}
|
||||
.ui-dialog-titlebar-button.ui-state-active::after,
|
||||
.ui-dialog-titlebar-button.ui-state-active::before,
|
||||
|
@ -13,18 +13,23 @@ offers many more features. It is available for
|
||||
|
||||
<h3>Latest news</h3>
|
||||
|
||||
<h4>8th May 2015</h4>
|
||||
<h4>12th September 2015</h4>
|
||||
<p>
|
||||
IITC 0.22.3 has been released. Changes include
|
||||
IITC 0.25.0 has been released. This is a critical update to fix things after a Niantic site update.
|
||||
</p>
|
||||
<p>
|
||||
Other changes include
|
||||
</p>
|
||||
<ul>
|
||||
<li>Lightman Shards supported</li>
|
||||
<li>Workaround for map loading not reaching 100%</li>
|
||||
<li>IITC Mobile: fix dialogs not appearing on Android 5 with the System WebView update</li>
|
||||
<li>Remove unnecessary prompt on botguard script response</li>
|
||||
<li>Plugin: Missions - full mission support</li>
|
||||
<li>Plugin: show-linked-portals - more details shown</li>
|
||||
<li>Plugin: show-more-portals - enabled again for one zoom level</li>
|
||||
<li>"Placeholder portals". Like stock intel, IITC creates clickable portals at the end of links. Portal highlighters
|
||||
won't work on these as they don't contain enough information</li>
|
||||
<li>Artifacts loading fixed after a change in data format by Niantic</li>
|
||||
<li>'show-more-portals' plugin is no longer required - Niantic now return all portals at zoom 15, rather than zoom 17</li>
|
||||
<li>New plugin: "cache-details-on-map". Caches the portal details as you select portals, and keeps these portals on the map
|
||||
even when you zoom out</li>
|
||||
<li>and other tweaks/bugfixes</li>
|
||||
</ul>
|
||||
<b>Update 17th September 2015</b>
|
||||
<p>IITC 0.25.1 released. This fixes search not working after MapQuest made a change to their API.</p>
|
||||
|
||||
<a class="btn btn-default btn-sm" href="?page=news">Older news</a>
|
||||
|
@ -1,5 +1,41 @@
|
||||
<h2>News</h2>
|
||||
|
||||
<h4>12th September 2015</h4>
|
||||
<p>
|
||||
IITC 0.25.0 has been released. This is a critical update to fix things after a Niantic site update.
|
||||
</p>
|
||||
<p>
|
||||
Other changes include
|
||||
</p>
|
||||
<ul>
|
||||
<li>"Placeholder portals". Like stock intel, IITC creates clickable portals at the end of links. Portal highlighters
|
||||
won't work on these as they don't contain enough information</li>
|
||||
<li>Artifacts loading fixed after a change in data format by Niantic</li>
|
||||
<li>'show-more-portals' plugin is no longer required - Niantic now return all portals at zoom 15, rather than zoom 17</li>
|
||||
<li>New plugin: "cache-details-on-map". Caches the portal details as you select portals, and keeps these portals on the map
|
||||
even when you zoom out</li>
|
||||
<li>and other tweaks/bugfixes</li>
|
||||
</ul>
|
||||
<b>Update 17th September 2015</b>
|
||||
<p>IITC 0.25.1 released. This fixes search not working after MapQuest made a change to their API.</p>
|
||||
|
||||
|
||||
<h4>27th June 2015</h4>
|
||||
<p>
|
||||
IITC 0.24.0 has been released. This is a critical update needed to fix the map not loading any data.
|
||||
</p>
|
||||
<p>
|
||||
NOTE: Niantic have removed portals from the map data at all but the closest of zoom levels (L1+ - zoom 15+).
|
||||
The stock intel site creates dummy placeholder portals at the anchors for links, allowing you to click and open the
|
||||
portal details. However, IITC does not do this yet. It will take some time to modify IITC and the plugins to allow
|
||||
for such placeholder portals, so making a first release without them.
|
||||
</p>
|
||||
|
||||
<h4>27th May 2015</h4>
|
||||
<p>
|
||||
IITC 0.23.0 has been released. This is a critical update needed to fix IITC after a Niantic site update.
|
||||
</p>
|
||||
|
||||
<h4>8th May 2015</h4>
|
||||
<p>
|
||||
IITC 0.22.3 has been released. Changes include
|
||||
|
Reference in New Issue
Block a user