This commit is contained in:
Jeremy Lindgren 2013-04-25 09:51:59 -05:00
commit 9908175bb2
29 changed files with 515 additions and 142 deletions

View File

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

View File

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

View File

@ -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 = ['<span class="invisep">&lt;</span>', '<span class="invisep">&gt;</span>'];
return '<tr><td>'+t+'</td><td>'+i[0]+'<mark class="nickname" onclick="window.chat.nicknameClicked(event, \'' + nick + '\')" ' + 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>';
}
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());
});
}

View File

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

View File

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

View File

@ -44,10 +44,7 @@ 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.imageUrl;
var lat = d.locationE6.latE6;
var lng = d.locationE6.lngE6;
var perma = '/intel?latE6='+lat+'&lngE6='+lng+'&z=17&pguid='+guid;

View File

@ -1,7 +1,8 @@
// UTILS + MISC ///////////////////////////////////////////////////////
window.aboutIITC = function(){
var v = '@@BUILDNAME@@-@@BUILDDATE@@'
var v = '@@BUILDNAME@@-@@BUILDDATE@@';
var attrib = '@@INCLUDEMD:ATTRIBUTION.md@@';
var a = ''
+ ' <div><b>About IITC</b></div> '
+ ' <div>Ingress Intel Total Conversion</div> '
@ -20,8 +21,11 @@ window.aboutIITC = function(){
+ ' MapQuest OSM tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png">'
+ ' </div>'
+ ' <hr>'
+ ' <div>Version: ' + v + '</div>';
alert(a);
+ ' <div>Version: ' + v + '</div>'
+ ' <hr>'
+ ' <div>' + attrib + '</div>';
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

View File

@ -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 = ''
+ ' <div id="gamestat">&nbsp;loading global control stats</div>'
+ ' <div id="geosearchwrapper">'
+ ' <input id="geosearch" placeholder="Search location…" type="text"/>'
+ ' <img src="@@INCLUDEIMAGE:images/current-location.png@@"/>'
+ ' <img src="@@INCLUDEIMAGE:images/current-location.png@@"/ title="Current Location">'
+ ' </div>'
+ ' <div id="portaldetails"></div>'
+ ' <input id="redeem" placeholder="Redeem code…" type="text"/>'

1
mobile/.gitignore vendored
View File

@ -7,3 +7,4 @@ libs/
proguard-project.txt
local.properties
assets/iitc.js
assets/plugins/

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cradle.iitc_mobile"
android:versionCode="9"
android:versionName="0.2.8" >
android:versionCode="11"
android:versionName="0.3" >
<uses-sdk
android:minSdkVersion="14"

View File

@ -1,7 +1,11 @@
How does this basically work?
-----------------------------
At the moment, the Android App is nothing more then a WebView which renders the normal web page. The IITC script is injected on page load and after this, it works the same way as on desktop browser. More functionality will be added soon...
At the moment, the Android App is a WebView which renders the normal web page. The IITC script is injected by calling ```iitc_view.loadIITC_JS(Context)```. The app uses a local version of the script, which is located in the assets folder, if no external source is given (see "pref\_iitc\_source").
Communication from app to script is handled by loading Javascript function calls. For example: ```iitc_view.loadUrl("javascript: window.goBack();");```
Communication from script to app is handled by the JavascriptInterface (see /mobile/src/com/cradle/iitc\_mobile/IITC_JSInterface.java). If a method ```foo(String)``` is defined in JSInterface, it can be called by ```android.foo("Hello World")``` in the IITC script.
Debugging
---------
@ -16,13 +20,8 @@ Debugging IITC(M) **before** it has booted requires the Android Developer Tools.
Building the APK
----------------
- **Eclipse:** Just import this project and klick the build button.
- **ant:**
Set the ANDROID_HOME environment variable:
```export ANDROID_HOME=/path/to/android_sdk```
and build the project with ant:
`ant debug`
- You can use `build_mobile.js`, too, which builds IITC, compresses
it and uses ant to build a release APK of IITCM. It requires that
you have Python and uglifyjs installed. You need to set the
`ANDROID_HOME`, like explained above.
Then build the app via the build.py script ```./build.py mobile```
- **Eclipse:** Just import this project and klick the build button. Ensure that you have iitc.js in your assets folder. This is automatically created, when executing ```./build.py mobile```. Otherwise, just copy the IITC script to the assets folder and rename it to iitc.js

View File

@ -1,6 +1,22 @@
ingress intel total conversion (IITC)
ingress intel total conversion mobile (IITCM)
=====================================
# [User documentation moved to the wiki. Please see there!](https://github.com/breunigs/ingress-intel-total-conversion/wiki/IITC-Mobile)
The Android App behaves like the desktop version, but uses the mobile view, which is optimized for mobile devices, as default. Furthermore, there are some nice additions:
### [For developer docs, please see HACKING.md](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/mobile/HACKING.md)
- it should be much faster than the standard mobile ingress intel map
- a gmaps intent is sent, when a portals Map link is clicked (lets you navigate to portals)
- a geolocate button (you have to enable GPS satellites + location access to use this feature)
- toggle between desktop and mobile view (nice for tablets)
- possibility to use a custom IITC script source
- a click on Portal link copies it to clipboard
- more features will be added soon...
**The App only works with Android 4.0+**
### [For developer docs, please see HACKING.md](https://github.com/jonatkins/ingress-intel-total-conversion/blob/master/mobile/HACKING.md)

View File

@ -32,6 +32,8 @@
</string>
<string name="pref_ui_cat">UI</string>
<string name="pref_plugins">Plugins</string>
<string name="pref_plugins_title">Available plugins</string>
<string name="pref_force_desktop">Force desktop mode</string>
<string name="pref_force_desktop_sum">Nice for tablets, looks awful on smartphones</string>
<string name="pref_developer_options">Developer options</string>

View File

@ -17,6 +17,10 @@
android:title="@string/pref_force_desktop"
android:summary="@string/pref_force_desktop_sum"
android:defaultValue="false" />
<MultiSelectListPreference
android:key="pref_plugins"
android:title="@string/pref_plugins"
android:dialogTitle="@string/pref_plugins_title"/>
</PreferenceCategory>
<PreferenceCategory

View File

@ -19,6 +19,14 @@ body {
}
#geosearch {
width: 100%;
}
#geosearchwrapper img {
display: none;
}
#chatcontrols {
height: 38px;
width: 100%;

View File

@ -56,7 +56,7 @@ public class IITC_Mobile extends Activity {
if (Intent.ACTION_VIEW.equals(action)) {
Uri uri = intent.getData();
String url = uri.toString();
if (intent.getScheme().equals("http://"))
if (intent.getScheme().equals("http"))
url = url.replace("http://", "https://");
Log.d("iitcm", "intent received url: " + url);
if (url.contains("ingress.com")) {

View File

@ -1,6 +1,11 @@
package com.cradle.iitc_mobile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
import android.app.Activity;
import android.content.res.AssetManager;
import android.os.Bundle;
public class IITC_Settings extends Activity {
@ -10,7 +15,52 @@ public class IITC_Settings extends Activity {
super.onCreate(savedInstanceState);
IITC_SettingsFragment settings = new IITC_SettingsFragment();
settings.setArguments(getIntent().getExtras());
AssetManager am = this.getAssets();
String[] asset_array = null;
try {
asset_array = am.list("plugins");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ArrayList<String> asset_list = new ArrayList<String>();
ArrayList<String> asset_values = new ArrayList<String>();
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()

View File

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

View File

@ -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<String> 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 its okay to load it and maybe replace it
// with our own content. This is used to block loading Niantic resources
// which arent 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);

View File

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

View File

@ -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('<aside><a href="http://ipas.graphracer.com/index.html#' + window.plugin.ipasLink.getHash(d.portalDetails) + '" target="ipaswindow" title="Use IAPS to simulate an attack on this portal">Simulate attack</a></aside>');
}
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() {

View File

@ -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 += '<span style="font-weight:bold;margin-left:10px;">Level '
+ playerLevel
+ (playerLevel < (window.MAX_XM_PER_LEVEL.length - 1) ? ' (guessed)' : '')
+ '</span>';
} else {
title += '<span style="font-weight:bold;margin-left:10px;">Level unknown</span>'
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = "'<strong>" + portalDetails.portalV2.descriptiveText.TITLE + "</strong>'<br/> <em>(" + portalDetails.portalV2.descriptiveText.ADDRESS + ")</em>";
var portalNameAdressTitle = $('<div/>').append('\'')
.append($('<strong/>').text(portalDetails.portalV2.descriptiveText.TITLE))
.append('\'')
.append($('<br/>'))
.append($('<em/>').text('(' + portalDetails.portalV2.descriptiveText.ADDRESS + ')'))
.html();
var imageUrl = (portalDetails.imageByUrl ? portalDetails.imageByUrl.imageUrl : window.DEFAULT_PORTAL_IMG);
portalInfoString = '<img src="' + imageUrl + '" class="minImg" alt="' + portalNameAdressAlt + '" title="' + portalNameAdressTitle + '"/>';
portalInfoString = $('<div/>').html($('<img/>').attr('src', imageUrl)
.attr('class', 'minImg')
.attr('alt', portalNameAdressAlt)
.attr('title', portalNameAdressTitle))
.html();
}
return portalInfoString;
};

View File

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