Merge pull request #4 from jonatkins/master

updating fork
This commit is contained in:
reckter
2015-11-17 10:28:44 +01:00
45 changed files with 1072 additions and 1002 deletions

View File

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

View File

@ -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();

View File

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

View File

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

View File

@ -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]),
});
}

View File

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

View File

@ -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);
}
}

View File

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

View File

@ -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);
}

View File

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

View File

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

View File

@ -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") {

View File

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

View File

@ -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];
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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">&gt;'+(tileParams.minLinkLength>1000?tileParams.minLinkLength/1000+'km':tileParams.minLinkLength+'m')+'</span>';
else
t += '<span id="loadlevel">all links</span>';
}
t +='</span>';

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -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 },
});
}

View File

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

View File

@ -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));
}
};

View File

@ -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);
}
}
}

View File

@ -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() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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