diff --git a/build.py b/build.py index 42b1e4bf..8d64942b 100755 --- a/build.py +++ b/build.py @@ -8,7 +8,14 @@ import base64 import sys import os import shutil +import json +import shelve +import hashlib +try: + import urllib2 +except ImportError: + import urllib.request as urllib2 # load settings file from buildsettings import buildSettings @@ -66,6 +73,33 @@ def loaderRaw(var): fn = var.group(1) return readfile(fn) +def loaderMD(var): + fn = var.group(1) + # use different MD.dat's for python 2 vs 3 incase user switches versions, as they are not compatible + db = shelve.open('build/MDv' + str(sys.version_info.major) + '.dat') + if 'files' in db: + files = db['files'] + else: + files = {} + file = readfile(fn) + filemd5 = hashlib.md5(file.encode('utf8')).hexdigest() + # check if file has already been parsed by the github api + if fn in files and filemd5 in files[fn]: + # use the stored copy if nothing has changed to avoid hiting the api more then the 60/hour when not signed in + db.close() + return files[fn][filemd5] + else: + url = 'https://api.github.com/markdown' + payload = {'text': file, 'mode': 'markdown'} + headers = {'Content-Type': 'application/json'} + req = urllib2.Request(url, json.dumps(payload).encode('utf8'), headers) + md = urllib2.urlopen(req).read().decode('utf8').replace('\n', '').replace('\'', '\\\'') + files[fn] = {} + files[fn][filemd5] = md + db['files'] = files + db.close() + return md + def loaderImage(var): fn = var.group(1) return 'data:image/png;base64,{0}'.format(base64.encodestring(open(fn, 'rb').read()).decode('utf8').replace('\n', '')) @@ -86,6 +120,7 @@ def doReplacements(script,updateUrl,downloadUrl): script = re.sub('@@INCLUDERAW:([0-9a-zA-Z_./-]+)@@', loaderRaw, script) script = re.sub('@@INCLUDESTRING:([0-9a-zA-Z_./-]+)@@', loaderString, script) + script = re.sub('@@INCLUDEMD:([0-9a-zA-Z_./-]+)@@', loaderMD, script) script = re.sub('@@INCLUDEIMAGE:([0-9a-zA-Z_./-]+)@@', loaderImage, script) script = script.replace('@@BUILDDATE@@', buildDate) @@ -156,6 +191,14 @@ for fn in glob.glob("plugins/*.user.js"): metafn = fn.replace('.user.js', '.meta.js') saveScriptAndMeta(script, os.path.join(outDir,fn), os.path.join(outDir,metafn)) +def copytree(src, dst, symlinks=False, ignore=None): + for item in os.listdir(src): + s = os.path.join(src, item) + d = os.path.join(dst, item) + if os.path.isdir(s): + shutil.copytree(s, d, symlinks, ignore) + else: + shutil.copy2(s, d) # if we're building mobile too if buildMobile: @@ -169,7 +212,12 @@ if buildMobile: pass shutil.copy(os.path.join(outDir,"total-conversion-build.user.js"), "mobile/assets/iitc.js") - # TODO? also copy plugins - once the mobile app supports plugins, that is + # also copy plugins + try: + os.makedirs("mobile/assets/plugins") + except: + pass + copytree(os.path.join(outDir,"plugins"), "mobile/assets/plugins") # now launch 'ant' to build the mobile project diff --git a/code/boot.js b/code/boot.js index 130f9265..caaad95f 100644 --- a/code/boot.js +++ b/code/boot.js @@ -118,8 +118,9 @@ window.setupMap = function() { //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', mazZoom: 18, detectRetena: true, subdomains: mqSubdomains}; - var mqMap = new L.TileLayer('http://{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg',mqMapOpt); + 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, detectRetena: true, subdomains: mqSubdomains}; //var mqSat = new L.TileLayer('http://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',mqSatOpt); @@ -128,8 +129,8 @@ window.setupMap = function() { /*0*/ mqMap, /*1*/ new L.Google('INGRESS'), /*2*/ new L.Google('ROADMAP'), - /*3*/ new L.Google('SATELLITE'), - /*4*/ new L.Google('HYBRID') + /*3*/ new L.Google('SATELLITE',{maxZoom:20}), + /*4*/ new L.Google('HYBRID',{maxZoom:20}) ]; diff --git a/code/chat.js b/code/chat.js index 919ce854..df42dd2a 100644 --- a/code/chat.js +++ b/code/chat.js @@ -410,12 +410,12 @@ window.chat.renderMsg = function(msg, nick, time, team, msgToPlayer, systemNarro var s = 'style="cursor:pointer; color:'+color+'"'; var title = nick.length >= 8 ? 'title="'+nick+'" class="help"' : ''; var i = ['<', '>']; - return ''+t+''+i[0]+''+ nick+''+i[1]+''+msg+''; + return ''+t+''+i[0]+''+ nick+''+i[1]+''+msg+''; } window.chat.addNickname= function(nick){ var c = document.getElementById("chattext"); - c.value = [c.value, nick, " "].join(" ").trim() + " "; + c.value = [c.value.trim(), nick].join(" ").trim() + " "; c.focus() } @@ -589,7 +589,11 @@ window.chat.setup = function() { window.requests.addRefreshFunction(chat.request); var cls = PLAYER.team === 'ALIENS' ? 'enl' : 'res'; - $('#chatinput mark').addClass(cls) + $('#chatinput mark').addClass(cls); + + $(window).on('click', '.nickname', function(event) { + window.chat.nicknameClicked(event, $(this).text()); + }); } diff --git a/code/map_data.js b/code/map_data.js index 8f9a42d4..419534f0 100644 --- a/code/map_data.js +++ b/code/map_data.js @@ -12,34 +12,30 @@ window.requestData = function() { requests.abort(); cleanUp(); - var magic = convertCenterLat(map.getCenter().lat); - var R = calculateR(magic); - var bounds = map.getBounds(); - // convert to point values - topRight = convertLatLngToPoint(bounds.getNorthEast(), magic, R); - bottomLeft = convertLatLngToPoint(bounds.getSouthWest() , magic, R); - // how many quadrants intersect the current view? - quadsX = Math.abs(bottomLeft.x - topRight.x); - quadsY = Math.abs(bottomLeft.y - topRight.y); + + var x1 = lngToTile(bounds.getNorthWest().lng, map.getZoom()); + var x2 = lngToTile(bounds.getNorthEast().lng, map.getZoom()); + var y1 = latToTile(bounds.getNorthWest().lat, map.getZoom()); + var y2 = latToTile(bounds.getSouthWest().lat, map.getZoom()); // will group requests by second-last quad-key quadrant tiles = {}; // walk in x-direction, starts right goes left - for(var i = 0; i <= quadsX; i++) { - var x = Math.abs(topRight.x - i); - var qk = pointToQuadKey(x, topRight.y); - var bnds = convertPointToLatLng(x, topRight.y, magic, R); - if(!tiles[qk.slice(0, -1)]) tiles[qk.slice(0, -1)] = []; - tiles[qk.slice(0, -1)].push(generateBoundsParams(qk, bnds)); - - // walk in y-direction, starts top, goes down - for(var j = 1; j <= quadsY; j++) { - var qk = pointToQuadKey(x, topRight.y + j); - var bnds = convertPointToLatLng(x, topRight.y + j, magic, R); - if(!tiles[qk.slice(0, -1)]) tiles[qk.slice(0, -1)] = []; - tiles[qk.slice(0, -1)].push(generateBoundsParams(qk, bnds)); + for (var x = x1; x <= x2; x++) { + for (var y = y1; y <= y2; y++) { + var tile_id = pointToTileId(map.getZoom(), x, y); + var bucket = Math.floor(x / 2) + "" + Math.floor(y / 2); + if (!tiles[bucket]) + tiles[bucket] = []; + tiles[bucket].push(generateBoundsParams( + tile_id, + tileToLat(y + 1, map.getZoom()), + tileToLng(x, map.getZoom()), + tileToLat(y, map.getZoom()), + tileToLng(x + 1, map.getZoom()) + )); } } @@ -47,7 +43,7 @@ window.requestData = function() { portalRenderLimit.init(); // finally send ajax requests $.each(tiles, function(ind, tls) { - data = { minLevelOfDetail: -1 }; + data = { zoom: map.getZoom() }; data.boundsParamsList = tls; window.requests.add(window.postAjax('getThinnedEntitiesV2', data, window.handleDataResponse, window.handleFailedRequest)); }); @@ -78,7 +74,7 @@ window.handleDataResponse = function(data, textStatus, jqXHR) { // portals can be brought to front, this costs extra time. They need // to be in the foreground, or they cannot be clicked. See // https://github.com/Leaflet/Leaflet/issues/185 - var ppp = []; + var ppp = {}; var p2f = {}; $.each(m, function(qk, val) { $.each(val.deletedGameEntityGuids || [], function(ind, guid) { @@ -105,9 +101,17 @@ window.handleDataResponse = function(data, textStatus, jqXHR) { && urlPortal !== ent[0] ) return; + if('imageByUrl' in ent[2] && 'imageUrl' in ent[2].imageByUrl) { + if(window.location.protocol === 'https:') { + ent[2].imageByUrl.imageUrl = ent[2].imageByUrl.imageUrl.indexOf('www.panoramio.com') !== -1 + ? ent[2].imageByUrl.imageUrl.replace(/^http:\/\/www/, 'https://ssl').replace('small', 'medium') + : ent[2].imageByUrl.imageUrl.replace(/^http:\/\//, '//'); + } + } else { + ent[2].imageByUrl = {'imageUrl': DEFAULT_PORTAL_IMG}; + } - - ppp.push(ent); // delay portal render + ppp[ent[0]] = ent; // delay portal render } else if(ent[2].edge !== undefined) { renderLink(ent); } else if(ent[2].capturedRegion !== undefined) { @@ -124,6 +128,25 @@ window.handleDataResponse = function(data, textStatus, jqXHR) { }); $.each(ppp, function(ind, portal) { + if ('portalV2' in portal[2] && 'linkedEdges' in portal[2].portalV2) { + $.each(portal[2].portalV2.linkedEdges, function (ind, edge) { + if (!ppp[edge.otherPortalGuid]) + return; + renderLink([ + edge.edgeGuid, + portal[1], + { + "controllingTeam": portal[2].controllingTeam, + "edge": { + "destinationPortalGuid": edge.isOrigin ? ppp[edge.otherPortalGuid][0] : portal[0], + "destinationPortalLocation": edge.isOrigin ? ppp[edge.otherPortalGuid][2].locationE6 : portal[2].locationE6, + "originPortalGuid": !edge.isOrigin ? ppp[edge.otherPortalGuid][0] : portal[0], + "originPortalLocation": !edge.isOrigin ? ppp[edge.otherPortalGuid][2].locationE6 : portal[2].locationE6 + } + } + ]); + }); + } if(portal[2].portalV2['linkedFields'] === undefined) { portal[2].portalV2['linkedFields'] = []; } diff --git a/code/map_data_calc_tools.js b/code/map_data_calc_tools.js index 2f383197..98535759 100644 --- a/code/map_data_calc_tools.js +++ b/code/map_data_calc_tools.js @@ -1,79 +1,45 @@ - - // MAP DATA REQUEST CALCULATORS ////////////////////////////////////// // Ingress Intel splits up requests for map data (portals, links, // fields) into tiles. To get data for the current viewport (i.e. what // is currently visible) it first calculates which tiles intersect. // For all those tiles, it then calculates the lat/lng bounds of that // tile and a quadkey. Both the bounds and the quadkey are “somewhat” -// required to get complete data. No idea how the projection between -// lat/lng and tiles works. -// What follows now are functions that allow conversion between tiles -// and lat/lng as well as calculating the quad key. The variable names -// may be misleading. -// The minified source for this code was in gen_dashboard.js after the -// “// input 89” line (alternatively: the class was called “Xe”). +// required to get complete data. +// +// Convertion functions courtesy of +// http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames -window.convertCenterLat = function(centerLat) { - return Math.round(256 * 0.9999 * Math.abs(1 / Math.cos(centerLat * DEG2RAD))); +window.lngToTile = function(lng, zoom) { + return Math.floor((lng + 180) / 360 * Math.pow(2, zoom)); } -window.calculateR = function(convCenterLat) { - return 1 << window.map.getZoom() - (convCenterLat / 256 - 1); +window.latToTile = function(lat, zoom) { + return Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom)); } -window.convertLatLngToPoint = function(latlng, magic, R) { - var x = (magic/2 + latlng.lng * magic / 360)*R; - var l = Math.sin(latlng.lat * DEG2RAD); - var y = (magic/2 + 0.5*Math.log((1+l)/(1-l)) * -(magic / (2*Math.PI)))*R; - return {x: Math.floor(x/magic), y: Math.floor(y/magic)}; +window.tileToLng = function(x, zoom) { + return x / Math.pow(2, zoom) * 360 - 180; } -window.convertPointToLatLng = function(x, y, magic, R) { - var e = {}; - e.sw = { - // orig function put together from all over the place - // lat: (2 * Math.atan(Math.exp((((y + 1) * magic / R) - (magic/ 2)) / (-1*(magic / (2 * Math.PI))))) - Math.PI / 2) / (Math.PI / 180), - // shortened version by your favorite algebra program. - lat: (360*Math.atan(Math.exp(Math.PI - 2*Math.PI*(y+1)/R)))/Math.PI - 90, - lng: 360*x/R-180 - }; - e.ne = { - //lat: (2 * Math.atan(Math.exp(((y * magic / R) - (magic/ 2)) / (-1*(magic / (2 * Math.PI))))) - Math.PI / 2) / (Math.PI / 180), - lat: (360*Math.atan(Math.exp(Math.PI - 2*Math.PI*y/R)))/Math.PI - 90, - lng: 360*(x+1)/R-180 - }; - return e; +window.tileToLat = function(y, zoom) { + var n = Math.PI - 2 * Math.PI * y / Math.pow(2, zoom); + return 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); } -// calculates the quad key for a given point. The point is not(!) in -// lat/lng format. -window.pointToQuadKey = function(x, y) { - var quadkey = []; - for(var c = window.map.getZoom(); c > 0; c--) { - // +-------+ quadrants are probably ordered like this - // | 0 | 1 | - // |---|---| - // | 2 | 3 | - // |---|---| - var quadrant = 0; - var e = 1 << c - 1; - (x & e) != 0 && quadrant++; // push right - (y & e) != 0 && (quadrant++, quadrant++); // push down - quadkey.push(quadrant); - } - return quadkey.join(""); +window.pointToTileId = function(zoom, x, y) { + return zoom + "_" + x + "_" + y; } -// given quadkey and bounds, returns the format as required by the +// given tile id and bounds, returns the format as required by the // Ingress API to request map data. -window.generateBoundsParams = function(quadkey, bounds) { +window.generateBoundsParams = function(tile_id, minLat, minLng, maxLat, maxLng) { return { - id: quadkey, - qk: quadkey, - minLatE6: Math.round(bounds.sw.lat * 1E6), - minLngE6: Math.round(bounds.sw.lng * 1E6), - maxLatE6: Math.round(bounds.ne.lat * 1E6), - maxLngE6: Math.round(bounds.ne.lng * 1E6) + id: tile_id, + qk: tile_id, + minLatE6: Math.round(minLat * 1E6), + minLngE6: Math.round(minLng * 1E6), + maxLatE6: Math.round(maxLat * 1E6), + maxLngE6: Math.round(maxLng * 1E6) }; } diff --git a/code/portal_detail_display.js b/code/portal_detail_display.js index 8937da8f..636230ed 100644 --- a/code/portal_detail_display.js +++ b/code/portal_detail_display.js @@ -44,10 +44,7 @@ window.renderPortalDetails = function(guid) { var resoDetails = '' + getResonatorDetails(d) + '
'; setPortalIndicators(d); - var img = d.imageByUrl && d.imageByUrl.imageUrl - ? d.imageByUrl.imageUrl - : DEFAULT_PORTAL_IMG; - + var img = d.imageByUrl.imageUrl; var lat = d.locationE6.latE6; var lng = d.locationE6.lngE6; var perma = '/intel?latE6='+lat+'&lngE6='+lng+'&z=17&pguid='+guid; diff --git a/code/utils_misc.js b/code/utils_misc.js index 84562c92..267a1c06 100644 --- a/code/utils_misc.js +++ b/code/utils_misc.js @@ -1,7 +1,8 @@ // UTILS + MISC /////////////////////////////////////////////////////// window.aboutIITC = function(){ - var v = '@@BUILDNAME@@-@@BUILDDATE@@' + var v = '@@BUILDNAME@@-@@BUILDDATE@@'; + var attrib = '@@INCLUDEMD:ATTRIBUTION.md@@'; var a = '' + '
About IITC
' + '
Ingress Intel Total Conversion
' @@ -20,8 +21,11 @@ window.aboutIITC = function(){ + ' MapQuest OSM tiles Courtesy of MapQuest ' + ' ' + '
' - + '
Version: ' + v + '
'; - alert(a); + + '
Version: ' + v + '
' + + '
' + + '
' + attrib + '
'; + alert(a, true, function() {$('.ui-dialog').removeClass('ui-dialog-aboutIITC');}); + $('.ui-dialog').addClass('ui-dialog-aboutIITC'); } @@ -197,8 +201,8 @@ window.renderLimitReached = function(ratio) { 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]; + if(z >= 17) return 0; + var conv = ['impossible', 8,8,8,7,7,6,6,5,4,4,3,3,2,2,1,1]; var minLevelByRenderLimit = portalRenderLimit.getMinLevel(); var result = minLevelByRenderLimit > conv[z] ? minLevelByRenderLimit diff --git a/main.js b/main.js index 9e57ee01..3bebd816 100644 --- a/main.js +++ b/main.js @@ -1,7 +1,7 @@ // ==UserScript== // @id ingress-intel-total-conversion@jonatkins // @name IITC: Ingress intel map total conversion -// @version 0.10.5.@@DATETIMEVERSION@@ +// @version 0.11.2.@@DATETIMEVERSION@@ // @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ @@ -87,7 +87,7 @@ document.getElementsByTagName('body')[0].innerHTML = '' + '
 loading global control stats
' + '
' + ' ' - + ' ' + + ' ' + '
' + '
' + ' ' diff --git a/mobile/.gitignore b/mobile/.gitignore index a56e372f..42222e62 100644 --- a/mobile/.gitignore +++ b/mobile/.gitignore @@ -7,3 +7,4 @@ libs/ proguard-project.txt local.properties assets/iitc.js +assets/plugins/ diff --git a/mobile/AndroidManifest.xml b/mobile/AndroidManifest.xml index d3fa4d8f..844d7462 100644 --- a/mobile/AndroidManifest.xml +++ b/mobile/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="11" + android:versionName="0.3" > UI + Plugins + Available plugins Force desktop mode Nice for tablets, looks awful on smartphones Developer options diff --git a/mobile/res/xml/preferences.xml b/mobile/res/xml/preferences.xml index fd50c037..2d72b3e1 100644 --- a/mobile/res/xml/preferences.xml +++ b/mobile/res/xml/preferences.xml @@ -17,6 +17,10 @@ android:title="@string/pref_force_desktop" android:summary="@string/pref_force_desktop_sum" android:defaultValue="false" /> + asset_list = new ArrayList(); + ArrayList asset_values = new ArrayList(); + + for (int i = 0; i < asset_array.length ; i++) { + if (asset_array[i].endsWith("user.js")) { + // find user plugin name for user readable entries + Scanner s = null; + String src = ""; + try { + s = new Scanner(am.open("plugins/" + asset_array[i])).useDelimiter("\\A"); + } catch (IOException e2) { + // TODO Auto-generated catch block + e2.printStackTrace(); + } + if (s != null) src = s.hasNext() ? s.next() : ""; + String header = src.substring(src.indexOf("==UserScript=="), src.indexOf("==/UserScript==")); + // remove new line comments and replace with space + // this way we get double spaces instead of newline + double slash + header = header.replace("\n//", " "); + // get a list of key-value...split on multiple spaces + String[] attributes = header.split(" +"); + String plugin_name = "not found"; + for (int j = 0; j < attributes.length; j++) { + // search for name and use the value + if (attributes[j].equals("@name")) plugin_name = attributes[j+1]; + } + asset_list.add(plugin_name); + // real value + asset_values.add(asset_array[i]); + } + } + + Bundle bundle = getIntent().getExtras(); + bundle.putStringArray("ASSETS", (String[]) asset_list.toArray(new String[0])); + bundle.putStringArray("ASSETS_VAL", (String[]) asset_values.toArray(new String[0])); + settings.setArguments(bundle); // Display the fragment as the main content. getFragmentManager().beginTransaction() diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_SettingsFragment.java b/mobile/src/com/cradle/iitc_mobile/IITC_SettingsFragment.java index 34e3c0c0..5a68f307 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_SettingsFragment.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_SettingsFragment.java @@ -6,6 +6,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.preference.EditTextPreference; import android.preference.ListPreference; +import android.preference.MultiSelectListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceFragment; @@ -21,7 +22,12 @@ public class IITC_SettingsFragment extends PreferenceFragment { iitc_version = getArguments().getString("iitc_version"); addPreferencesFromResource(R.xml.preferences); - + + //plugins + MultiSelectListPreference pref_plugins = (MultiSelectListPreference) findPreference("pref_plugins"); + pref_plugins.setEntries(getArguments().getStringArray("ASSETS")); + pref_plugins.setEntryValues(getArguments().getStringArray("ASSETS_VAL")); + // set build version ListPreference pref_build_version = (ListPreference) findPreference("pref_build_version"); PackageManager pm = getActivity().getPackageManager(); diff --git a/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java b/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java index d92d189d..d2a62897 100644 --- a/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java +++ b/mobile/src/com/cradle/iitc_mobile/IITC_WebViewClient.java @@ -3,6 +3,7 @@ package com.cradle.iitc_mobile; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.AssetManager; import android.net.Uri; import android.net.http.SslError; import android.preference.PreferenceManager; @@ -17,6 +18,7 @@ import java.io.InputStream; import java.io.IOException; import java.net.URL; import java.util.Scanner; +import java.util.Set; public class IITC_WebViewClient extends WebViewClient { private static final ByteArrayInputStream style = new ByteArrayInputStream( @@ -44,7 +46,7 @@ public class IITC_WebViewClient extends WebViewClient { String[] attributes = header.split(" +"); String iitc_version = "not found"; for (int i = 0; i < attributes.length; i++) { - // search vor version and use the value + // search for version and use the value if (attributes[i].equals("@version")) iitc_version = attributes[i+1]; } return iitc_version; @@ -91,6 +93,39 @@ public class IITC_WebViewClient extends WebViewClient { handler.proceed() ; }; + // plugins should be loaded after the main script is injected + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + + // get the plugin preferences + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + Set plugin_list = sharedPref.getStringSet("pref_plugins", null); + + // iterate through all enabled plugins and load them + if (plugin_list != null) { + AssetManager am = context.getAssets(); + String[] plugin_array = plugin_list.toArray(new String[0]); + + for(int i = 0; i < plugin_list.size(); i++) { + if (plugin_array[i].endsWith("user.js")); + { + Log.d("iitcm", "adding plugin " + plugin_array[i]); + Scanner s = null; + String src = ""; + try { + s = new Scanner(am.open("plugins/" + plugin_array[i])).useDelimiter("\\A"); + } catch (IOException e2) { + // TODO Auto-generated catch block + e2.printStackTrace(); + } + if (s != null) src = s.hasNext() ? s.next() : ""; + view.loadUrl("javascript:" + src); + } + } + } + } + // Check every external resource if it’s okay to load it and maybe replace it // with our own content. This is used to block loading Niantic resources // which aren’t required and to inject IITC early into the site. @@ -119,6 +154,11 @@ public class IITC_WebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.contains("ingress.com")) { + // reload iitc if a poslink is clicked inside the app + if (url.contains("intel?ll=") || (url.contains("latE6") && url.contains("lngE6"))) { + Log.d("iitcm", "should be an internal clicked position link...reload script for: " + url); + ((IITC_Mobile) context).loadUrl(url); + } return false; } else { Log.d("iitcm", "no ingress intel link, start external app to load url: " + url); diff --git a/plugins/ap-list.user.js b/plugins/ap-list.user.js index c8a4d575..9bf1c09a 100644 --- a/plugins/ap-list.user.js +++ b/plugins/ap-list.user.js @@ -1,11 +1,11 @@ // ==UserScript== // @id iitc-plugin-ap-list@xelio // @name IITC plugin: AP List -// @version 0.5.1.@@DATETIMEVERSION@@ +// @version 0.5.2.@@DATETIMEVERSION@@ // @namespace https://github.com/jonatkins/ingress-intel-total-conversion // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ -// @description [@@BUILDNAME@@-@@BUILDDATE@@] List top 10 portals by AP of either faction. Other functions and controls please refer to the Userguide. +// @description [@@BUILDNAME@@-@@BUILDDATE@@] List portals by AP of either faction or by effective level. Other functions and controls please refer to the Userguide. // @include https://www.ingress.com/intel* // @include http://www.ingress.com/intel* // @match https://www.ingress.com/intel* diff --git a/plugins/ipas-link.user.js b/plugins/ipas-link.user.js index 50f7c994..ac158c10 100644 --- a/plugins/ipas-link.user.js +++ b/plugins/ipas-link.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @id iitc-plugin-ipas-link@graphracer // @name IITC Plugin: simulate an attack on portal -// @version 0.1.1.@@DATETIMEVERSION@@ +// @version 0.2.0.@@DATETIMEVERSION@@ // @namespace https://github.com/xosofox/IPAS // @updateURL @@UPDATEURL@@ // @downloadURL @@DOWNLOADURL@@ @@ -30,16 +30,34 @@ window.plugin.ipasLink.addLink = function(d) { $('.linkdetails').append(''); } -window.plugin.ipasLink.getHash = function(d) { - var hashParts=[]; - $.each(d.resonatorArray.resonators, function(ind, reso) { - if (reso) { - hashParts.push(reso.level + "," + reso.distanceToPortal + "," + reso.energyTotal); - } else { - hashParts.push(1 + "," + 35 + "," + 0); // Dummy values, the only important one is energy=0 - } - }); - return hashParts.join(";")+"|" + "0,0,0,0"; //shields not implemented yet +window.plugin.ipasLink.getHash = function (d) { + var hashParts = []; + $.each(d.resonatorArray.resonators, function (ind, reso) { + if (reso) { + hashParts.push(reso.level + "," + reso.distanceToPortal + "," + reso.energyTotal); + } else { + hashParts.push("1,20,0"); + } + }); + var resos = hashParts.join(";"); + + hashParts = []; + $.each(d.portalV2.linkedModArray, function (ind, mod) { + //shields only, so far... + var s = "0"; + if (mod) { + if (mod.type === "RES_SHIELD") { + s = mod.rarity.charAt(0).toLowerCase(); + } + } + hashParts.push(s); + }); + var shields = hashParts.join(","); + return resos + "|" + shields; +} + +var setup = function () { + window.plugin.ipasLink.setupCallback(); } var setup = function() { diff --git a/plugins/player-tracker.user.js b/plugins/player-tracker.user.js index 864a006a..7f20df6c 100644 --- a/plugins/player-tracker.user.js +++ b/plugins/player-tracker.user.js @@ -266,12 +266,13 @@ window.plugin.playerTracker.drawData = function() { if(window.plugin.guessPlayerLevels !== undefined && window.plugin.guessPlayerLevels.fetchLevelByPlayer !== undefined) { var playerLevel = window.plugin.guessPlayerLevels.fetchLevelByPlayer(pguid); - if (playerLevel === undefined) playerLevel = 1; //if player level unknown, assume level 1 if(playerLevel !== undefined) { title += 'Level ' + playerLevel + (playerLevel < (window.MAX_XM_PER_LEVEL.length - 1) ? ' (guessed)' : '') + ''; + } else { + title += 'Level unknown' } } diff --git a/plugins/portal-highlighter-level-color.user.js b/plugins/portal-highlighter-level-color.user.js new file mode 100644 index 00000000..ac6344ff --- /dev/null +++ b/plugins/portal-highlighter-level-color.user.js @@ -0,0 +1,51 @@ +// ==UserScript== +// @id iitc-plugin-highlight-portals-level-color@vita10gy +// @name IITC plugin: highlight portals by level color +// @version 0.1.0.@@DATETIMEVERSION@@ +// @namespace https://github.com/jonatkins/ingress-intel-total-conversion +// @updateURL @@UPDATEURL@@ +// @downloadURL @@DOWNLOADURL@@ +// @description [@@BUILDNAME@@-@@BUILDDATE@@] Uses the fill color of the portals level color. +// @include https://www.ingress.com/intel* +// @include http://www.ingress.com/intel* +// @match https://www.ingress.com/intel* +// @match http://www.ingress.com/intel* +// ==/UserScript== + +function wrapper() { +// ensure plugin framework is there, even if iitc is not yet loaded +if(typeof window.plugin !== 'function') window.plugin = function() {}; + + +// PLUGIN START //////////////////////////////////////////////////////// +// use own namespace for plugin + +window.plugin.portalHighligherPortalsLevelColor = function() {}; + +window.plugin.portalHighligherPortalsLevelColor.colorLevel = function(data) { + var d = data.portal.options.details; + var portal_level = Math.floor(getPortalLevel(d)); + var opacity = .6; + data.portal.setStyle({fillColor: COLORS_LVL[portal_level], fillOpacity: opacity}); + window.COLOR_SELECTED_PORTAL = '#f0f'; +} + +var setup = function() { + window.addPortalHighlighter('Level Color', window.plugin.portalHighligherPortalsLevelColor.colorLevel); +} + +// PLUGIN END ////////////////////////////////////////////////////////// + +if(window.iitcLoaded && typeof setup === 'function') { + setup(); +} else { + if(window.bootPlugins) + window.bootPlugins.push(setup); + else + window.bootPlugins = [setup]; +} +} // wrapper end +// inject code into site context +var script = document.createElement('script'); +script.appendChild(document.createTextNode('('+ wrapper +')();')); +(document.body || document.head || document.documentElement).appendChild(script); diff --git a/plugins/portal-highlighter-portal-ap-energy-relative.user.js b/plugins/portal-highlighter-portal-ap-energy-relative.user.js new file mode 100644 index 00000000..677c2ab5 --- /dev/null +++ b/plugins/portal-highlighter-portal-ap-energy-relative.user.js @@ -0,0 +1,118 @@ +// ==UserScript== +// @id iitc-plugin-highlight-portals-by-ap-by-energy-relative@vita10gy +// @name IITC plugin: highlight portals by ap/energy (relative) +// @version 0.1.1.@@DATETIMEVERSION@@ +// @namespace https://github.com/jonatkins/ingress-intel-total-conversion +// @updateURL @@UPDATEURL@@ +// @downloadURL @@DOWNLOADURL@@ +// @description [@@BUILDNAME@@-@@BUILDDATE@@] Uses the fill color of the portals to denote AP/Energy value relative to what's currently on the screen. Brighter is better. Orange means your standard 8 down 8 up swap. +// @include https://www.ingress.com/intel* +// @include http://www.ingress.com/intel* +// @match https://www.ingress.com/intel* +// @match http://www.ingress.com/intel* +// ==/UserScript== + +function wrapper() { +// ensure plugin framework is there, even if iitc is not yet loaded +if(typeof window.plugin !== 'function') window.plugin = function() {}; + + +// PLUGIN START //////////////////////////////////////////////////////// + +// use own namespace for plugin +window.plugin.portalHighligherPortalAPPerEnergyRelative = function() {}; + +window.plugin.portalHighligherPortalAPPerEnergyRelative.minAP = null; +window.plugin.portalHighligherPortalAPPerEnergyRelative.maxAP = null; +//This is the AP for a run of the mill takedown/putback +window.plugin.portalHighligherPortalAPPerEnergyRelative.baseSwapAP = 2350; + + +window.plugin.portalHighligherPortalAPPerEnergyRelative.highlight = function(data) { + var d = data.portal.options.details; + var color = 'red'; + + if(window.plugin.portalHighligherPortalAPPerEnergyRelative.minAP == null || + window.plugin.portalHighligherPortalAPPerEnergyRelative.maxAP == null) { + window.plugin.portalHighligherPortalAPPerEnergyRelative.calculateAPLevels(); + } + var minApE = window.plugin.portalHighligherPortalAPPerEnergyRelative.minAP; + var maxApE = window.plugin.portalHighligherPortalAPPerEnergyRelative.maxAP; + + if(PLAYER.team !== d.controllingTeam.team) { + var ap = getAttackApGain(d); + var energy = getCurrentPortalEnergy(d); + if(energy < 1) { + energy = 1; + } + portal_ap = ap.enemyAp; + + var opacity = 1; + if(minApE !== maxApE) { + opacity = ((ap.enemyAp / energy) - minApE) / (maxApE - minApE); + } + + if(opacity < 0) { + opacity = 0; + } + if(opacity > 1) { + opacity = 1; + } + data.portal.setStyle({fillColor: color, fillOpacity: opacity}); + window.COLOR_SELECTED_PORTAL = '#f0f'; + } +} + +window.plugin.portalHighligherPortalAPPerEnergyRelative.resetAPLevels = function() { + window.plugin.portalHighligherPortalAPPerEnergyRelative.minAP = null; + window.plugin.portalHighligherPortalAPPerEnergyRelative.maxAP = null; +} + +window.plugin.portalHighligherPortalAPPerEnergyRelative.calculateAPLevels = function() { + var displayBounds = map.getBounds(); + $.each(window.portals, function(qk, portal) { + if(displayBounds.contains(portal.getLatLng())) { + if(PLAYER.team !== portal.options.details.controllingTeam.team) { + var ap = getAttackApGain(portal.options.details); + var energy = getCurrentPortalEnergy(portal.options.details); + if(energy < 1) { + energy = 1; + } + var portal_ap = ap.enemyAp / energy; + if(window.plugin.portalHighligherPortalAPPerEnergyRelative.minAP === null || + portal_ap < window.plugin.portalHighligherPortalAPPerEnergyRelative.minAP) { + window.plugin.portalHighligherPortalAPPerEnergyRelative.minAP = portal_ap; + } + if(window.plugin.portalHighligherPortalAPPerEnergyRelative.maxAP === null || + portal_ap > window.plugin.portalHighligherPortalAPPerEnergyRelative.maxAP) { + window.plugin.portalHighligherPortalAPPerEnergyRelative.maxAP = portal_ap; + } + + } + } + }); +} + + + +var setup = function() { + window.addPortalHighlighter('AP/Energy (Relative)', window.plugin.portalHighligherPortalAPPerEnergyRelative.highlight); + window.addHook('requestFinished', window.plugin.portalHighligherPortalAPPerEnergyRelative.resetAPLevels); + +} + +// PLUGIN END ////////////////////////////////////////////////////////// + +if(window.iitcLoaded && typeof setup === 'function') { + setup(); +} else { + if(window.bootPlugins) + window.bootPlugins.push(setup); + else + window.bootPlugins = [setup]; +} +} // wrapper end +// inject code into site context +var script = document.createElement('script'); +script.appendChild(document.createTextNode('('+ wrapper +')();')); +(document.body || document.head || document.documentElement).appendChild(script); diff --git a/plugins/portal-highlighter-portal-ap-relative.user.js b/plugins/portal-highlighter-portal-ap-relative.user.js index 08059865..a96941bb 100644 --- a/plugins/portal-highlighter-portal-ap-relative.user.js +++ b/plugins/portal-highlighter-portal-ap-relative.user.js @@ -90,10 +90,8 @@ window.plugin.portalHighligherPortalAPRelative.calculateAPLevels = function() { }); } - - var setup = function() { - window.addPortalHighlighter('Portal AP (Relative)', window.plugin.portalHighligherPortalAPRelative.highlight); + window.addPortalHighlighter('AP (Relative)', window.plugin.portalHighligherPortalAPRelative.highlight); window.addHook('requestFinished', window.plugin.portalHighligherPortalAPRelative.resetAPLevels); } diff --git a/plugins/portal-highlighter-portal-ap.user.js b/plugins/portal-highlighter-portal-ap.user.js index 3dc17f6c..30ced897 100644 --- a/plugins/portal-highlighter-portal-ap.user.js +++ b/plugins/portal-highlighter-portal-ap.user.js @@ -54,7 +54,7 @@ window.plugin.portalHighligherPortalAP.highlight = function(data) { } var setup = function() { - window.addPortalHighlighter('Portal AP', window.plugin.portalHighligherPortalAP.highlight); + window.addPortalHighlighter('AP (Static)', window.plugin.portalHighligherPortalAP.highlight); } // PLUGIN END ////////////////////////////////////////////////////////// diff --git a/plugins/scale-bar.user.js b/plugins/scale-bar.user.js index 5e784c12..23329456 100644 --- a/plugins/scale-bar.user.js +++ b/plugins/scale-bar.user.js @@ -28,7 +28,7 @@ window.plugin.scaleBar.setup = function() { // Before you ask: yes, I explicitely turned off imperial units. Imperial units // are worse than Internet Explorer 6 whirring fans combined. Upgrade to the metric // system already. - window.map.addControl(new L.Control.Scale({position: 'topleft', imperial: false})); + window.map.addControl(new L.Control.Scale({position: 'topleft', imperial: false, maxWidth: 200})); }; var setup = window.plugin.scaleBar.setup; diff --git a/plugins/show-linked-portals.user.js b/plugins/show-linked-portals.user.js index a42003d0..e380557c 100644 --- a/plugins/show-linked-portals.user.js +++ b/plugins/show-linked-portals.user.js @@ -71,9 +71,18 @@ window.plugin.showLinkedPortal.getPortalByGuid = function (guid) { var portalDetails = window.portals[guid].options.details; portalInfoString = ''; var portalNameAdressAlt = "'" + portalDetails.portalV2.descriptiveText.TITLE + "' (" + portalDetails.portalV2.descriptiveText.ADDRESS + ")"; - var portalNameAdressTitle = "'" + portalDetails.portalV2.descriptiveText.TITLE + "'
(" + portalDetails.portalV2.descriptiveText.ADDRESS + ")"; + var portalNameAdressTitle = $('
').append('\'') + .append($('').text(portalDetails.portalV2.descriptiveText.TITLE)) + .append('\'') + .append($('
')) + .append($('').text('(' + portalDetails.portalV2.descriptiveText.ADDRESS + ')')) + .html(); var imageUrl = (portalDetails.imageByUrl ? portalDetails.imageByUrl.imageUrl : window.DEFAULT_PORTAL_IMG); - portalInfoString = '' + portalNameAdressAlt + ''; + portalInfoString = $('
').html($('').attr('src', imageUrl) + .attr('class', 'minImg') + .attr('alt', portalNameAdressAlt) + .attr('title', portalNameAdressTitle)) + .html(); } return portalInfoString; }; diff --git a/style.css b/style.css index e1827e13..ad563fe2 100644 --- a/style.css +++ b/style.css @@ -68,6 +68,10 @@ body { color: #fff; } +.nickname { + cursor: pointer !important; +} + a { color: #ffce00; cursor: pointer; @@ -434,6 +438,7 @@ input { #geosearchwrapper img{ vertical-align: bottom; margin-bottom: 2px; + cursor: pointer; } ::-webkit-input-placeholder { font-style: italic; @@ -691,9 +696,8 @@ h3 { } #largepreview img { box-shadow: 0 0 40px #000; -} -#largepreview img { border: 2px solid #f8ff5e; + background-color: rgba(8, 48, 78, 0.9); /* as some images - eg ZipCar - have transparency */ } /* tooltips, dialogs */ @@ -762,6 +766,11 @@ h3 { text-decoration: underline; } +.ui-dialog-aboutIITC { + max-width: 600px !important; + width: 600px !important; +} + td { padding: 0; vertical-align: top;