// SETUP ///////////////////////////////////////////////////////////// // these functions set up specific areas after the boot function // created a basic framework. All of these functions should only ever // be run once. // Used to disable on multitouch devices window.showZoom = true; 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 img = $(this).find('img')[0]; var w = img.naturalWidth, c = $('#portaldetails').attr('class'); var d = dialog({ html: '' + img.outerHTML + '', title: $(this).parent().find('h3.title').html() }); // We have to dynamically set the width of this dialog, so get the .ui-dialog component var p = d.parents('.ui-dialog'); // Don't let this dialog get smaller than the default maximum dialog width var width = Math.max(parseInt(p.css('max-width')), w); p.css('min-width', width + 'px'); p.css('width', width + 'px'); }); } // 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 m = window.map; var add = function(layer) { if(!m.hasLayer(layer.layer)) m.addLayer(layer.layer); }; var rem = function(layer) { if(m.hasLayer(layer.layer)) m.removeLayer(layer.layer); }; var isChecked = $(e.target).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 $.each(window.layerChooser._layers, function(ind, layer) { if(!layer.overlay) return true; add(layer); }); } else { // uncheck all var keep = $.trim($(e.target).text()); $.each(window.layerChooser._layers, function(ind, layer) { if(layer.overlay !== true) return true; if(layer.name === keep) { add(layer); return true; } rem(layer); }); } e.preventDefault(); }); } // Setup the function to record the on/off status of overlay layerGroups window.setupLayerChooserStatusRecorder = function() { // Record already added layerGroups $.each(window.layerChooser._layers, function(ind, chooserEntry) { if(!chooserEntry.overlay) return true; var display = window.map.hasLayer(chooserEntry.layer); window.updateDisplayedLayerGroup(chooserEntry.name, display); }); // Record layerGroups change window.map.on('layeradd layerremove', function(e) { var id = L.stamp(e.layer); var layerGroup = this._layers[id]; if (layerGroup && layerGroup.overlay) { var display = (e.type === 'layeradd'); window.updateDisplayedLayerGroup(layerGroup.name, display); } }, window.layerChooser); } window.setupStyles = function() { $('head').append(''); } window.setupMap = function() { $('#map').text(''); //OpenStreetMap attribution - required by several of the layers osmAttribution = 'Map data © OpenStreetMap contributors'; //OpenStreetMap tiles - we shouldn't use these by default, or even an option - https://wiki.openstreetmap.org/wiki/Tile_usage_policy // "Heavy use (e.g. distributing an app that uses tiles from openstreetmap.org) is forbidden without prior permission from the System Administrators" //var osmOpt = {attribution: osmAttribution, maxZoom: 18, detectRetina: true}; //var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', osmOpt); //CloudMade layers - only 500,000 tiles/month in their free plan. nowhere near enough for IITC var cmOpt = {attribution: osmAttribution+', Imagery © CloudMade', maxZoom: 18, detectRetina: true}; //var cmMin = new L.TileLayer('http://{s}.tile.cloudmade.com/{your api key here}/22677/256/{z}/{x}/{y}.png', cmOpt); //var cmMid = new L.TileLayer('http://{s}.tile.cloudmade.com/{your api key here}/999/256/{z}/{x}/{y}.png', cmOpt); //MapQuest offer tiles - http://developer.mapquest.com/web/products/open/map //their usage policy has no limits (except required notification above 4000 tiles/sec - we're perhaps at 50 tiles/sec based on CloudMade stats) var mqSubdomains = [ 'otile1','otile2', 'otile3', 'otile4' ]; var mqTileUrlPrefix = window.location.protocol !== 'https:' ? 'http://{s}.mqcdn.com' : 'https://{s}-s.mqcdn.com'; var mqMapOpt = {attribution: osmAttribution+', Tiles Courtesy of MapQuest', maxZoom: 18, subdomains: mqSubdomains}; var mqMap = new L.TileLayer(mqTileUrlPrefix+'/tiles/1.0.0/map/{z}/{x}/{y}.jpg',mqMapOpt); //MapQuest satellite coverage outside of the US is rather limited - so not really worth having as we have google as an option //var mqSatOpt = {attribution: 'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency', mazZoom: 18, subdomains: mqSubdomains}; //var mqSat = new L.TileLayer('http://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',mqSatOpt); var views = [ /*0*/ mqMap, /*1*/ new L.Google('INGRESS',{maxZoom:20}), /*2*/ new L.Google('ROADMAP',{maxZoom:20}), /*3*/ new L.Google('SATELLITE',{maxZoom:20}), /*4*/ new L.Google('HYBRID',{maxZoom:20}), /*5*/ new L.Google('TERRAIN',{maxZoom:15}) ]; window.map = new L.Map('map', $.extend(getPosition(), {zoomControl: window.showZoom} )); var addLayers = {}; var hiddenLayer = []; portalsLayers = []; for(var i = 0; i <= 8; i++) { portalsLayers[i] = L.layerGroup([]); map.addLayer(portalsLayers[i]); var t = (i === 0 ? 'Unclaimed' : 'Level ' + i) + ' Portals'; addLayers[t] = portalsLayers[i]; // Store it in hiddenLayer to remove later if(!isLayerGroupDisplayed(t, true)) hiddenLayer.push(portalsLayers[i]); } fieldsLayer = L.layerGroup([]); map.addLayer(fieldsLayer, true); addLayers['Fields'] = fieldsLayer; // Store it in hiddenLayer to remove later if(!isLayerGroupDisplayed('Fields', true)) hiddenLayer.push(fieldsLayer); linksLayer = L.layerGroup([]); map.addLayer(linksLayer, true); addLayers['Links'] = linksLayer; // Store it in hiddenLayer to remove later if(!isLayerGroupDisplayed('Links', true)) hiddenLayer.push(linksLayer); window.layerChooser = new L.Control.Layers({ 'MapQuest OSM': views[0], 'Default Ingress Map': views[1], 'Google Roads': views[2], 'Google Satellite': views[3], 'Google Hybrid': views[4], 'Google Terrain': views[5] }, addLayers); // Remove the hidden layer after layerChooser built, to avoid messing up ordering of layers $.each(hiddenLayer, function(ind, layer){ map.removeLayer(layer); }); 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 { convertCookieToLocalStorage('ingress.intelmap.type'); map.addLayer(views[localStorage['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); map.on('zoomend', function() { window.storeMapPosition(); // remove all resonators if zoom out to < RESONATOR_DISPLAY_ZOOM_LEVEL if(isResonatorsShow()) return; for(var i = 1; i < portalsLayers.length; i++) { portalsLayers[i].eachLayer(function(item) { var itemGuid = item.options.guid; // check if 'item' is a resonator if(getTypeByGuid(itemGuid) != TYPE_RESONATOR) return true; portalsLayers[i].removeLayer(item); }); } console.log('Remove all resonators'); }); map.on('baselayerchange', function () { var selInd = $('[name=leaflet-base-layers]:checked').parent().index(); localStorage['ingress.intelmap.type']=selInd; }); // map update status handling map.on('movestart zoomstart', function() { window.mapRunsUserAction = true }); map.on('moveend zoomend', function() { window.mapRunsUserAction = false }); // update map hooks map.on('movestart zoomstart', function() { window.requests.abort(); window.startRefreshTimeout(-1); }); map.on('moveend zoomend', function() { window.startRefreshTimeout(ON_MOVE_REFRESH*1000) }); window.addResumeFunction(window.requestData); window.requests.addRefreshFunction(window.requestData); // start the refresh process with a small timeout, so the first data request happens quickly // (the code originally called the request function directly, and triggered a normal delay for the nxt refresh. // however, the moveend/zoomend gets triggered on map load, causing a duplicate refresh. this helps prevent that window.startRefreshTimeout(ON_MOVE_REFRESH*1000); }; // renders player details into the website. Since the player info is // 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; var lvlUpAp = digits(nextLvlAp-ap); var lvlApProg = Math.round((ap-thisLvlAp)/(nextLvlAp-thisLvlAp)*100); var xmMax = MAX_XM_PER_LEVEL[level]; var xmRatio = Math.round(PLAYER.energy/xmMax*100); var cls = PLAYER.team === 'ALIENS' ? 'enl' : 'res'; var t = 'Level:\t' + level + '\n' + 'XM:\t' + PLAYER.energy + ' / ' + xmMax + '\n' + 'AP:\t' + digits(ap) + '\n' + (level < 8 ? 'level up in:\t' + lvlUpAp + ' AP' : 'Congrats! (neeeeerd)') + '\n\Invites:\t'+PLAYER.available_invites; + '\n\nNote: your player stats can only be updated by a full reload (F5)'; $('#playerstat').html('' + '