merge main

This commit is contained in:
vita10gy
2013-03-09 14:59:02 -06:00
106 changed files with 2827 additions and 1283 deletions

View File

@ -5,6 +5,31 @@
// created a basic framework. All of these functions should only ever
// be run once.
window.setupBackButton = function() {
var c = window.isSmartphone()
? window.smartphone.mapButton
: $('#chatcontrols a.active');
window.setupBackButton._actions = [c.get(0)];
$('#chatcontrols a').click(function() {
// ignore shrink button
if($(this).hasClass('toggle')) return;
window.setupBackButton._actions.push(this);
window.setupBackButton._actions = window.setupBackButton._actions.slice(-2);
});
window.goBack = function() {
var a = window.setupBackButton._actions[0];
if(!a) return;
$(a).click();
window.setupBackButton._actions = [a];
}
}
window.setupLargeImagePreview = function() {
$('#portaldetails').on('click', '.imgpreview', function() {
var ex = $('#largepreview');
@ -23,6 +48,26 @@ window.setupLargeImagePreview = function() {
});
}
// adds listeners to the layer chooser such that a long press hides
// all custom layers except the long pressed one.
window.setupLayerChooserSelectOne = function() {
$('.leaflet-control-layers-overlays').on('click taphold', 'label', function(e) {
if(!e) return;
if(!(e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.type === 'taphold')) return;
var isChecked = $(this).find('input').is(':checked');
var checkSize = $('.leaflet-control-layers-overlays input:checked').length;
if((isChecked && checkSize === 1) || checkSize === 0) {
// if nothing is selected or the users long-clicks the only
// selected element, assume all boxes should be checked again
$('.leaflet-control-layers-overlays input:not(:checked)').click();
} else {
// uncheck all
$('.leaflet-control-layers-overlays input:checked').click();
$(this).find('input').click();
}
});
}
window.setupStyles = function() {
$('head').append('<style>' +
@ -58,10 +103,6 @@ window.setupMap = function() {
{zoomControl: !(localStorage['iitc.zoom.buttons'] === 'false')}
));
try {
map.addLayer(views[readCookie('ingress.intelmap.type')]);
} catch(e) { map.addLayer(views[0]); }
var addLayers = {};
portalsLayers = [];
@ -81,16 +122,24 @@ window.setupMap = function() {
addLayers['Links'] = linksLayer;
window.layerChooser = new L.Control.Layers({
'OSM Cloudmade Midnight': views[0],
'OSM Cloudmade Minimal': views[1],
'OSM Midnight': views[0],
'OSM Minimal': views[1],
'OSM Mapnik': views[2],
'Google Roads Ingress Style': views[3],
'Default Ingress Map': views[3],
'Google Roads': views[4],
'Google Satellite': views[5],
'Google Hybrid': views[6]
}, addLayers);
map.addControl(window.layerChooser);
// set the map AFTER adding the layer chooser, or Chrome reorders the
// layers. This likely leads to broken layer selection because the
// views/cookie order does not match the layer chooser order.
try {
map.addLayer(views[readCookie('ingress.intelmap.type')]);
} catch(e) { map.addLayer(views[0]); }
map.attributionControl.setPrefix('');
// listen for changes and store them in cookies
map.on('moveend', window.storeMapPosition);
@ -136,11 +185,13 @@ window.setupMap = function() {
// included as inline script in the original site, the data is static
// and cannot be updated.
window.setupPlayerStat = function() {
PLAYER.guid = playerNameToGuid(PLAYER.nickname);
var level;
var ap = parseInt(PLAYER.ap);
for(level = 0; level < MIN_AP_FOR_LEVEL.length; level++) {
if(ap < MIN_AP_FOR_LEVEL[level]) break;
}
PLAYER.level = level;
var thisLvlAp = MIN_AP_FOR_LEVEL[level-1];
var nextLvlAp = MIN_AP_FOR_LEVEL[level] || ap;
@ -163,13 +214,22 @@ window.setupPlayerStat = function() {
$('#playerstat').html(''
+ '<h2 title="'+t+'">'+level+'&nbsp;'
+ '<div id="name">'
+ '<span class="'+cls+'">'+PLAYER.nickname+'</span>'
+ '<div>'
+ '<a href="https://www.ingress.com/_ah/logout?continue=https://www.google.com/accounts/Logout%3Fcontinue%3Dhttps://appengine.google.com/_ah/logout%253Fcontinue%253Dhttps://www.ingress.com/intel%26service%3Dah" id="signout">sign out</a>'
+ '</div>'
+ '<div id="stats">'
+ '<sup>XM: '+xmRatio+'%</sup>'
+ '<sub>' + (level < 8 ? 'level: '+lvlApProg+'%' : 'max level') + '</sub>'
+ '</div>'
+ '</h2>'
);
$('#name').mouseenter(function() {
$('#signout').show();
}).mouseleave(function() {
$('#signout').hide();
});
}
window.setupSidebarToggle = function() {
@ -226,6 +286,14 @@ window.setupDialogs = function() {
}
}
window.setupTaphold = function() {
@@INCLUDERAW:external/taphold.js@@
}
window.setupQRLoadLib = function() {
@@INCLUDERAW:external/jquery.qrcode.min.js@@
}
// BOOTING ///////////////////////////////////////////////////////////
@ -233,17 +301,18 @@ window.setupDialogs = function() {
function boot() {
window.debug.console.overwriteNativeIfRequired();
console.log('loading done, booting. Built: ' + window.iitcBuildDate);
console.log('loading done, booting. Built: @@BUILDDATE@@');
if(window.deviceID) console.log('Your device ID: ' + window.deviceID);
window.runOnSmartphonesBeforeBoot();
// overwrite default Leaflet Marker icon to be a neutral color
var base = 'http://breunigs.github.com/ingress-intel-total-conversion/dist/images/';
var base = 'https://iitcserv.appspot.com/dist/images';
L.Icon.Default.imagePath = base;
window.iconEnl = L.Icon.Default.extend({options: { iconUrl: base + 'marker-green.png' } });
window.iconRes = L.Icon.Default.extend({options: { iconUrl: base + 'marker-blue.png' } });
window.iconEnl = L.Icon.Default.extend({options: { iconUrl: base + '/marker-green.png' } });
window.iconRes = L.Icon.Default.extend({options: { iconUrl: base + '/marker-blue.png' } });
window.setupTaphold();
window.setupStyles();
window.setupDialogs();
window.setupMap();
@ -255,6 +324,9 @@ function boot() {
window.setupPlayerStat();
window.setupTooltips();
window.chat.setup();
window.setupQRLoadLib();
window.setupLayerChooserSelectOne();
window.setupBackButton();
// read here ONCE, so the URL is only evaluated one time after the
// necessary data has been loaded.
urlPortal = getURLParam('pguid');
@ -287,26 +359,18 @@ function boot() {
// Copyright (c) 2010 Chris O'Hara <cohara87@gmail.com>. MIT Licensed
function asyncLoadScript(a){return function(b,c){var d=document.createElement("script");d.type="text/javascript",d.src=a,d.onload=b,d.onerror=c,d.onreadystatechange=function(){var a=this.readyState;if(a==="loaded"||a==="complete")d.onreadystatechange=null,b()},head.insertBefore(d,head.firstChild)}}(function(a){a=a||{};var b={},c,d;c=function(a,d,e){var f=a.halt=!1;a.error=function(a){throw a},a.next=function(c){c&&(f=!1);if(!a.halt&&d&&d.length){var e=d.shift(),g=e.shift();f=!0;try{b[g].apply(a,[e,e.length,g])}catch(h){a.error(h)}}return a};for(var g in b){if(typeof a[g]=="function")continue;(function(e){a[e]=function(){var g=Array.prototype.slice.call(arguments);if(e==="onError"){if(d)return b.onError.apply(a,[g,g.length]),a;var h={};return b.onError.apply(h,[g,g.length]),c(h,null,"onError")}return g.unshift(e),d?(a.then=a[e],d.push(g),f?a:a.next()):c({},[g],e)}})(g)}return e&&(a.then=a[e]),a.call=function(b,c){c.unshift(b),d.unshift(c),a.next(!0)},a.next()},d=a.addMethod=function(d){var e=Array.prototype.slice.call(arguments),f=e.pop();for(var g=0,h=e.length;g<h;g++)typeof e[g]=="string"&&(b[e[g]]=f);--h||(b["then"+d.substr(0,1).toUpperCase()+d.substr(1)]=f),c(a)},d("chain",function(a){var b=this,c=function(){if(!b.halt){if(!a.length)return b.next(!0);try{null!=a.shift().call(b,c,b.error)&&c()}catch(d){b.error(d)}}};c()}),d("run",function(a,b){var c=this,d=function(){c.halt||--b||c.next(!0)},e=function(a){c.error(a)};for(var f=0,g=b;!c.halt&&f<g;f++)null!=a[f].call(c,d,e)&&d()}),d("defer",function(a){var b=this;setTimeout(function(){b.next(!0)},a.shift())}),d("onError",function(a,b){var c=this;this.error=function(d){c.halt=!0;for(var e=0;e<b;e++)a[e].call(c,d)}})})(this);var head=document.getElementsByTagName("head")[0]||document.documentElement;addMethod("load",function(a,b){for(var c=[],d=0;d<b;d++)(function(b){c.push(asyncLoadScript(a[b]))})(d);this.call("run",c)})
try { console.log('Loading included JS now'); } catch(e) {}
@@INCLUDERAW:external/leaflet.js@@
// modified version of https://github.com/shramov/leaflet-plugins. Also
// contains the default Ingress map style.
var LEAFLETGOOGLE = 'http://breunigs.github.com/ingress-intel-total-conversion/dist/leaflet_google.js';
@@INCLUDERAW:external/leaflet_google.js@@
@@INCLUDERAW:external/autolink.js@@
@@INCLUDERAW:external/oms.min.js@@
try { console.log('done loading included JS'); } catch(e) {}
var JQUERY = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
var JQUERYUI = 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.0/jquery-ui.min.js';
var LEAFLET = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.js';
var AUTOLINK = 'http://breunigs.github.com/ingress-intel-total-conversion/dist/autolink.js';
var EMPTY = 'data:text/javascript;base64,';
// dont download resources which have been injected already
var ir = window && window.internalResources ? window.internalResources : [];
if(ir.indexOf('jquery') !== -1) JQUERY = EMPTY;
if(ir.indexOf('jqueryui') !== -1) JQUERYUI = EMPTY;
if(ir.indexOf('leaflet') !== -1) LEAFLET = EMPTY;
if(ir.indexOf('autolink') !== -1) AUTOLINK = EMPTY;
if(ir.indexOf('leafletgoogle') !== -1) LEAFLETGOOGLE = EMPTY;
var JQUERYUI = 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.0/jquery-ui.min.js';
// after all scripts have loaded, boot the actual app
load(JQUERY, LEAFLET, AUTOLINK).then(LEAFLETGOOGLE, JQUERYUI).onError(function (err) {
alert('Could not all resources, the script likely wont work.\n\nIf this happend the first time for you, its probably a temporary issue. Just wait a bit and try again.\n\nIf you installed the script for the first time and this happens:\n try disabling NoScript if you have it installed\n press CTRL+SHIFT+K in Firefox or CTRL+SHIFT+I in Chrome/Opera and reload the page. Additional info may be available in the console.\n Open an issue at https://github.com/breunigs/ingress-intel-total-conversion/issues');
}).thenRun(boot);
load(JQUERY).then(JQUERYUI).thenRun(boot);

View File

@ -279,8 +279,14 @@ window.chat.writeDataToHash = function(newData, storageHash, skipSecureMsgs) {
case 'PORTAL':
var latlng = [markup[1].latE6/1E6, markup[1].lngE6/1E6];
var js = 'window.zoomToAndShowPortal(\''+markup[1].guid+'\', ['+latlng[0]+', '+latlng[1]+'])';
msg += '<a onclick="'+js+'" title="'+markup[1].address+'" class="help">'+markup[1].name+'</a>';
var perma = 'https://ingress.com/intel?latE6='+markup[1].latE6+'&lngE6='+markup[1].lngE6+'&z=17&pguid='+markup[1].guid;
var js = 'window.zoomToAndShowPortal(\''+markup[1].guid+'\', ['+latlng[0]+', '+latlng[1]+']);return false';
msg += '<a onclick="'+js+'"'
+ ' title="'+markup[1].address+'"'
+ ' href="'+perma+'" class="help">'
+ window.chat.getChatPortalName(markup[1])
+ '</a>';
break;
case 'SECURE':
@ -299,6 +305,16 @@ window.chat.writeDataToHash = function(newData, storageHash, skipSecureMsgs) {
});
}
// Override portal names that are used over and over, such as 'US Post Office'
window.chat.getChatPortalName = function(markup) {
var name = markup.name;
if(name === 'US Post Office') {
var address = markup.address.split(',');
name = 'USPS: ' + address[0];
}
return name;
}
// renders data from the data-hash to the element defined by the given
// ID. Set 3rd argument to true if it is likely that old data has been
// added. Latter is only required for scrolling.
@ -341,7 +357,7 @@ window.chat.renderMsg = function(msg, nick, time, team) {
var s = 'style="color:'+COLORS[team]+'"';
var title = nick.length >= 8 ? 'title="'+nick+'" class="help"' : '';
var i = ['<span class="invisep">&lt;</span>', '<span class="invisep">&gt;</span>'];
return '<tr><td>'+t+'</td><td>'+i[0]+'<mark '+s+'>'+nick+'</mark>'+i[1]+'</td><td>'+msg+'</td></tr>';
return '<tr><td>'+t+'</td><td>'+i[0]+'<mark class="nickname" '+s+'>'+nick+'</mark>'+i[1]+'</td><td>'+msg+'</td></tr>';
}
@ -383,6 +399,7 @@ window.chat.needMoreMessages = function() {
if(activeTab === 'debug') return;
var activeChat = $('#chat > :visible');
if(activeChat.length === 0) return;
var hasScrollbar = scrollBottom(activeChat) !== 0 || activeChat.scrollTop() !== 0;
var nearTop = activeChat.scrollTop() <= CHAT_REQUEST_SCROLL_TOP;
@ -402,6 +419,7 @@ window.chat.chooser = function(event) {
var tt = t.text();
var mark = $('#chatinput mark');
var input = $('#chatinput input');
$('#chatcontrols .active').removeClass('active');
t.addClass('active');
@ -412,11 +430,13 @@ window.chat.chooser = function(event) {
switch(tt) {
case 'faction':
input.css('color', '');
mark.css('color', '');
mark.text('tell faction:');
break;
case 'public':
input.css('cssText', 'color: red !important');
mark.css('cssText', 'color: red !important');
mark.text('broadcast:');
break;
@ -424,6 +444,7 @@ window.chat.chooser = function(event) {
case 'compact':
case 'full':
mark.css('cssText', 'color: #bbb !important');
input.css('cssText', 'color: #bbb !important');
mark.text('tell Jarvis:');
break;
@ -567,13 +588,13 @@ window.chat.postMsg = function() {
if(c === 'debug') return new Function (msg)();
var public = c === 'public';
var publik = c === 'public';
var latlng = map.getCenter();
var data = {message: msg,
latE6: Math.round(latlng.lat*1E6),
lngE6: Math.round(latlng.lng*1E6),
factionOnly: !public};
factionOnly: !publik};
var errMsg = 'Your message could not be delivered. You can copy&' +
'paste it here and try again if you want:\n\n' + msg;
@ -581,7 +602,7 @@ window.chat.postMsg = function() {
window.postAjax('sendPlext', data,
function(response) {
if(response.error) alert(errMsg);
if(public) chat.requestPublic(false); else chat.requestFaction(false); },
if(publik) chat.requestPublic(false); else chat.requestFaction(false); },
function() {
alert(errMsg);
}

View File

@ -15,7 +15,7 @@ window.updateGameScore = function(data) {
var es = '<span class="enl" style="width:'+ep+'%;">&nbsp;'+Math.round(ep)+'%</span>';
$('#gamestat').html(rs+es).one('click', function() { window.updateGameScore() });
// help cursor via “#gamestat span”
$('#gamestat').attr('title', 'Resistance:\t'+r+' MindUnits\nEnlightenment:\t'+e+' MindUnits');
$('#gamestat').attr('title', 'Resistance:\t'+r+' MindUnits\nEnlightened:\t'+e+' MindUnits');
window.setTimeout('window.updateGameScore', REFRESH_GAME_SCORE*1000);
}

View File

@ -12,6 +12,7 @@ window.setupGeosearch = function() {
northEast = new L.LatLng(b[1], b[3]),
bounds = new L.LatLngBounds(southWest, northEast);
window.map.fitBounds(bounds);
if(window.isSmartphone()) window.smartphone.mapButton.click();
});
e.preventDefault();
});

View File

@ -43,13 +43,18 @@
// redrawn. It is called early on in the
// code/map_data.js#renderPortal as long as there was an
// old portal for the guid.
// checkRenderLimit: callback is passed the argument of
// {reached : false} to indicate that the renderlimit is reached
// set reached to true.
// requestFinished: called after each request finished. Argument is
// {success: boolean} indicated the request success or fail.
window._hooks = {}
window.VALID_HOOKS = ['portalAdded', 'portalDetailsUpdated',
'publicChatDataAvailable', 'portalDataLoaded', 'beforePortalReRender'];
'publicChatDataAvailable', 'portalDataLoaded', 'beforePortalReRender',
'checkRenderLimit', 'requestFinished'];
window.runHooks = function(event, data) {
if(VALID_HOOKS.indexOf(event) === -1) throw('Unknown event type: ' + event);

View File

@ -43,25 +43,35 @@ window.requestData = function() {
}
}
// Reset previous result of Portal Render Limit handler
portalRenderLimit.init();
// finally send ajax requests
$.each(tiles, function(ind, tls) {
data = { minLevelOfDetail: -1 };
data.boundsParamsList = tls;
window.requests.add(window.postAjax('getThinnedEntitiesV2', data, window.handleDataResponse));
window.requests.add(window.postAjax('getThinnedEntitiesV2', data, window.handleDataResponse, window.handleFailedRequest));
});
}
// Handle failed map data request
window.handleFailedRequest = function() {
if(requests.isLastRequest('getThinnedEntitiesV2')) {
var leftOverPortals = portalRenderLimit.mergeLowLevelPortals(null);
handlePortalsRender(leftOverPortals);
}
runHooks('requestFinished', {success: false});
}
// works on map data response and ensures entities are drawn/updated.
window.handleDataResponse = function(data, textStatus, jqXHR) {
// remove from active ajax queries list
if(!data || !data.result) {
window.failedRequestCount++;
console.warn(data);
handleFailedRequest();
return;
}
var portalUpdateAvailable = false;
var portalInUrlAvailable = false;
var m = data.result.map;
// defer rendering of portals because there is no z-index in SVG.
// this means that whats rendered last ends up on top. While the
@ -71,7 +81,7 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
var ppp = [];
var p2f = {};
$.each(m, function(qk, val) {
$.each(val.deletedGameEntityGuids, function(ind, guid) {
$.each(val.deletedGameEntityGuids || [], function(ind, guid) {
if(getTypeByGuid(guid) === TYPE_FIELD && window.fields[guid] !== undefined) {
$.each(window.fields[guid].options.vertices, function(ind, vertex) {
if(window.portals[vertex.guid] === undefined) return true;
@ -82,14 +92,12 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
window.removeByGuid(guid);
});
$.each(val.gameEntities, function(ind, ent) {
$.each(val.gameEntities || [], function(ind, ent) {
// ent = [GUID, id(?), details]
// format for links: { controllingTeam, creator, edge }
// format for portals: { controllingTeam, turret }
if(ent[2].turret !== undefined) {
if(selectedPortal === ent[0]) portalUpdateAvailable = true;
if(urlPortal && ent[0] == urlPortal) portalInUrlAvailable = true;
var latlng = [ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6];
if(!window.getPaddedBounds().contains(latlng)
@ -125,18 +133,37 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
}
});
// Preserve and restore "selectedPortal" between portal re-render
if(portalUpdateAvailable) var oldSelectedPortal = selectedPortal;
// Process the portals with portal render limit handler first
// Low level portal will hold until last request
var newPpp = portalRenderLimit.splitOrMergeLowLevelPortals(ppp);
handlePortalsRender(newPpp);
runHooks('portalDataLoaded', {portals : ppp});
$.each(ppp, function(ind, portal) { renderPortal(portal); });
resolvePlayerNames();
renderUpdateStatus();
runHooks('requestFinished', {success: true});
}
var selectedPortalLayer = portals[oldSelectedPortal];
if(portalUpdateAvailable && selectedPortalLayer) selectedPortal = oldSelectedPortal;
window.handlePortalsRender = function(portals) {
var portalInUrlAvailable = false;
if(selectedPortalLayer) {
// Preserve selectedPortal because it will get lost on re-rendering
// the portal
var oldSelectedPortal = selectedPortal;
runHooks('portalDataLoaded', {portals : portals});
$.each(portals, function(ind, portal) {
//~ if(selectedPortal === portal[0]) portalUpdateAvailable = true;
if(urlPortal && portal[0] === urlPortal) portalInUrlAvailable = true;
renderPortal(portal);
});
// restore selected portal if still available
var selectedPortalGroup = portals[oldSelectedPortal];
if(selectedPortalGroup) {
selectedPortal = oldSelectedPortal;
renderPortalDetails(selectedPortal);
try {
selectedPortalLayer.bringToFront();
selectedPortalGroup.bringToFront();
} catch(e) { /* portal is now visible, catch Leaflet error */ }
}
@ -144,9 +171,6 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
renderPortalDetails(urlPortal);
urlPortal = null; // select it only once
}
if(portalUpdateAvailable) renderPortalDetails(selectedPortal);
resolvePlayerNames();
}
// removes entities that are still handled by Leaflet, although they
@ -174,10 +198,13 @@ window.cleanUp = function() {
cnt[1]++;
linksLayer.removeLayer(link);
});
fieldsLayer.eachLayer(function(field) {
if(b.intersects(field.getBounds())) return;
cnt[2]++;
fieldsLayer.removeLayer(field);
fieldsLayer.eachLayer(function(fieldgroup) {
fieldgroup.eachLayer(function(item) {
if(!item.options.guid) return true; // Skip MU div container as this doesn't have the bounds we need
if(b.intersects(item.getBounds())) return;
cnt[2]++;
fieldsLayer.removeLayer(fieldgroup);
});
});
console.log('removed out-of-bounds: '+cnt[0]+' portals, '+cnt[1]+' links, '+cnt[2]+' fields');
}
@ -261,8 +288,11 @@ window.renderPortal = function(ent) {
// pre-loads player names for high zoom levels
loadPlayerNamesForPortal(ent[2]);
var lvWeight = Math.max(2, portalLevel / 1.5);
var lvRadius = Math.max(portalLevel + 3, 5);
var lvWeight = Math.max(2, Math.floor(portalLevel) / 1.5);
var lvRadius = Math.floor(portalLevel) + 4;
if(team === window.TEAM_NONE) {
lvRadius = 7;
}
var p = L.circleMarker(latlng, {
radius: lvRadius + (L.Browser.mobile ? PORTAL_RADIUS_ENLARGE_MOBILE : 0),
@ -422,6 +452,7 @@ window.isResonatorsShow = function() {
window.isSameResonator = function(oldRes, newRes) {
if(!oldRes && !newRes) return true;
if(!oldRes || !newRes) return false;
if(typeof oldRes !== typeof newRes) return false;
if(oldRes.level !== newRes.level) return false;
if(oldRes.energyTotal !== newRes.energyTotal) return false;
@ -481,8 +512,18 @@ window.renderLink = function(ent) {
weight:2,
clickable: false,
guid: ent[0],
smoothFactor: 10
data: ent[2],
smoothFactor: 0 // doesnt work for two points anyway, so disable
});
// determine which links are very short and dont render them at all.
// in most cases this will go unnoticed, but improve rendering speed.
poly._map = window.map;
poly.projectLatlngs();
var op = poly._originalPoints;
var dist = Math.abs(op[0].x - op[1].x) + Math.abs(op[0].y - op[1].y);
if(dist <= 10) {
return;
}
if(!getPaddedBounds().intersects(poly.getBounds())) return;
@ -501,17 +542,18 @@ window.renderField = function(ent) {
if(Object.keys(fields).length >= MAX_DRAWN_FIELDS)
return window.removeByGuid(ent[0]);
// assume that fields never change. If they do, they will have a
// different ID.
if(findEntityInLeaflet(fieldsLayer, fields, ent[0])) return;
var old = findEntityInLeaflet(fieldsLayer, window.fields, ent[0]);
// If this already exists and the zoom level has not changed, we don't need to do anything
if(old && map.getZoom() === old.options.creationZoom) return;
var team = getTeam(ent[2]);
var reg = ent[2].capturedRegion;
var latlngs = [
[reg.vertexA.location.latE6/1E6, reg.vertexA.location.lngE6/1E6],
[reg.vertexB.location.latE6/1E6, reg.vertexB.location.lngE6/1E6],
[reg.vertexC.location.latE6/1E6, reg.vertexC.location.lngE6/1E6]
L.latLng(reg.vertexA.location.latE6/1E6, reg.vertexA.location.lngE6/1E6),
L.latLng(reg.vertexB.location.latE6/1E6, reg.vertexB.location.lngE6/1E6),
L.latLng(reg.vertexC.location.latE6/1E6, reg.vertexC.location.lngE6/1E6)
];
var poly = L.polygon(latlngs, {
fillColor: COLORS[team],
fillOpacity: 0.25,
@ -523,16 +565,77 @@ window.renderField = function(ent) {
guid: ent[0],
data: ent[2]});
// determine which fields are too small to be rendered and dont
// render them, so they dont count towards the maximum fields limit.
// This saves some DOM operations as well, but given the relatively
// low amount of fields there isnt much to gain.
// The algorithm is the same as used by Leaflet.
poly._map = window.map;
poly.projectLatlngs();
var count = L.LineUtil.simplify(poly._originalPoints, 6).length;
if(count <= 2) return;
if(!getPaddedBounds().intersects(poly.getBounds())) return;
// Curve fit equation to normalize zoom window area
var areaZoomRatio = calcTriArea(latlngs)/Math.exp(14.2714860198866-1.384987247*map.getZoom());
var countForMUDisplay = L.LineUtil.simplify(poly._originalPoints, FIELD_MU_DISPLAY_POINT_TOLERANCE).length
// Do nothing if zoom did not change. We need to recheck the field if the
// zoom level is different then when the field was rendered as it could
// now be appropriate or not to show an MU count
if(old) {
var layerCount = 0;
old.eachLayer(function(item) {
layerCount++;
});
// Don't do anything since we already have an MU display and we still want to
if(areaZoomRatio > FIELD_MU_DISPLAY_AREA_ZOOM_RATIO && countForMUDisplay > 2 && layerCount === 2) return;
// Don't do anything since we don't have an MU display and don't want to
if(areaZoomRatio <= FIELD_MU_DISPLAY_AREA_ZOOM_RATIO && countForMUDisplay <= 2 && layerCount === 1) return;
removeByGuid(ent[0]);
}
// put both in one group, so they can be handled by the same logic.
if (areaZoomRatio > FIELD_MU_DISPLAY_AREA_ZOOM_RATIO && countForMUDisplay > 2) {
// centroid of field for placing MU count at
var centroid = [
(latlngs[0].lat + latlngs[1].lat + latlngs[2].lat)/3,
(latlngs[0].lng + latlngs[1].lng + latlngs[2].lng)/3
];
var fieldMu = L.marker(centroid, {
icon: L.divIcon({
className: 'fieldmu',
iconSize: [70,12],
html: digits(ent[2].entityScore.entityScore)
}),
clickable: false
});
var f = L.layerGroup([poly, fieldMu]);
} else {
var f = L.layerGroup([poly]);
}
f.options = {
vertices: reg,
lastUpdate: ent[1],
creationZoom: map.getZoom(),
guid: ent[0],
data: ent[2]
};
// However, LayerGroups (and FeatureGroups) dont fire add/remove
// events, thus this listener will be attached to the field. It
// doesnt matter to which element these are bound since Leaflet
// will add/remove all elements of the LayerGroup at once.
poly.on('remove', function() { delete window.fields[this.options.guid]; });
poly.on('add', function() {
// enable for debugging
if(window.fields[this.options.guid]) console.warn('duplicate field detected');
window.fields[this.options.guid] = this;
window.fields[this.options.guid] = f;
this.bringToBack();
});
poly.addTo(fieldsLayer);
f.addTo(fieldsLayer);
}

View File

@ -17,6 +17,17 @@ window.getPlayerName = function(guid) {
return '{'+guid.slice(0, 12)+'}';
}
window.playerNameToGuid = function(playerName){
var guid = null;
$.each(Object.keys(localStorage), function(ind,key) {
if(playerName === localStorage[key]) {
guid = key;
return false;
}
});
return guid;
}
// resolves all player GUIDs that have been added to the list. Reruns
// renderPortalDetails when finished, so that then-unresolved names
// get replaced by their correct versions.

View File

@ -22,11 +22,14 @@ window.renderPortalDetails = function(guid) {
var linksText = [linkExpl('links'), linkExpl(' ↳ ' + links.incoming+'&nbsp;&nbsp;•&nbsp;&nbsp;'+links.outgoing+' ↴')];
var player = d.captured && d.captured.capturingPlayerId
? getPlayerName(d.captured.capturingPlayerId)
? '<span class="nickname">' + getPlayerName(d.captured.capturingPlayerId) + '</span>'
: null;
var playerText = player ? ['owner', player] : null;
var time = d.captured ? unixTimeToString(d.captured.capturedTime) : null;
var time = d.captured
? '<span title="' + unixTimeToString(d.captured.capturedTime, true) + '">'
+ unixTimeToString(d.captured.capturedTime) + '</span>'
: null;
var sinceText = time ? ['since', time] : null;
var linkedFields = ['fields', d.portalV2.linkedFields.length];
@ -41,13 +44,15 @@ window.renderPortalDetails = function(guid) {
var resoDetails = '<table id="resodetails">' + getResonatorDetails(d) + '</table>';
setPortalIndicators(d);
var img = d.imageByUrl && d.imageByUrl.imageUrl ? d.imageByUrl.imageUrl : DEFAULT_PORTAL_IMG;
var img = d.imageByUrl && d.imageByUrl.imageUrl
? d.imageByUrl.imageUrl
: DEFAULT_PORTAL_IMG;
var lat = d.locationE6.latE6;
var lng = d.locationE6.lngE6;
var perma = 'http://ingress.com/intel?latE6='+lat+'&lngE6='+lng+'&z=17&pguid='+guid;
var perma = 'https://ingress.com/intel?latE6='+lat+'&lngE6='+lng+'&z=17&pguid='+guid;
var imgTitle = 'title="'+getPortalDescriptionFromDetails(d)+'\n\nClick to show full image."';
var gmaps = 'https://maps.google.com/?q='+lat/1E6+','+lng/1E6;
var poslinks = 'window.showPortalPosLinks('+lat/1E6+','+lng/1E6+')';
var postcard = 'Send in a postcard. Will put it online after receiving. Address:\\n\\nStefan Breunig\\nINF 305 R045\\n69120 Heidelberg\\nGermany';
$('#portaldetails')
@ -63,7 +68,7 @@ window.renderPortalDetails = function(guid) {
+ randDetails
+ resoDetails
+ '<div class="linkdetails">'+ '<aside><a href="'+perma+'">portal link</a></aside>'
+ '<aside><a href="'+gmaps+'" target="_blank">gmaps</a></aside>'
+ '<aside><a onclick="'+poslinks+'">poslinks</a></aside>'
+ '<aside><a onclick="alert(\''+postcard+'\');">donate</a></aside>'
+ '<aside><a onclick="window.reportPortalIssue()">report issue</a></aside>'
+ '</div>'

View File

@ -132,6 +132,7 @@ window.renderResonatorDetails = function(slot, level, nrg, dist, nick) {
var meter = '<span class="meter" title="'+inf+'">' + fill + lbar + '</span>';
}
nick = nick ? '<span class="nickname">'+nick+'</span>' : null;
return [meter, nick || ''];
}

View File

@ -64,7 +64,7 @@ window.getAvgResoDist = function(d) {
sum += parseInt(reso.distanceToPortal);
resos++;
});
return sum/resos;
return resos ? sum/resos : 0;
}
window.getAttackApGain = function(d) {

155
code/portal_render_limit.js Normal file
View File

@ -0,0 +1,155 @@
// PORTAL RENDER LIMIT HANDLER ///////////////////////////////////////
// Functions to handle hiding low level portal when portal render
// limit is reached.
//
// On initialization, previous minLevel will preserve to previousMinLevel
// with zoom level difference.
//
// After initialized and reset in window.requestData(), "processPortals"
// intercept all portals data in "handleDataResponse". Put the count of
// new portals to newPortalsPerLevel[portal level]. And split portals
// into two parts base on previousMinLevel. Portals with level >=
// previousMinLevel will return as result and continue to render.
// Others will save to portalsPreviousMinLevel. If there is no more
// active request of map data, portals will not split and
// portalsPreviousMinLevel will add back to result and render base on
// current minLevel.
//
// "handleFailRequest" is added to handle the case when the last request
// failed and "processPortals" didn't get called. It will get
// portalsPreviousMinLevel base on current minLevel and render them.
//
// "getMinLevel" will be called by "getMinPortalLevel" in utils_misc.js
// to determine min portal level to draw on map.
//
// "getMinLevel" will return minLevel and call "setMinLevel" if
// minLevel hasn't set yet.
//
// In "setMinLevel", it will loop through all portal level from
// high to low, and sum total portal count (old + new) to check
// minLevel.
//
// In each call of window.handleDataResponse(), it will call
// "resetCounting" to reset previous response data. But minLevel
// is preserved and only replaced when render limit reached in
// higher level, until next window.requestData() called and reset.
//
window.portalRenderLimit = function() {}
window.portalRenderLimit.initialized = false;
window.portalRenderLimit.minLevelSet = false;
window.portalRenderLimit.minLevel = -1;
window.portalRenderLimit.previousMinLevel = -1;
window.portalRenderLimit.previousZoomLevel;
window.portalRenderLimit.newPortalsPerLevel = new Array(MAX_PORTAL_LEVEL + 1);
window.portalRenderLimit.portalsPreviousMinLevel = new Array(MAX_PORTAL_LEVEL + 1);
window.portalRenderLimit.init = function () {
var currentZoomLevel = map.getZoom();
portalRenderLimit.previousZoomLevel = portalRenderLimit.previousZoomLevel || currentZoomLevel;
// If there is a minLevel set in previous run, calculate previousMinLevel with it.
if(portalRenderLimit.minLevelSet) {
var zoomDiff = currentZoomLevel - portalRenderLimit.previousZoomLevel;
portalRenderLimit.previousMinLevel = Math.max(portalRenderLimit.minLevel - zoomDiff, -1);
portalRenderLimit.previousMinLevel = Math.min(portalRenderLimit.previousMinLevel, MAX_PORTAL_LEVEL);
}
portalRenderLimit.previousZoomLevel = currentZoomLevel;
portalRenderLimit.initialized = true;
portalRenderLimit.minLevel = -1;
portalRenderLimit.resetCounting();
portalRenderLimit.resetPortalsPreviousMinLevel();
}
window.portalRenderLimit.resetCounting = function() {
portalRenderLimit.minLevelSet = false;
for(var i = 0; i <= MAX_PORTAL_LEVEL; i++) {
portalRenderLimit.newPortalsPerLevel[i] = 0;
}
}
window.portalRenderLimit.resetPortalsPreviousMinLevel = function() {
for(var i = 0; i <= MAX_PORTAL_LEVEL; i++) {
portalRenderLimit.portalsPreviousMinLevel[i] = new Array();
}
}
window.portalRenderLimit.splitOrMergeLowLevelPortals = function(originPortals) {
portalRenderLimit.resetCounting();
portalRenderLimit.countingPortals(originPortals);
var resultPortals = requests.isLastRequest('getThinnedEntitiesV2')
? portalRenderLimit.mergeLowLevelPortals(originPortals)
: portalRenderLimit.splitLowLevelPortals(originPortals);
return resultPortals;
}
window.portalRenderLimit.countingPortals = function(portals) {
$.each(portals, function(ind, portal) {
var portalGuid = portal[0];
var portalLevel = parseInt(getPortalLevel(portal[2]));
var layerGroup = portalsLayers[portalLevel];
if(findEntityInLeaflet(layerGroup, window.portals, portalGuid)) return true;
portalRenderLimit.newPortalsPerLevel[portalLevel]++;
});
}
window.portalRenderLimit.splitLowLevelPortals = function(portals) {
var resultPortals = new Array();
$.each(portals, function(ind, portal) {
var portalLevel = parseInt(getPortalLevel(portal[2]));
if(portalLevel < portalRenderLimit.previousMinLevel) {
portalRenderLimit.portalsPreviousMinLevel[portalLevel].push(portal);
}else{
resultPortals.push(portal);
}
});
return resultPortals;
}
window.portalRenderLimit.mergeLowLevelPortals = function(appendTo) {
var resultPortals = appendTo ? appendTo : new Array();
for(var i = portalRenderLimit.getMinLevel();
i < portalRenderLimit.previousMinLevel;
i++) {
$.merge(resultPortals, portalRenderLimit.portalsPreviousMinLevel[i]);
}
// Reset portalsPreviousMinLevel, ensure they return only once
portalRenderLimit.resetPortalsPreviousMinLevel();
return resultPortals;
}
window.portalRenderLimit.getMinLevel = function() {
if(!portalRenderLimit.initialized) return -1;
if(!portalRenderLimit.minLevelSet) portalRenderLimit.setMinLevel();
return portalRenderLimit.minLevel;
}
window.portalRenderLimit.setMinLevel = function() {
var totalPortalsCount = 0;
var newMinLevel = MAX_PORTAL_LEVEL + 1;
// Find the min portal level under render limit
while(newMinLevel > 0) {
var oldPortalCount = layerGroupLength(portalsLayers[newMinLevel - 1]);
var newPortalCount = portalRenderLimit.newPortalsPerLevel[newMinLevel - 1];
totalPortalsCount += oldPortalCount + newPortalCount;
if(totalPortalsCount >= MAX_DRAWN_PORTALS)
break;
newMinLevel--;
}
// If render limit reached at max portal level, still let portal at max level render
newMinLevel = Math.min(newMinLevel, MAX_PORTAL_LEVEL);
portalRenderLimit.minLevel = Math.max(newMinLevel, portalRenderLimit.minLevel);
portalRenderLimit.minLevelSet = true;
}

View File

@ -3,44 +3,69 @@
// REDEEMING /////////////////////////////////////////////////////////
window.handleRedeemResponse = function(data, textStatus, jqXHR) {
if (data.error) {
if(data.error) {
var error = '';
if (data.error === 'ALREADY_REDEEMED') {
if(data.error === 'ALREADY_REDEEMED') {
error = 'The passcode has already been redeemed.';
} else if (data.error === 'ALREADY_REDEEMED_BY_PLAYER') {
} else if(data.error === 'ALREADY_REDEEMED_BY_PLAYER') {
error = 'You have already redeemed this passcode.';
} else if (data.error === 'INVALID_PASSCODE') {
} else if(data.error === 'INVALID_PASSCODE') {
error = 'This passcode is invalid.';
} else {
error = 'The passcode cannot be redeemed.';
error = 'There was a problem redeeming the passcode. Try again?';
}
alert("Error: " + data.error + "\n" + error);
} else if (data.result) {
var res_level = 0, res_count = 0;
var xmp_level = 0, xmp_count = 0;
var shield_rarity = '', shield_count = 0;
// This assumes that each passcode gives only one type of resonator/XMP/shield.
// This may break at some point, depending on changes to passcode functionality.
for (var i in data.result.inventoryAward) {
alert('<strong>' + data.error + '</strong>\n' + error);
} else if(data.result) {
var tblResult = $('<table class="redeem-result" />');
tblResult.append($('<tr><th colspan="2">Passcode accepted!</th></tr>'));
if(data.result.apAward)
tblResult.append($('<tr><td>+</td><td>' + data.result.apAward + 'AP</td></tr>'));
if(data.result.xmAward)
tblResult.append($('<tr><td>+</td><td>' + data.result.xmAward + 'XM</td></tr>'));
var resonators = {};
var bursts = {};
var shields = {};
for(var i in data.result.inventoryAward) {
var acquired = data.result.inventoryAward[i][2];
if (acquired.modResource) {
if (acquired.modResource.resourceType === 'RES_SHIELD') {
shield_rarity = acquired.modResource.rarity.split('_').map(function (i) {return i[0]}).join('');
shield_count++;
if(acquired.modResource) {
if(acquired.modResource.resourceType === 'RES_SHIELD') {
var rarity = acquired.modResource.rarity.split('_').map(function (i) {return i[0]}).join('');
if(!shields[rarity]) shields[rarity] = 0;
shields[rarity] += 1;
}
} else if (acquired.resourceWithLevels) {
if (acquired.resourceWithLevels.resourceType === 'EMITTER_A') {
res_level = acquired.resourceWithLevels.level;
res_count++;
} else if (acquired.resourceWithLevels.resourceType === 'EMP_BURSTER') {
xmp_level = acquired.resourceWithLevels.level;
xmp_count++;
} else if(acquired.resourceWithLevels) {
if(acquired.resourceWithLevels.resourceType === 'EMITTER_A') {
var level = acquired.resourceWithLevels.level
if(!resonators[level]) resonators[level] = 0;
resonators[level] += 1;
} else if(acquired.resourceWithLevels.resourceType === 'EMP_BURSTER') {
var level = acquired.resourceWithLevels.level
if(!bursts[level]) bursts[level] = 0;
bursts[level] += 1;
}
}
}
$.each(resonators, function(lvl, count) {
var text = 'Resonator';
if(count >= 2) text += ' ('+count+')';
tblResult.append($('<tr ><td style="color: ' +window.COLORS_LVL[lvl]+ ';">L' +lvl+ '</td><td>' + text + '</td></tr>'));
});
$.each(bursts, function(lvl, count) {
var text = 'Xmp Burster';
if(count >= 2) text += ' ('+count+')';
tblResult.append($('<tr ><td style="color: ' +window.COLORS_LVL[lvl]+ ';">L' +lvl+ '</td><td>' + text + '</td></tr>'));
});
$.each(shields, function(lvl, count) {
var text = 'Portal Shield';
if(count >= 2) text += ' ('+count+')';
tblResult.append($('<tr><td>'+lvl+'</td><td>'+text+'</td></tr>'));
});
alert("Passcode redeemed!\n" + [data.result.apAward + 'AP', data.result.xmAward + 'XM', res_count + 'xL' + res_level + ' RES', xmp_count + 'xL' + xmp_level + ' XMP', shield_count + 'x' + shield_rarity + ' SHIELD'].join('/'));
alert(tblResult, true);
}
}
@ -49,6 +74,19 @@ window.setupRedeem = function() {
if((e.keyCode ? e.keyCode : e.which) != 13) return;
var data = {passcode: $(this).val()};
window.postAjax('redeemReward', data, window.handleRedeemResponse,
function() { alert('The HTTP request failed. Either your code is invalid or their servers are down. No way to tell.'); });
function(response) {
var extra = '';
if(response && response.status) {
if(response.status === 429) {
extra = 'You have been rate-limited by the server. Wait a bit and try again.';
} else {
extra = 'The server indicated an error.';
}
extra += '\nResponse: HTTP <a href="http://httpstatus.es/' + response.status + '" alt="HTTP ' + response.status + '">' + response.status + '</a>.';
} else {
extra = 'No status code was returned.';
}
alert('<strong>The HTTP request failed.</strong> ' + extra);
});
});
}

View File

@ -111,3 +111,14 @@ window.requests._callOnRefreshFunctions = function() {
window.requests.addRefreshFunction = function(f) {
window.requests._onRefreshFunctions.push(f);
}
window.requests.isLastRequest = function(action) {
var result = true;
$.each(window.activeRequests, function(ind, req) {
if(req.action === action) {
result = false;
return false;
}
});
return result;
}

View File

@ -49,13 +49,6 @@ window.runOnSmartphonesBeforeBoot = function() {
$('#chatcontrols').append(smartphone.mapButton).append(smartphone.sideButton);
// add event to portals that allows long press to switch to sidebar
window.addHook('portalAdded', function(data) {
data.portal.on('dblclick', function() {
window.lastClickedPortal = this.options.guid;
});
});
window.addHook('portalDetailsUpdated', function(data) {
var x = $('.imgpreview img').removeClass('hide');
@ -83,4 +76,23 @@ window.runOnSmartphonesAfterBoot = function() {
$('#portaldetails').off('click', '**');
$('.leaflet-right').addClass('leaflet-left').removeClass('leaflet-right');
// make buttons in action bar flexible
var l = $('#chatcontrols a:visible');
l.css('width', 100/l.length + '%');
// add event to portals that allows long press to switch to sidebar
window.addHook('portalAdded', function(data) {
data.portal.on('add', function() {
if(!this._container || this.options.addedTapHoldHandler) return;
this.options.addedTapHoldHandler = true;
var guid = this.options.guid;
// this is a hack, accessing Leaflets private _container is evil
$(this._container).on('taphold', function() {
window.renderPortalDetails(guid);
window.smartphone.sideButton.click();
});
});
});
}

View File

@ -2,6 +2,14 @@
// UTILS + MISC ///////////////////////////////////////////////////////
window.layerGroupLength = function(layerGroup) {
var layersCount = 0;
var layers = layerGroup._layers;
if (layers)
layersCount = Object.keys(layers).length;
return layersCount;
}
// retrieves parameter from the URL?query=string.
window.getURLParam = function(param) {
var v = document.URL;
@ -50,11 +58,11 @@ window.postAjax = function(action, data, success, error) {
data = JSON.stringify($.extend({method: 'dashboard.'+action}, data));
var remove = function(data, textStatus, jqXHR) { window.requests.remove(jqXHR); };
var errCnt = function(jqXHR) { window.failedRequestCount++; window.requests.remove(jqXHR); };
return $.ajax({
var result = $.ajax({
// use full URL to avoid issues depending on how people set their
// slash. See:
// https://github.com/breunigs/ingress-intel-total-conversion/issues/56
url: 'http://www.ingress.com/rpc/dashboard.'+action,
url: 'https://www.ingress.com/rpc/dashboard.'+action,
type: 'POST',
data: data,
dataType: 'json',
@ -65,6 +73,8 @@ window.postAjax = function(action, data, success, error) {
req.setRequestHeader('X-CSRFToken', readCookie('csrftoken'));
}
});
result.action = action;
return result;
}
// converts unix timestamps to HH:mm:ss format if it was today;
@ -96,8 +106,16 @@ window.rangeLinkClick = function() {
window.smartphone.mapButton.click();
}
window.showPortalPosLinks = function(lat, lng) {
var qrcode = '<div id="qrcode"></div>';
var script = '<script>$(\'#qrcode\').qrcode({text:\'GEO:'+lat+','+lng+'\'});</script>';
var gmaps = '<a href="https://maps.google.com/?q='+lat+','+lng+'">gmaps</a>';
var osm = '<a href="http://www.openstreetmap.org/?mlat='+lat+'&mlon='+lng+'&zoom=16">OSM</a>';
alert('<div style="text-align: center;">' + qrcode + script + gmaps + ' ' + osm + '</div>');
}
window.reportPortalIssue = function(info) {
var t = 'Redirecting you to a Google Help Page. Once there, click on “Contact Us” in the upper right corner.\n\nThe text box contains all necessary information. Press CTRL+C to copy it.';
var t = 'Redirecting you to a Google Help Page.\n\nThe text box contains all necessary information. Press CTRL+C to copy it.';
var d = window.portals[window.selectedPortal].options.details;
var info = 'Your Nick: ' + PLAYER.nickname + ' '
@ -107,7 +125,7 @@ window.reportPortalIssue = function(info) {
//codename, approx addr, portalname
if(prompt(t, info) !== null)
location.href = 'https://support.google.com/ingress?hl=en';
location.href = 'https://support.google.com/ingress?hl=en&contact=1';
}
window._storedPaddedBounds = undefined;
@ -117,6 +135,7 @@ window.getPaddedBounds = function() {
window._storedPaddedBounds = null;
});
}
if(renderLimitReached(0.7)) return window.map.getBounds();
if(window._storedPaddedBounds) return window._storedPaddedBounds;
var p = window.map.getBounds().pad(VIEWPORT_PAD_RATIO);
@ -124,18 +143,30 @@ window.getPaddedBounds = function() {
return p;
}
window.renderLimitReached = function() {
if(Object.keys(portals).length >= MAX_DRAWN_PORTALS) return true;
if(Object.keys(links).length >= MAX_DRAWN_LINKS) return true;
if(Object.keys(fields).length >= MAX_DRAWN_FIELDS) return true;
return false;
// returns true if the render limit has been reached. The default ratio
// is 1, which means it will tell you if there are more items drawn than
// acceptable. A value of 0.9 will tell you if 90% of the amount of
// acceptable entities have been drawn. You can use this to heuristi-
// cally detect if the render limit will be hit.
window.renderLimitReached = function(ratio) {
ratio = ratio || 1;
if(Object.keys(portals).length*ratio >= MAX_DRAWN_PORTALS) return true;
if(Object.keys(links).length*ratio >= MAX_DRAWN_LINKS) return true;
if(Object.keys(fields).length*ratio >= MAX_DRAWN_FIELDS) return true;
var param = { 'reached': false };
window.runHooks('checkRenderLimit', param);
return param.reached;
}
window.getMinPortalLevel = function() {
var z = map.getZoom();
if(z >= 16) return 0;
var conv = ['impossible', 8,7,7,6,6,5,5,4,4,3,3,2,2,1,1];
return conv[z];
var minLevelByRenderLimit = portalRenderLimit.getMinLevel();
var result = minLevelByRenderLimit > conv[z]
? minLevelByRenderLimit
: conv[z];
return result;
}
// returns number of pixels left to scroll down before reaching the
@ -213,7 +244,7 @@ window.setPermaLink = function(elm) {
var lat = Math.round(c.lat*1E6);
var lng = Math.round(c.lng*1E6);
var qry = 'latE6='+lat+'&lngE6='+lng+'&z=' + (map.getZoom()-1);
$(elm).attr('href', 'http://www.ingress.com/intel?' + qry);
$(elm).attr('href', 'https://www.ingress.com/intel?' + qry);
}
window.uniqueArray = function(arr) {
@ -267,3 +298,8 @@ window.convertTextToTableMagic = function(text) {
table += '</table>';
return table;
}
// Given 3 sets of points in an array[3]{lat, lng} returns the area of the triangle
window.calcTriArea = function(p) {
return Math.abs((p[0].lat*(p[1].lng-p[2].lng)+p[1].lat*(p[2].lng-p[0].lng)+p[2].lat*(p[0].lng-p[1].lng))/2);
}