merge main

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

12
ATTRIBUTION.md Normal file
View File

@ -0,0 +1,12 @@
This project is licensed under the permissive ISC license. Parts imported from other projects remain under their respective licenses:
- [autolink-js by Bryan Woods; MIT](https://github.com/bryanwoods/autolink-js)
- [load.js by Chris O'Hara; MIT](https://github.com/chriso/load.js)
- [leaflet.js; custom license (but appears free)](http://leafletjs.com/)
- [leaflet.draw.js; by jacobtoye; MIT](https://github.com/Leaflet/Leaflet.draw)
- [`leaflet_google.js` by Pavel Shramov; same as Leaftlet](https://github.com/shramov/leaflet-plugins) (modified, though)
- [jquery.qrcode.js by Jerome Etienne; MIT](https://github.com/jeromeetienne/jquery-qrcode)
- [oms.min.js by George MacKerron; MIT](https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet)
- [taphold.js by Rich Adams; unknown](https://github.com/richadams/jquery-taphold)
- StackOverflow-CopyPasta is attributed in the source; [CC-Wiki](https://creativecommons.org/licenses/by-sa/3.0/)
- all Ingress/Niantic related stuff obviously remains non-free and is still copyrighted by Niantic/Google

View File

@ -27,11 +27,13 @@ Please follow the these guidelines. Some are just preference, others are good pr
- use identity operators: `===` and `!==`. [Why do I want this?](http://stackoverflow.com/a/359509/1684530)
- jQuery is your friend
- indent using two spaces
- opening brace on the same line with a space after the if/for/etc: `if(blub) {`
- opening brace on the same line: `if(blub) {`
- else clauses: `} else if(blub) {` or `} else {`
- there should be no space after `if`, `for`, etc. E.g. `if(true) { doStuff(); } else { dontDoStuff(); }`
- comments: `// this is a comment`
- quotes: Use single-quotes for JavaScript and double-quotes for HTML content. Example: `$('body').append('<div id="soup">Soup!</div>');`.
- there is no length limit on lines, but try to keep them short where suitable
- ensure you remove *all* trailing whitespace before submitting your patch. If you editor doesnt detect those for you, try `grep -nE "[[:space:]]+$" «filename»`
Sending patches
@ -63,12 +65,15 @@ How do I report bugs?
---------------------
**Try this first**:
- update the user script and all its plugins. Even if you think there is no update.
- update the user script and all its plugins.
- after updating, go to the intel page.
- press `SHIFT+F5` (or shift-click the reload button). Wait for the page to load.
- press `CTRL+F5`, same as above.
If your issue persists, continue. Provide **all** of the information below, even if you dont think this is necessary.
You can also try to [install the most recent developer version (“nightly”)]
(https://www.dropbox.com/sh/lt9p0s40kt3cs6m/3xzpyiVBnF) and repeat the steps above. Maybe your issue has already been fixed? The nightly versions will update to the next stable release, so you dont need to worry about that.
If your issue persists, continue. The next step is to look for existing issues, maybe someone else has a similar problem. You can look [through the existing issues](https://github.com/breunigs/ingress-intel-total-conversion/issues?sort=updated&state=open) or use the search function on the top right. If your issue persists, open a new issue and provide **all** of the information below, even if you dont think this is necessary.
- a descriptive title
- your browser and its version

24
NEWS.md
View File

@ -1,8 +1,25 @@
CHANGES IN 0.7
==============
CHANGES IN 0.7.5 0.7.8
========================
This is an emergency release to keep IITC working with Niantics switch to HTTPS. It appears they will roll it out for everyone soon, so IITC now requires HTTPS for everyone; support for HTTP was dropped to keep things sane. Additionally, the following things were changed from 0.7.1:
- Feature: the “gmaps” link for each portal has been replaced with a “poslinks” one. It offers Google Maps, OpenStreetMap and QR-Codes for easy transfer to your mobile phone (by Merovius).
- Feature: the exact capture time is now shown in a tooltip in the portal details (by j16sdiz)
- Change: most scripts are now included in the UserScript directly. Was the easiest solution to the HTTPS issue.
- Change: minor improvements when render limit is about to be hit.
- Bugfix: map base layer wasnt always remembered in Chrome
- Bugfix: QR Code rendering broken (in 0.7.5, fixed in 0.7.6)
- Bugfix: Script broken in Firefox sometimes (fixed in 0.7.7)
- Bugfix: some graphics were not available due to GitHubs limits. Affected plugins: draw tools and player tracker. Need to update IITC and both plugins for the fix to come into effect. (fixed in 0.7.8)
CHANGES IN 0.7 / 0.7.1
----------------------
- 0.7.1 fixes an oversight that prevented some portals from showing (by saithis)
### General
- from now on there will be [nightly builds](https://www.dropbox.com/sh/lt9p0s40kt3cs6m/3xzpyiVBnF) available. You need to manually update them if you want to stay on nightly. You should be offered to update to the next release version, though. Be sure to [have read the guide on how to report bugs](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#how-do-i-report-bugs) before using a nightly version.
- from now on there will be [nightly builds](https://iitcserv.appspot.com/iitc-nightly/) available. You need to manually update them if you want to stay on nightly. You should be offered to update to the next release version, though. Be sure to [have read the guide on how to report bugs](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#how-do-i-report-bugs) before using a nightly version.
- IITC has [a shiny new user guide now](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/USERGUIDE.md). Please point new users to it, it should answer most of their questions and also teach them how to make good bug reports.
### Main Script
@ -49,7 +66,6 @@ CHANGES IN 0.6 / 0.61
- **SECURITY**: Chat was vulnerable to XSS attacks. Update as soon as
possible.
- Feature: [**more plugins**](https://github.com/breunigs/ingress-intel-total-conversion/tree/gh-pages/plugins#readme)
- weakened portals: highlights portals with few resonators or ones
that are decayed, making it easier to see where

View File

@ -1,101 +1,4 @@
ingress intel total conversion (IITC)
=====================================
Its annoying to extend the intel page with new features because the minified code makes it hard to grasp whats going on. Also, one has to play catch up each time Niantic put up a new version because all the variables might get new names.
So instead, heres a userscript that starts from scratch (click to zoom):
[![Screenshot of the total conversion in Johannesburg](http://breunigs.github.com/ingress-intel-total-conversion/screenshots/screen_small.png)](http://breunigs.github.com/ingress-intel-total-conversion/screenshots/screen.png)
Features / User Guide
---------------------
You already know you want it, why add a feature list here? Instead, [**read the user guide** for tricks and less obvious features](https://github.com/breunigs/ingress-intel-total-conversion/tree/gh-pages/USERGUIDE.md). If you have questions, the user guide will also likely answer them.
IITC can be [extended with the use of plugins](https://github.com/breunigs/ingress-intel-total-conversion/tree/gh-pages/plugins), so have a look at those if you want (or need) more.
Install
-------
Current version is 0.7. [See NEWS.md for details](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/NEWS.md).
[**INSTALL**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js)
### Firefox
- Install [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) or [Scriptish](https://addons.mozilla.org/en-US/firefox/addon/scriptish/).
- Click install link: [install](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js)
- Confirm security question.
- Reload page.
*NoScript:* To make the script work whitelist at least these domains: `ingress.com github.com leafletjs.com googleapis.com`. If you want to see the cool font also whitelist `googleusercontent.com`.
### Chrome
- Install [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo/details).
- Click install link: [install](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js)
- **Now beware:** a OK/cancel dialog pops up. It **does not** allow you to cancel the installation. Choose **OK** to install the script with Tampermonkey.
- Confirm once again.
- Reload page.
*Note:* Tampermonkey is optional. However, it offers auto-update, shows correct version numbers and installing user scripts is much easier. If you have installed the scripts directly into Chrome before, I recommend you switch to Tampermonkey. To do so, uninstall the IITC scripts and click each install link again. Follow the procedure explained above.
### Opera
- Download the script: [download](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js)
- put it into your user_js folder (thats `~/.opera/user_js` on Unix). If you cant find it [see Operas docs](http://www.opera.com/docs/userjs/using/#writingscripts).
- reload the page
*Note*: You need to update the scripts manually.
[**INSTALL**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js)
Reporting Issues
----------------
[tutorial / guide / please read / **free candy**](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#how-do-i-report-bugs)
Contributing
------------
Please do!
(Obviously, Resistance folks must send in complete patches while Enlightenment gals and guys may just open feature request ☺). If you want to hack the source, please [read HACKING.md for details](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md) .
**So far, these people have contributed:**
[Bananeweizen](https://github.com/Bananeweizen),
[blakjakau](https://github.com/blakjakau),
[cmrn](https://github.com/cmrn),
[epf](https://github.com/epf),
[integ3r](https://github.com/integ3r),
[JasonMillward](https://github.com/JasonMillward),
[jonatkins](https://github.com/jonatkins),
[mledoze](https://github.com/mledoze),
[OshiHidra](https://github.com/OshiHidra),
[phoenixsong6](https://github.com/phoenixsong6),
[Pirozek](https://github.com/Pirozek),
[saithis](https://github.com/saithis),
[Scrool](https://github.com/Scrool),
[sorgo](https://github.com/sorgo),
[tpenner](https://github.com/tpenner),
[vita10gy](https://github.com/vita10gy),
[Xelio](https://github.com/Xelio),
[ZauberNerd](https://github.com/ZauberNerd)
Attribution & License
---------------------
This project is licensed under the permissive ISC license. Parts imported from other projects remain under their respective licenses:
- [autolink-js by Bryan Woods; MIT](https://github.com/bryanwoods/autolink-js)
- [load.js by Chris O'Hara; MIT](https://github.com/chriso/load.js)
- [leaflet.js; custom license (but appears free)](http://leafletjs.com/)
- [leaflet.draw.js; by jacobtoye; MIT](https://github.com/Leaflet/Leaflet.draw)
- [`leaflet_google.js` by Pavel Shramov; same as Leaftlet](https://github.com/shramov/leaflet-plugins) (modified, though)
- StackOverflow-CopyPasta is attributed in the source; [CC-Wiki](https://creativecommons.org/licenses/by-sa/3.0/)
- all Ingress/Niantic related stuff obviously remains non-free and is still copyrighted by Niantic/Google
# [Documentation moved to the wiki. Please see there!](https://github.com/breunigs/ingress-intel-total-conversion/wiki#head)

View File

@ -1,154 +1,4 @@
Userguide
=========
USER DOCUMENTATION MOVED
====
table of contents:
- [Installation](#installation)
- [General Usage](#general-usage)
- [Chat](#chat)
- [Map Display](#map-display)
- [Map Status / Updates](#map-status--updates)
- [Sidebar](#sidebar)
- [Bugs and help requests](#reporting-bugs--i-need-more-help)
Installation
------------
[See main README.md file for browser specific instructions](https://github.com/breunigs/ingress-intel-total-conversion#install).
General Usage
-------------
- many things have more information in tooltips. Let your cursor rest over stuff to show the tooltip. *Hint:* your cursor changes into a question mark / help cursor if theres a tooltip available.
- single left click on portals to show details
- double left click on portals to zoom in to them
Chat
----
The chat is split up into several categories. It usually only shows messages for the current map view. It may lag behind if Niantics servers are slow. It also has a minimum radius of six km. This means that even if you zoom in very much, it will still show messages from a larger area around you.
**The chat categories are:**
- full: shows all automated messages *(23:57 \<apj\> destroyed an L3 Resonator on Two Spikes)*
- compact: shows only the latest automated message per user
- public: shows user generated public messages (both Enlightenment and Resistance can read it)
- faction: shows messages for own faction (e.g. only Resistance can read Resistance messages)
**Posting messages:**
- You can post in the faction and public categories only
- Your message will be tagged with the coordinates in the center of the map.
- Your zoom level does not matter. Zooming out will not show your messages to more users.
Map Display
-----------
You can customize many aspects of how the map is rendered in the layer chooser. The layer chooser is available from the icon in the top right corner, left of the sidebar.
**Background / Street Map / Base Layer:**
All these refer to the same thing. The base layer is stored across sessions. The default one uses OpenStreetMap data with a style that resembles the default Ingress one. There are other styles available.
The layers from Google Maps are available as well. Google requires that their maps are only displayed with their tools. Therefore they cannot be as tightly integrated as the OpenStreetMap ones. Thats the reason why they lag behind when zooming or dragging the map.
**Portals:**
You can filter portals by level. Select the ones you want to see in the layer chooser. If lower level portals are striked-through, this means you need to zoom in further to see them. This is a server limitation, not one in IITC. Your settings are discarded after a reload.
**Resonators:**
Resonators are shown at their actual positions if you zoom in close enough. They become lighter the less energy they have left. They are color coded to show their level.
**Fields / Links:**
They are handled the same way portals are, see above.
**Other:**
When you select a portal its outer ring becomes red. Theres also a small yellow circle around it which depicts the hack range. You need to be in hack range to hack the portal or update its resonators or mods.
If you are zoomed out quite a bit, theres a larger red circle. This is the link range. Only portals within this link range can be linked while standing at the selected portal. [Click the range in the sidebar to zoom to link range for the selected portal](#random-details).
**Note** that plugins may also add themselves to the layer chooser.
Map Status / Updates
--------------------
It shows if there are currently operations pending. This includes chat updates as well as map data requests. Updates happen every 45s to 90s, depending on how far zoomed in you are. Zoom in closer for faster updates.
It also shows which portals are being loaded/shown. Zoom in to see lower level portals. This is a limit of the server and not IITC. Portals levels that cannot be shown are also striked through in the layer chooser.
**Failures:** If a data request failed, it is retried once. Only if the retry fails as well, a “failure” message is shown in the map status. You can either wait for the next automatic update or move the map a little. Also try to zoom in to request less data, which makes it less likely that the servers fail. The failure counter is reset on the next auto update or if you move the map.
**Render Limit:** The script tries to stay responsive. If too much data needs to be rendered, this cannot be guaranteed. Instead it will simply stop drawing portals/links/fields and show “render limit” in the map status. Zoom in to solve this.
Sidebar
-------
The sidebar is mainly used to show game stats and portal details. However, it also allows you to perform certain actions.
### General usage:
- single click a portal to show details about it in the sidebar
- the portal information is updated automatically, as long as the selected portal is kept in view and you do not zoom out too much.
- the sidebar may be collapsed. Click the triangle button that stands out at the left hand side.
- the sidebar **can be scrolled** if your screen is too small. Use your scroll wheel.
- almost anything has tooltips. See [General usage](#general-usage) above.
### Details:
Starting from the top, the sidebar shows this information:
#### logged in user, global MU, search
- Details about you, the logged in user. This is only updated if you reload the page. This is a limitation of Ingress, not IITC.
- it shows your current level followed by your nick
- to the right, it shows to percentages. The upper one, e.g. “XM: 37%” tells you how much your XM bar is filled. The lower one, e.g. “level: 37%“ tells you that you have gathered 37% of the AP required for the next level. It shows “max level” if you have reached max level.
- the tooltip mainly shows you absolute numbers instead of percentages. It also shows how many invites you have.
- The next bar is a visual representation of global MindUnits (MU) per faction. It is updated every now and then. The tooltip shows the absolute MU count per faction.
- Search Location: You can search for continents, countries, cities or street addresses. If there is at least one result your are taken to the most likely immediately. There is no feedback if the entered location was not found. Rule of thumb: if it takes longer than three seconds, try again. [Read about supported formats in the user guide for this service](https://wiki.openstreetmap.org/wiki/Nominatim).
#### Portal details.
- Portal name, may be abbreviated by the server if its too long.
- Portal image. Can be clicked to show the full image. The tooltip shows street address and postal code for the portal. It may also show attribution data for the portal image, if available.
- Portal level is located in the upper right corner and may overlay the image.
- Portal mods are shown in right below the portal image. An empty box means that this slot is free and no mod is installed. Otherwise the name of the installed mod is displayed. The color depends on the rare-ness of the mod. Each slot has its own tooltip that shows mod specific details and who installed it.
##### “random details”
“Random Details” are displayed in four columns. The outer ones show the data while the inner ones are the titles.
- owner: who deployed the first resonator after it has been neutral/unclaimed.
- since: when was the first resonator deployed after it has been neutral/unclaimed.
- range: shows how far links made from this portal can be. Click on the value to zoom out to link range. The red circle shows how far links may reach.
- energy: shows current and maximum energy if fully charged. The tooltip contains the exact numbers.
- links: shows incoming and outgoing links. The tooltip explains the icons.
- reso dist: shows the average distance the resonators have to the portal.
- fields: how many fields are connected to this portal
- AP Gain: estimate of how many AP you gain if you take down this portal and deploy resonators of your own faction. Tooltip breaks this number down into parts.
##### Resonators
The nickname to the left and right show who deployed this resonator. The bars in the middle indicate the charge of each resonator. The color depends on the level, which is also shown in the bar. The tooltip repeats some of that data along with other details. The top left resonator is the north one, top right is north east and so on. They are roughly ordered like they appear on a normal map:
```
N NE
NW E
W SE
SW S
```
#### portal related links
- Portal link: use it show others a portal. IITC users will automatically zoomed to the location and shown portal details as soon as theyre available. Vanilla map users will only be zoomed to location.
- Report issue: redirects you to Niantic report issue page. Allows you to copy all required information before going there.
- GMaps: shows you the portals location in Google Maps for routing and similar purposes.
#### Redeeming, General Links and functions
- Redeem code: allows you to redeem codes to receive goodies. If you copied them from the Internet, they are probably invalid already.
- Toolbox: plugins may add links here. The default ones are:
- permalink. use it to show your current map view to others. Does not select a portal. Works with the normal intel map, too.
- IITCs page. Visit our homepage. Be in awe. Drool.
Reporting Bugs / I need more help
---------------------------------
[Please read the “how do I report bugs” here](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#how-do-i-report-bugs).
[All user documentation has been moved to the wiki. Please see there!](https://github.com/breunigs/ingress-intel-total-conversion/wiki/Userguide-\(Main-Vanilla-IITC\))

View File

@ -1,20 +1,36 @@
#!/usr/bin/env python3
#!/usr/bin/env python
import glob
import time
import re
import io
def readfile(fn):
with open(fn, 'Ur', encoding='utf8') as f:
with io.open(fn, 'Ur', encoding='utf8') as f:
return f.read()
def loaderString(var):
fn = var.group(1)
return readfile(fn).replace('\n', '\\n').replace('\'', '\\\'')
def loaderRaw(var):
fn = var.group(1)
return readfile(fn)
c = '\n\n'.join(map(readfile, glob.glob('code/*')))
n = time.strftime('%Y-%m-%d-%H%M%S')
m = readfile('main.js').replace('@@BUILDDATE@@', n)
m = readfile('main.js')
m = m.split('@@INJECTHERE@@')
m.insert(1, c)
t = '\n\n'.join(m)
m = '\n\n'.join(m)
with open('iitc-debug.user.js', 'w', encoding='utf8') as f:
f.write(t)
m = m.replace('@@BUILDDATE@@', n)
m = re.sub('@@INCLUDERAW:([0-9a-zA-Z_./-]+)@@', loaderRaw, m)
m = re.sub('@@INCLUDESTRING:([0-9a-zA-Z_./-]+)@@', loaderString, m)
with io.open('iitc-debug.user.js', 'w', encoding='utf8') as f:
f.write(m)
# vim: ai si ts=4 sw=4 sts=4 et

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

169
dist/delaunay.js vendored Normal file
View File

@ -0,0 +1,169 @@
// Source from https://github.com/ironwallaby/delaunay
window.delaunay = function() {};
window.delaunay.Triangle = function (a, b, c) {
this.a = a
this.b = b
this.c = c
var A = b.x - a.x,
B = b.y - a.y,
C = c.x - a.x,
D = c.y - a.y,
E = A * (a.x + b.x) + B * (a.y + b.y),
F = C * (a.x + c.x) + D * (a.y + c.y),
G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)),
minx, miny, dx, dy
/* If the points of the triangle are collinear, then just find the
* extremes and use the midpoint as the center of the circumcircle. */
if(Math.abs(G) < 0.000001) {
minx = Math.min(a.x, b.x, c.x)
miny = Math.min(a.y, b.y, c.y)
dx = (Math.max(a.x, b.x, c.x) - minx) * 0.5
dy = (Math.max(a.y, b.y, c.y) - miny) * 0.5
this.x = minx + dx
this.y = miny + dy
this.r = dx * dx + dy * dy
}
else {
this.x = (D*E - B*F) / G
this.y = (A*F - C*E) / G
dx = this.x - a.x
dy = this.y - a.y
this.r = dx * dx + dy * dy
}
}
function byX(a, b) {
return b.x - a.x
}
function dedup(edges) {
var j = edges.length,
a, b, i, m, n
outer: while(j) {
b = edges[--j]
a = edges[--j]
i = j
while(i) {
n = edges[--i]
m = edges[--i]
if((a === m && b === n) || (a === n && b === m)) {
edges.splice(j, 2)
edges.splice(i, 2)
j -= 2
continue outer
}
}
}
}
window.delaunay.triangulate = function (vertices) {
/* Bail if there aren't enough vertices to form any triangles. */
if(vertices.length < 3)
return []
/* Ensure the vertex array is in order of descending X coordinate
* (which is needed to ensure a subquadratic runtime), and then find
* the bounding box around the points. */
vertices.sort(byX)
var i = vertices.length - 1,
xmin = vertices[i].x,
xmax = vertices[0].x,
ymin = vertices[i].y,
ymax = ymin
while(i--) {
if(vertices[i].y < ymin) ymin = vertices[i].y
if(vertices[i].y > ymax) ymax = vertices[i].y
}
/* Find a supertriangle, which is a triangle that surrounds all the
* vertices. This is used like something of a sentinel value to remove
* cases in the main algorithm, and is removed before we return any
* results.
*
* Once found, put it in the "open" list. (The "open" list is for
* triangles who may still need to be considered; the "closed" list is
* for triangles which do not.) */
var dx = xmax - xmin,
dy = ymax - ymin,
dmax = (dx > dy) ? dx : dy,
xmid = (xmax + xmin) * 0.5,
ymid = (ymax + ymin) * 0.5,
open = [
new window.delaunay.Triangle(
{x: xmid - 20 * dmax, y: ymid - dmax, __sentinel: true},
{x: xmid , y: ymid + 20 * dmax, __sentinel: true},
{x: xmid + 20 * dmax, y: ymid - dmax, __sentinel: true}
)
],
closed = [],
edges = [],
j, a, b
/* Incrementally add each vertex to the mesh. */
i = vertices.length
while(i--) {
/* For each open triangle, check to see if the current point is
* inside it's circumcircle. If it is, remove the triangle and add
* it's edges to an edge list. */
edges.length = 0
j = open.length
while(j--) {
/* If this point is to the right of this triangle's circumcircle,
* then this triangle should never get checked again. Remove it
* from the open list, add it to the closed list, and skip. */
dx = vertices[i].x - open[j].x
if(dx > 0 && dx * dx > open[j].r) {
closed.push(open[j])
open.splice(j, 1)
continue
}
/* If not, skip this triangle. */
dy = vertices[i].y - open[j].y
if(dx * dx + dy * dy > open[j].r)
continue
/* Remove the triangle and add it's edges to the edge list. */
edges.push(
open[j].a, open[j].b,
open[j].b, open[j].c,
open[j].c, open[j].a
)
open.splice(j, 1)
}
/* Remove any doubled edges. */
dedup(edges)
/* Add a new triangle for each edge. */
j = edges.length
while(j) {
b = edges[--j]
a = edges[--j]
open.push(new window.delaunay.Triangle(a, b, vertices[i]))
}
}
/* Copy any remaining open triangles to the closed list, and then
* remove any triangles that share a vertex with the supertriangle. */
Array.prototype.push.apply(closed, open)
i = closed.length
while(i--)
if(closed[i].a.__sentinel ||
closed[i].b.__sentinel ||
closed[i].c.__sentinel)
closed.splice(i, 1)
/* Yay, we're done! */
return closed
}

BIN
dist/images/layers.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

BIN
dist/images/marker-blue_2x.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
dist/images/marker-green_2x.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
dist/images/marker-icon_2x.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -92,6 +92,7 @@ L.Google = L.Class.extend({
var map = new google.maps.Map(this._container, {
center: this._google_center,
zoom: 0,
tilt: 0,
styles: this._styles,
mapTypeId: this._type,
disableDefaultUI: true,
@ -138,8 +139,8 @@ L.Google = L.Class.extend({
_resize: function() {
var size = this._map.getSize();
if (this._container.style.width == size.x &&
this._container.style.height == size.y)
if (parseInt(this._container.style.width) == size.x &&
parseInt(this._container.style.height) == size.y)
return;
this._container.style.width = size.x + 'px';
this._container.style.height = size.y + 'px';

4
dist/style.0.7.css vendored
View File

@ -401,6 +401,10 @@ input {
font-style: italic;
}
.leaflet-control-layers input {
height: auto;
padding: 0;
}
/* portal title and image */
h3 {

File diff suppressed because one or more lines are too long

28
external/jquery.qrcode.min.js vendored Normal file
View File

@ -0,0 +1,28 @@
(function(r){r.fn.qrcode=function(h){var s;function u(a){this.mode=s;this.data=a}function o(a,c){this.typeNumber=a;this.errorCorrectLevel=c;this.modules=null;this.moduleCount=0;this.dataCache=null;this.dataList=[]}function q(a,c){if(void 0==a.length)throw Error(a.length+"/"+c);for(var d=0;d<a.length&&0==a[d];)d++;this.num=Array(a.length-d+c);for(var b=0;b<a.length-d;b++)this.num[b]=a[b+d]}function p(a,c){this.totalCount=a;this.dataCount=c}function t(){this.buffer=[];this.length=0}u.prototype={getLength:function(){return this.data.length},
write:function(a){for(var c=0;c<this.data.length;c++)a.put(this.data.charCodeAt(c),8)}};o.prototype={addData:function(a){this.dataList.push(new u(a));this.dataCache=null},isDark:function(a,c){if(0>a||this.moduleCount<=a||0>c||this.moduleCount<=c)throw Error(a+","+c);return this.modules[a][c]},getModuleCount:function(){return this.moduleCount},make:function(){if(1>this.typeNumber){for(var a=1,a=1;40>a;a++){for(var c=p.getRSBlocks(a,this.errorCorrectLevel),d=new t,b=0,e=0;e<c.length;e++)b+=c[e].dataCount;
for(e=0;e<this.dataList.length;e++)c=this.dataList[e],d.put(c.mode,4),d.put(c.getLength(),j.getLengthInBits(c.mode,a)),c.write(d);if(d.getLengthInBits()<=8*b)break}this.typeNumber=a}this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17;this.modules=Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=Array(this.moduleCount);for(var b=0;b<this.moduleCount;b++)this.modules[d][b]=null}this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-
7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(a,c);7<=this.typeNumber&&this.setupTypeNumber(a);null==this.dataCache&&(this.dataCache=o.createData(this.typeNumber,this.errorCorrectLevel,this.dataList));this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,c){for(var d=-1;7>=d;d++)if(!(-1>=a+d||this.moduleCount<=a+d))for(var b=-1;7>=b;b++)-1>=c+b||this.moduleCount<=c+b||(this.modules[a+d][c+b]=
0<=d&&6>=d&&(0==b||6==b)||0<=b&&6>=b&&(0==d||6==d)||2<=d&&4>=d&&2<=b&&4>=b?!0:!1)},getBestMaskPattern:function(){for(var a=0,c=0,d=0;8>d;d++){this.makeImpl(!0,d);var b=j.getLostPoint(this);if(0==d||a>b)a=b,c=d}return c},createMovieClip:function(a,c,d){a=a.createEmptyMovieClip(c,d);this.make();for(c=0;c<this.modules.length;c++)for(var d=1*c,b=0;b<this.modules[c].length;b++){var e=1*b;this.modules[c][b]&&(a.beginFill(0,100),a.moveTo(e,d),a.lineTo(e+1,d),a.lineTo(e+1,d+1),a.lineTo(e,d+1),a.endFill())}return a},
setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(a=8;a<this.moduleCount-8;a++)null==this.modules[6][a]&&(this.modules[6][a]=0==a%2)},setupPositionAdjustPattern:function(){for(var a=j.getPatternPosition(this.typeNumber),c=0;c<a.length;c++)for(var d=0;d<a.length;d++){var b=a[c],e=a[d];if(null==this.modules[b][e])for(var f=-2;2>=f;f++)for(var i=-2;2>=i;i++)this.modules[b+f][e+i]=-2==f||2==f||-2==i||2==i||0==f&&0==i?!0:!1}},setupTypeNumber:function(a){for(var c=
j.getBCHTypeNumber(this.typeNumber),d=0;18>d;d++){var b=!a&&1==(c>>d&1);this.modules[Math.floor(d/3)][d%3+this.moduleCount-8-3]=b}for(d=0;18>d;d++)b=!a&&1==(c>>d&1),this.modules[d%3+this.moduleCount-8-3][Math.floor(d/3)]=b},setupTypeInfo:function(a,c){for(var d=j.getBCHTypeInfo(this.errorCorrectLevel<<3|c),b=0;15>b;b++){var e=!a&&1==(d>>b&1);6>b?this.modules[b][8]=e:8>b?this.modules[b+1][8]=e:this.modules[this.moduleCount-15+b][8]=e}for(b=0;15>b;b++)e=!a&&1==(d>>b&1),8>b?this.modules[8][this.moduleCount-
b-1]=e:9>b?this.modules[8][15-b-1+1]=e:this.modules[8][15-b-1]=e;this.modules[this.moduleCount-8][8]=!a},mapData:function(a,c){for(var d=-1,b=this.moduleCount-1,e=7,f=0,i=this.moduleCount-1;0<i;i-=2)for(6==i&&i--;;){for(var g=0;2>g;g++)if(null==this.modules[b][i-g]){var n=!1;f<a.length&&(n=1==(a[f]>>>e&1));j.getMask(c,b,i-g)&&(n=!n);this.modules[b][i-g]=n;e--; -1==e&&(f++,e=7)}b+=d;if(0>b||this.moduleCount<=b){b-=d;d=-d;break}}}};o.PAD0=236;o.PAD1=17;o.createData=function(a,c,d){for(var c=p.getRSBlocks(a,
c),b=new t,e=0;e<d.length;e++){var f=d[e];b.put(f.mode,4);b.put(f.getLength(),j.getLengthInBits(f.mode,a));f.write(b)}for(e=a=0;e<c.length;e++)a+=c[e].dataCount;if(b.getLengthInBits()>8*a)throw Error("code length overflow. ("+b.getLengthInBits()+">"+8*a+")");for(b.getLengthInBits()+4<=8*a&&b.put(0,4);0!=b.getLengthInBits()%8;)b.putBit(!1);for(;!(b.getLengthInBits()>=8*a);){b.put(o.PAD0,8);if(b.getLengthInBits()>=8*a)break;b.put(o.PAD1,8)}return o.createBytes(b,c)};o.createBytes=function(a,c){for(var d=
0,b=0,e=0,f=Array(c.length),i=Array(c.length),g=0;g<c.length;g++){var n=c[g].dataCount,h=c[g].totalCount-n,b=Math.max(b,n),e=Math.max(e,h);f[g]=Array(n);for(var k=0;k<f[g].length;k++)f[g][k]=255&a.buffer[k+d];d+=n;k=j.getErrorCorrectPolynomial(h);n=(new q(f[g],k.getLength()-1)).mod(k);i[g]=Array(k.getLength()-1);for(k=0;k<i[g].length;k++)h=k+n.getLength()-i[g].length,i[g][k]=0<=h?n.get(h):0}for(k=g=0;k<c.length;k++)g+=c[k].totalCount;d=Array(g);for(k=n=0;k<b;k++)for(g=0;g<c.length;g++)k<f[g].length&&
(d[n++]=f[g][k]);for(k=0;k<e;k++)for(g=0;g<c.length;g++)k<i[g].length&&(d[n++]=i[g][k]);return d};s=4;for(var j={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,
78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var c=a<<10;0<=j.getBCHDigit(c)-j.getBCHDigit(j.G15);)c^=j.G15<<j.getBCHDigit(c)-j.getBCHDigit(j.G15);return(a<<10|c)^j.G15_MASK},getBCHTypeNumber:function(a){for(var c=a<<12;0<=j.getBCHDigit(c)-
j.getBCHDigit(j.G18);)c^=j.G18<<j.getBCHDigit(c)-j.getBCHDigit(j.G18);return a<<12|c},getBCHDigit:function(a){for(var c=0;0!=a;)c++,a>>>=1;return c},getPatternPosition:function(a){return j.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,c,d){switch(a){case 0:return 0==(c+d)%2;case 1:return 0==c%2;case 2:return 0==d%3;case 3:return 0==(c+d)%3;case 4:return 0==(Math.floor(c/2)+Math.floor(d/3))%2;case 5:return 0==c*d%2+c*d%3;case 6:return 0==(c*d%2+c*d%3)%2;case 7:return 0==(c*d%3+(c+d)%2)%2;default:throw Error("bad maskPattern:"+
a);}},getErrorCorrectPolynomial:function(a){for(var c=new q([1],0),d=0;d<a;d++)c=c.multiply(new q([1,l.gexp(d)],0));return c},getLengthInBits:function(a,c){if(1<=c&&10>c)switch(a){case 1:return 10;case 2:return 9;case s:return 8;case 8:return 8;default:throw Error("mode:"+a);}else if(27>c)switch(a){case 1:return 12;case 2:return 11;case s:return 16;case 8:return 10;default:throw Error("mode:"+a);}else if(41>c)switch(a){case 1:return 14;case 2:return 13;case s:return 16;case 8:return 12;default:throw Error("mode:"+
a);}else throw Error("type:"+c);},getLostPoint:function(a){for(var c=a.getModuleCount(),d=0,b=0;b<c;b++)for(var e=0;e<c;e++){for(var f=0,i=a.isDark(b,e),g=-1;1>=g;g++)if(!(0>b+g||c<=b+g))for(var h=-1;1>=h;h++)0>e+h||c<=e+h||0==g&&0==h||i==a.isDark(b+g,e+h)&&f++;5<f&&(d+=3+f-5)}for(b=0;b<c-1;b++)for(e=0;e<c-1;e++)if(f=0,a.isDark(b,e)&&f++,a.isDark(b+1,e)&&f++,a.isDark(b,e+1)&&f++,a.isDark(b+1,e+1)&&f++,0==f||4==f)d+=3;for(b=0;b<c;b++)for(e=0;e<c-6;e++)a.isDark(b,e)&&!a.isDark(b,e+1)&&a.isDark(b,e+
2)&&a.isDark(b,e+3)&&a.isDark(b,e+4)&&!a.isDark(b,e+5)&&a.isDark(b,e+6)&&(d+=40);for(e=0;e<c;e++)for(b=0;b<c-6;b++)a.isDark(b,e)&&!a.isDark(b+1,e)&&a.isDark(b+2,e)&&a.isDark(b+3,e)&&a.isDark(b+4,e)&&!a.isDark(b+5,e)&&a.isDark(b+6,e)&&(d+=40);for(e=f=0;e<c;e++)for(b=0;b<c;b++)a.isDark(b,e)&&f++;a=Math.abs(100*f/c/c-50)/5;return d+10*a}},l={glog:function(a){if(1>a)throw Error("glog("+a+")");return l.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;256<=a;)a-=255;return l.EXP_TABLE[a]},EXP_TABLE:Array(256),
LOG_TABLE:Array(256)},m=0;8>m;m++)l.EXP_TABLE[m]=1<<m;for(m=8;256>m;m++)l.EXP_TABLE[m]=l.EXP_TABLE[m-4]^l.EXP_TABLE[m-5]^l.EXP_TABLE[m-6]^l.EXP_TABLE[m-8];for(m=0;255>m;m++)l.LOG_TABLE[l.EXP_TABLE[m]]=m;q.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var c=Array(this.getLength()+a.getLength()-1),d=0;d<this.getLength();d++)for(var b=0;b<a.getLength();b++)c[d+b]^=l.gexp(l.glog(this.get(d))+l.glog(a.get(b)));return new q(c,0)},mod:function(a){if(0>
this.getLength()-a.getLength())return this;for(var c=l.glog(this.get(0))-l.glog(a.get(0)),d=Array(this.getLength()),b=0;b<this.getLength();b++)d[b]=this.get(b);for(b=0;b<a.getLength();b++)d[b]^=l.gexp(l.glog(a.get(b))+c);return(new q(d,0)).mod(a)}};p.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],
[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,
116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,
43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,
3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,
55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,
45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];p.getRSBlocks=function(a,c){var d=p.getRsBlockTable(a,c);if(void 0==d)throw Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+c);for(var b=d.length/3,e=[],f=0;f<b;f++)for(var h=d[3*f+0],g=d[3*f+1],j=d[3*f+2],l=0;l<h;l++)e.push(new p(g,j));return e};p.getRsBlockTable=function(a,c){switch(c){case 1:return p.RS_BLOCK_TABLE[4*(a-1)+0];case 0:return p.RS_BLOCK_TABLE[4*(a-1)+1];case 3:return p.RS_BLOCK_TABLE[4*
(a-1)+2];case 2:return p.RS_BLOCK_TABLE[4*(a-1)+3]}};t.prototype={get:function(a){return 1==(this.buffer[Math.floor(a/8)]>>>7-a%8&1)},put:function(a,c){for(var d=0;d<c;d++)this.putBit(1==(a>>>c-d-1&1))},getLengthInBits:function(){return this.length},putBit:function(a){var c=Math.floor(this.length/8);this.buffer.length<=c&&this.buffer.push(0);a&&(this.buffer[c]|=128>>>this.length%8);this.length++}};"string"===typeof h&&(h={text:h});h=r.extend({},{render:"canvas",width:256,height:256,typeNumber:-1,
correctLevel:2,background:"#ffffff",foreground:"#000000"},h);return this.each(function(){var a;if("canvas"==h.render){a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();var c=document.createElement("canvas");c.width=h.width;c.height=h.height;for(var d=c.getContext("2d"),b=h.width/a.getModuleCount(),e=h.height/a.getModuleCount(),f=0;f<a.getModuleCount();f++)for(var i=0;i<a.getModuleCount();i++){d.fillStyle=a.isDark(f,i)?h.foreground:h.background;var g=Math.ceil((i+1)*b)-Math.floor(i*b),
j=Math.ceil((f+1)*b)-Math.floor(f*b);d.fillRect(Math.round(i*b),Math.round(f*e),g,j)}}else{a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();c=r("<table></table>").css("width",h.width+"px").css("height",h.height+"px").css("border","0px").css("border-collapse","collapse").css("background-color",h.background);d=h.width/a.getModuleCount();b=h.height/a.getModuleCount();for(e=0;e<a.getModuleCount();e++){f=r("<tr></tr>").css("height",b+"px").appendTo(c);for(i=0;i<a.getModuleCount();i++)r("<td></td>").css("width",
d+"px").css("background-color",a.isDark(e,i)?h.foreground:h.background).appendTo(f)}}a=c;jQuery(a).appendTo(this)})}})(jQuery);

457
external/leaflet.css vendored Normal file
View File

@ -0,0 +1,457 @@
/* required styles */
.leaflet-map-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-pane,
.leaflet-overlay-pane,
.leaflet-shadow-pane,
.leaflet-marker-pane,
.leaflet-popup-pane,
.leaflet-overlay-pane svg,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
-ms-touch-action: none;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container img {
max-width: none !important;
}
/* stupid Android 2 doesn't understand "max-width: none" properly */
.leaflet-container img.leaflet-image-layer {
max-width: 15000px !important;
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
}
.leaflet-tile-pane { z-index: 2; }
.leaflet-objects-pane { z-index: 3; }
.leaflet-overlay-pane { z-index: 4; }
.leaflet-shadow-pane { z-index: 5; }
.leaflet-marker-pane { z-index: 6; }
.leaflet-popup-pane { z-index: 7; }
/* control positioning */
.leaflet-control {
position: relative;
z-index: 7;
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile,
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-tile-loaded,
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile,
.leaflet-touching .leaflet-zoom-animated {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-clickable {
cursor: pointer;
}
.leaflet-container {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging,
.leaflet-dragging .leaflet-clickable,
.leaflet-dragging .leaflet-container {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #05f;
background: white;
opacity: 0.5;
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 0 8px rgba(0,0,0,0.4);
border: 1px solid #888;
-webkit-border-radius: 5px;
border-radius: 5px;
}
.leaflet-bar-part {
background-color: rgba(255, 255, 255, 0.8);
border-bottom: 1px solid #aaa;
}
.leaflet-bar-part-top {
-webkit-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
}
.leaflet-bar-part-bottom {
-webkit-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
border-bottom: none;
}
.leaflet-touch .leaflet-bar {
-webkit-border-radius: 10px;
border-radius: 10px;
}
.leaflet-touch .leaflet-bar-part {
border-bottom: 4px solid rgba(0,0,0,0.3);
}
.leaflet-touch .leaflet-bar-part-top {
-webkit-border-radius: 7px 7px 0 0;
border-radius: 7px 7px 0 0;
}
.leaflet-touch .leaflet-bar-part-bottom {
-webkit-border-radius: 0 0 7px 7px;
border-radius: 0 0 7px 7px;
border-bottom: none;
}
/* zoom control */
.leaflet-container .leaflet-control-zoom {
margin-left: 13px;
margin-top: 12px;
}
.leaflet-control-zoom a {
width: 22px;
height: 22px;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-control-zoom a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-control-zoom a:hover {
background-color: #fff;
color: #777;
}
.leaflet-control-zoom-in {
font: bold 18px/24px Arial, Helvetica, sans-serif;
}
.leaflet-control-zoom-out {
font: bold 23px/20px Tahoma, Verdana, sans-serif;
}
.leaflet-control-zoom a.leaflet-control-zoom-disabled {
cursor: default;
background-color: rgba(255, 255, 255, 0.8);
color: #bbb;
}
.leaflet-touch .leaflet-control-zoom a {
width: 30px;
height: 30px;
}
.leaflet-touch .leaflet-control-zoom-in {
font-size: 24px;
line-height: 29px;
}
.leaflet-touch .leaflet-control-zoom-out {
font-size: 28px;
line-height: 24px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 7px rgba(0,0,0,0.4);
background: #f8f8f9;
-webkit-border-radius: 8px;
border-radius: 8px;
}
.leaflet-control-layers-toggle {
background-image: url(https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background-color: rgba(255, 255, 255, 0.7);
box-shadow: 0 0 5px #bbb;
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
color: black;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
text-shadow: 1px 1px 1px #fff;
background-color: rgba(255, 255, 255, 0.5);
box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2);
white-space: nowrap;
overflow: hidden;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-control-zoom {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-control-zoom {
border: 4px solid rgba(0,0,0,0.3);
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
-webkit-border-radius: 20px;
border-radius: 20px;
}
.leaflet-popup-content {
margin: 14px 20px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
margin: 0 auto;
width: 40px;
height: 20px;
position: relative;
overflow: hidden;
}
.leaflet-popup-tip {
width: 15px;
height: 15px;
padding: 1px;
margin: -8px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
background: white;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 5px 0 0;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
.leaflet-editing-icon {
-webkit-border-radius: 2px;
border-radius: 2px;
}

8
external/leaflet.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -36,12 +36,11 @@ L.Google = L.Class.extend({
this._initContainer();
this._initMapObject();
// set up events
map.on('viewreset', this._resetCallback, this);
this._map.options.zoomAnimation = false;
this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
// set up events
//~ map.on('viewreset', this._resetCallback, this);
map.on('move', this._update, this);
//map.on('moveend', this._update, this);
this._reset();
this._update();
@ -51,7 +50,8 @@ L.Google = L.Class.extend({
this._map._container.removeChild(this._container);
//this._container = null;
this._map.off('viewreset', this._resetCallback, this);
//~ this._map.off('viewreset', this._resetCallback, this);
this._map.options.zoomAnimation = true;
this._map.off('move', this._update, this);
//this._map.off('moveend', this._update, this);
@ -93,6 +93,7 @@ L.Google = L.Class.extend({
center: this._google_center,
zoom: 0,
styles: this._styles,
tilt: 0,
mapTypeId: this._type,
disableDefaultUI: true,
keyboardShortcuts: false,
@ -121,28 +122,22 @@ L.Google = L.Class.extend({
_update: function() {
this._resize();
var bounds = this._map.getBounds();
var ne = bounds.getNorthEast();
var sw = bounds.getSouthWest();
var google_bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(sw.lat, sw.lng),
new google.maps.LatLng(ne.lat, ne.lng)
);
var center = this._map.getCenter();
var _center = new google.maps.LatLng(center.lat, center.lng);
this._google.setCenter(_center);
this._google.setZoom(this._map.getZoom());
//this._google.fitBounds(google_bounds);
},
_resize: function() {
var size = this._map.getSize();
if (this._container.style.width == size.x &&
this._container.style.height == size.y)
if (parseInt(this._container.style.width) == size.x &&
parseInt(this._container.style.height) == size.y)
return;
this._container.style.width = size.x + 'px';
this._container.style.height = size.y + 'px';
google.maps.event.trigger(this._google, "resize");
},

19
external/oms.min.js vendored Normal file
View File

@ -0,0 +1,19 @@
(function(){/*
OverlappingMarkerSpiderfier
https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet
Copyright (c) 2011 - 2012 George MacKerron
Released under the MIT licence: http://opensource.org/licenses/mit-license
Note: The Leaflet maps API must be included *before* this code
*/
(function(){var n={}.hasOwnProperty,o=[].slice;null!=this.L&&(this.OverlappingMarkerSpiderfier=function(){function l(c,b){var a,e,g,f,d=this;this.map=c;null==b&&(b={});for(a in b)n.call(b,a)&&(e=b[a],this[a]=e);this.initMarkerArrays();this.listeners={};f=["click","zoomend"];e=0;for(g=f.length;e<g;e++)a=f[e],this.map.addEventListener(a,function(){return d.unspiderfy()})}var d,i;d=l.prototype;d.VERSION="0.2.5";i=2*Math.PI;d.keepSpiderfied=!1;d.nearbyDistance=20;d.circleSpiralSwitchover=9;d.circleFootSeparation=
25;d.circleStartAngle=i/12;d.spiralFootSeparation=28;d.spiralLengthStart=11;d.spiralLengthFactor=5;d.legWeight=1.5;d.legColors={usual:"#222",highlighted:"#f00"};d.initMarkerArrays=function(){this.markers=[];return this.markerListeners=[]};d.addMarker=function(c){var b,a=this;if(null!=c._oms)return this;c._oms=!0;b=function(){return a.spiderListener(c)};c.addEventListener("click",b);this.markerListeners.push(b);this.markers.push(c);return this};d.getMarkers=function(){return this.markers.slice(0)};
d.removeMarker=function(c){var b,a;null!=c._omsData&&this.unspiderfy();b=this.arrIndexOf(this.markers,c);if(0>b)return this;a=this.markerListeners.splice(b,1)[0];c.removeEventListener("click",a);delete c._oms;this.markers.splice(b,1);return this};d.clearMarkers=function(){var c,b,a,e,g;this.unspiderfy();g=this.markers;c=a=0;for(e=g.length;a<e;c=++a)b=g[c],c=this.markerListeners[c],b.removeEventListener("click",c),delete b._oms;this.initMarkerArrays();return this};d.addListener=function(c,b){var a,
e;(null!=(e=(a=this.listeners)[c])?e:a[c]=[]).push(b);return this};d.removeListener=function(c,b){var a;a=this.arrIndexOf(this.listeners[c],b);0>a||this.listeners[c].splice(a,1);return this};d.clearListeners=function(c){this.listeners[c]=[];return this};d.trigger=function(){var c,b,a,e,g,f;b=arguments[0];c=2<=arguments.length?o.call(arguments,1):[];b=null!=(a=this.listeners[b])?a:[];f=[];e=0;for(g=b.length;e<g;e++)a=b[e],f.push(a.apply(null,c));return f};d.generatePtsCircle=function(c,b){var a,e,
g,f,d;g=this.circleFootSeparation*(2+c)/i;e=i/c;d=[];for(a=f=0;0<=c?f<c:f>c;a=0<=c?++f:--f)a=this.circleStartAngle+a*e,d.push(new L.Point(b.x+g*Math.cos(a),b.y+g*Math.sin(a)));return d};d.generatePtsSpiral=function(c,b){var a,e,g,f,d;g=this.spiralLengthStart;a=0;d=[];for(e=f=0;0<=c?f<c:f>c;e=0<=c?++f:--f)a+=this.spiralFootSeparation/g+5.0E-4*e,e=new L.Point(b.x+g*Math.cos(a),b.y+g*Math.sin(a)),g+=i*this.spiralLengthFactor/a,d.push(e);return d};d.spiderListener=function(c){var b,a,e,g,f,d,h,i,j;b=
null!=c._omsData;(!b||!this.keepSpiderfied)&&this.unspiderfy();if(b)return this.trigger("click",c);g=[];f=[];d=this.nearbyDistance*this.nearbyDistance;e=this.map.latLngToLayerPoint(c.getLatLng());j=this.markers;h=0;for(i=j.length;h<i;h++)b=j[h],a=this.map.latLngToLayerPoint(b.getLatLng()),this.ptDistanceSq(a,e)<d?g.push({marker:b,markerPt:a}):f.push(b);return 1===g.length?this.trigger("click",c):this.spiderfy(g,f)};d.makeHighlightListeners=function(c){var b=this;return{highlight:function(){return c._omsData.leg.setStyle({color:b.legColors.highlighted})},
unhighlight:function(){return c._omsData.leg.setStyle({color:b.legColors.usual})}}};d.spiderfy=function(c,b){var a,e,g,d,m,h,i,j,l,k;this.spiderfying=!0;k=c.length;a=this.ptAverage(function(){var a,b,e;e=[];a=0;for(b=c.length;a<b;a++)i=c[a],e.push(i.markerPt);return e}());d=k>=this.circleSpiralSwitchover?this.generatePtsSpiral(k,a).reverse():this.generatePtsCircle(k,a);a=function(){var a,b,i,k=this;i=[];a=0;for(b=d.length;a<b;a++)g=d[a],e=this.map.layerPointToLatLng(g),l=this.minExtract(c,function(a){return k.ptDistanceSq(a.markerPt,
g)}),h=l.marker,m=new L.Polyline([h.getLatLng(),e],{color:this.legColors.usual,weight:this.legWeight,clickable:!1}),this.map.addLayer(m),h._omsData={usualPosition:h.getLatLng(),leg:m},this.legColors.highlighted!==this.legColors.usual&&(j=this.makeHighlightListeners(h),h._omsData.highlightListeners=j,h.addEventListener("mouseover",j.highlight),h.addEventListener("mouseout",j.unhighlight)),h.setLatLng(e),h.setZIndexOffset(1E6),i.push(h);return i}.call(this);delete this.spiderfying;this.spiderfied=!0;
return this.trigger("spiderfy",a,b)};d.unspiderfy=function(c){var b,a,e,d,f,i,h;null==c&&(c=null);if(null==this.spiderfied)return this;this.unspiderfying=!0;d=[];e=[];h=this.markers;f=0;for(i=h.length;f<i;f++)b=h[f],null!=b._omsData?(this.map.removeLayer(b._omsData.leg),b!==c&&b.setLatLng(b._omsData.usualPosition),b.setZIndexOffset(0),a=b._omsData.highlightListeners,null!=a&&(b.removeEventListener("mouseover",a.highlight),b.removeEventListener("mouseout",a.unhighlight)),delete b._omsData,d.push(b)):
e.push(b);delete this.unspiderfying;delete this.spiderfied;this.trigger("unspiderfy",d,e);return this};d.ptDistanceSq=function(c,b){var a,e;a=c.x-b.x;e=c.y-b.y;return a*a+e*e};d.ptAverage=function(c){var b,a,e,d,f;d=a=e=0;for(f=c.length;d<f;d++)b=c[d],a+=b.x,e+=b.y;c=c.length;return new L.Point(a/c,e/c)};d.minExtract=function(c,b){var a,d,g,f,i,h;g=i=0;for(h=c.length;i<h;g=++i)if(f=c[g],f=b(f),!("undefined"!==typeof a&&null!==a)||f<d)d=f,a=g;return c.splice(a,1)[0]};d.arrIndexOf=function(c,b){var a,
d,g,f;if(null!=c.indexOf)return c.indexOf(b);a=g=0;for(f=c.length;g<f;a=++g)if(d=c[a],d===b)return a;return-1};return l}())}).call(this);}).call(this);
/* Sun 6 May 2012 17:49:10 BST */

106
external/taphold.js vendored Normal file
View File

@ -0,0 +1,106 @@
// @author Rich Adams <rich@richadams.me>
// Implements a tap and hold functionality. If you click/tap and release, it will trigger a normal
// click event. But if you click/tap and hold for 1s, it will trigger a taphold event instead.
;(function($)
{
// When start of a taphold event is triggered.
function startHandler(event)
{
var $elem = jQuery(this);
// If object also has click handler, store it and unbind. Taphold will trigger the
// click itself, rather than normal propagation.
if (typeof $elem.data("events") != "undefined"
&& typeof $elem.data("events").click != "undefined")
{
// Find the one without a namespace defined.
for (var c in $elem.data("events").click)
{
if ($elem.data("events").click[c].namespace == "")
{
var handler = $elem.data("events").click[c].handler
$elem.data("taphold_click_handler", handler);
$elem.unbind("click", handler);
break;
}
}
}
// Otherwise, if a custom click handler was explicitly defined, then store it instead.
else if (typeof event.data != "undefined"
&& event.data != null
&& typeof event.data.clickHandler == "function")
{
$elem.data("taphold_click_handler", event.data.clickHandler);
}
// Reset the flags
$elem.data("taphold_triggered", false); // If a hold was triggered
$elem.data("taphold_clicked", false); // If a click was triggered
$elem.data("taphold_cancelled", false); // If event has been cancelled.
// Set the timer for the hold event.
$elem.data("taphold_timer",
setTimeout(function()
{
// If event hasn't been cancelled/clicked already, then go ahead and trigger the hold.
if (!$elem.data("taphold_cancelled")
&& !$elem.data("taphold_clicked"))
{
// Trigger the hold event, and set the flag to say it's been triggered.
$elem.trigger(jQuery.extend(event, jQuery.Event("taphold")));
$elem.data("taphold_triggered", true);
}
}, 1000));
}
// When user ends a tap or click, decide what we should do.
function stopHandler(event)
{
var $elem = jQuery(this);
// If taphold has been cancelled, then we're done.
if ($elem.data("taphold_cancelled")) { return; }
// Clear the hold timer. If it hasn't already triggered, then it's too late anyway.
clearTimeout($elem.data("taphold_timer"));
// If hold wasn't triggered and not already clicked, then was a click event.
if (!$elem.data("taphold_triggered")
&& !$elem.data("taphold_clicked"))
{
// If click handler, trigger it.
if (typeof $elem.data("taphold_click_handler") == "function")
{
$elem.data("taphold_click_handler")(jQuery.extend(event, jQuery.Event("click")));
}
// Set flag to say we've triggered the click event.
$elem.data("taphold_clicked", true);
}
}
// If a user prematurely leaves the boundary of the object we're working on.
function leaveHandler(event)
{
// Cancel the event.
$(this).data("taphold_cancelled", true);
}
var taphold = $.event.special.taphold =
{
setup: function(data)
{
$(this).bind("touchstart mousedown", data, startHandler)
.bind("touchend mouseup", stopHandler)
.bind("touchmove mouseleave", leaveHandler);
},
teardown: function(namespaces)
{
$(this).unbind("touchstart mousedown", startHandler)
.unbind("touchend mouseup", stopHandler)
.unbind("touchmove mouseleave", leaveHandler);
}
};
})(jQuery);

BIN
images/layers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

BIN
images/marker-blue_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
images/marker-green_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
images/marker-icon_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

50
main.js
View File

@ -1,25 +1,32 @@
// ==UserScript==
// @id ingress-intel-total-conversion@breunigs
// @name intel map total conversion
// @version 0.6-@@BUILDDATE@@
// @version 0.7.7-@@BUILDDATE@@
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js
// @description total conversion for the ingress intel map.
// @include http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
// REPLACE ORIG SITE ///////////////////////////////////////////////////
if(document.getElementsByTagName('html')[0].getAttribute('itemscope') != null)
throw('Ingress Intel Website is down, not a userscript issue.');
window.iitcBuildDate = '@@BUILDDATE@@';
// disable vanilla JS
window.onload = function() {};
if(window.location.protocol !== 'https:') {
var redir = window.location.href.replace(/^http:/, 'https:');
window.location = redir;
throw('Need to load HTTPS version.');
}
// rescue user data from original page
var scr = document.getElementsByTagName('script');
for(var x in scr) {
@ -35,8 +42,8 @@ if(!d) {
// page doesnt have a script tag with player information.
if(document.getElementById('header_email')) {
// however, we are logged in.
setTimeout('location.reload();', 10*1000);
throw('Page doesnt have player data, but you are logged in. Reloading in 10s.');
setTimeout('location.reload();', 3*1000);
throw('Page doesnt have player data, but you are logged in. Reloading in 3s.');
}
// FIXME: handle nia takedown in progress
throw('Couldnt retrieve player data. Are you logged in?');
@ -51,32 +58,18 @@ for(var i = 0; i < d.length; i++) {
// player information is now available in a hash like this:
// window.PLAYER = {"ap": "123", "energy": 123, "available_invites": 123, "nickname": "somenick", "team": "ALIENS||RESISTANCE"};
var ir = window.internalResources || [];
var mainstyle = 'http://breunigs.github.com/ingress-intel-total-conversion/style.css?@@BUILDDATE@@';
var smartphone = 'http://breunigs.github.com/ingress-intel-total-conversion/mobile/smartphone.css?@@BUILDDATE@@';
var leaflet = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.css';
var coda = 'http://fonts.googleapis.com/css?family=Coda';
// remove complete page. We only wanted the user-data and the pages
// security context so we can access the API easily. Setup as much as
// possible without requiring scripts.
document.getElementsByTagName('head')[0].innerHTML = ''
//~ + '<link rel="stylesheet" type="text/css" href="http://0.0.0.0:8000/style.css"/>'
+ '<title>Ingress Intel Map</title>'
+ (ir.indexOf('mainstyle') === -1
? '<link rel="stylesheet" type="text/css" href="'+mainstyle+'"/>'
: '')
+ (ir.indexOf('leafletcss') === -1
? '<link rel="stylesheet" type="text/css" href="'+leaflet+'"/>'
: '')
+ '<style>@@INCLUDESTRING:style.css@@</style>'
+ '<style>@@INCLUDESTRING:external/leaflet.css@@</style>'
// this navigator check is also used in code/smartphone.js
+ (ir.indexOf('smartphonecss') === -1 && navigator.userAgent.match(/Android.*Mobile/)
? '<link rel="stylesheet" type="text/css" href="'+smartphone+'"/>'
+ (navigator.userAgent.match(/Android.*Mobile/)
? '<style>@@INCLUDESTRING:mobile/smartphone.css@@</style>'
: '')
+ (ir.indexOf('codafont') === -1
? '<link rel="stylesheet" type="text/css" href="'+coda+'"/>'
: '');
+ '<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Coda"/>';
document.getElementsByTagName('body')[0].innerHTML = ''
+ '<div id="map">Loading, please wait</div>'
@ -156,9 +149,13 @@ window.MAX_DRAWN_LINKS = 400;
window.MAX_DRAWN_FIELDS = 200;
// Minimum zoom level resonator will display
window.RESONATOR_DISPLAY_ZOOM_LEVEL = 17;
// Minimum area to zoom ratio that field MU's will display
window.FIELD_MU_DISPLAY_AREA_ZOOM_RATIO = 0.001;
// Point tolerance for displaying MU's
window.FIELD_MU_DISPLAY_POINT_TOLERANCE = 60
window.COLOR_SELECTED_PORTAL = '#f00';
window.COLORS = ['#FFCE00', '#0088FF', '#03FE03']; // none, res, enl
window.COLORS = ['#FFCE00', '#0088FF', '#03DC03']; // none, res, enl
window.COLORS_LVL = ['#000', '#FECE5A', '#FFA630', '#FF7315', '#E40000', '#FD2992', '#EB26CD', '#C124E0', '#9627F4'];
window.COLORS_MOD = {VERY_RARE: '#F78AF6', RARE: '#AD8AFF', COMMON: '#84FBBD'};
@ -179,7 +176,7 @@ window.RANGE_INDICATOR_COLOR = 'red'
window.PORTAL_RADIUS_ENLARGE_MOBILE = 5;
window.DEFAULT_PORTAL_IMG = 'http://commondatastorage.googleapis.com/ingress/img/default-portal-image.png';
window.DEFAULT_PORTAL_IMG = 'https://commondatastorage.googleapis.com/ingress/img/default-portal-image.png';
window.NOMINATIM = 'http://nominatim.openstreetmap.org/search?format=json&limit=1&q=';
// INGRESS CONSTANTS /////////////////////////////////////////////////
@ -195,6 +192,9 @@ window.DESTROY_FIELD = 750; //AP for destroying field
window.CAPTURE_PORTAL = 500; //AP for capturing a portal
window.DEPLOY_RESONATOR = 125; //AP for deploying a resonator
window.COMPLETION_BONUS = 250; //AP for deploying all resonators on portal
window.UPGRADE_ANOTHERS_RESONATOR = 65; //AP for upgrading another's resonator
window.MAX_PORTAL_LEVEL = 8;
window.MAX_RESO_PER_PLAYER = [0, 8, 4, 4, 4, 2, 2, 1, 1];
// OTHER MORE-OR-LESS CONSTANTS //////////////////////////////////////
window.TEAM_NONE = 0;

9
mobile/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.classpath
.project
.settings/
assets/
bin/
gen/
libs/
proguard-project.txt
local.properties

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cradle.iitc_mobile"
android:versionCode="1"
android:versionName="0.1.5" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="10" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="@drawable/ingress_iitc"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.cradle.iitc_mobile.IITC_Mobile"
android:theme="@android:style/Theme.NoTitleBar"
android:label="@string/app_name"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="www.ingress.com" android:scheme="https" android:pathPrefix="/intel"></data>
</intent-filter>
</activity>
</application>
</manifest>

24
mobile/HACKING.md Normal file
View File

@ -0,0 +1,24 @@
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...
Debugging
---------
If you want to debug the APK, I suggest [reading up on Googles documentation](https://developer.android.com/index.html).
Debugging IITC(M) **after** it has booted is relatively easy: you can switch to the “debug” tab, which is a low end developer console. It renders all calls to `console.*`, so you can use it just like you expect. It may be easier to develop in a desktop browser. Set it up like explained [in the normal hacking guide](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md), but fake your user agent or modify the detection in `code/smartphone.js` and `main.js`. The device ID is printed to the debug console on IITC boot.
Debugging IITC(M) **before** it has booted requires the Android Developer Tools. Connecting your device and running `adb logcat` should print the debug log to your computer until the low-end dev console mentioned above is available.
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`

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>AppTemplate</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jakbox.ingressTC"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:hardwareAccelerated="false"
android:largeHeap="false"
android:debuggable="true">
<activity
android:label="@string/app_name"
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

File diff suppressed because one or more lines are too long

View File

@ -1,6 +0,0 @@
/** Automatically generated file. DO NOT MODIFY */
package com.jakbox.ingressTC;
public final class BuildConfig {
public final static boolean DEBUG = true;
}

View File

@ -1,27 +0,0 @@
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.jakbox.ingressTC;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class raw {
public static final int loader=0x7f040000;
}
public static final class string {
public static final int app_name=0x7f050000;
public static final int first_page=0x7f050001;
public static final int jakboxBootLoader=0x7f050002;
}
}

View File

@ -1,40 +0,0 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/first_page" />
</LinearLayout>

View File

@ -1,13 +0,0 @@
function rebuildUI() {
if(window.UIDone == true) { return; }
if(!document.getElementById("map_canvas")) {
setTimeout(rebuildUI, 50);
return;
} else {
window.UIDone = true;
}
window.console.log(window.deviceID);
window.loadJS("http://mathphys.fsk.uni-heidelberg.de:8000/test.js")
//window.Android.TCReady();
}
rebuildUI();

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Ingress Intel Total Conversion</string>
<string name="first_page">Connecting to Ingress Intel Map ... </string>
<string name="jakboxBootLoader">
bootLoader = function() {
if(document.getElementById(\"map_canvas\")) {
window.stop();
document.body.innerHTML = "";
} else {
setTimeout(bootLoader, 50);
}
};
setTimeout(bootLoader, 5);
</string>
</resources>

View File

@ -1,149 +0,0 @@
/********************************************************************************
Ingress Intel Total Converion - Mobile & Tablet
Android WebView wrapper/loader for iitc (ingress intel total conversion)
iitc source @ https://github.com/breunigs/ingress-intel-total-conversion
Original Author: Jason Grima - jason@jakbox.net
*********************************************************************************/
package com.jakbox.ingressTC;
import android.app.*;
import android.os.*;
import android.content.Context;
import android.view.*;
import android.widget.*;
import android.webkit.*;
import android.provider.Settings.Secure;
import android.util.Log;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.lang.StringBuffer;
public class MainActivity extends Activity
{
WebView web; // webview to hold the ingress site (and login etc)
WebView splash; // splash screen, just a bit of pretty
MyChrome chrome; // for logging, progress, etc
MyClient client; // for controlling the webview's
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE); // hide the app title bar, Xeen requested
super.onCreate(savedInstanceState);
// little animated title/splash page for IngressIntelTC
// separate webview so we can be loading stuff in the background
JSInterface js = new JSInterface(this, this); //js.setActivity(this);
this.splash = new WebView(this);
splash.getSettings().setJavaScriptEnabled(true); // because we use JS to trigger the CSS animation
splash.getSettings().setAllowFileAccess(true); // because the splash page is store in the apk
splash.getSettings().setAllowFileAccessFromFileURLs(true); // in case we add more scripts/images to the splash page
splash.loadUrl("file:///android_asset/js/ingressSplash.html"); // there is is!
splash.addJavascriptInterface(js, "android");
setContentView(splash);
// righto, load the the website (ingress.com/intel) with a bunch of permissions that may/or not be needed
this.web = new WebView(this);
this.client = new MyClient();
web.setWebViewClient(this.client);
web.getSettings().setJavaScriptEnabled(true);
web.getSettings().setGeolocationEnabled(true);
web.getSettings().setAllowFileAccess(true);
web.getSettings().setAllowFileAccessFromFileURLs(true);
web.getSettings().setDatabaseEnabled(true);
web.getSettings().setDomStorageEnabled(true);
web.getSettings().setGeolocationEnabled(true);
this.chrome = new MyChrome(); // the chrome let's us get console and progress feedback from the page
web.setWebChromeClient(this.chrome);
web.addJavascriptInterface(js, "android"); // ready to go
}
public void onRestoreInstanceState(Bundle state) {
this.splash.restoreState(state);
this.web.restoreState(state);
}
public void onSaveInstanceState(Bundle state) {
this.splash.saveState(state);
this.web.saveState(state);
}
public void showWeb() { setContentView(web); splash.destroy(); Log.d("com.jakbox.ingressTC", "Flipping to web/ingress view"); }
public void showSplash() { setContentView(splash); web.destroy(); Log.d("com.jakbox.ingressTC", "Flipping to splash/loading"); }
final class JSInterface {
Context context; MainActivity act;
public JSInterface (Context c, MainActivity a) { this.context = c; this.act = a; }
public void setActivity(MainActivity a) { this.act = a; }
// the @JavascriptInterface is needed for 4.2 devices to access this
@JavascriptInterface
public void pageReady(int ready) {
if(ready != 0) {
// the loader reports we're good to go, switch to ingress webview
this.act.runOnUiThread(new Runnable() {
public void run() { showWeb(); }
});
} else {
// the loader reports we're not ready, switch to splash page
this.act.runOnUiThread(new Runnable() {
public void run() { showSplash(); }
});
}
}
@JavascriptInterface
public void loadBehind(final String url) {
this.act.runOnUiThread(new Runnable() {
public void run() {
Log.d("com.jakbox.ingressTC", "Loading (Ingress?) website in back webview");
web.loadUrl(url); /* push the url to the webview */ }
});
}
}
private class MyClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView web, String Url) {
return false;
}
@Override
public void onPageFinished(WebView web, String Url) {
// here we conditionally load some external javascript
Log.d("com.jakbox.ingressTC", "Page loading, injecting IITC JS");
if(web.getUrl().contains("ingress.com/intel")) {
// if NOT ingress, we may have been redirected to the google auth/login page
web.loadUrl("javascript: window.stop(); ");
// first stop the page from loading too much
web.loadUrl("javascript: window.deviceID='"+Secure.getString(getContentResolver(), Secure.ANDROID_ID)+"'; ");
// create a JS loader on the page
web.loadUrl("javascript: if(!window.loadJS) { window.loadJS = function(scr) { var s = document.createElement('script'); s.src = scr+(scr.indexOf('file:///')==-1?'?ts="+System.currentTimeMillis()+"':''); s.type = 'text/javascript'; s.async = true; var st = document.getElementsByTagName('script')[0]; st.parentNode.insertBefore(s, st); } };");
// actually hijack the page... if it's the right page... let the loader deal with that
// should pull this loader into a local resource, maybe...
// running it from the web for now ...
web.loadUrl("javascript: if(!window.hijacked) { window.loadJS('http://mathphys.fsk.uni-heidelberg.de:8000/bootstrap.js'); window.hijacked = true;}");
}
}
}
private class MyChrome extends WebChromeClient {
@Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
// Always grant permission since the app itself requires location
// permission and the user has therefore already granted it
callback.invoke(origin, true, false);
}
public boolean onConsoleMessage(ConsoleMessage cm) {
// log window.console stuff into the syslog for ADB output
Log.d("com.jakbox.ingressTC", cm.message() + " -- "
+ (cm.sourceId()==null?"main document":cm.sourceId())
+ ":Line " + cm.lineNumber() );
return true;
}
}
}

View File

@ -1,75 +1,6 @@
IITC Mobile (IITCM)
===================
ingress intel total conversion (IITC)
=====================================
```
# # ####### ####### ###### ####### # ###### # #
## # # # # # # # # # # # # #
# # # # # # # # # # # # # # #
# # # # # # ###### ##### # # # # #
# # # # # # # # # ####### # # #
# ## # # # # # # # # # # #
# # ####### # # # ####### # # ###### #
```
# [User documentation moved to the wiki. Please see there!](https://github.com/breunigs/ingress-intel-total-conversion/wiki/IITC-Mobile)
- This is **alpha quality** software
- There are more bugs than in all Indiana Jones titles combined
- It will kill your data plan. Caching has been explicitly disabled for testing, so every restart nets you ~500 KiB of code. Since it uses raster maps instead of vector ones, the transfer volume is larger than for Google Maps, too.
### read this guide thoroughly.
How to report bugs
------------------
Be sure to [read the normal guide on how to report bugs](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#how-do-i-report-bugs). In addition, provide these details:
- Android version
- device used (exact name)
- screen size in pixels
**I will close all bug reports without this information without comment.** Gather all information, then make a new issue. It seems you cant reopen a ticket if I close it, so this is necessary unfortunately.
Can I share this?
-----------------
You may, but only with technically skilled friends. If you do please point them to this page only. Reading these guides ensures only high quality bug reports are made which speeds up development. This is in everyones interest, as the time spent on working on bogus reports is not spent on improvements.
Once the app is ready for a broader audience, this will be made easier. For now it should only be used by developers who can help or send in improvements.
How do I…?
----------
- **Login:** on tablets, this should just work. On smartphones youll see a black screen with some blue lines. This is the normal Ingress login page and you need to scroll to see the login button.
- **Clear Data/Cache:** Open app launcher and find the IITCM icon. Press-and-hold and then drag it to “App Info” on the top right of the screen. Theres a “clear data” and “clear cache” button.
- **Logout:** Not possible from within the app. See *Clear Data/Cache* and use “clear data”.
- **Reload:** Not possible from within the app. Instead, open your recent applications and force close IITCM. It should reload after restarting.
- **“Your account has not been enabled to play Ingress“**: See *Reload*.
- **Install:** You can find this out yourself. If you dont know how, then please wait for a more polished version of IITCM.
- **Update:** You can check if theres a new APK build by looking at the [apk_version](https://github.com/breunigs/ingress-intel-total-conversion/raw/gh-pages/mobile/apk_version file)[.](https://mathphys.fsk.uni-heidelberg.de/~stefan/test/IngressIntelTC.apk) It contains the date of the last update. The app uses the latest IITC hourly build automatically. You may need to clear your cache to force a reload, see *Clear Data/Cache*. Also note that the dot after “apk_version” is the download link to the APK.
How does this basically work?
-----------------------------
The Android App uses a WebView to render the normal web page. Some code is required to make this work like one would expect, but in a nutshell this is a webbrowser without URL bar. On page load, the app injects `bootstrap.js` which is a specialized loader. While it has some additional checks to make the boot process nicer on slower mobile phones but is mainly required to load vanilla IITC. Its not possible to load IITC directly due to contraints defined by the injection method. After IITC is injected, it works the same way as on desktop browsers. Tablets are served the desktop version and apart from larger portals there is no change to the GUI. Smartphones execute the code in `code/smartphone.js` and load `smartphone.css`. This is required because their display is too small for the desktop version.
Debugging
---------
If you want to debug the APK, I suggest [reading up on Googles documentation](https://developer.android.com/index.html).
Debugging IITC(M) **after** it has booted is relatively easy: you can switch to the “debug” tab, which is a low end developer console. It renders all calls to `console.*`, so you can use it just like you expect. It may be easier to develop in a desktop browser. Set it up like explained [in the normal hacking guide](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md), but fake your user agent or modify the detection in `code/smartphone.js` and `main.js`. You dont need to rebuild the APK to point it to your `iitc-test.user.js` file. Instead, modify `mobile/bootstrap.js` and add yourself with device ID and URL, then [send in a pull request](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#sending-patches). The device ID is printed to the debug console on IITC boot.
Debugging IITC(M) **before** it has booted requires the Android Developer Tools. Connecting your device and running `adb logcat` should print the debug log to your computer until the low-end dev console mentioned above is available. You may need to root your device.
Building the APK
----------------
No idea. Please write docs and send patches.
Download APK
------------
Well, glad you read to the end. Enjoy and send patches, bug reports, postcards and love. (If you are wondering where the download link is, Im wondering if you have actually read the page.)
### [For developer docs, please see HACKING.md](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/mobile/HACKING.md)

View File

@ -1 +0,0 @@
0.1-2013-02-23

59
mobile/bootstrap.js vendored
View File

@ -1,59 +0,0 @@
var rebuidAttemptCount=0;
var iitcReadyCount=0;
var iitcURL;
function rebuildUI() {
// this is sub-optimal, but we need a way to know if we're on a login page, or the real page.
if(document.getElementById('dashboard_container')) {
var dash = document.getElementById('dashboard_container');
// dashboard is there, not conclusive
if(dash.childNodes[0].innerHTML == 'Welcome to Ingress') {
// not really 'ready', but the user needs the page for login purposes
android.pageReady(1);
return;
} else if(!document.getElementById('map_canvas')) {
// not loaded yet? we'll wait
if(rebuidAttemptCount < 10) { // wait up to 5 seconds, then assume failure and show the page
setTimeout(rebuildUI, 500);
rebuidAttemptCount++;
if(window.console) { console.log('Looking for Ingress Intel UI elements: ' + rebuidAttemptCount); }
return;
} else {
if(window.console) { console.log('Something is wrong, flipping views'); }
android.pageReady(1);
return;
}
} else {
window.UIDone = true;
}
window.console.log(window.deviceID);
window.console.log(navigator.userAgent);
switch(window.deviceID) {
case '41ddb619ea1fe75a': // blakjakau - TABLET
iitcURL = 'http://mathphys.fsk.uni-heidelberg.de:8000/test.js';
break;
case 'f30c2cce86c1c7': // breunigs
iitcURL = 'http://mathphys.fsk.uni-heidelberg.de:8000/test.js';
break;
default:
iitcURL = 'http://mathphys.fsk.uni-heidelberg.de:8000/test.js';
}
window.loadJS(iitcURL); // load iitc and let it do its thing.
window.iitcReadyTimer = setInterval(function() {
if(window.iitcLoaded == true || iitcReadyCount > 10) {
//wait up to 10 seconds from calling iitc to fliping the webviews
//if for some reason iitc isn't loading, the user will just get the vanila ingres.com/intel experience
try {
android.pageReady(1); // tell the app to flip the webviews
clearInterval(window.iitcReadyTimer); // and we're done.
} catch(e) { if(window.console) { console.log(e.message); } }
}
iitcReadyCount++;
}, 500);
}
}
rebuildUI();

92
mobile/build.xml Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="IITC_Mobile" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

6
mobile/lint.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="SetJavaScriptEnabled">
<ignore path="src/com/cradle/ittc_mobile/iitc_webview.java" />
</issue>
</lint>

View File

@ -3,9 +3,12 @@
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-10
target=android-17

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.cradle.iitc_mobile.IITC_WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>

22
mobile/res/menu/main.xml Normal file
View File

@ -0,0 +1,22 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/reload_button"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/reload"></item>
<item android:id="@+id/version_num"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/version"></item>
<item android:id="@+id/cache_clear"
android:orderInCategory="90"
android:showAsAction="never"
android:title="@string/cache_clear"></item>
<item android:id="@+id/locate"
android:orderInCategory="80"
android:showAsAction="never"
android:title="@string/locate"></item>
</menu>

View File

@ -0,0 +1,8 @@
<resources>
<!--
Customize dimensions originally defined in res/values/dimens.xml (such as
screen margins) for sw600dp devices (e.g. 7" tablets) here.
-->
</resources>

View File

@ -0,0 +1,9 @@
<resources>
<!--
Customize dimensions originally defined in res/values/dimens.xml (such as
screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
-->
<dimen name="activity_horizontal_margin">128dp</dimen>
</resources>

View File

@ -0,0 +1,11 @@
<resources>
<!--
Base application theme for API 11+. This theme completely replaces
AppBaseTheme from res/values/styles.xml on API 11+ devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Holo.Light">
<!-- API 11 theme customizations can go here. -->
</style>
</resources>

View File

@ -0,0 +1,12 @@
<resources>
<!--
Base application theme for API 14+. This theme completely replaces
AppBaseTheme from BOTH res/values/styles.xml and
res/values-v11/styles.xml on API 14+ devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- API 14 theme customizations can go here. -->
</style>
</resources>

View File

@ -0,0 +1,7 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">IITC mobile</string>
<string name="action_settings">Settings</string>
<string name="reload">Reload IITC</string>
<string name="version">Print Version</string>
<string name="cache_clear">Clear Cache</string>
<string name="locate">Get Location</string>
</resources>

View File

@ -0,0 +1,20 @@
<resources>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
</resources>

View File

@ -15,11 +15,12 @@ body {
#chatcontrols {
height: 38px;
width: 100%;
}
/* hide shrink button */
#chatcontrols a:first-child {
display: none;
display: none;
}
#chatcontrols a {
@ -29,6 +30,9 @@ body {
vertical-align: middle;
line-height: 36px;
text-decoration: none;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
#chat {

View File

@ -0,0 +1,139 @@
package com.cradle.iitc_mobile;
import com.cradle.iitc_mobile.R;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Toast;
public class IITC_Mobile extends Activity {
private IITC_WebView iitc_view;
private boolean back_button_pressed = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// we do not want to reload our page every time we switch orientations...
// so restore state if activity was already created
if(savedInstanceState != null) {
((IITC_WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
else {
// load new iitc web view with ingress intel page
iitc_view= (IITC_WebView) findViewById(R.id.webview);
Intent intent = getIntent();
String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
Uri uri = intent.getData();
String url = uri.toString();
Log.d("Intent received", "url: " + url);
if (url.contains("ingress.com")) {
Log.d("Intent received", "loading url...");
iitc_view.loadUrl(url);
}
}
else {
Log.d("No Intent call", "loading https://www.ingress.com/intel");
iitc_view.loadUrl("https://www.ingress.com/intel");
}
// listen to touches (think we need this)
iitc_view.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
back_button_pressed = false;
// return false to indicate, that we don't consumed this event. this leads
// to the execution of our touch event
return false;
}
});
}
}
// save instance state to avoid reloading on orientation change
@Override
protected void onSaveInstanceState(Bundle outState) {
iitc_view.saveState(outState);
}
// we want a self defined behavior on resume
@Override
protected void onResume() {
super.onResume();
this.back_button_pressed = false;
}
// we want a self defined behavior for the back button
@Override
public void onBackPressed() {
if (this.back_button_pressed) {
super.onBackPressed();
return;
}
iitc_view.loadUrl("javascript: window.goBack();");
this.back_button_pressed = true;
Toast.makeText(this, "Press twice to exit", Toast.LENGTH_SHORT).show();
// reset back button after 0.5 seconds
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
back_button_pressed=false;
}
}, 500);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.reload_button:
iitc_view.reload();
return true;
// print version number
case R.id.version_num:
PackageInfo pinfo;
try {
pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
Toast.makeText(this, "Build version: " + pinfo.versionName, Toast.LENGTH_SHORT).show();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return true;
// clear cache
case R.id.cache_clear:
iitc_view.clearHistory();
iitc_view.clearFormData();
iitc_view.clearCache(true);
return true;
// get the users current location and focus it on map
case R.id.locate:
iitc_view.loadUrl("javascript: window.map.locate({setView : true, maxZoom: 13});");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View File

@ -0,0 +1,58 @@
package com.cradle.iitc_mobile;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.GeolocationPermissions;
@SuppressLint("SetJavaScriptEnabled")
public class IITC_WebView extends WebView {
private WebSettings settings;
private IITC_WebViewClient webclient;
// init web view
private void iitc_init() {
settings = this.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
settings.setGeolocationEnabled(true);
// our webchromeclient should share geolocation with the iitc script
// allow access by default
this.setWebChromeClient(new WebChromeClient() {
@Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
});
webclient = new IITC_WebViewClient();
this.setWebViewClient(webclient);
}
// constructors -------------------------------------------------
public IITC_WebView(Context context) {
super(context);
iitc_init();
}
public IITC_WebView(Context context, AttributeSet attrs) {
super(context, attrs);
iitc_init();
}
public IITC_WebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
iitc_init();
}
//----------------------------------------------------------------
}

View File

@ -0,0 +1,37 @@
package com.cradle.iitc_mobile;
import android.net.http.SslError;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class IITC_WebViewClient extends WebViewClient {
// enable https
@Override
public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
handler.proceed() ;
};
// injecting IITC when page is loaded
@Override
public void onPageFinished(WebView web, String Url) {
Log.d("loading finish", web.getUrl());
if (web.getUrl().contains("ingress.com/intel") && !web.getUrl().contains("accounts")) {
// first check for cookies, than inject javascript
// this enables the user to login if necessary
CookieManager cm = CookieManager.getInstance();
final String cookie = cm.getCookie("https://www.ingress.com/intel");
if(cookie != null) {
web.loadUrl("javascript: (function() { "
+ "var script=document.createElement('script');"
+ "script.type='text/javascript';"
+ "script.src='https://iitcserv.appspot.com/iitc-nightly/iitc-nightly-latest.user.js';"
+ "document.getElementsByTagName('head').item(0).appendChild(script);"
+ "})()");
}
}
}
}

View File

@ -1,10 +0,0 @@
#!/bin/sh
./build.py
cp iitc-debug.user.js dist/total-conversion-build.user.js
cp style.css dist/style.css
cp external/* dist/
rm -r dist/images/
cp -r images/ dist/images/
echo 'Change path of style.css to dist/style.css'

View File

@ -1,27 +1,9 @@
Plugins
=======
USER DOCUMENTATION MOVED!
=========================
Install
-------
Plugins are installed the same way the total conversion script is. Please see there for specific instructions for your browser.
Available Plugins
-----------------
- [**Compute AP Stats**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/compute-ap-stats.user.js) Shows the potential AP an agent could obtain by destroying and rebuilding all the portals in the current zoom area.
- [**Draw Tools**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/draw-tools.user.js) allows to draw circles and lines on the map to aid you with planning your next big field. [View screenshot](http://breunigs.github.com/ingress-intel-total-conversion/screenshots/plugin_draw_tools.png)
- [**Guess Player Level**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/guess-player-levels.user.js) looks for the highest placed resonator per player in the current view to guess the player level.
- [**Highlight Weakened Portals**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-portal-weakness.user.js) fill portals with red to indicate portal's state of disrepair. The brighter the color the more attention needed (recharge, shields, resonators). A dashed portal means a resonator is missing.
- [**Player Tracker**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/player-tracker.user.js) Draws trails for user actions in the last hour. At the last known location theres a tooltip that shows the data in a table. [View screenshot](http://breunigs.github.com/ingress-intel-total-conversion/screenshots/plugin_player_tracker.png).
- [**Render Limit Increase**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/render-limit-increase.user.js) increases render limits. Good for high density areas (e.g. London, UK) and faster PCs.
- [**Resonator Display Zoom Level Decrease**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/resonator-display-zoom-level-decrease.user.js) Resonator start displaying earlier.
- [**Show Portal Address**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-address.user.js) Shows portal address in the side panel.
### available only with the development version
[Read HACKING.md file](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#hacking) to learn how to build the development version yourself. If **and only if** [you have read how to report bugs](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/HACKING.md#how-do-i-report-bugs), you may beta test the [nightly](https://www.dropbox.com/sh/lt9p0s40kt3cs6m/3xzpyiVBnF) version.
[The plugins list has been moved to the wiki. Please see there!](https://github.com/breunigs/ingress-intel-total-conversion/wiki/Plugins)
What follows is developer documentation only.

View File

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-compute-ap-stats@Hollow011
// @name iitc: Compute AP statistics
// @version 0.2
// @version 0.3
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/compute-ap-stats.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/compute-ap-stats.user.js
// @description Tries to determine overal AP stats for the current zoom
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {
@ -44,6 +44,21 @@ window.plugin.compAPStats.onPositionMove = function() {
+ '</table>');
}
window.plugin.compAPStats.missingResonatorAP = function(portal) {
var resAP = 0;
var missing_resonators = 0;
$.each(portal.resonatorArray.resonators, function(ind, reso) {
if(reso === null) {
missing_resonators++;
}
});
if(missing_resonators > 0) {
resAP = window.DEPLOY_RESONATOR * missing_resonators;
resAP += window.COMPLETION_BONUS;
}
return(resAP);
};
window.plugin.compAPStats.compAPStats = function() {
var totalAP_RES = 0;
@ -78,6 +93,9 @@ window.plugin.compAPStats.compAPStats = function() {
if(!field) return true;
allEnlFields.push(field);
});
totalAP_ENL += window.plugin.compAPStats.missingResonatorAP(d);
}
else if (getTeam(d) === TEAM_RES) {
totalAP_ENL += portalSum;
@ -91,6 +109,9 @@ window.plugin.compAPStats.compAPStats = function() {
if(!field) return true;
allResFields.push(field);
});
totalAP_RES += window.plugin.compAPStats.missingResonatorAP(d);
} else {
// it's a neutral portal, potential for both teams. by definition no fields or edges
totalAP_ENL += portalSum;

View File

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-draw-tools@breunigs
// @name iitc: draw tools
// @version 0.2
// @version 0.2.2
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/draw-tools.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/draw-tools.user.js
// @description Allows you to draw things into the current map so you may plan your next move
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {
@ -29,12 +29,15 @@ var DRAW_TOOLS_SHAPE_OPTIONS = {
window.plugin.drawTools = function() {};
window.plugin.drawTools.loadExternals = function() {
var base = 'http://breunigs.github.com/ingress-intel-total-conversion/dist';
var base = 'https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist';
//~ var base = 'http://0.0.0.0:8000/dist';
$('head').append('<link rel="stylesheet" href="'+base+'/leaflet.draw.0.1.6.css" />');
load(base+'/leaflet.draw.0.1.6.js').thenRun(window.plugin.drawTools.boot);
// overwrite default Leaflet Marker icon.
L.Icon.Default.imagePath = base + '/images';
// FIXME: this is currently manually included from
// external/leaflet.draw.0.1.6.css. It should either be loaded remotely
// automatically or the buildscript should include it here, similar to
// how it works for the main script.
$('head').append('<style>.leaflet-container .leaflet-control-draw {margin-left: 13px;margin-top: 12px;}.leaflet-control-draw a {background-position: 50% 50%;background-repeat: no-repeat;display: block;width: 22px;height: 22px;}.leaflet-control-draw a:hover {background-color: #fff;}.leaflet-touch .leaflet-control-draw a {width: 27px;height: 27px;}.leaflet-control-draw-polyline {background-image: url(https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/images/draw-polyline.png);}.leaflet-control-draw-polygon {background-image: url(https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/images/draw-polygon.png);}.leaflet-control-draw-rectangle {background-image: url(https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/images/draw-rectangle.png);}.leaflet-control-draw-circle {background-image: url(https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/images/draw-circle.png);}.leaflet-control-draw-marker {background-image: url(https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/images/draw-marker-icon.png);}.leaflet-mouse-marker {background-color: #fff;cursor: crosshair;}.leaflet-draw-label {background-color: #fff;border: 1px solid #ccc;color: #222;font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;margin-left: 20px;margin-top: -21px;padding: 2px 4px;position: absolute;white-space: nowrap;z-index: 6;}.leaflet-error-draw-label {background-color: #F2DEDE;border-color: #E6B6BD;color: #B94A48;}.leaflet-draw-label-single {margin-top: -12px}.leaflet-draw-label-subtext {color: #999;}.leaflet-draw-guide-dash {font-size: 1%;opacity: 0.6;position: absolute;width: 5px;height: 5px;}.leaflet-flash-anim {-webkit-animation-duration: 0.66s; -moz-animation-duration: 0.66s; -o-animation-duration: 0.66s; animation-duration: 0.66s;-webkit-animation-fill-mode: both; -moz-animation-fill-mode: both; -o-animation-fill-mode: both; animation-fill-mode: both;-webkit-animation-name: leaflet-flash; -moz-animation-name: leaflet-flash; -o-animation-name: leaflet-flash; animation-name: leaflet-flash;}@-webkit-keyframes leaflet-flash {0%, 50%, 100% { opacity: 1; }25%, 75% { opacity: 0.3; }}@-moz-keyframes leaflet-flash {0%, 50%, 100% { opacity: 1; }25%, 75% { opacity: 0.3; }}@-o-keyframes leaflet-flash {0%, 50%, 100% { opacity: 1; }25%, 75% { opacity: 0.3; }}@keyframes leaflet-flash {0%, 50%, 100% { opacity: 1; }25%, 75% { opacity: 0; }}</style>');
}

View File

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-guess-player-levels@breunigs
// @name iitc: guess player level
// @version 0.2
// @version 0.3
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/guess-player-levels.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/guess-player-levels.user.js
// @description Tries to determine player levels from the data available in the current view
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {
@ -43,7 +43,7 @@ window.plugin.guessPlayerLevels.setLevelTitle = function(dom) {
var text;
if (nick in playersNamed) {
text = 'Min player level: ' + playersNamed[nick];
if(playersNamed[nick] === window.MAX_XM_PER_LEVEL - 1) text += ' (guessed)';
if(playersNamed[nick] < window.MAX_XM_PER_LEVEL.length - 1) text += ' (guessed)';
} else {
text = 'Min player level unknown';
}
@ -88,23 +88,34 @@ window.plugin.guessPlayerLevels.guess = function() {
});
var s = 'the players have at least the following level:\n\n';
s += 'Resistance:\t&nbsp;&nbsp;&nbsp;\tEnlightenment:\t\n';
s += 'Resistance:\t&nbsp;&nbsp;&nbsp;\tEnlightened:\t\n';
var namesR = plugin.guessPlayerLevels.sort(playersRes);
var namesE = plugin.guessPlayerLevels.sort(playersEnl);
var totallvlR = 0;
var totallvlE = 0;
var max = Math.max(namesR.length, namesE.length);
for(var i = 0; i < max; i++) {
var nickR = namesR[i];
var lvlR = playersRes[nickR];
var lineR = nickR ? nickR + ':\t' + lvlR : '\t';
if(!isNaN(parseInt(lvlR)))
totallvlR += parseInt(lvlR);
var nickE = namesE[i];
var lvlE = playersEnl[nickE];
var lineE = nickE ? nickE + ':\t' + lvlE : '\t';
if(!isNaN(parseInt(lvlE)))
totallvlE += parseInt(lvlE);
s += lineR + '\t\t' + lineE + '\n';
s += '\n'+lineR + '\t' + lineE + '\n';
}
s += '\nTotal level :\t'+totallvlR+'\tTotal level :\t'+totallvlE;
s += '\nTotal player:\t'+namesR.length+'\tTotal player:\t'+namesE.length;
var averageR = 0, averageE = 0;
if (namesR.length > 0) averageR = (totallvlR/namesR.length);
if (namesE.length > 0) averageE = (totallvlE/namesE.length);
s += '\nAverage level:\t'+averageR.toFixed(2)+'\tAverage level:\t'+averageE.toFixed(2);
s += '\n\nIf there are some unresolved names, simply try again.'
console.log(s);
alert(s);

137
plugins/max-links.user.js Normal file
View File

@ -0,0 +1,137 @@
// ==UserScript==
// @id max-links@boombuler
// @name iitc: Max-Links-Plugin
// @version 0.2
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/max-links.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/max-links.user.js
// @description Calculates how to link the portals to create the maximum number of fields.
// @include https://www.ingress.com/intel*
// @match https://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.maxLinks = function() {};
// const values
window.plugin.maxLinks.MAX_DRAWN_LINKS = 400;
window.plugin.maxLinks.MAX_DRAWN_LINKS_INCREASED_LIMIT = 1000;
window.plugin.maxLinks.STROKE_STYLE = {
color: '#FF0000',
opacity: 1,
weight:2,
clickable: false,
smoothFactor: 10
};
window.plugin.maxLinks._delaunayScriptLocation = 'https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/delaunay.js';
window.plugin.maxLinks.layer = null;
window.plugin.maxLinks._updating = false;
window.plugin.maxLinks._renderLimitReached = false;
window.plugin.maxLinks.updateLayer = function() {
if (window.plugin.maxLinks._updating ||
window.plugin.maxLinks.layer === null ||
!window.map.hasLayer(window.plugin.maxLinks.layer))
return;
window.plugin.maxLinks._updating = true;
window.plugin.maxLinks.layer.clearLayers();
var locations = [];
var minX = 0;
var minY = 0;
$.each(window.portals, function(guid, portal) {
var loc = portal.options.details.locationE6;
var nloc = { x: loc.lngE6, y: loc.latE6 };
if (nloc.x < minX)
minX = nloc.x;
if (nloc.y < minY)
minY = nloc.y;
locations.push(nloc);
});
$.each(locations, function(idx, nloc) {
nloc.x += Math.abs(minX);
nloc.y += Math.abs(minY);
});
var triangles = window.delaunay.triangulate(locations);
var drawnLinks = 0;
window.plugin.maxLinks._renderLimitReached = false;
var renderlimit = window.USE_INCREASED_RENDER_LIMIT ?
window.plugin.maxLinks.MAX_DRAWN_LINKS_INCREASED_LIMIT :
window.plugin.maxLinks.MAX_DRAWN_LINKS;
$.each(triangles, function(idx, triangle) {
if (drawnLinks <= renderlimit) {
triangle.draw(window.plugin.maxLinks.layer, minX, minY)
drawnLinks += 3;
} else {
window.plugin.maxLinks._renderLimitReached = true;
}
});
window.plugin.maxLinks._updating = false;
window.renderUpdateStatus();
}
window.plugin.maxLinks.setup = function() {
load(window.plugin.maxLinks._delaunayScriptLocation).thenRun(function() {
window.delaunay.Triangle.prototype.draw = function(layer, divX, divY) {
var drawLine = function(src, dest) {
var poly = L.polyline([[(src.y + divY)/1E6, (src.x + divX)/1E6], [(dest.y + divY)/1E6, (dest.x + divX)/1E6]], window.plugin.maxLinks.STROKE_STYLE);
poly.addTo(layer);
};
drawLine(this.a, this.b);
drawLine(this.b, this.c);
drawLine(this.c, this.a);
}
window.plugin.maxLinks.layer = L.layerGroup([]);
window.addHook('checkRenderLimit', function(e) {
if (window.map.hasLayer(window.plugin.maxLinks.layer) &&
window.plugin.maxLinks._renderLimitReached)
e.reached = true;
});
window.addHook('portalDataLoaded', function(e) {
if (window.map.hasLayer(window.plugin.maxLinks.layer))
window.plugin.maxLinks.updateLayer();
});
window.map.on('layeradd', function(e) {
if (e.layer === window.plugin.maxLinks.layer)
window.plugin.maxLinks.updateLayer();
});
window.map.on('zoomend moveend', window.plugin.maxLinks.updateLayer);
window.layerChooser.addOverlay(window.plugin.maxLinks.layer, 'Maximum Links');
});
}
var setup = window.plugin.maxLinks.setup;
// 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

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-player-tracker@breunigs
// @name iitc: player tracker
// @version 0.5
// @version 0.7
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/player-tracker.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/player-tracker.user.js
// @description draws trails for the path a user went onto the map. Only draws the last hour. Does not request chat data on its own, even if that would be useful.
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {
@ -80,7 +80,8 @@ window.plugin.playerTracker.processNewData = function(data) {
// field was originally created. Therefore its not clear which
// portal the player is at, so ignore it.
if(markup[1].plain.indexOf('destroyed the Link') !== -1
|| markup[1].plain.indexOf('destroyed a Control Field') !== -1) {
|| markup[1].plain.indexOf('destroyed a Control Field') !== -1
|| markup[1].plain.indexOf('Your Link') !== -1) {
skipThisMessage = true;
return false;
}
@ -163,13 +164,14 @@ window.plugin.playerTracker.processNewData = function(data) {
}
window.plugin.playerTracker.getLatLngFromEvent = function(ev) {
var lats = $.map(ev.latlngs, function(ll) { return [ll[0]] });
var lngs = $.map(ev.latlngs, function(ll) { return [ll[1]] });
var latmax = Math.max.apply(null, lats);
var latmin = Math.min.apply(null, lats);
var lngmax = Math.max.apply(null, lngs);
var lngmin = Math.min.apply(null, lngs);
return L.latLng((latmax + latmin) / 2, (lngmax + lngmin) / 2);
var lats = 0;
var lngs = 0;
$.each(ev.latlngs, function() {
lats += this[0];
lngs += this[1];
});
return L.latLng(lats / ev.latlngs.length, lngs / ev.latlngs.length);
}
window.plugin.playerTracker.ago = function(time, now) {
@ -203,9 +205,9 @@ window.plugin.playerTracker.drawData = function() {
var evtsLength = playerData.events.length;
var last = playerData.events[evtsLength-1];
var ago = plugin.playerTracker.ago;
var color = playerData.team === 'ALIENS' ? '#029C02' : '#00789C';
var cssClass = playerData.team === 'ALIENS' ? 'enl' : 'res';
var title =
'<span style="font-weight:bold; color:'+color+'">' + playerData.nick + '</span>\n'
'<span class="nickname '+ cssClass+'" style="font-weight:bold;">' + playerData.nick + '</span>\n'
+ ago(last.time, now) + ' minutes ago\n'
+ last.name;
// show previous data in tooltip

View File

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-render-limit-increase@jonatkins
// @name iitc: render limit increase
// @version 0.1
// @version 0.1.1
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/render-limit-increase.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/render-limit-increase.user.js
// @description Increase the render limits, so less likely to be hit in higher density areas
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {
@ -29,7 +29,7 @@ window.plugin.renderLimitIncrease.setHigherLimits = function() {
// thing like that, Leaflet doc isnt too specific). Setting it too low
// makes the missing data on move/zoom out more obvious. Setting it too
// high causes too many items to be drawn, making drag&drop sluggish.
// default for iitc is 0.3. a lower value reduces overdraw
// default for iitc is 0.3. a lower value reduces overdraw
window.VIEWPORT_PAD_RATIO = 0.1;
@ -39,7 +39,7 @@ window.plugin.renderLimitIncrease.setHigherLimits = function() {
window.MAX_DRAWN_PORTALS = 3000;
window.MAX_DRAWN_LINKS = 1000;
window.MAX_DRAWN_FIELDS = 500;
window.USE_INCREASED_RENDER_LIMIT = true; // Used for other plugins
};
var setup = window.plugin.renderLimitIncrease.setHigherLimits;

View File

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-reso-energy-pct-in-portal-detail@xelio
// @name iitc: reso energy pct in portal detail
// @version 0.1
// @version 0.1.1
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/reso-energy-pct-in-portal-detail.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/reso-energy-pct-in-portal-detail.user.js
// @description Show resonator energy percentage on resonator energy bar in portal detail panel.
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {

View File

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-resonator-display-zoom-level-decrease@xelio
// @name iitc: resonator display zoom level decrease
// @version 1.0
// @version 1.0.1
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/resonator-display-zoom-level-decrease.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/resonator-display-zoom-level-decrease.user.js
// @description Resonator start display earlier
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {

48
plugins/scale-bar.user.js Normal file
View File

@ -0,0 +1,48 @@
// ==UserScript==
// @id iitc-plugin-scale-bar@breunigs
// @name iitc: scale bar
// @version 0.1
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/scale-bar.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/scale-bar.user.js
// @description shows scale bar on the map
// @include https://www.ingress.com/intel*
// @match https://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.scaleBar = function() {};
window.plugin.scaleBar.setup = function() {
$('head').append('<style>.leaflet-control-scale { position: absolute; top: 2px; left: 40px; } </style>');
// 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}));
};
var setup = window.plugin.scaleBar.setup;
// 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

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-show-address@vita10gy
// @name iitc: show portal address in sidebar
// @version 0.2
// @version 0.2.1
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-address.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-address.user.js
// @description Portal address will show in the sidebar.
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {

View File

@ -1,13 +1,13 @@
// ==UserScript==
// @id iitc-plugin-show-portal-weakness@vita10gy
// @name iitc: show portal weakness
// @version 0.2
// @version 0.6
// @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-portal-weakness.user.js
// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-portal-weakness.user.js
// @description Uses the fill color of the portals to denote if the portal is weak (Needs recharging, missing a resonator, needs shields)
// @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel*
// @description Uses the fill color of the portals to denote if the portal is weak (Needs recharging, missing a resonator, needs shields) Red, needs energy and shields. Orange, only needs energy (either recharge or resonators). Yellow, only needs shields.
// @include https://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// ==/UserScript==
function wrapper() {
@ -21,60 +21,78 @@ if(typeof window.plugin !== 'function') window.plugin = function() {};
window.plugin.portalWeakness = function() {};
window.plugin.portalWeakness.portalAdded = function(data) {
var d = data.portal.options.details;
var portal_weakness = 0;
if(getTeam(d) != 0)
{
if(window.getTotalPortalEnergy(d)> 0 && window.getCurrentPortalEnergy(d) < window.getTotalPortalEnergy(d))
{
if(getTeam(d) !== 0) {
var only_shields = true;
var missing_shields = 0;
if(window.getTotalPortalEnergy(d) > 0 && window.getCurrentPortalEnergy(d) < window.getTotalPortalEnergy(d)) {
portal_weakness = 1 - (window.getCurrentPortalEnergy(d)/window.getTotalPortalEnergy(d));
only_shields = false;
}
//Ding the portal for every missing sheild.
$.each(d.portalV2.linkedModArray, function(ind, mod)
{
if(mod == null)
{
//Ding the portal for every missing sheild.
$.each(d.portalV2.linkedModArray, function(ind, mod) {
if(mod === null) {
missing_shields++;
portal_weakness += .03;
}
});
//Ding the portal for every missing resonator.
var resCount = 0;
$.each(d.resonatorArray.resonators, function(ind, reso)
{
if(reso == null) {
$.each(d.resonatorArray.resonators, function(ind, reso) {
if(reso === null) {
portal_weakness += .125;
}
else {
only_shields = false;
} else {
resCount++;
}
});
if(portal_weakness<0) {
if(portal_weakness < 0) {
portal_weakness = 0;
}
if(portal_weakness>1)
{
if(portal_weakness > 1) {
portal_weakness = 1;
}
if(portal_weakness>0)
{
var color = 'red';
var fill_opacity = Math.round((portal_weakness*.8 + .2)*100)/100;
var params = {fillColor: color, fillOpacity: fill_opacity, radius: data.portal.options.radius+1};
if(resCount<8)
{
}
if(portal_weakness > 0) {
var fill_opacity = portal_weakness*.7 + .3;
var color = 'orange';
if(only_shields) {
color = 'yellow';
//If only shields are missing, make portal yellow
// but fill more than usual since pale yellow is basically invisible
fill_opacity = missing_shields*.15 + .1;
} else if(missing_shields > 0) {
color = 'red';
}
fill_opacity = Math.round(fill_opacity*100)/100;
var params = {fillColor: color, fillOpacity: fill_opacity};
if(resCount < 8) {
// Hole per missing resonator
var dash = new Array(8-resCount + 1).join("1,4,") + "100,0"
params["dashArray"] = dash;
}
data.portal.setStyle(params);
} else {
data.portal.setStyle({color: COLORS[getTeam(data.portal.options.details)],
fillOpacity: 0.5,
dashArray: null});
}
}
}
window.plugin.portalWeakness.portalDataLoaded = function(data) {
$.each(data.portals, function(ind, portal) {
if(window.portals[portal[0]]) {
window.plugin.portalWeakness.portalAdded({portal: window.portals[portal[0]]});
}
});
}
var setup = function() {
window.addHook('portalAdded', window.plugin.portalWeakness.portalAdded);
window.addHook('portalDataLoaded', window.plugin.portalWeakness.portalDataLoaded);
window.COLOR_SELECTED_PORTAL = '#f0f';
}

BIN
screenshots/apk_qr_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

BIN
screenshots/mobile_chat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
screenshots/mobile_map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Some files were not shown because too many files have changed in this diff Show More