Merge branch 'master' into release

This commit is contained in:
Jon Atkins 2014-04-29 04:37:15 +01:00
commit 13da501548
35 changed files with 904 additions and 351 deletions

View File

@ -78,6 +78,21 @@ window.setupLayerChooserStatusRecorder = function() {
}); });
} }
window.layerChooserSetDisabledStates = function() {
// layer selector - enable/disable layers that aren't visible due to zoom level
var minlvl = getMinPortalLevel();
var portalSelection = $('.leaflet-control-layers-overlays label');
//it's an array - 0=unclaimed, 1=lvl 1, 2=lvl 2, ..., 8=lvl 8 - 9 relevant entries
//mark all levels below (but not at) minlvl as disabled
portalSelection.slice(0, minlvl).addClass('disabled').attr('title', 'Zoom in to show those.');
//and all from minlvl to 8 as enabled
portalSelection.slice(minlvl, 8+1).removeClass('disabled').attr('title', '');
//TODO? some generic mechanism where other layers can have their disabled state marked on/off? a few
//plugins have code to do it by hand already
}
window.setupStyles = function() { window.setupStyles = function() {
$('head').append('<style>' + $('head').append('<style>' +
[ '#largepreview.enl img { border:2px solid '+COLORS[TEAM_ENL]+'; } ', [ '#largepreview.enl img { border:2px solid '+COLORS[TEAM_ENL]+'; } ',
@ -137,7 +152,9 @@ window.setupMap = function() {
center: [0,0], center: [0,0],
zoom: 1, zoom: 1,
zoomControl: (typeof android !== 'undefined' && android && android.showZoom) ? android.showZoom() : true, zoomControl: (typeof android !== 'undefined' && android && android.showZoom) ? android.showZoom() : true,
minZoom: 1 minZoom: 1,
// zoomAnimation: false,
markerZoomAnimation: false
}); });
// add empty div to leaflet control areas - to force other leaflet controls to move around IITC UI elements // add empty div to leaflet control areas - to force other leaflet controls to move around IITC UI elements
@ -279,6 +296,9 @@ window.setupMap = function() {
map.on('movestart', function() { window.mapRunsUserAction = true; window.requests.abort(); window.startRefreshTimeout(-1); }); map.on('movestart', function() { window.mapRunsUserAction = true; window.requests.abort(); window.startRefreshTimeout(-1); });
map.on('moveend', function() { window.mapRunsUserAction = false; window.startRefreshTimeout(ON_MOVE_REFRESH*1000); }); map.on('moveend', function() { window.mapRunsUserAction = false; window.startRefreshTimeout(ON_MOVE_REFRESH*1000); });
map.on('zoomend', function() { window.layerChooserSetDisabledStates(); });
window.layerChooserSetDisabledStates();
// on zoomend, check to see the zoom level is an int, and reset the view if not // on zoomend, check to see the zoom level is an int, and reset the view if not
// (there's a bug on mobile where zoom levels sometimes end up as fractional levels. this causes the base map to be invisible) // (there's a bug on mobile where zoom levels sometimes end up as fractional levels. this causes the base map to be invisible)
map.on('zoomend', function() { map.on('zoomend', function() {
@ -353,20 +373,20 @@ window.setMapBaseLayer = function() {
// included as inline script in the original site, the data is static // included as inline script in the original site, the data is static
// and cannot be updated. // and cannot be updated.
window.setupPlayerStat = function() { window.setupPlayerStat = function() {
var level; // stock site updated to supply the actual player level, AP requirements and XM capacity values
var ap = parseInt(PLAYER.ap); var level = PLAYER.verified_level;
for(level = 0; level < MIN_AP_FOR_LEVEL.length; level++) { PLAYER.level = level; //for historical reasons IITC expects PLAYER.level to contain the current player level
if(ap < MIN_AP_FOR_LEVEL[level]) break;
}
PLAYER.level = level;
var thisLvlAp = MIN_AP_FOR_LEVEL[level-1]; var ap = parseInt(PLAYER.ap);
var nextLvlAp = MIN_AP_FOR_LEVEL[level] || ap; var thisLvlAp = parseInt(PLAYER.min_ap_for_current_level);
var nextLvlAp = parseInt(PLAYER.min_ap_for_next_level);
if (nextLvlAp) {
var lvlUpAp = digits(nextLvlAp-ap); var lvlUpAp = digits(nextLvlAp-ap);
var lvlApProg = Math.round((ap-thisLvlAp)/(nextLvlAp-thisLvlAp)*100); var lvlApProg = Math.round((ap-thisLvlAp)/(nextLvlAp-thisLvlAp)*100);
} // else zero nextLvlAp - so at maximum level(?)
var xmMax = parseInt(PLAYER.xm_capacity);
var xmMax = MAX_XM_PER_LEVEL[level];
var xmRatio = Math.round(PLAYER.energy/xmMax*100); var xmRatio = Math.round(PLAYER.energy/xmMax*100);
var cls = PLAYER.team === 'RESISTANCE' ? 'res' : 'enl'; var cls = PLAYER.team === 'RESISTANCE' ? 'res' : 'enl';
@ -375,7 +395,7 @@ window.setupPlayerStat = function() {
var t = 'Level:\t' + level + '\n' var t = 'Level:\t' + level + '\n'
+ 'XM:\t' + PLAYER.energy + ' / ' + xmMax + '\n' + 'XM:\t' + PLAYER.energy + ' / ' + xmMax + '\n'
+ 'AP:\t' + digits(ap) + '\n' + 'AP:\t' + digits(ap) + '\n'
+ (level < 8 ? 'level up in:\t' + lvlUpAp + ' AP' : 'Congrats! (neeeeerd)') + (nextLvlAp > 0 ? 'level up in:\t' + lvlUpAp + ' AP' : 'Maximul level reached(!)')
+ '\n\Invites:\t'+PLAYER.available_invites + '\n\Invites:\t'+PLAYER.available_invites
+ '\n\nNote: your player stats can only be updated by a full reload (F5)'; + '\n\nNote: your player stats can only be updated by a full reload (F5)';
@ -387,7 +407,7 @@ window.setupPlayerStat = function() {
+ '</div>' + '</div>'
+ '<div id="stats">' + '<div id="stats">'
+ '<sup>XM: '+xmRatio+'%</sup>' + '<sup>XM: '+xmRatio+'%</sup>'
+ '<sub>' + (level < 8 ? 'level: '+lvlApProg+'%' : 'max level') + '</sub>' + '<sub>' + (nextLvlAp > 0 ? 'level: '+lvlApProg+'%' : 'max level') + '</sub>'
+ '</div>' + '</div>'
+ '</h2>' + '</h2>'
); );
@ -520,7 +540,6 @@ window.setupLayerChooserApi = function() {
// BOOTING /////////////////////////////////////////////////////////// // BOOTING ///////////////////////////////////////////////////////////
function boot() { function boot() {
try { //EXPERIMENTAL TEST
if(!isSmartphone()) // TODO remove completely? if(!isSmartphone()) // TODO remove completely?
window.debug.console.overwriteNativeIfRequired(); window.debug.console.overwriteNativeIfRequired();
@ -530,17 +549,13 @@ function boot() {
var iconDefImage = '@@INCLUDEIMAGE:images/marker-icon.png@@'; var iconDefImage = '@@INCLUDEIMAGE:images/marker-icon.png@@';
var iconDefRetImage = '@@INCLUDEIMAGE:images/marker-icon-2x.png@@'; var iconDefRetImage = '@@INCLUDEIMAGE:images/marker-icon-2x.png@@';
var iconShadowImage = '@@INCLUDEIMAGE:images/marker-shadow.png@@';
L.Icon.Default = L.Icon.extend({options: { L.Icon.Default = L.Icon.extend({options: {
iconUrl: iconDefImage, iconUrl: iconDefImage,
iconRetinaUrl: iconDefRetImage, iconRetinaUrl: iconDefRetImage,
shadowUrl: iconShadowImage,
shadowRetinaUrl: iconShadowImage,
iconSize: new L.Point(25, 41), iconSize: new L.Point(25, 41),
iconAnchor: new L.Point(12, 41), iconAnchor: new L.Point(12, 41),
popupAnchor: new L.Point(1, -34), popupAnchor: new L.Point(1, -34),
shadowSize: new L.Point(41, 41)
}}); }});
window.setupIdle(); window.setupIdle();
@ -644,20 +659,13 @@ function boot() {
android.bootFinished(); android.bootFinished();
} }
//EXPERIMENTAL TEST
} catch(e) {
console.log('Exception caught in IITC boot function - will fail to start');
console.log(e);
debugger;
throw e;
}
} }
@@INCLUDERAW:external/load.js@@ @@INCLUDERAW:external/load.js@@
try { console.log('Loading included JS now'); } catch(e) {} try { console.log('Loading included JS now'); } catch(e) {}
@@INCLUDERAW:external/leaflet.js@@ @@INCLUDERAW:external/leaflet-src.js@@
@@INCLUDERAW:external/L.Geodesic.js@@ @@INCLUDERAW:external/L.Geodesic.js@@
// modified version of https://github.com/shramov/leaflet-plugins. Also // modified version of https://github.com/shramov/leaflet-plugins. Also
// contains the default Ingress map style. // contains the default Ingress map style.

View File

@ -41,9 +41,7 @@ window.chat._oldBBox = null;
window.chat.genPostData = function(isFaction, storageHash, getOlderMsgs) { window.chat.genPostData = function(isFaction, storageHash, getOlderMsgs) {
if(typeof isFaction !== 'boolean') throw('Need to know if public or faction chat.'); if(typeof isFaction !== 'boolean') throw('Need to know if public or faction chat.');
// get window bounds, and extend to the minimum chat radius var b = map.getBounds();
chat._localRangeCircle.setLatLng(map.getCenter());
var b = map.getBounds().extend(chat._localRangeCircle.getBounds());
// set a current bounding box if none set so far // set a current bounding box if none set so far
if (!chat._oldBBox) chat._oldBBox = b; if (!chat._oldBBox) chat._oldBBox = b;
@ -574,8 +572,6 @@ window.chat.keepScrollPosition = function(box, scrollBefore, isOldMsgs) {
// //
window.chat.setup = function() { window.chat.setup = function() {
window.chat._localRangeCircle = L.circle(map.getCenter(), CHAT_MIN_RANGE*1000);
if (localStorage['iitc-chat-tab']) { if (localStorage['iitc-chat-tab']) {
var t = $('<a>'+localStorage['iitc-chat-tab']+'</a>'); var t = $('<a>'+localStorage['iitc-chat-tab']+'</a>');
window.chat.chooseAnchor(t); window.chat.chooseAnchor(t);

View File

@ -17,16 +17,21 @@ window.updateGameScore = function(data) {
// to detect the problem and try a different set is easiest in a place where there's only a single request of that type // to detect the problem and try a different set is easiest in a place where there's only a single request of that type
// sent at once, and it has no extra parameters. this method matches those requirements // sent at once, and it has no extra parameters. this method matches those requirements
if (data.error || (data.indexOf && data.indexOf('"error"') != -1)) { if (data.error || (data.indexOf && data.indexOf('"error"') != -1)) {
window.updateGameScoreFailCount++; if (data.error == 'missing version') {
if (window.updateGameScoreFailCount <= window.requestParameterMunges.length) { dialog({
//TODO: methods to try a different munge set? title: 'Reload IITC',
// window.activeRequestMungeSet = (window.activeRequestMungeSet+1) % window.requestParameterMunges.length; html: '<p>IITC is using an outdated munge set. This can happen when Niantic update the standard intel site.</p>'
// console.warn('IITC munge issue - cycling to set '+window.activeRequestMungeSet); +'<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.'
updateGameScore(); +'In this case, try clearing your cache, or waiting 15-30 minutes for the stale data to expire.</p>',
buttons: {
'RELOAD': function() { window.location.reload(); }
}
});
return; return;
} else { } else {
console.error('IITC munge issue - and retry limit reached. IITC will likely fail'); console.error('game score failed to load');
} }
} }

View File

@ -24,7 +24,7 @@ window.getMapZoomTileParameters = function(zoom) {
} catch(e) { } catch(e) {
console.warn(e); console.warn(e);
// known correct as of 2014-03-14 // known correct as of 2014-03-19
ZOOM_TO_TILES_PER_EDGE = [32, 32, 32, 32, 256, 256, 256, 1024, 1024, 1536, 4096, 4096, 6500, 6500, 6500]; ZOOM_TO_TILES_PER_EDGE = [32, 32, 32, 32, 256, 256, 256, 1024, 1024, 1536, 4096, 4096, 6500, 6500, 6500];
MAX_TILES_PER_EDGE = 9000; MAX_TILES_PER_EDGE = 9000;
ZOOM_TO_LEVEL = [8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 3, 2, 2, 1, 1]; ZOOM_TO_LEVEL = [8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 3, 2, 2, 1, 1];

View File

@ -3,7 +3,7 @@
window.RenderDebugTiles = function() { window.RenderDebugTiles = function() {
this.CLEAR_CHECK_TIME = 0.25; this.CLEAR_CHECK_TIME = L.Path.CANVAS ? 2.0 : 0.5;
this.FADE_TIME = 2.0; this.FADE_TIME = 2.0;
this.debugTileLayer = L.layerGroup(); this.debugTileLayer = L.layerGroup();
@ -57,6 +57,7 @@ window.RenderDebugTiles.prototype.setState = function(id,state) {
case 'request-fail': col='#a00'; fill='#666'; break; case 'request-fail': col='#a00'; fill='#666'; break;
case 'tile-fail': col='#f00'; fill='#666'; break; case 'tile-fail': col='#f00'; fill='#666'; break;
case 'tile-timeout': col='#ff0'; fill='#666'; break; case 'tile-timeout': col='#ff0'; fill='#666'; break;
case 'render-queue': col='#f0f'; fill='#f0f'; break;
} }
this.setColour (id, col, fill); this.setColour (id, col, fill);
if (clearDelay >= 0) { if (clearDelay >= 0) {
@ -72,8 +73,11 @@ window.RenderDebugTiles.prototype.setState = function(id,state) {
window.RenderDebugTiles.prototype.startTimer = function(waitTime) { window.RenderDebugTiles.prototype.startTimer = function(waitTime) {
var _this = this; var _this = this;
if (!this.timer) { if (!_this.timer) {
this.timer = setTimeout ( function() { _this.timer = undefined; _this.runClearPass(); }, waitTime ); // a timeout of 0 firing the actual timeout - helps things run smoother
_this.timer = setTimeout ( function() {
_this.timer = setTimeout ( function() { _this.timer = undefined; _this.runClearPass(); }, waitTime );
}, 0);
} }
} }

View File

@ -10,15 +10,16 @@ window.Render = function() {
this.CLUSTER_SIZE = L.Browser.mobile ? 16 : 8; // the map is divided into squares of this size in pixels for clustering purposes. mobile uses larger markers, so therefore larger clustering areas this.CLUSTER_SIZE = L.Browser.mobile ? 16 : 8; // the map is divided into squares of this size in pixels for clustering purposes. mobile uses larger markers, so therefore larger clustering areas
this.CLUSTER_PORTAL_LIMIT = 4; // no more than this many portals are drawn in each cluster square this.CLUSTER_PORTAL_LIMIT = 4; // no more than this many portals are drawn in each cluster square
// link length, in pixels, to be visible. use the portal cluster size, as shorter than this is likely hidden
this.entityVisibilityZoom = undefined; // under the portals
this.LINK_VISIBLE_PIXEL_LENGTH = this.CLUSTER_SIZE;
this.portalMarkerScale = undefined; this.portalMarkerScale = undefined;
} }
// start a render pass. called as we start to make the batch of data requests to the servers // start a render pass. called as we start to make the batch of data requests to the servers
window.Render.prototype.startRenderPass = function() { window.Render.prototype.startRenderPass = function(level,bounds) {
this.isRendering = true; this.isRendering = true;
this.deletedGuid = {}; // object - represents the set of all deleted game entity GUIDs seen in a render pass this.deletedGuid = {}; // object - represents the set of all deleted game entity GUIDs seen in a render pass
@ -26,6 +27,16 @@ window.Render.prototype.startRenderPass = function() {
this.seenPortalsGuid = {}; this.seenPortalsGuid = {};
this.seenLinksGuid = {}; this.seenLinksGuid = {};
this.seenFieldsGuid = {}; this.seenFieldsGuid = {};
this.bounds = bounds;
this.clearPortalsBelowLevel(level);
this.resetPortalClusters();
this.resetLinkVisibility();
this.rescalePortalMarkers();
} }
window.Render.prototype.clearPortalsBelowLevel = function(level) { window.Render.prototype.clearPortalsBelowLevel = function(level) {
@ -41,40 +52,6 @@ window.Render.prototype.clearPortalsBelowLevel = function(level) {
console.log('Render: deleted '+count+' portals by level'); console.log('Render: deleted '+count+' portals by level');
} }
window.Render.prototype.clearEntitiesOutsideBounds = function(bounds) {
var pcount=0, lcount=0, fcount=0;
for (var guid in window.portals) {
var p = portals[guid];
if (!bounds.contains (p.getLatLng()) && guid !== selectedPortal && !artifact.isInterestingPortal(guid)) {
this.deletePortalEntity(guid);
pcount++;
}
}
for (var guid in window.links) {
var l = links[guid];
if (!bounds.intersects (l.getBounds())) {
this.deleteLinkEntity(guid);
lcount++;
}
}
for (var guid in window.fields) {
var f = fields[guid];
if (!bounds.intersects (f.getBounds())) {
this.deleteFieldEntity(guid);
fcount++;
}
}
console.log('Render: deleted '+pcount+' portals, '+lcount+' links, '+fcount+' fields by bounds check');
}
// TODO? as well as clearing portals by level, and clearing entities outside the bounds...
// can we clear unneeded 'fake' links after zooming out? based on the portals no longer being available to construct
// the data? (not *required* - as they'll be removed in the endRenderPass code - but clearing things earlier rather than
// later is preferred, if possible)
// process deleted entity list and entity data // process deleted entity list and entity data
window.Render.prototype.processTileData = function(tiledata) { window.Render.prototype.processTileData = function(tiledata) {
@ -167,7 +144,6 @@ window.Render.prototype.endRenderPass = function() {
} }
window.Render.prototype.bringPortalsToFront = function() { window.Render.prototype.bringPortalsToFront = function() {
return;
for (var lvl in portalsFactionLayers) { for (var lvl in portalsFactionLayers) {
// portals are stored in separate layers per faction // portals are stored in separate layers per faction
// to avoid giving weight to one faction or another, we'll push portals to front based on GUID order // to avoid giving weight to one faction or another, we'll push portals to front based on GUID order
@ -396,27 +372,25 @@ window.Render.prototype.createLinkEntity = function(ent,faked) {
window.links[ent[0]] = poly; window.links[ent[0]] = poly;
// only add the link to the layer if it's long enough to be seen
if (this.linkVisible(poly)) {
linksFactionLayers[poly.options.team].addLayer(poly); linksFactionLayers[poly.options.team].addLayer(poly);
} }
}
window.Render.prototype.updateEntityVisibility = function() { window.Render.prototype.rescalePortalMarkers = function() {
if (this.entityVisibilityZoom === undefined || this.entityVisibilityZoom != map.getZoom()) {
this.entityVisibilityZoom = map.getZoom();
this.resetPortalClusters();
if (this.portalMarkerScale === undefined || this.portalMarkerScale != portalMarkerScale()) { if (this.portalMarkerScale === undefined || this.portalMarkerScale != portalMarkerScale()) {
this.portalMarkerScale = portalMarkerScale(); this.portalMarkerScale = portalMarkerScale();
console.log('Render: map zoom '+map.getZoom()+' changes portal scale to '+portalMarkerScale()+' - redrawing all portals'); console.log('Render: map zoom '+map.getZoom()+' changes portal scale to '+portalMarkerScale()+' - redrawing all portals');
//NOTE: we're not calling this because it resets highlights - we're calling it as it resets the style (inc size) of all portal markers //NOTE: we're not calling this because it resets highlights - we're calling it as it
// resets the style (inc size) of all portal markers, applying the new scale
resetHighlightedPortals(); resetHighlightedPortals();
} }
} }
}
@ -452,7 +426,7 @@ window.Render.prototype.resetPortalClusters = function() {
var guid = c[i]; var guid = c[i];
var p = window.portals[guid]; var p = window.portals[guid];
var layerGroup = portalsFactionLayers[parseInt(p.options.level)][p.options.team]; var layerGroup = portalsFactionLayers[parseInt(p.options.level)][p.options.team];
if (i<this.CLUSTER_PORTAL_LIMIT || p.options.guid == selectedPortal || artifact.isInterestingPortal(p.options.guid)) { if ((i<this.CLUSTER_PORTAL_LIMIT || p.options.guid == selectedPortal || artifact.isInterestingPortal(p.options.guid)) && this.bounds.contains(p.getLatLng())) {
if (!layerGroup.hasLayer(p)) { if (!layerGroup.hasLayer(p)) {
layerGroup.addLayer(p); layerGroup.addLayer(p);
} }
@ -479,9 +453,11 @@ window.Render.prototype.addPortalToMapLayer = function(portal) {
// however, it won't make a lot of visible difference compared to just pushing to the end of the list, then // however, it won't make a lot of visible difference compared to just pushing to the end of the list, then
// adding to the visible layer if the list is below the limit // adding to the visible layer if the list is below the limit
if (this.portalClusters[cid].length < this.CLUSTER_PORTAL_LIMIT || portal.options.guid == selectedPortal || artifact.isInterestingPortal(portal.options.guid)) { if (this.portalClusters[cid].length < this.CLUSTER_PORTAL_LIMIT || portal.options.guid == selectedPortal || artifact.isInterestingPortal(portal.options.guid)) {
if (this.bounds.contains(portal.getLatLng())) {
portalsFactionLayers[parseInt(portal.options.level)][portal.options.team].addLayer(portal); portalsFactionLayers[parseInt(portal.options.level)][portal.options.team].addLayer(portal);
} }
} }
}
window.Render.prototype.removePortalFromMapLayer = function(portal) { window.Render.prototype.removePortalFromMapLayer = function(portal) {
@ -495,7 +471,7 @@ window.Render.prototype.removePortalFromMapLayer = function(portal) {
var index = this.portalClusters[cid].indexOf(portal.options.guid); var index = this.portalClusters[cid].indexOf(portal.options.guid);
if (index >= 0) { if (index >= 0) {
this.portalClusters[cid].splice(index,1); this.portalClusters[cid].splice(index,1);
// FIXME? if this portal was in on the screen (in the first 10), and we still have 10+ portals, add the new 10to to the screen? // FIXME? if this portal was in on the screen (in the first 10), and we still have 10+ portals, add the new 10th to the screen?
} }
} }
} }
@ -512,3 +488,50 @@ window.Render.prototype.getPortalClusterID = function(portal) {
} }
window.Render.prototype.linkVisible = function(link) {
if (!this.bounds.intersects(link.getBounds())) {
return false;
}
var lengthSquared = this.getLinkPixelLengthSquared (link);
return lengthSquared >= this.LINK_VISIBLE_PIXEL_LENGTH*this.LINK_VISIBLE_PIXEL_LENGTH;
}
window.Render.prototype.resetLinkVisibility = function() {
for (var guid in window.links) {
var link = window.links[guid];
var visible = this.linkVisible(link);
if (visible) {
if (!linksFactionLayers[link.options.team].hasLayer(link)) linksFactionLayers[link.options.team].addLayer(link);
} else {
if (linksFactionLayers[link.options.team].hasLayer(link)) linksFactionLayers[link.options.team].removeLayer(link);
}
}
}
window.Render.prototype.getLinkPixelLengthSquared = function(link) {
var z = map.getZoom();
var latLngs = link.getLatLngs();
if (latLngs.length != 2) {
console.warn ('Link had '+latLngs.length+' points - expected 2!');
return undefined;
}
var point0 = map.project(latLngs[0]);
var point1 = map.project(latLngs[1]);
var dx = point0.x - point1.x;
var dy = point0.y - point1.y;
var lengthSquared = (dx*dx)+(dy*dy);
return lengthSquared;
}

View File

@ -12,6 +12,10 @@ window.MapDataRequest = function() {
this.activeRequestCount = 0; this.activeRequestCount = 0;
this.requestedTiles = {}; this.requestedTiles = {};
this.renderQueue = [];
this.renderQueueTimer = undefined;
this.renderQueuePaused = false;
this.idle = false; this.idle = false;
@ -27,30 +31,37 @@ window.MapDataRequest = function() {
this.MAX_TILE_RETRIES = 2; this.MAX_TILE_RETRIES = 2;
// refresh timers // refresh timers
this.MOVE_REFRESH = 1; //time, after a map move (pan/zoom) before starting the refresh processing this.MOVE_REFRESH = 3; //time, after a map move (pan/zoom) before starting the refresh processing
this.STARTUP_REFRESH = 3; //refresh time used on first load of IITC this.STARTUP_REFRESH = 3; //refresh time used on first load of IITC
this.IDLE_RESUME_REFRESH = 5; //refresh time used after resuming from idle this.IDLE_RESUME_REFRESH = 5; //refresh time used after resuming from idle
// after one of the above, there's an additional delay between preparing the refresh (clearing out of bounds, // after one of the above, there's an additional delay between preparing the refresh (clearing out of bounds,
// processing cache, etc) and actually sending the first network requests // processing cache, etc) and actually sending the first network requests
this.DOWNLOAD_DELAY = 3; //delay after preparing the data download before tile requests are sent this.DOWNLOAD_DELAY = 1; //delay after preparing the data download before tile requests are sent
// a short delay between one request finishing and the queue being run for the next request. // a short delay between one request finishing and the queue being run for the next request.
// this gives a chance of other requests finishing, allowing better grouping of retries in new requests // this gives a chance of other requests finishing, allowing better grouping of retries in new requests
this.RUN_QUEUE_DELAY = 0.5; this.RUN_QUEUE_DELAY = 0.2;
// delay before requeuing tiles in failed requests // delay before processing the queue after failed requests
this.BAD_REQUEST_REQUEUE_DELAY = 10; // longer delay before retrying a completely failed request - as in this case the servers are struggling this.BAD_REQUEST_RUN_QUEUE_DELAY = 10; // longer delay before doing anything after errors (other than TIMEOUT)
// a delay before processing the queue after requeuing tiles. this gives a chance for other requests to finish // delay before processing the queue after error==TIMEOUT requests. this is a less severe error than other errors
// or other requeue actions to happen before the queue is processed, allowing better grouping of requests this.TIMEOUT_REQUEST_RUN_QUEUE_DELAY = 2;
// however, the queue may be processed sooner if a previous timeout was set
this.REQUEUE_DELAY = 1;
this.REFRESH_CLOSE = 120; // refresh time to use for close views z>12 when not idle and not moving // render queue
this.REFRESH_FAR = 600; // refresh time for far views z <= 12 // 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 : (typeof android === 'undefined') ? 2000 : 500;
// delay before repeating the render loop. this gives a better chance for user interaction
this.RENDER_PAUSE = 0.05; //50ms
this.REFRESH_CLOSE = 300; // refresh time to use for close views z>12 when not idle and not moving
this.REFRESH_FAR = 900; // refresh time for far views z <= 12
this.FETCH_TO_REFRESH_FACTOR = 2; //refresh time is based on the time to complete a data fetch, times this value this.FETCH_TO_REFRESH_FACTOR = 2; //refresh time is based on the time to complete a data fetch, times this value
// ensure we have some initial map status // ensure we have some initial map status
@ -82,7 +93,7 @@ window.MapDataRequest.prototype.mapMoveStart = function() {
this.setStatus('paused'); this.setStatus('paused');
this.clearTimeout(); this.clearTimeout();
this.pauseRenderQueue(true);
} }
window.MapDataRequest.prototype.mapMoveEnd = function() { window.MapDataRequest.prototype.mapMoveEnd = function() {
@ -100,6 +111,7 @@ window.MapDataRequest.prototype.mapMoveEnd = function() {
if (remainingTime > this.MOVE_REFRESH) { if (remainingTime > this.MOVE_REFRESH) {
this.setStatus('done','Map moved, but no data updates needed'); this.setStatus('done','Map moved, but no data updates needed');
this.refreshOnTimeout(remainingTime); this.refreshOnTimeout(remainingTime);
this.pauseRenderQueue(false);
return; return;
} }
} }
@ -136,8 +148,11 @@ window.MapDataRequest.prototype.refreshOnTimeout = function(seconds) {
console.log('starting map refresh in '+seconds+' seconds'); console.log('starting map refresh in '+seconds+' seconds');
// 'this' won't be right inside the callback, so save it // 'this' won't be right inside the callback, so save it
var savedContext = this; // also, double setTimeout used to ensure the delay occurs after any browser-related rendering/updating/etc
this.timer = setTimeout ( function() { savedContext.timer = undefined; savedContext.refresh(); }, seconds*1000); var _this = this;
this.timer = setTimeout ( function() {
_this.timer = setTimeout ( function() { _this.timer = undefined; _this.refresh(); }, seconds*1000);
}, 0);
this.timerExpectedTimeoutTime = new Date().getTime() + seconds*1000; this.timerExpectedTimeoutTime = new Date().getTime() + seconds*1000;
} }
@ -167,6 +182,7 @@ window.MapDataRequest.prototype.refresh = function() {
this.refreshStartTime = new Date().getTime(); this.refreshStartTime = new Date().getTime();
this.debugTiles.reset(); this.debugTiles.reset();
this.resetRenderQueue();
// a 'set' to keep track of hard failures for tiles // a 'set' to keep track of hard failures for tiles
this.tileErrorCount = {}; this.tileErrorCount = {};
@ -211,11 +227,8 @@ window.MapDataRequest.prototype.refresh = function() {
window.runHooks ('mapDataRefreshStart', {bounds: bounds, mapZoom: mapZoom, dataZoom: dataZoom, minPortalLevel: tileParams.level, tileBounds: dataBounds}); window.runHooks ('mapDataRefreshStart', {bounds: bounds, mapZoom: mapZoom, dataZoom: dataZoom, minPortalLevel: tileParams.level, tileBounds: dataBounds});
this.render.startRenderPass(); this.render.startRenderPass(tileParams.level, dataBounds);
this.render.clearPortalsBelowLevel(tileParams.level);
this.render.clearEntitiesOutsideBounds(dataBounds);
this.render.updateEntityVisibility();
this.render.processGameEntities(artifact.getArtifactEntities()); this.render.processGameEntities(artifact.getArtifactEntities());
@ -248,22 +261,16 @@ window.MapDataRequest.prototype.refresh = function() {
//TODO: with recent backend changes there are now multiple zoom levels of data that is identical except perhaps for some //TODO: with recent backend changes there are now multiple zoom levels of data that is identical except perhaps for some
// reduction of detail when zoomed out. to take good advantage of the cache, a check for cached data at a closer zoom // reduction of detail when zoomed out. to take good advantage of the cache, a check for cached data at a closer zoom
// but otherwise the same parameters (min portal level, tiles per edge) will mean less downloads when zooming out // but otherwise the same parameters (min portal level, tiles per edge) will mean less downloads when zooming out
// (however, the default code in getDataZoomForMapZoom currently reduces the need for this, as it forces the furthest
// out zoom tiles for a detail level)
if (this.cache && this.cache.isFresh(tile_id) ) { if (this.cache && this.cache.isFresh(tile_id) ) {
// data is fresh in the cache - just render it // data is fresh in the cache - just render it
this.debugTiles.setState(tile_id, 'cache-fresh'); this.pushRenderQueue(tile_id,this.cache.get(tile_id),'cache-fresh');
this.render.processTileData (this.cache.get(tile_id));
this.cachedTileCount += 1; this.cachedTileCount += 1;
} else { } else {
// no fresh data // no fresh data
// render the cached stale data, if we have it. this ensures *something* appears quickly when possible
// var old_data = this.cache && this.cache.get(tile_id);
// if (old_data) {
// this.render.processTileData (old_data);
// }
// tile needed. calculate the distance from the centre of the screen, to optimise the load order // tile needed. calculate the distance from the centre of the screen, to optimise the load order
var latCenter = (latNorth+latSouth)/2; var latCenter = (latNorth+latSouth)/2;
@ -315,37 +322,19 @@ window.MapDataRequest.prototype.refresh = function() {
window.MapDataRequest.prototype.delayProcessRequestQueue = function(seconds,isFirst) { window.MapDataRequest.prototype.delayProcessRequestQueue = function(seconds,isFirst) {
if (this.timer === undefined) { if (this.timer === undefined) {
var savedContext = this; var _this = this;
this.timer = setTimeout ( function() { savedContext.timer = undefined; savedContext.processRequestQueue(isFirst); }, seconds*1000 ); this.timer = setTimeout ( function() {
_this.timer = setTimeout ( function() { _this.timer = undefined; _this.processRequestQueue(isFirst); }, seconds*1000 );
}, 0);
} }
} }
window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) { window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) {
// if nothing left in the queue, end the render. otherwise, send network requests // if nothing left in the queue, finish
if (Object.keys(this.queuedTiles).length == 0) { if (Object.keys(this.queuedTiles).length == 0) {
// we leave the renderQueue code to handle ending the render pass now
this.render.endRenderPass();
var endTime = new Date().getTime();
var duration = (endTime - this.refreshStartTime)/1000;
console.log('finished requesting data! (took '+duration+' seconds to complete)');
window.runHooks ('mapDataRefreshEnd', {});
var longStatus = 'Tiles: ' + this.cachedTileCount + ' cached, ' +
this.successTileCount + ' loaded, ' +
(this.staleTileCount ? this.staleTileCount + ' stale, ' : '') +
(this.failedTileCount ? this.failedTileCount + ' failed, ' : '') +
'in ' + duration + ' seconds';
// refresh timer based on time to run this pass, with a minimum of REFRESH seconds
var minRefresh = map.getZoom()>12 ? this.REFRESH_CLOSE : this.REFRESH_FAR;
var refreshTimer = Math.max(minRefresh, duration*this.FETCH_TO_REFRESH_FACTOR);
this.refreshOnTimeout(refreshTimer);
this.setStatus (this.failedTileCount ? 'errors' : this.staleTileCount ? 'out of date' : 'done', longStatus);
return; return;
} }
@ -436,8 +425,7 @@ window.MapDataRequest.prototype.requeueTile = function(id, error) {
var data = this.cache ? this.cache.get(id) : undefined; var data = this.cache ? this.cache.get(id) : undefined;
if (data) { if (data) {
// we have cached data - use it, even though it's stale // we have cached data - use it, even though it's stale
this.debugTiles.setState (id, 'cache-stale'); this.pushRenderQueue(id,data,'cache-stale');
this.render.processTileData (data);
this.staleTileCount += 1; this.staleTileCount += 1;
} else { } else {
// no cached data // no cached data
@ -472,7 +460,7 @@ window.MapDataRequest.prototype.handleResponse = function (data, tiles, success)
var timeoutTiles = []; var timeoutTiles = [];
if (!success || !data || !data.result) { if (!success || !data || !data.result) {
console.warn("Request.handleResponse: request failed - requeuing..."); console.warn('Request.handleResponse: request failed - requeuing...'+(data && data.error?' error: '+data.error:''));
//request failed - requeue all the tiles(?) //request failed - requeue all the tiles(?)
@ -514,9 +502,8 @@ window.MapDataRequest.prototype.handleResponse = function (data, tiles, success)
// if this tile was in the render list, render it // if this tile was in the render list, render it
// (requests aren't aborted when new requests are started, so it's entirely possible we don't want to render it!) // (requests aren't aborted when new requests are started, so it's entirely possible we don't want to render it!)
if (id in this.queuedTiles) { if (id in this.queuedTiles) {
this.debugTiles.setState (id, 'ok');
this.render.processTileData (val); this.pushRenderQueue(id,val,'ok');
delete this.queuedTiles[id]; delete this.queuedTiles[id];
this.successTileCount += 1; this.successTileCount += 1;
@ -532,9 +519,12 @@ window.MapDataRequest.prototype.handleResponse = function (data, tiles, success)
window.runHooks('requestFinished', {success: true}); window.runHooks('requestFinished', {success: true});
} }
// set the queue delay based on any errors or timeouts
var nextQueueDelay = errorTiles.length > 0 ? this.BAD_REQUEST_RUN_QUEUE_DELAY :
timeoutTiles.length > 0 ? this.TIMEOUT_REQUEST_RUN_QUEUE_DELAY :
this.RUN_QUEUE_DELAY;
console.log ('getThinnedEntities status: '+tiles.length+' tiles: '+successTiles.length+' successful, '+timeoutTiles.length+' timed out, '+errorTiles.length+' failed'); console.log ('getThinnedEntities status: '+tiles.length+' tiles: '+successTiles.length+' successful, '+timeoutTiles.length+' timed out, '+errorTiles.length+' failed. delay '+nextQueueDelay+' seconds');
// requeue any 'timeout' tiles immediately // requeue any 'timeout' tiles immediately
if (timeoutTiles.length > 0) { if (timeoutTiles.length > 0) {
@ -545,27 +535,127 @@ window.MapDataRequest.prototype.handleResponse = function (data, tiles, success)
} }
} }
// but for other errors, delay before retrying (as the server is having issues)
if (errorTiles.length > 0) { if (errorTiles.length > 0) {
//setTimeout has no way of passing the 'context' (aka 'this') to it's function
var savedContext = this;
setTimeout (function() {
for (var i in errorTiles) { for (var i in errorTiles) {
var id = errorTiles[i]; var id = errorTiles[i];
delete savedContext.requestedTiles[id]; delete this.requestedTiles[id];
savedContext.requeueTile(id, true); this.requeueTile(id, true);
} }
savedContext.delayProcessRequestQueue(this.REQUEUE_DELAY);
}, this.BAD_REQUEST_REQUEUE_DELAY*1000);
} }
for (var i in successTiles) { for (var i in successTiles) {
var id = successTiles[i]; var id = successTiles[i];
delete this.requestedTiles[id]; delete this.requestedTiles[id];
} }
//.. should this also be delayed a small amount? //.. should this also be delayed a small amount?
this.delayProcessRequestQueue(this.RUN_QUEUE_DELAY); this.delayProcessRequestQueue(nextQueueDelay);
}
window.MapDataRequest.prototype.resetRenderQueue = function() {
this.renderQueue = [];
if (this.renderQueueTimer) {
clearTimeout(this.renderQueueTimer);
this.renderQueueTimer = undefined;
}
this.renderQueuePaused = false;
}
window.MapDataRequest.prototype.pushRenderQueue = function (id, data, status) {
this.debugTiles.setState(id,'render-queue');
this.renderQueue.push({
id:id,
// the data in the render queue is modified as we go, so we need to copy the values of the arrays. just storing the reference would modify the data in the cache!
deleted: (data.deletedGameEntityGuids||[]).slice(0),
entities: (data.gameEntities||[]).slice(0),
status:status});
if (!this.renderQueuePaused) {
this.startQueueTimer(this.RENDER_PAUSE);
}
}
window.MapDataRequest.prototype.startQueueTimer = function(delay) {
if (this.renderQueueTimer === undefined) {
var _this = this;
this.renderQueueTimer = setTimeout( function() {
_this.renderQueueTimer = setTimeout ( function() { _this.renderQueueTimer = undefined; _this.processRenderQueue(); }, (delay||0)*1000 );
}, 0);
}
}
window.MapDataRequest.prototype.pauseRenderQueue = function(pause) {
this.renderQueuePaused = pause;
if (pause) {
if (this.renderQueueTimer) {
clearTimeout(this.renderQueueTimer);
this.renderQueueTimer = undefined;
}
} else {
if (this.renderQueue.length > 0) {
this.startQueueTimer(this.RENDER_PAUSE);
}
}
}
window.MapDataRequest.prototype.processRenderQueue = function() {
var drawEntityLimit = this.RENDER_BATCH_SIZE;
//TODO: we don't take account of how many of the entities are actually new/removed - they
// could already be drawn and not changed. will see how it works like this...
while (drawEntityLimit > 0 && this.renderQueue.length > 0) {
var current = this.renderQueue[0];
if (current.deleted.length > 0) {
var deleteThisPass = current.deleted.splice(0,drawEntityLimit);
drawEntityLimit -= deleteThisPass.length;
this.render.processDeletedGameEntityGuids(deleteThisPass);
}
if (drawEntityLimit > 0 && current.entities.length > 0) {
var drawThisPass = current.entities.splice(0,drawEntityLimit);
drawEntityLimit -= drawThisPass.length;
this.render.processGameEntities(drawThisPass);
}
if (current.deleted.length == 0 && current.entities.length == 0) {
this.renderQueue.splice(0,1);
this.debugTiles.setState(current.id, current.status);
}
}
if (this.renderQueue.length > 0) {
this.startQueueTimer(this.RENDER_PAUSE);
} else if (Object.keys(this.queuedTiles).length == 0) {
this.render.endRenderPass();
var endTime = new Date().getTime();
var duration = (endTime - this.refreshStartTime)/1000;
console.log('finished requesting data! (took '+duration+' seconds to complete)');
window.runHooks ('mapDataRefreshEnd', {});
var longStatus = 'Tiles: ' + this.cachedTileCount + ' cached, ' +
this.successTileCount + ' loaded, ' +
(this.staleTileCount ? this.staleTileCount + ' stale, ' : '') +
(this.failedTileCount ? this.failedTileCount + ' failed, ' : '') +
'in ' + duration + ' seconds';
// refresh timer based on time to run this pass, with a minimum of REFRESH seconds
var minRefresh = map.getZoom()>12 ? this.REFRESH_CLOSE : this.REFRESH_FAR;
var refreshTimer = Math.max(minRefresh, duration*this.FETCH_TO_REFRESH_FACTOR);
this.refreshOnTimeout(refreshTimer);
this.setStatus (this.failedTileCount ? 'errors' : this.staleTileCount ? 'out of date' : 'done', longStatus);
}
} }

View File

@ -115,7 +115,15 @@ function extractMungeFromStock() {
foundMunges.quadKeys = result[1] || result[2]; foundMunges.quadKeys = result[1] || result[2];
// GET_PAGINATED_PLEXTS // GET_PAGINATED_PLEXTS
var reg = new RegExp('GET_PAGINATED_PLEXTS, [a-z] = [a-z] \\|\\| nemesis.dashboard.BoundsParams.getBoundsParamsForWorld\\(\\), [a-z] = [a-z] \\|\\| -1, [a-z] = [a-z] \\|\\| -1, [a-z] = {'+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'Math.round\\([a-z].bounds.sw.lat\\(\\) \\* 1E6\\), '+mungeRegExpLit+'Math.round\\([a-z].bounds.sw.lng\\(\\) \\* 1E6\\), '+mungeRegExpLit+'Math.round\\([a-z].bounds.ne.lat\\(\\) \\* 1E6\\), '+mungeRegExpLit+'Math.round\\([a-z].bounds.ne.lng\\(\\) \\* 1E6\\), '+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z]};\n *[a-z]'+mungeRegExpProp+' = [a-z];\n *[a-z] > -1 && \\([a-z]'+mungeRegExpProp+' = true\\);', 'm'); var reg = new RegExp('[a-z] = {'+mungeRegExpLit+'[a-z], '
+mungeRegExpLit+'Math.round\\(1E6 \\* [a-z].bounds.sw.lat\\(\\)\\), '
+mungeRegExpLit+'Math.round\\(1E6 \\* [a-z].bounds.sw.lng\\(\\)\\), '
+mungeRegExpLit+'Math.round\\(1E6 \\* [a-z].bounds.ne.lat\\(\\)\\), '
+mungeRegExpLit+'Math.round\\(1E6 \\* [a-z].bounds.ne.lng\\(\\)\\), '
+mungeRegExpLit+'[a-z], '+mungeRegExpLit+'[a-z]};\n'
+' *[a-z]'+mungeRegExpProp+' = [a-z];\n'
+' *-1 < [a-z] && \\([a-z]'+mungeRegExpProp+' = !0\\);', 'm');
var result = reg.exec(nemesis.dashboard.network.PlextStore.prototype.getPlexts.toString()); var result = reg.exec(nemesis.dashboard.network.PlextStore.prototype.getPlexts.toString());
foundMunges.desiredNumItems = result[1] || result[2]; foundMunges.desiredNumItems = result[1] || result[2];

View File

@ -168,8 +168,8 @@ window.getPortalMiscDetails = function(guid,d) {
links[link.isOrigin ? 'outgoing' : 'incoming']++; links[link.isOrigin ? 'outgoing' : 'incoming']++;
}); });
function linkExpl(t) { return '<tt title="↳ incoming links\n↴ outgoing links\n• is the portal">'+t+'</tt>'; } 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.incoming+'&nbsp;&nbsp;•&nbsp;&nbsp;'+links.outgoing+' ↴')]; var linksText = [linkExpl('links'), linkExpl(links.outgoing+' out / '+links.incoming+' in')];
var player = d.captured && d.captured.capturingPlayerId var player = d.captured && d.captured.capturingPlayerId
? '<span class="nickname">' + d.captured.capturingPlayerId + '</span>' ? '<span class="nickname">' + d.captured.capturingPlayerId + '</span>'

View File

@ -166,11 +166,6 @@ window.runOnSmartphonesAfterBoot = function() {
addHook('portalSelected', window.setAndroidPermalink); addHook('portalSelected', window.setAndroidPermalink);
} }
// Force lower render limits for mobile
window.VIEWPORT_PAD_RATIO = 0.1;
window.MAX_DRAWN_PORTALS = 500;
window.MAX_DRAWN_LINKS = 200;
window.MAX_DRAWN_FIELDS = 100;
} }
window.setAndroidPermalink = function() { window.setAndroidPermalink = function() {

View File

@ -2,6 +2,8 @@
// gives user feedback about pending operations. Draws current status // gives user feedback about pending operations. Draws current status
// to website. Updates info in layer chooser. // to website. Updates info in layer chooser.
window.renderUpdateStatusTimer_ = undefined;
window.renderUpdateStatus = function() { window.renderUpdateStatus = function() {
var progress = 1; var progress = 1;
@ -41,28 +43,6 @@ window.renderUpdateStatus = function() {
t += '...unknown...'; t += '...unknown...';
} }
/*
if(mapRunsUserAction)
t += '<span class="help" title="Paused due to user interaction">paused</span';
else if(isIdle())
t += '<span style="color:#888">Idle</span>';
else if(window.requests._quickRefreshPending)
t += 'refreshing';
else if(window.activeRequests.length > 0)
t += window.activeRequests.length + ' requests';
else {
// tooltip with detailed tile counts
t += '<span class="help" title="'+window.statusTotalMapTiles+' tiles: '+window.statusCachedMapTiles+' cached, '+window.statusSuccessMapTiles+' successful, '+window.statusStaleMapTiles+' stale, '+window.statusErrorMapTiles+' failed">';
// basic error/out of date/up to date message
if (window.statusErrorMapTiles) t += '<span style="color:#f66">Errors</span>';
else if (window.statusStaleMapTiles) t += '<span style="color:#fa6">Out of date</span>';
else t += 'Up to date';
t += '</span>';
}
*/
t += '</span>'; t += '</span>';
//request status //request status
@ -71,16 +51,15 @@ window.renderUpdateStatus = function() {
if (window.failedRequestCount > 0) if (window.failedRequestCount > 0)
t += ' <span style="color:#f66">' + window.failedRequestCount + ' failed</span>' t += ' <span style="color:#f66">' + window.failedRequestCount + ' failed</span>'
// layer selector - enable/disable layers that aren't visible due to zoom level
//FIXME! move this somewhere more suitable!
var portalSelection = $('.leaflet-control-layers-overlays label'); //it's possible that updating the status bar excessively causes some performance issues. so rather than doing it
//it's an array - 0=unclaimed, 1=lvl 1, 2=lvl 2, ..., 8=lvl 8 - 9 relevant entries //immediately, delay it to the next javascript event loop, cancelling any pending update
//mark all levels below (but not at) minlvl as disabled // will also cause any browser-related rendering to occur first, before the status actually updates
portalSelection.slice(0, minlvl).addClass('disabled').attr('title', 'Zoom in to show those.');
//and all from minlvl to 8 as enabled
portalSelection.slice(minlvl, 8+1).removeClass('disabled').attr('title', '');
if (window.renderUpdateStatusTimer_) clearTimeout(window.renderUpdateStatusTimer_);
window.renderUpdateStatusTimer_ = setTimeout ( function() {
window.renderUpdateStatusTimer_ = undefined;
$('#innerstatus').html(t); $('#innerstatus').html(t);
//$('#updatestatus').click(function() { startRefreshTimeout(10); }); //$('#updatestatus').click(function() { startRefreshTimeout(10); });
@ -93,4 +72,6 @@ window.renderUpdateStatus = function() {
if (typeof android !== 'undefined' && android && android.setProgress) if (typeof android !== 'undefined' && android && android.setProgress)
android.setProgress(progress); android.setProgress(progress);
}, 0);
} }

View File

@ -41,8 +41,8 @@ Modified by qnstie 2013-07-17 to maintain compatibility with Leaflet.draw
// points code that have been seen // points code that have been seen
function geodesicConvertLine(startLatLng, endLatLng, convertedPoints) { function geodesicConvertLine(startLatLng, endLatLng, convertedPoints) {
var R = 6378137; // earth radius in meters (doesn't have to be exact) var R = 6378137; // earth radius in meters (doesn't have to be exact)
var d2r = L.LatLng.DEG_TO_RAD; var d2r = Math.PI/180.0;
var r2d = L.LatLng.RAD_TO_DEG; var r2d = 180.0/Math.PI;
// maths based on http://williams.best.vwh.net/avform.htm#Int // maths based on http://williams.best.vwh.net/avform.htm#Int
@ -180,8 +180,8 @@ Modified by qnstie 2013-07-17 to maintain compatibility with Leaflet.draw
_calcPoints: function() { _calcPoints: function() {
var R = 6378137; //earth radius in meters (approx - taken from leaflet source code) var R = 6378137; //earth radius in meters (approx - taken from leaflet source code)
var d2r = L.LatLng.DEG_TO_RAD; var d2r = Math.PI/180.0;
var r2d = L.LatLng.RAD_TO_DEG; var r2d = 180.0/Math.PI;
//console.log("geodesicCircle: radius = "+this._mRadius+"m, centre "+this._latlng.lat+","+this._latlng.lng); //console.log("geodesicCircle: radius = "+this._mRadius+"m, centre "+this._latlng.lat+","+this._latlng.lng);
// circle radius as an angle from the centre of the earth // circle radius as an angle from the centre of the earth

View File

@ -5072,10 +5072,35 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path :
return this; return this;
}, },
onAdd: function (map) {
this._map = map;
if (!this._container) {
this._initElements();
this._initEvents();
}
this.projectLatlngs();
this._updatePath();
if (this._container) {
this._map._pathRoot.appendChild(this._container);
}
this.fire('add');
map.on({
'viewreset': this.projectLatlngs,
'moveend': this._updatePath,
'canvasredraw': this._updatePath
}, this);
},
onRemove: function (map) { onRemove: function (map) {
map map
.off('viewreset', this.projectLatlngs, this) .off('viewreset', this.projectLatlngs, this)
.off('moveend', this._updatePath, this); .off('moveend', this._updatePath, this)
.off('canvasredraw', this._updatePath, this);
if (this.options.clickable) { if (this.options.clickable) {
this._map.off('click', this._onClick, this); this._map.off('click', this._onClick, this);
@ -5089,13 +5114,13 @@ L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path :
_requestUpdate: function () { _requestUpdate: function () {
if (this._map && !L.Path._updateRequest) { if (this._map && !L.Path._updateRequest) {
L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map); L.Path._updateRequest = L.Util.requestAnimFrame(this._fireCanvasRedraw, this._map);
} }
}, },
_fireMapMoveEnd: function () { _fireCanvasRedraw: function () {
L.Path._updateRequest = null; L.Path._updateRequest = null;
this.fire('moveend'); this.fire('canvasredraw');
}, },
_initElements: function () { _initElements: function () {
@ -5215,6 +5240,7 @@ L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {}
this.on('zoomend', this._endPathZoom); this.on('zoomend', this._endPathZoom);
} }
this.on('moveend', this._updateCanvasViewport); this.on('moveend', this._updateCanvasViewport);
this.on('canvasredraw', this._updateCanvasViewport);
this._updateCanvasViewport(); this._updateCanvasViewport();
} }
}, },

View File

@ -28,7 +28,7 @@ window.S2 = {};
var LatLngToXYZ = function(latLng) { var LatLngToXYZ = function(latLng) {
var d2r = L.LatLng.DEG_TO_RAD; var d2r = Math.PI/180.0;
var phi = latLng.lat*d2r; var phi = latLng.lat*d2r;
var theta = latLng.lng*d2r; var theta = latLng.lng*d2r;
@ -39,7 +39,7 @@ var LatLngToXYZ = function(latLng) {
}; };
var XYZToLatLng = function(xyz) { var XYZToLatLng = function(xyz) {
var r2d = L.LatLng.RAD_TO_DEG; var r2d = 180.0/Math.PI;
var lat = Math.atan2(xyz[2], Math.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1])); var lat = Math.atan2(xyz[2], Math.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]));
var lng = Math.atan2(xyz[1], xyz[0]); var lng = Math.atan2(xyz[1], xyz[0]);

30
main.js
View File

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @id ingress-intel-total-conversion@jonatkins // @id ingress-intel-total-conversion@jonatkins
// @name IITC: Ingress intel map total conversion // @name IITC: Ingress intel map total conversion
// @version 0.16.6.@@DATETIMEVERSION@@ // @version 0.16.7.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@ // @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@ // @downloadURL @@DOWNLOADURL@@
@ -23,7 +23,6 @@ window.iitcBuildDate = '@@BUILDDATE@@';
window.onload = function() {}; window.onload = function() {};
document.body.onload = function() {}; document.body.onload = function() {};
// rescue user data from original page // rescue user data from original page
var scr = document.getElementsByTagName('script'); var scr = document.getElementsByTagName('script');
for(var x in scr) { for(var x in scr) {
@ -115,12 +114,23 @@ function wrapper(info) {
// (not the full GM_info - it contains the ENTIRE script source!) // (not the full GM_info - it contains the ENTIRE script source!)
window.script_info = info; window.script_info = info;
// disabling of some cruft left behind by the stock site
try {
goog.events.removeAll();
goog.Timer.clear();
} catch(e) {
console.warn('Exception from trying to clear stock site stuff');
console.warn(e);
debugger; // debugger break
}
// LEAFLET PREFER CANVAS /////////////////////////////////////////////// // LEAFLET PREFER CANVAS ///////////////////////////////////////////////
// Set to true if Leaflet should draw things using Canvas instead of SVG // Set to true if Leaflet should draw things using Canvas instead of SVG
// Disabled for now because it has several bugs: flickering, constant // Disabled for now because it has several bugs: flickering, constant
// CPU usage and it continuously fires the moveend event. // CPU usage and it continuously fires the moveend event.
L_PREFER_CANVAS = false;
//L_PREFER_CANVAS = false;
// CONFIG OPTIONS //////////////////////////////////////////////////// // CONFIG OPTIONS ////////////////////////////////////////////////////
window.REFRESH = 30; // refresh view every 30s (base time) window.REFRESH = 30; // refresh view every 30s (base time)
@ -129,20 +139,8 @@ window.ON_MOVE_REFRESH = 2.5; //refresh time to use after a movement event
window.MINIMUM_OVERRIDE_REFRESH = 10; //limit on refresh time since previous refresh, limiting repeated move refresh rate window.MINIMUM_OVERRIDE_REFRESH = 10; //limit on refresh time since previous refresh, limiting repeated move refresh rate
window.REFRESH_GAME_SCORE = 15*60; // refresh game score every 15 minutes window.REFRESH_GAME_SCORE = 15*60; // refresh game score every 15 minutes
window.MAX_IDLE_TIME = 4*60; // stop updating map after 4min idling window.MAX_IDLE_TIME = 4*60; // stop updating map after 4min idling
window.PRECACHE_PLAYER_NAMES_ZOOM = 17; // zoom level to start pre-resolving player names
window.HIDDEN_SCROLLBAR_ASSUMED_WIDTH = 20; window.HIDDEN_SCROLLBAR_ASSUMED_WIDTH = 20;
window.SIDEBAR_WIDTH = 300; window.SIDEBAR_WIDTH = 300;
// chat messages are requested for the visible viewport. On high zoom
// levels this gets pretty pointless, so request messages in at least a
// X km radius.
window.CHAT_MIN_RANGE = 6;
// this controls how far data is being drawn outside the viewport. Set
// it 0 to only draw entities that intersect the current view. A value
// of one will render an area twice the size of the viewport (or some-
// thing like that, Leaflet doc isnt too specific). Setting it too low
// makes the missing data on move/zoom out more obvious. Setting it too
// high causes too many items to be drawn, making drag&drop sluggish.
window.VIEWPORT_PAD_RATIO = 0.3;
// how many items to request each query // how many items to request each query
window.CHAT_PUBLIC_ITEMS = 50; window.CHAT_PUBLIC_ITEMS = 50;
@ -182,8 +180,6 @@ window.NOMINATIM = 'http://nominatim.openstreetmap.org/search?format=json&limit=
// INGRESS CONSTANTS ///////////////////////////////////////////////// // INGRESS CONSTANTS /////////////////////////////////////////////////
// http://decodeingress.me/2012/11/18/ingress-portal-levels-and-link-range/ // http://decodeingress.me/2012/11/18/ingress-portal-levels-and-link-range/
window.RESO_NRG = [0, 1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000]; window.RESO_NRG = [0, 1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000];
window.MAX_XM_PER_LEVEL = [0, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000];
window.MIN_AP_FOR_LEVEL = [0, 2500, 20000, 70000, 150000, 300000, 600000, 1200000];
window.HACK_RANGE = 40; // in meters, max. distance from portal to be able to access it window.HACK_RANGE = 40; // in meters, max. distance from portal to be able to access it
window.OCTANTS = ['E', 'NE', 'N', 'NW', 'W', 'SW', 'S', 'SE']; window.OCTANTS = ['E', 'NE', 'N', 'NW', 'W', 'SW', 'S', 'SE'];
window.OCTANTS_ARROW = ['→', '↗', '↑', '↖', '←', '↙', '↓', '↘']; window.OCTANTS_ARROW = ['→', '↗', '↑', '↖', '←', '↙', '↓', '↘'];

View File

@ -1,5 +1,3 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<settings default=""> <settings default="" />
<module2copyright />
</settings>
</component> </component>

View File

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cradle.iitc_mobile" package="com.cradle.iitc_mobile"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="67" android:versionCode="68"
android:versionName="0.10.6"> android:versionName="0.10.7">
<uses-sdk <uses-sdk
android:minSdkVersion="14" android:minSdkVersion="14"

View File

@ -1,9 +1,8 @@
How does this basically work? How does this basically work?
----------------------------- -----------------------------
At the moment, the Android App is a WebView which renders the normal web page. The IITC script is injected by calling ```iitc_view.loadIITC_JS(Context)```. The app uses a local version of the script, which is located in the assets folder, if no external source is given (see "pref\_iitc\_source"). At the moment, the Android App is a WebView which renders the normal web page. The IITC script is injected by calling ```iitc_view.loadIITC_JS(Context)```. The app uses a local version of the script, which is located in the assets folder.
Communication from app to script is handled by loading Javascript function calls. For example: ```iitc_view.loadUrl("javascript: window.alert('foo');");```
Communication from app to script is handled by loading Javascript function calls. For example: ```iitc_view.loadUrl("javascript: window.goBack();");```
Communication from script to app is handled by the JavascriptInterface (see /mobile/src/com/cradle/iitc\_mobile/IITC_JSInterface.java). If a method ```foo(String)``` is defined in JSInterface, it can be called by ```android.foo("Hello World")``` in the IITC script. Communication from script to app is handled by the JavascriptInterface (see /mobile/src/com/cradle/iitc\_mobile/IITC_JSInterface.java). If a method ```foo(String)``` is defined in JSInterface, it can be called by ```android.foo("Hello World")``` in the IITC script.

View File

@ -1,11 +1,13 @@
ingress intel total conversion mobile (IITCM) ingress intel total conversion mobile (IITCm)
===================================== =====================================
The Android App behaves like the desktop version, but uses the mobile view, which is optimized for mobile devices, as default. Furthermore, there are some nice additions: The Android App behaves like the desktop version, but uses the mobile view, which is optimized for mobile devices, as default. Furthermore, there are some nice additions:
- it should be much faster than the standard mobile ingress intel map - it should be much faster than the standard mobile ingress intel map
- plugin support - supports all official IITC plugins
- easy installation of unofficial plugins (installation via plugin preference, click on *.user.js file or URL, email attachement etc.)
- show users current location - show users current location
@ -13,17 +15,13 @@ The Android App behaves like the desktop version, but uses the mobile view, whic
- a geolocate button (you have to enable GPS satellites + location access to use this feature) - a geolocate button (you have to enable GPS satellites + location access to use this feature)
- possibility to use a custom IITC script source
- a click on Portal link copies it to clipboard
- in-app layer chooser
- in-app IITC buttons
- in-app search - in-app search
- support for unofficial plugins. Just copy the *.user.js files to ```<storage_path>/IITC_Mobile/plugins/``` and they should appear at the top of the plugin list. Note: For every option a new persistent database entry is created. If you want to remove a plugin from your external storage you want to ensure that it is disabled in the settings, otherwise IITCM will always try to load it on start-up. If you messed this up you can wipe app data or add the plugin back to storage, disable it and remove it. Another option would be: Do nothing...it should work even so. - configurable fullscreen modes (includes immersive mode on Android 4.4+)
- navigation drawers for IITC views, layer chooser and highlighters
- in-app screenshots
- developer mode: all script source will be loaded from ```<storage_path>/IITC_Mobile/dev/``` - developer mode: all script source will be loaded from ```<storage_path>/IITC_Mobile/dev/```

View File

@ -66,10 +66,13 @@ window.plugin.userLocation.setup = function() {
}; };
window.plugin.userLocation.onZoomEnd = function() { window.plugin.userLocation.onZoomEnd = function() {
if(window.map.getZoom() < 16) if(window.map.getZoom() < 16 || L.Path.CANVAS) {
if (window.plugin.userLocation.locationLayer.hasLayer(window.plugin.userLocation.circle))
window.plugin.userLocation.locationLayer.removeLayer(window.plugin.userLocation.circle); window.plugin.userLocation.locationLayer.removeLayer(window.plugin.userLocation.circle);
else } else {
if (!window.plugin.userLocation.locationLayer.hasLayer(window.plugin.userLocation.circle))
window.plugin.userLocation.locationLayer.addLayer(window.plugin.userLocation.circle); window.plugin.userLocation.locationLayer.addLayer(window.plugin.userLocation.circle);
}
}; };
window.plugin.userLocation.locate = function(lat, lng, accuracy) { window.plugin.userLocation.locate = function(lat, lng, accuracy) {

View File

@ -334,7 +334,7 @@ public class IITC_FileManager {
// create the chooser Intent // create the chooser Intent
final Intent target = new Intent(Intent.ACTION_GET_CONTENT) final Intent target = new Intent(Intent.ACTION_GET_CONTENT)
.setType("file/*") .setType("text/*")
.addCategory(Intent.CATEGORY_OPENABLE); .addCategory(Intent.CATEGORY_OPENABLE);
final IITC_Mobile iitc = (IITC_Mobile) mActivity; final IITC_Mobile iitc = (IITC_Mobile) mActivity;

View File

@ -16,6 +16,7 @@ import java.io.ByteArrayInputStream;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
public class IITC_WebViewClient extends WebViewClient { public class IITC_WebViewClient extends WebViewClient {
@ -57,11 +58,9 @@ public class IITC_WebViewClient extends WebViewClient {
private void loadScripts(final IITC_WebView view) { private void loadScripts(final IITC_WebView view) {
final List<String> scripts = new LinkedList<String>(); final List<String> scripts = new LinkedList<String>();
scripts.add("script" + DOMAIN + "/total-conversion-build.user.js");
// get the plugin preferences // get the plugin preferences
final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mIitc); final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mIitc);
final Map<String, ?> all_prefs = sharedPref.getAll(); final TreeMap<String, ?> all_prefs = new TreeMap<String, Object>(sharedPref.getAll());
// iterate through all plugins // iterate through all plugins
for (final Map.Entry<String, ?> entry : all_prefs.entrySet()) { for (final Map.Entry<String, ?> entry : all_prefs.entrySet()) {
@ -80,6 +79,8 @@ public class IITC_WebViewClient extends WebViewClient {
scripts.add("script" + DOMAIN + "/user-location.user.js"); scripts.add("script" + DOMAIN + "/user-location.user.js");
} }
scripts.add("script" + DOMAIN + "/total-conversion-build.user.js");
final String js = "(function(){['" + TextUtils.join("','", scripts) + "'].forEach(function(src) {" + final String js = "(function(){['" + TextUtils.join("','", scripts) + "'].forEach(function(src) {" +
"var script = document.createElement('script');script.src = '//'+src;" + "var script = document.createElement('script');script.src = '//'+src;" +
"(document.body || document.head || document.documentElement).appendChild(script);" + "(document.body || document.head || document.documentElement).appendChild(script);" +

View File

@ -675,6 +675,35 @@
} }
} }
window.plugin.bookmarks.autoDrawCalcDistance = function() {
var latlngs = [];
var uuu = $('#bkmrksAutoDrawer a.bkmrk.selected').each(function(i) {
var tt = $(this).data('latlng');
latlngs[i] = tt;
});
var distance = null;
if(latlngs.length == 2) {
distance = L.latLng(latlngs[0]).distanceTo(latlngs[1]);
}
$('#bkmrksAutoDrawerDistance').remove();
if(distance !== null) {
distance = Math.round(distance);
var text = 'Distance between portals: ';
if(distance > 1000)
text += digits(distance / 1000) + 'km';
else
text += digits(distance) + 'm';
$('<div>')
.html(text)
.attr('id', 'bkmrksAutoDrawerDistance')
.appendTo('#bkmrksAutoDrawer');
}
}
window.plugin.bookmarks.dialogLoadList = function() { window.plugin.bookmarks.dialogLoadList = function() {
var r = 'The "<a href="http://iitc.jonatkins.com/?page=desktop#plugin-draw-tools" target="_BLANK"><strong>Draw Tools</strong></a>" plugin is required.</span>'; var r = 'The "<a href="http://iitc.jonatkins.com/?page=desktop#plugin-draw-tools" target="_BLANK"><strong>Draw Tools</strong></a>" plugin is required.</span>';
@ -724,7 +753,9 @@
+ '<input style="vertical-align: middle;" type="checkbox" id="bkmrkClearSelection" checked>' + '<input style="vertical-align: middle;" type="checkbox" id="bkmrkClearSelection" checked>'
+ ' Clear selection after drawing</label>' + ' Clear selection after drawing</label>'
+ '<p style="color:red;text-align:center;margin-bottom:9px;">You must select 2 or 3 portals.</p>' + '<p style="color:red;text-align:center;margin-bottom:9px;">You must select 2 or 3 portals.</p>'
+ '<div onclick="window.plugin.bookmarks.autoDrawCalcDistance();return false;">'
+ element + element
+ '</div>'
+ '</div>'; + '</div>';
} }
return r; return r;
@ -872,6 +903,8 @@
iconSize: [30,40] iconSize: [30,40]
}) })
}); });
star.on('click', function() { renderPortalDetails(guid); });
window.plugin.bookmarks.starLayers[guid] = star; window.plugin.bookmarks.starLayers[guid] = star;
star.addTo(window.plugin.bookmarks.starLayerGroup); star.addTo(window.plugin.bookmarks.starLayerGroup);
} }

View File

@ -0,0 +1,50 @@
// ==UserScript==
// @id iitc-plugin-canvas-render@jonatkins
// @name IITC plugin: Use Canvas rendering
// @category Tweaks
// @version 0.1.0.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@
// @description [@@BUILDNAME@@-@@BUILDDATE@@] EXPERIMENTAL: use canvas-based rendering. Can be faster when viewing dense areas. Limited testing of the feature so far
// @include https://www.ingress.com/intel*
// @include http://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @grant unsafeWindow
// ==/UserScript==
// NON-STANDARD plugin - try and set the variable early, as
// we need this global variable set before leaflet initialises
window.L_PREFER_CANVAS = true;
if (typeof unsafeWindow !== 'undefined') unsafeWindow.L_PREFER_CANVAS = true; //doesn't actually work... :/
@@PLUGINSTART@@
// PLUGIN START ////////////////////////////////////////////////////////
// we need this global variable set before leaflet initialises
window.L_PREFER_CANVAS = true;
// use own namespace for plugin
window.plugin.canvasRendering = function() {};
window.plugin.canvasRendering.setup = function() {
// nothing we can do here - other than check that canvas rendering was enabled
if (!L.Path.CANVAS) {
dialog({
title:'Canvas Render Warning',
text:'The Canvas Rendering plugin failed to enable canvas rendering in leaflet. This will occur if it initialises too late.\n'
+'Try re-ordering userscripts so Canvas Rendering is before the main IITC script.'
});
}
};
var setup = window.plugin.canvasRendering.setup;
// PLUGIN END //////////////////////////////////////////////////////////
@@PLUGINEND@@

View File

@ -0,0 +1,115 @@
// ==UserScript==
// @id iitc-plugin-marker-canvas-icon@jonatkins
// @name IITC plugin: Marker drawn using icons from canvas data URLs
// @category Tweaks
// @version 0.1.0.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@
// @description [@@BUILDNAME@@-@@BUILDDATE@@] EXPERIMENTAL: draw markers using individual Leaflet Icons, created from canvas elements converted to data: URLs
// @include https://www.ingress.com/intel*
// @include http://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @grant none
// ==/UserScript==
@@PLUGINSTART@@
// PLUGIN START ////////////////////////////////////////////////////////
// use own namespace for plugin
window.plugin.markerIconCanvasUrl = function() {};
window.plugin.markerIconCanvasUrl.setup = function() {
// create a new marker. 'data' contain the IITC-specific entity data to be stored in the object options
window.createMarker = function(latlng, data) {
var icon = createMarkerIcon(data);
var options = L.extend({}, data, { icon: icon });
var marker = L.marker (latlng, options);
marker.bringToFront = function(){}; //TEMP - until the rest of IITC code is changed to take account of non-path markers
return marker;
}
window.setMarkerStyle = function(marker, selected) {
var icon = createMarkerIcon(marker.options,selected);
marker.setIcon(icon);
}
window.createMarkerIcon = function(details,selected) {
var scale = window.portalMarkerScale();
var lvlWeight = Math.max(2, Math.floor(details.level) / 1.5) * scale;
var lvlRadius = (details.team === window.TEAM_NONE ? 7 : Math.floor(details.level) + 4) * scale;
lvlRadius += (L.Browser.mobile ? PORTAL_RADIUS_ENLARGE_MOBILE*scale : 0);
var fillColor = COLORS[details.team];
var fillAlpha = 0.5;
var lineColor = selected ? COLOR_SELECTED_PORTAL : COLORS[details.team];
var lineAlpha = 1.0;
var cacheKey = ([lvlRadius,lvlWeight,fillColor,fillAlpha,lineColor,lineAlpha]).join(':');
if (!window.markerIconCache[cacheKey]) {
window.markerIconCache[cacheKey] = createNewMarkerIcon(lvlRadius,lvlWeight,fillColor,fillAlpha,lineColor,lineAlpha);
}
return window.markerIconCache[cacheKey];
}
window.markerIconCache = {};
window.createNewMarkerIcon = function (radius,weight,fillcol,fillalpha,linecol,linealpha) {
var size = Math.ceil(radius + weight/2)*2;
var anchor = Math.floor(size/2);
var canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(anchor,anchor,radius,0,2*Math.PI);
ctx.fillStyle = fillcol;
ctx.globalAlpha = fillalpha;
ctx.fill();
ctx.lineWidth = weight;
ctx.strokeStyle = linecol;
ctx.globalAlpha = linealpha;
ctx.stroke();
var dataurl = canvas.toDataURL();
return L.icon({
iconUrl: dataurl,
iconSize: [size,size],
iconAnchor: [anchor,anchor]
});
}
};
var setup = window.plugin.markerIconCanvasUrl.setup;
// PLUGIN END //////////////////////////////////////////////////////////
@@PLUGINEND@@

View File

@ -0,0 +1,79 @@
// ==UserScript==
// @id iitc-plugin-marker-divicon-svg@jonatkins
// @name IITC plugin: Marker drawn using separate SVGs
// @category Tweaks
// @version 0.1.0.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@
// @description [@@BUILDNAME@@-@@BUILDDATE@@] EXPERIMENTAL: draw markers using individual Leaflet DivIcons, as SVG
// @include https://www.ingress.com/intel*
// @include http://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @grant none
// ==/UserScript==
@@PLUGINSTART@@
// PLUGIN START ////////////////////////////////////////////////////////
// use own namespace for plugin
window.plugin.markerDivIconSvg = function() {};
window.plugin.markerDivIconSvg.setup = function() {
// create a new marker. 'data' contain the IITC-specific entity data to be stored in the object options
window.createMarker = function(latlng, data) {
var icon = createMarkerDivIcon(data);
var options = L.extend({}, data, { icon: icon });
var marker = L.marker (latlng, options);
return marker;
}
window.setMarkerStyle = function(marker, selected) {
var icon = createMarkerDivIcon(marker.options);
marker.setIcon(icon);
}
window.createMarkerDivIcon = function(details) {
var scale = window.portalMarkerScale();
var lvlWeight = Math.max(2, Math.floor(details.level) / 1.5) * scale;
var lvlRadius = (details.team === window.TEAM_NONE ? 7 : Math.floor(details.level) + 4) * scale;
lvlRadius += (L.Browser.mobile ? PORTAL_RADIUS_ENLARGE_MOBILE*scale : 0);
var size = Math.ceil(lvlRadius + lvlWeight/2)*2;
var anchor = Math.floor(size/2);
var svg = '<svg width="'+size+'" height="'+size+'">'
+ '<circle cx="'+anchor+'" cy="'+anchor+'" r="'+lvlRadius+'" stroke="'+COLORS[details.team]+'" stroke-width="'+lvlWeight+'" fill="'+COLORS[details.team]+'" fill-opacity="0.5" />'
+ '</svg>';
return L.divIcon({
iconSize: [size,size],
iconAnchor: [anchor,anchor],
className: 'portal-marker',
html: svg
});
}
};
var setup = window.plugin.markerDivIconSvg.setup;
// PLUGIN END //////////////////////////////////////////////////////////
@@PLUGINEND@@

View File

@ -2,7 +2,7 @@
// @id iitc-plugin-guess-player-levels@breunigs // @id iitc-plugin-guess-player-levels@breunigs
// @name IITC plugin: guess player level // @name IITC plugin: guess player level
// @category Info // @category Info
// @version 0.5.1.@@DATETIMEVERSION@@ // @version 0.5.2.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@ // @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@ // @downloadURL @@DOWNLOADURL@@
@ -84,6 +84,7 @@ window.plugin.guessPlayerLevels.setLevelTitle = function(dom) {
var el = $(dom); var el = $(dom);
var nick = el.text(); var nick = el.text();
if (nick[0] == '@') nick = nick.substring(1);
var details = window.plugin.guessPlayerLevels.fetchLevelDetailsByPlayer(nick); var details = window.plugin.guessPlayerLevels.fetchLevelDetailsByPlayer(nick);

View File

@ -2,7 +2,7 @@
// @id max-links@boombuler // @id max-links@boombuler
// @name IITC plugin: Max Links // @name IITC plugin: Max Links
// @category Layer // @category Layer
// @version 0.4.2.@@DATETIMEVERSION@@ // @version 0.4.3.@@DATETIMEVERSION@@
// @updateURL @@UPDATEURL@@ // @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@ // @downloadURL @@DOWNLOADURL@@
// @description [@@BUILDNAME@@-@@BUILDDATE@@] Calculate how to link the portals to create a reasonably tidy set of links/fields. Enable from the layer chooser. (Max Links is a poor name, but remains for historical reasons.) // @description [@@BUILDNAME@@-@@BUILDDATE@@] Calculate how to link the portals to create a reasonably tidy set of links/fields. Enable from the layer chooser. (Max Links is a poor name, but remains for historical reasons.)
@ -68,11 +68,6 @@ window.plugin.maxLinks.updateLayer = function() {
window.plugin.maxLinks.layer.clearLayers(); window.plugin.maxLinks.layer.clearLayers();
if (Object.keys(window.portals).length > window.plugin.maxLinks.MAX_PORTALS_TO_LINK) {
window.plugin.maxLinks.addErrorMarker();
return;
}
var locations = []; var locations = [];
var bounds = map.getBounds(); var bounds = map.getBounds();
@ -81,9 +76,15 @@ window.plugin.maxLinks.updateLayer = function() {
if (bounds.contains(ll)) { if (bounds.contains(ll)) {
var p = map.project (portal.getLatLng(), window.plugin.maxLinks.PROJECT_ZOOM); var p = map.project (portal.getLatLng(), window.plugin.maxLinks.PROJECT_ZOOM);
locations.push(p); locations.push(p);
if (locations.length > window.plugin.maxLinks.MAX_PORTALS_TO_LINK) return false; //$.each break
} }
}); });
if (locations.length > window.plugin.maxLinks.MAX_PORTALS_TO_LINK) {
window.plugin.maxLinks.addErrorMarker();
return;
}
var triangles = window.delaunay.triangulate(locations); var triangles = window.delaunay.triangulate(locations);
var drawnLinkCount = 0; var drawnLinkCount = 0;

View File

@ -399,7 +399,9 @@ window.plugin.playerTracker.drawData = function() {
dashArray: "5,8" dashArray: "5,8"
}; };
L.multiPolyline(polyLine, opts).addTo(plugin.playerTracker.drawnTracesEnl); $.each(polyLine,function(ind,poly) {
L.polyline(poly, opts).addTo(plugin.playerTracker.drawnTracesEnl);
});
}); });
$.each(polyLineByAgeRes, function(i, polyLine) { $.each(polyLineByAgeRes, function(i, polyLine) {
if(polyLine.length === 0) return true; if(polyLine.length === 0) return true;
@ -412,7 +414,9 @@ window.plugin.playerTracker.drawData = function() {
dashArray: "5,8" dashArray: "5,8"
}; };
L.multiPolyline(polyLine, opts).addTo(plugin.playerTracker.drawnTracesRes); $.each(polyLine, function(ind,poly) {
L.polyline(poly, opts).addTo(plugin.playerTracker.drawnTracesRes);
});
}); });
} }

View File

@ -2,7 +2,7 @@
// @id iitc-plugin-portal-level-numbers@rongou // @id iitc-plugin-portal-level-numbers@rongou
// @name IITC plugin: Portal Level Numbers // @name IITC plugin: Portal Level Numbers
// @category Layer // @category Layer
// @version 0.1.2.@@DATETIMEVERSION@@ // @version 0.1.4.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@ // @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@ // @downloadURL @@DOWNLOADURL@@
@ -19,50 +19,16 @@
// PLUGIN START //////////////////////////////////////////////////////// // PLUGIN START ////////////////////////////////////////////////////////
// use own namespace for plugin // use own namespace for plugin
window.plugin.portalLevelNumbers = function() {}; window.plugin.portalLevelNumbers = function() {
};
window.plugin.portalLevelNumbers.ICON_SIZE = 12;
window.plugin.portalLevelNumbers.MOBILE_SCALE = 1.5;
window.plugin.portalLevelNumbers.levelLayers = {}; window.plugin.portalLevelNumbers.levelLayers = {};
window.plugin.portalLevelNumbers.levelLayerGroup = null; window.plugin.portalLevelNumbers.levelLayerGroup = null;
// Use portal add and remove event to control render of portal level numbers window.plugin.portalLevelNumbers.setupCSS = function() {
window.plugin.portalLevelNumbers.portalAdded = function(data) {
data.portal.on('add', function() {
plugin.portalLevelNumbers.renderLevel(this.options.guid, this.getLatLng());
});
data.portal.on('remove', function() {
plugin.portalLevelNumbers.removeLevel(this.options.guid);
});
}
window.plugin.portalLevelNumbers.renderLevel = function(guid,latLng) {
plugin.portalLevelNumbers.removeLevel(guid);
var p = window.portals[guid];
var levelNumber = p.options.level;
var level = L.marker(latLng, {
icon: L.divIcon({
className: 'plugin-portal-level-numbers',
iconAnchor: [6,7],
iconSize: [12,10],
html: levelNumber
}),
guid: guid
});
plugin.portalLevelNumbers.levelLayers[guid] = level;
level.addTo(plugin.portalLevelNumbers.levelLayerGroup);
}
window.plugin.portalLevelNumbers.removeLevel = function(guid) {
var previousLayer = plugin.portalLevelNumbers.levelLayers[guid];
if(previousLayer) {
plugin.portalLevelNumbers.levelLayerGroup.removeLayer(previousLayer);
delete plugin.portalLevelNumbers.levelLayers[guid];
}
}
var setup = function() {
$("<style>") $("<style>")
.prop("type", "text/css") .prop("type", "text/css")
.html(".plugin-portal-level-numbers {\ .html(".plugin-portal-level-numbers {\
@ -75,12 +41,140 @@ var setup = function() {
-webkit-text-size-adjust:none;\ -webkit-text-size-adjust:none;\
}") }")
.appendTo("head"); .appendTo("head");
}
window.plugin.portalLevelNumbers.removeLabel = function(guid) {
var previousLayer = window.plugin.portalLevelNumbers.levelLayers[guid];
if(previousLayer) {
window.plugin.portalLevelNumbers.levelLayerGroup.removeLayer(previousLayer);
delete plugin.portalLevelNumbers.levelLayers[guid];
}
}
window.plugin.portalLevelNumbers.addLabel = function(guid,latLng) {
// remove old layer before updating
window.plugin.portalLevelNumbers.removeLabel(guid);
// add portal level to layers
var p = window.portals[guid];
var levelNumber = p.options.level;
var level = L.marker(latLng, {
icon: L.divIcon({
className: 'plugin-portal-level-numbers',
iconSize: [window.plugin.portalLevelNumbers.ICON_SIZE, window.plugin.portalLevelNumbers.ICON_SIZE],
html: levelNumber
}),
guid: guid
});
plugin.portalLevelNumbers.levelLayers[guid] = level;
level.addTo(plugin.portalLevelNumbers.levelLayerGroup);
}
window.plugin.portalLevelNumbers.updatePortalLabels = function() {
var SQUARE_SIZE = L.Browser.mobile ? (window.plugin.portalLevelNumbers.ICON_SIZE + 3) * window.plugin.portalLevelNumbers.MOBILE_SCALE
: (window.plugin.portalLevelNumbers.ICON_SIZE + 3);
// as this is called every time layers are toggled, there's no point in doing it when the layer is off
if (!map.hasLayer(window.plugin.portalLevelNumbers.levelLayerGroup)) {
return;
}
var portalPoints = {};
for (var guid in window.portals) {
var p = window.portals[guid];
if (p._map) { // only consider portals added to the map
var point = map.project(p.getLatLng());
portalPoints[guid] = point;
}
}
// for efficient testing of intersection, group portals into buckets based on the defined rectangle size
var buckets = {};
for (var guid in portalPoints) {
var point = portalPoints[guid];
var bucketId = L.point([Math.floor(point.x/(SQUARE_SIZE*2)),Math.floor(point.y/SQUARE_SIZE*2)]);
// the guid is added to four buckets. this way, when testing for overlap we don't need to test
// all 8 buckets surrounding the one around the particular portal, only the bucket it is in itself
var bucketIds = [bucketId, bucketId.add([1,0]), bucketId.add([0,1]), bucketId.add([1,1])];
for (var i in bucketIds) {
var b = bucketIds[i].toString();
if (!buckets[b]) buckets[b] = {};
buckets[b][guid] = true;
}
}
var coveredPortals = {};
for (var bucket in buckets) {
var bucketGuids = buckets[bucket];
for (var guid in bucketGuids) {
var point = portalPoints[guid];
// the bounds used for testing are twice as wide as the rectangle. this is so that there's no left/right
// overlap between two different portals text
var southWest = point.subtract([SQUARE_SIZE, SQUARE_SIZE]);
var northEast = point.add([SQUARE_SIZE, SQUARE_SIZE]);
var largeBounds = L.bounds(southWest, northEast);
for (var otherGuid in bucketGuids) {
// do not check portals already marked as covered
if (guid != otherGuid && !coveredPortals[otherGuid]) {
var otherPoint = portalPoints[otherGuid];
if (largeBounds.contains(otherPoint)) {
// another portal is within the rectangle - remove if it has not a higher level
if (portals[guid].options.level > portals[otherGuid].options.level) continue;
else coveredPortals[guid] = true;
break;
}
}
}
}
}
for (var guid in coveredPortals) {
delete portalPoints[guid];
}
// remove any not wanted
for (var guid in window.plugin.portalLevelNumbers.levelLayers) {
if (!(guid in portalPoints)) {
window.plugin.portalLevelNumbers.removeLabel(guid);
}
}
// and add those we do
for (var guid in portalPoints) {
window.plugin.portalLevelNumbers.addLabel(guid, portals[guid].getLatLng());
}
}
// as calculating portal marker visibility can take some time when there's lots of portals shown, we'll do it on
// a short timer. this way it doesn't get repeated so much
window.plugin.portalLevelNumbers.delayedUpdatePortalLabels = function(wait) {
if (window.plugin.portalLevelNumbers.timer === undefined) {
window.plugin.portalLevelNumbers.timer = setTimeout ( function() {
window.plugin.portalLevelNumbers.timer = undefined;
window.plugin.portalLevelNumbers.updatePortalLabels();
}, wait*1000);
}
}
var setup = function() {
window.plugin.portalLevelNumbers.setupCSS();
window.plugin.portalLevelNumbers.levelLayerGroup = new L.LayerGroup(); window.plugin.portalLevelNumbers.levelLayerGroup = new L.LayerGroup();
window.addLayerGroup('Portal Levels', window.plugin.portalLevelNumbers.levelLayerGroup, true); window.addLayerGroup('Portal Levels', window.plugin.portalLevelNumbers.levelLayerGroup, true);
window.addHook('portalAdded', window.plugin.portalLevelNumbers.portalAdded); window.addHook('requestFinished', function() { setTimeout(function(){window.plugin.portalLevelNumbers.delayedUpdatePortalLabels(3.0);},1); });
window.addHook('mapDataRefreshEnd', function() { window.plugin.portalLevelNumbers.delayedUpdatePortalLabels(0.5); });
window.map.on('overlayadd overlayremove', function() { setTimeout(function(){window.plugin.portalLevelNumbers.delayedUpdatePortalLabels(1.0);},1); });
} }
// PLUGIN END ////////////////////////////////////////////////////////// // PLUGIN END //////////////////////////////////////////////////////////

View File

@ -2,7 +2,7 @@
// @id iitc-plugin-portals-list@teo96 // @id iitc-plugin-portals-list@teo96
// @name IITC plugin: show list of portals // @name IITC plugin: show list of portals
// @category Info // @category Info
// @version 0.1.0.@@DATETIMEVERSION@@ // @version 0.1.1.@@DATETIMEVERSION@@
// @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @namespace https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL @@UPDATEURL@@ // @updateURL @@UPDATEURL@@
// @downloadURL @@DOWNLOADURL@@ // @downloadURL @@DOWNLOADURL@@
@ -84,8 +84,8 @@ window.plugin.portalslist.getPortals = function() {
'guid': i, 'guid': i,
'teamN': teamN, // TEAM_NONE, TEAM_RES or TEAM_ENL 'teamN': teamN, // TEAM_NONE, TEAM_RES or TEAM_ENL
'team': d.team, // "NEUTRAL", "RESISTANCE" or "ENLIGHTENED" 'team': d.team, // "NEUTRAL", "RESISTANCE" or "ENLIGHTENED"
'name': d.title, 'name': d.title || '(untitled)',
'nameLower': d.title.toLowerCase(), 'nameLower': d.title && d.title.toLowerCase(),
'level': portal.options.level, 'level': portal.options.level,
'health': d.health, 'health': d.health,
'resCount': d.resCount, 'resCount': d.resCount,
@ -242,7 +242,7 @@ window.plugin.portalslist.getPortalLink = function(portal,guid) {
var latlng = [coord.lat, coord.lng].join(); var latlng = [coord.lat, coord.lng].join();
var jsSingleClick = 'window.renderPortalDetails(\''+guid+'\');return false'; var jsSingleClick = 'window.renderPortalDetails(\''+guid+'\');return false';
var jsDoubleClick = 'window.zoomToAndShowPortal(\''+guid+'\', ['+latlng+']);return false'; var jsDoubleClick = 'window.zoomToAndShowPortal(\''+guid+'\', ['+latlng+']);return false';
var perma = '/intel?latE6='+coord.lat+'&lngE6='+coord.lng+'&z=17&pguid='+guid; var perma = '/intel?ll='+coord.lat+','+coord.lng+'&z=17&pll='+coord.lat+','+coord.lng;
//Use Jquery to create the link, which escape characters in TITLE and ADDRESS of portal //Use Jquery to create the link, which escape characters in TITLE and ADDRESS of portal
var a = $('<a>',{ var a = $('<a>',{

View File

@ -149,7 +149,7 @@ window.plugin.regions.update = function() {
// the six cube side boundaries. we cheat by hard-coding the coords as it's simple enough // the six cube side boundaries. we cheat by hard-coding the coords as it's simple enough
var latLngs = [ [45,-180], [35.264389682754654,-135], [35.264389682754654,-45], [35.264389682754654,45], [35.264389682754654,135], [45,180]]; var latLngs = [ [45,-180], [35.264389682754654,-135], [35.264389682754654,-45], [35.264389682754654,45], [35.264389682754654,135], [45,180]];
var globalCellOptions = {color: 'red', weight: 7, opacity: 0.5}; var globalCellOptions = {color: 'red', weight: 7, opacity: 0.5, clickable: false };
for (var i=0; i<latLngs.length-1; i++) { for (var i=0; i<latLngs.length-1; i++) {
// the geodesic line code can't handle a line/polyline spanning more than (or close to?) 180 degrees, so we draw // the geodesic line code can't handle a line/polyline spanning more than (or close to?) 180 degrees, so we draw

View File

@ -13,6 +13,29 @@ offers many more features. It is available for
<h3>Latest news</h3> <h3>Latest news</h3>
<h4>14th March 2014</h4>
<p>
IITC 0.16.6 and IITC Mobile 0.10.6 have just been released. This is a critical update required to successfully load
portals when zoomed in (L3+ or closer) to the map. Changes include:
</p>
<ul>
<li>debug tiles fade for completed tiles</li>
<li>warning in sidebar when standard layers are turned off</li>
<li>player level: fixed AP values for L2/L3</li>
<li>Plugins:
<ul>
<li>portal-counts: percentage in pie chart</li>
<li>guess-player-levels: guess level based on portal attacks (attack range as calculated from log)</li>
</ul></li>
<li>Mobile:
<ul>
<li>send screenshot from menu</li>
<li>fixed sharing interface (caused crash on some devices)</li>
<li>show loading indicator while log is being loaded</li>
<li>configurable menu</li>
</ul></li>
</ul>
<h4>22nd February 2014</h4> <h4>22nd February 2014</h4>
<p> <p>
IITC 0.16.5 and IITC Mobile 0.10.5 have just been released. This version is required to work with a change made to the IITC 0.16.5 and IITC Mobile 0.10.5 have just been released. This version is required to work with a change made to the

View File

@ -1,5 +1,28 @@
<h2>News</h2> <h2>News</h2>
<h4>14th March 2014</h4>
<p>
IITC 0.16.6 and IITC Mobile 0.10.6 have just been released. This is a critical update required to successfully load
portals when zoomed in (L3+ or closer) to the map. Changes include:
</p>
<ul>
<li>debug tiles fade for completed tiles</li>
<li>warning in sidebar when standard layers are turned off</li>
<li>player level: fixed AP values for L2/L3</li>
<li>Plugins:
<ul>
<li>portal-counts: percentage in pie chart</li>
<li>guess-player-levels: guess level based on portal attacks (attack range as calculated from log)</li>
</ul></li>
<li>Mobile:
<ul>
<li>send screenshot from menu</li>
<li>fixed sharing interface (caused crash on some devices)</li>
<li>show loading indicator while log is being loaded</li>
<li>configurable menu</li>
</ul></li>
</ul>
<h4>22nd February 2014</h4> <h4>22nd February 2014</h4>
<p> <p>
IITC 0.16.5 and IITC Mobile 0.10.5 have just been released. This version is required to work with a change made to the IITC 0.16.5 and IITC Mobile 0.10.5 have just been released. This version is required to work with a change made to the

View File

@ -15,6 +15,7 @@ builds.
<?php <?php
include_once ( "code/desktop-download.php" ); include_once ( "code/desktop-download.php" );
include_once ( "code/mobile-download.php" );
$path = "test"; $path = "test";
@ -42,25 +43,11 @@ else
{ {
} }
$apkfile = "$path/IITC_Mobile-$path.apk";
?> ?>
<h3 id="test-desktop">Desktop test build</h3>
<?php
iitcDesktopDownload ( $path );
?>
<hr>
<h4>Desktop test plugins</h4>
<?php
iitcDesktopPluginDownloadTable ( $path );
?>
<hr>
<h3 id="test-mobile">Mobile test build</h3> <h3 id="test-mobile">Mobile test build</h3>
<div class="alert alert-block alert-info"> <div class="alert alert-block alert-info">
@ -70,10 +57,6 @@ Test builds will be called "IITCm Test" - while the regular release builds remai
<?php <?php
include_once ( "code/mobile-download.php" );
$apkfile = "$path/IITC_Mobile-$path.apk";
if ( file_exists($apkfile) ) if ( file_exists($apkfile) )
{ {
@ -87,5 +70,21 @@ else
?> ?>
<h3 id="test-desktop">Desktop test build</h3>
<?php
iitcDesktopDownload ( $path );
?>
<hr>
<h4>Desktop test plugins</h4>
<?php
iitcDesktopPluginDownloadTable ( $path );
?>
<hr>