Merge remote-tracking branch 'upstream/gh-pages' into gh-pages

This commit is contained in:
Jon Atkins
2013-02-11 20:00:20 +00:00
18 changed files with 4172 additions and 149 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
iitc-debug.user.js

45
NEWS.md
View File

@ -1,5 +1,46 @@
CHANGES in 0.4 CHANGES IN 0.5 / 0.51
============== =====================
- Feature: draw resonators on map on high zoom levels (by Xelio)
- Feature: show AP if portal is taken down (by Pirozek)
- Feature: collapsible sidebar (by cmrn)
- Feature: fields connected to portal (by phoenixsong6)
- Feature: Permalink feature
- Feature: chat now more copy&paste friendly (by scrool)
- Feature: display max. energy for portal (by scrool)
- Feature: auto-reload if page states your account is not enabled for
Ingress
- Feature: You are now alerted if some of the resources fail to load
- Change: portal level should now stand out better against more
backgrouds (by jonatkins)
- Change: increased hack range from 35m to 40m
- Change: Sidebar now semi-transparent, just like chat (by cmrn)
- Change: portals are now sizes according to their level (by OshiHidra)
- Change: resonators are now more aligned to their octant (thanks
Worros for helping confirming the slot-to-octant matching)
- Change: release versions are now put in `dist/`. This should avoid
update issues in the future.
- Bugfix: entities would be drawn again if they were hidden while using
the map
- Bugfix: Python 3+ now required for building to fix encoding issues
- Bugfix: portal mod rendering of unclaimed portals wrong
- Bugfix: chat/sidebar arrows have gap or overlap (by mledoze)
- Bugfix: entities invisible after hiding some of them
- Bugfix: some portals in automated view were not clickable
- Bugfix: resonators + nicks were sometimes misaligned
- Bugfix: portal mod boxes sometimes misaligned
- Plugin: guess-player-levels now also shows guessed level in tooltip
Additional fixes in 0.51:
- Bugfix: sidebar not visible in Chrome
- Bugfix: layer chooser not usable if sidebar collapsed
- Bugfix: range link not working
CHANGES IN 0.4
--------------
- Feature: display resonator charge percentage in tooltip (by Xelio) - Feature: display resonator charge percentage in tooltip (by Xelio)
- Feature: display resonator level in reso bar (by JasonMillward) - Feature: display resonator level in reso bar (by JasonMillward)

View File

@ -37,9 +37,9 @@ Features
Install Install
------- -------
Current version is 0.4. See [NEWS.md](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/NEWS.md) for details. Current version is 0.51. See [NEWS.md](https://github.com/breunigs/ingress-intel-total-conversion/blob/gh-pages/NEWS.md) for details.
[**INSTALL**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/total-conversion-build.user.js) [**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. Reload page. **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. Reload page.
@ -60,7 +60,7 @@ Current version is 0.4. See [NEWS.md](https://github.com/breunigs/ingress-intel-
[**INSTALL**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/total-conversion-build.user.js) [**INSTALL**](https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/dist/total-conversion-build.user.js)
Contributing Contributing
@ -81,6 +81,7 @@ Contributors
[JasonMillward](https://github.com/JasonMillward), [JasonMillward](https://github.com/JasonMillward),
[mledoze](https://github.com/mledoze), [mledoze](https://github.com/mledoze),
[OshiHidra](https://github.com/OshiHidra), [OshiHidra](https://github.com/OshiHidra),
[phoenixsong6](https://github.com/phoenixsong6),
[Pirozek](https://github.com/Pirozek), [Pirozek](https://github.com/Pirozek),
[Scrool](https://github.com/Scrool), [Scrool](https://github.com/Scrool),
[sorgo](https://github.com/sorgo), [sorgo](https://github.com/sorgo),
@ -91,11 +92,13 @@ Contributors
Hacking Hacking
------- -------
Execute `./build.py` to effectively concatenate `main.js` with all the files in `code/`. It generates the user script which may be installed into your browser. Do not modify `total-conversion-build.user.js` manually, because it is automatically generated. Please dont include it in patches either, because it makes merging harder. Instead, modify the files in `code/` and have that file built for you. Execute `./build.py` to effectively concatenate `main.js` with all the files in `code/`. It generates the user script which may be installed into your browser. Do not modify `ttic-debug.user.js` manually, because it is automatically generated. Instead, modify the files in `code/` and have that file built for you. The files in `dist/` are for release only and should not be touched by you.
`style.css` contains most styles required for the user-script. The extra ones can be found in `code/boot.js#window.setupStyles`. Only CSS rules that depend on config variables should be defined there. `style.css` contains most styles required for the user-script. The extra ones can be found in `code/boot.js#window.setupStyles`. Only CSS rules that depend on config variables should be defined there.
`leaflet_google.js` contains some code to display Google Maps imagery with Leaflet, which is a slightly modified version [of this gist](https://gist.github.com/4504864). The code likely was originally written by Pavel Shramov. `external/leaflet_google.js` contains some code to display Google Maps imagery with Leaflet, which is a slightly modified version [of this gist](https://gist.github.com/4504864). The code likely was originally written by Pavel Shramov.
`external/autolink.js` is the same file as distributed by Bryan Woods.
My dev setup is like this: My dev setup is like this:

View File

@ -2,7 +2,7 @@
./build.py ./build.py
FORMAT=$(echo "\033[1;33m%w%f\033[0m written") FORMAT=$(echo "\033[1;33m%w%f\033[0m written")
while inotifywait -qre close_write --exclude "total-conversion-build.user.js|.git*" --format "$FORMAT" . while inotifywait -qre close_write --exclude "iitc-debug.user.js|.git*" --format "$FORMAT" .
do do
./build.py ./build.py
done done

View File

@ -14,7 +14,7 @@ m = m.split('@@INJECTHERE@@')
m.insert(1, c) m.insert(1, c)
t = '\n\n'.join(m) t = '\n\n'.join(m)
with open('total-conversion-build.user.js', 'w', encoding='utf8') as f: with open('iitc-debug.user.js', 'w', encoding='utf8') as f:
f.write(t) f.write(t)
# vim: ai si ts=4 sw=4 sts=4 et # vim: ai si ts=4 sw=4 sts=4 et

View File

@ -27,7 +27,8 @@ window.setupLargeImagePreview = function() {
window.setupStyles = function() { window.setupStyles = function() {
$('head').append('<style>' + $('head').append('<style>' +
[ '#largepreview.enl img { border:2px solid '+COLORS[TEAM_ENL]+'; } ', [ 'body:after { display: none } ',
'#largepreview.enl img { border:2px solid '+COLORS[TEAM_ENL]+'; } ',
'#largepreview.res img { border:2px solid '+COLORS[TEAM_RES]+'; } ', '#largepreview.res img { border:2px solid '+COLORS[TEAM_RES]+'; } ',
'#largepreview.none img { border:2px solid '+COLORS[TEAM_NONE]+'; } ', '#largepreview.none img { border:2px solid '+COLORS[TEAM_NONE]+'; } ',
'#chatcontrols { bottom: '+(CHAT_SHRINKED+24)+'px; }', '#chatcontrols { bottom: '+(CHAT_SHRINKED+24)+'px; }',
@ -173,16 +174,16 @@ window.setupPlayerStat = function() {
window.setupSidebarToggle = function() { window.setupSidebarToggle = function() {
$('#sidebartoggle').on('click', function() { $('#sidebartoggle').on('click', function() {
var toggle = $('#sidebartoggle'); var toggle = $('#sidebartoggle');
var sidebar = $('#sidebar'); var sidebar = $('#scrollwrapper');
if(sidebar.is(':visible')) { if(sidebar.is(':visible')) {
sidebar.hide(); sidebar.hide().css('z-index', 1);
$('.leaflet-right').css('margin-right','0'); $('.leaflet-right').css('margin-right','0');
toggle.html('◢<br>◥'); toggle.html('<span class="toggle open"></span>');
toggle.css('right', '0'); toggle.css('right', '0');
} else { } else {
sidebar.show(); sidebar.css('z-index', 1001).show();
$('.leaflet-right').css('margin-right', SIDEBAR_WIDTH+1+'px'); $('.leaflet-right').css('margin-right', SIDEBAR_WIDTH+1+'px');
toggle.html('◣<br>◤'); toggle.html('<span class="toggle close"></span>');
toggle.css('right', SIDEBAR_WIDTH+1+'px'); toggle.css('right', SIDEBAR_WIDTH+1+'px');
} }
}); });
@ -227,10 +228,10 @@ function asyncLoadScript(a){return function(b,c){var d=document.createElement("s
// modified version of https://github.com/shramov/leaflet-plugins. Also // modified version of https://github.com/shramov/leaflet-plugins. Also
// contains the default Ingress map style. // contains the default Ingress map style.
var LLGMAPS = 'http://breunigs.github.com/ingress-intel-total-conversion/external/leaflet_google.js'; var LLGMAPS = 'http://breunigs.github.com/ingress-intel-total-conversion/dist/leaflet_google.js';
var JQUERY = 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js'; var JQUERY = 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js';
var LEAFLET = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.js'; var LEAFLET = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.js';
var AUTOLINK = 'http://breunigs.github.com/ingress-intel-total-conversion/external/autolink.js'; var AUTOLINK = 'http://breunigs.github.com/ingress-intel-total-conversion/dist/autolink.js';
// after all scripts have loaded, boot the actual app // after all scripts have loaded, boot the actual app
load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS).onError(function (err) { load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS).onError(function (err) {

View File

@ -469,14 +469,14 @@ window.chat.getActive = function() {
window.chat.toggle = function() { window.chat.toggle = function() {
var c = $('#chat, #chatcontrols'); var c = $('#chat, #chatcontrols');
if(c.hasClass('expand')) { if(c.hasClass('expand')) {
$('#chatcontrols a:first').text('◢◣'); $('#chatcontrols a:first').html('<span class="toggle expand"></span>');
c.removeClass('expand'); c.removeClass('expand');
var div = $('#chat > div:visible'); var div = $('#chat > div:visible');
div.data('ignoreNextScroll', true); div.data('ignoreNextScroll', true);
div.scrollTop(99999999); // scroll to bottom div.scrollTop(99999999); // scroll to bottom
$('.leaflet-control').css('margin-left', '13px'); $('.leaflet-control').css('margin-left', '13px');
} else { } else {
$('#chatcontrols a:first').text('◥◤'); $('#chatcontrols a:first').html('<span class="toggle shrink"></span>');
c.addClass('expand'); c.addClass('expand');
$('.leaflet-control').css('margin-left', '720px'); $('.leaflet-control').css('margin-left', '720px');
chat.needMoreMessages(); chat.needMoreMessages();

View File

@ -116,10 +116,13 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
}); });
$.each(ppp, function(ind, portal) { $.each(ppp, function(ind, portal) {
if(p2f[portal[0]] === undefined) if(portal[2].portalV2['linkedFields'] === undefined) {
portal[2].portalV2['linkedFields'] = []; portal[2].portalV2['linkedFields'] = [];
else }
portal[2].portalV2['linkedFields'] = $.unique(p2f[portal[0]]); if(p2f[portal[0]] !== undefined) {
$.merge(p2f[portal[0]], portal[2].portalV2['linkedFields']);
portal[2].portalV2['linkedFields'] = uniqueArray(p2f[portal[0]]);
}
}); });
$.each(ppp, function(ind, portal) { renderPortal(portal); }); $.each(ppp, function(ind, portal) { renderPortal(portal); });
@ -225,11 +228,9 @@ window.renderPortal = function(ent) {
// nothing for the portal changed, so dont update. Let resonators // nothing for the portal changed, so dont update. Let resonators
// manage themselves if they want to be updated. // manage themselves if they want to be updated.
if(!u) return renderResonators(ent); if(!u) return renderResonators(ent);
removeByGuid(ent[0]);
}
// there were changes, remove old portal // there were changes, remove old portal
removeByGuid(ent[0]); removeByGuid(ent[0]);
}
var latlng = [ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6]; var latlng = [ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6];

View File

@ -7,7 +7,7 @@
window.getRangeText = function(d) { window.getRangeText = function(d) {
var range = getPortalRange(d); var range = getPortalRange(d);
return ['range', return ['range',
+ '<a onclick="window.rangeLinkClick()">' '<a onclick="window.rangeLinkClick()">'
+ (range > 1000 + (range > 1000
? Math.round(range/1000) + 'km' ? Math.round(range/1000) + 'km'
: Math.round(range) + 'm') : Math.round(range) + 'm')

View File

@ -210,3 +210,9 @@ window.setPermaLink = function(elm) {
var qry = 'latE6='+lat+'&lngE6='+lng+'&z=' + map.getZoom(); var qry = 'latE6='+lat+'&lngE6='+lng+'&z=' + map.getZoom();
$(elm).attr('href', 'http://www.ingress.com/intel?' + qry); $(elm).attr('href', 'http://www.ingress.com/intel?' + qry);
} }
window.uniqueArray = function(arr) {
return $.grep(arr, function(v, i) {
return $.inArray(v, arr) === i;
});
}

33
dist/autolink.js vendored Normal file
View File

@ -0,0 +1,33 @@
// Generated by CoffeeScript 1.4.0
(function() {
var autoLink,
__slice = [].slice;
autoLink = function() {
var callbackThunk, key, link_attributes, option, options, url_pattern, value;
options = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
link_attributes = '';
option = options[0];
url_pattern = /(^|\s)(\b(https?|ftp):\/\/[\-A-Z0-9+\u0026@#\/%?=~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~_|])/gi;
if (!(options.length > 0)) {
return this.replace(url_pattern, "$1<a href='$2'>$2</a>");
}
if ((option['callback'] != null) && typeof option['callback'] === 'function') {
callbackThunk = option['callback'];
delete option['callback'];
}
for (key in option) {
value = option[key];
link_attributes += " " + key + "='" + value + "'";
}
return this.replace(url_pattern, function(match, space, url) {
var link, returnCallback;
returnCallback = callbackThunk && callbackThunk(url);
link = returnCallback || ("<a href='" + url + "'" + link_attributes + ">" + url + "</a>");
return "" + space + link;
});
};
String.prototype['autoLink'] = autoLink;
}).call(this);

152
dist/leaflet_google.js vendored Normal file
View File

@ -0,0 +1,152 @@
/*
* L.TileLayer is used for standard xyz-numbered tile layers.
*/
L.Google = L.Class.extend({
includes: L.Mixin.Events,
options: {
minZoom: 0,
maxZoom: 18,
tileSize: 256,
subdomains: 'abc',
errorTileUrl: '',
attribution: '',
opacity: 1,
continuousWorld: false,
noWrap: false,
},
// Possible types: SATELLITE, ROADMAP, HYBRID, INGRESS
initialize: function(type, options, styles) {
L.Util.setOptions(this, options);
if(type === 'INGRESS') {
type = 'ROADMAP';
this._styles = [{featureType:"all", elementType:"all", stylers:[{visibility:"on"}, {hue:"#0091ff"}, {invert_lightness:true}]}, {featureType:"water", elementType:"all", stylers:[{visibility:"on"}, {hue:"#005eff"}, {invert_lightness:true}]}, {featureType:"poi", stylers:[{visibility:"off"}]}, {featureType:"transit", elementType:"all", stylers:[{visibility:"off"}]}];
} else {
this._styles = null;
}
this._type = google.maps.MapTypeId[type || 'SATELLITE'];
},
onAdd: function(map, insertAtTheBottom) {
this._map = map;
this._insertAtTheBottom = insertAtTheBottom;
// create a container div for tiles
this._initContainer();
this._initMapObject();
// set up events
map.on('viewreset', this._resetCallback, this);
this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this);
map.on('move', this._update, this);
//map.on('moveend', this._update, this);
this._reset();
this._update();
},
onRemove: function(map) {
this._map._container.removeChild(this._container);
//this._container = null;
this._map.off('viewreset', this._resetCallback, this);
this._map.off('move', this._update, this);
//this._map.off('moveend', this._update, this);
},
getAttribution: function() {
return this.options.attribution;
},
setOpacity: function(opacity) {
this.options.opacity = opacity;
if (opacity < 1) {
L.DomUtil.setOpacity(this._container, opacity);
}
},
_initContainer: function() {
var tilePane = this._map._container
first = tilePane.firstChild;
if (!this._container) {
this._container = L.DomUtil.create('div', 'leaflet-google-layer leaflet-top leaflet-left');
this._container.id = "_GMapContainer";
}
if (true) {
tilePane.insertBefore(this._container, first);
this.setOpacity(this.options.opacity);
var size = this._map.getSize();
this._container.style.width = size.x + 'px';
this._container.style.height = size.y + 'px';
}
},
_initMapObject: function() {
this._google_center = new google.maps.LatLng(0, 0);
var map = new google.maps.Map(this._container, {
center: this._google_center,
zoom: 0,
styles: this._styles,
mapTypeId: this._type,
disableDefaultUI: true,
keyboardShortcuts: false,
draggable: false,
disableDoubleClickZoom: true,
scrollwheel: false,
streetViewControl: false
});
var _this = this;
this._reposition = google.maps.event.addListenerOnce(map, "center_changed",
function() { _this.onReposition(); });
map.backgroundColor = '#ff0000';
this._google = map;
},
_resetCallback: function(e) {
this._reset(e.hard);
},
_reset: function(clearOldContainer) {
this._initContainer();
},
_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)
return;
this._container.style.width = size.x + 'px';
this._container.style.height = size.y + 'px';
google.maps.event.trigger(this._google, "resize");
},
onReposition: function() {
//google.maps.event.trigger(this._google, "resize");
}
});

605
dist/style.css vendored Normal file
View File

@ -0,0 +1,605 @@
/* general rules ******************************************************/
html, body, #map {
height: 100%;
}
body {
font-size: 14px;
font-family: "coda",arial,helvetica,sans-serif;
margin: 0;
}
#scrollwrapper {
overflow: hidden;
position: fixed;
right: -38px;
top: 0;
width: 340px;
bottom: 45px;
z-index: 1001;
}
#sidebar {
background-color: rgba(8, 48, 78, 0.9);
border-left: 1px solid #20A8B1;
color: #888;
position: relative;
left: 0;
top: 0;
max-height: 100%;
overflow-y:scroll;
overflow-x:hidden;
z-index: 3000;
}
#sidebartoggle {
display: block;
padding: 20px 5px;
margin-top: -31px;
line-height: 10px;
position: absolute;
top: 50%;
z-index: 3001;
background-color: rgba(8, 48, 78, 0.9);
color: #FFCE00;
border: 1px solid #20A8B1;
border-right: none;
border-radius: 5px 0 0 5px;
text-decoration: none;
}
.enl {
color: #03fe03 !important;
}
.res {
color: #00c5ff !important;
}
.none {
color: #fff;
}
a {
color: #ffce00;
cursor: pointer;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* map display, required because GMaps uses a high z-index which is
* normally above Leaflets vector pane */
.leaflet-map-pane {
z-index: 1000;
}
.leaflet-control-layers-overlays label.disabled {
text-decoration: line-through;
cursor: help;
}
.help {
cursor: help;
}
.toggle {
display: block;
height: 0;
width: 0;
}
/* chat ***************************************************************/
#chatcontrols {
color: #FFCE00;
background: rgba(8, 48, 78, 0.9);
position: absolute;
left: 0;
z-index: 3001;
height: 26px;
padding-left:1px;
}
#chatcontrols.expand {
top: 0;
bottom: auto;
}
#chatcontrols a {
margin-left: -1px;
display: inline-block;
width: 94px;
text-align: center;
height: 24px;
line-height: 24px;
border: 1px solid #20A8B1;
vertical-align: top;
}
#chatcontrols a:first-child {
letter-spacing:-1px;
text-decoration: none !important;
}
#chatcontrols a.active {
border-color: #FFCE00;
border-bottom-width:0px;
font-weight:bold
}
#chatcontrols a.active + a {
border-left-color: #FFCE00
}
#chatcontrols .toggle {
border-left: 10px solid transparent;
border-right: 10px solid transparent;
margin: 6px auto auto;
}
#chatcontrols .expand {
border-bottom: 10px solid #FFCE00;
}
#chatcontrols .shrink {
border-top: 10px solid #FFCE00;
}
#chat {
position: absolute;
width: 708px;
bottom: 24px;
left: 0;
z-index: 3000;
background: rgba(8, 48, 78, 0.9);
font-size: 12.6px;
color: #eee;
border: 1px solid #20A8B1;
border-bottom: 0;
}
em {
color: red;
font-style: normal;
}
#chat.expand {
height:auto;
top: 25px;
}
#chatpublic, #chatautomated {
display: none;
}
#chat > div {
overflow-x:hidden;
overflow-y:scroll;
height: 100%; /* fallback for Opera which doesnt support calc */
height: calc(100% - 4px);
height: -webkit-calc(100% - 4px);
height: -moz-calc(100% - 4px);
padding: 2px;
position:relative;
}
#chat p {
display: block;
padding: 1px 2px;
margin:0;
}
#chat time {
cursor: help;
}
time, mark, #chat span, #chat a {
font-family: Verdana, sans-serif;
font-size: 12.6px;
vertical-align: top;
}
time {
display: inline-block;
width: 44px;
color: #bbb;
}
mark {
display: inline-block;
width: 91px;
margin-right:4px;
overflow:hidden;
vertical-align: top;
background: transparent;
}
summary {
color: #bbb;
display: inline-block;
font-family: Verdana,sans-serif;
height: 16px;
overflow: hidden;
padding: 0 2px;
white-space: nowrap;
width: 683px;
}
#chat span {
display: inline-block;
width: 540px;
}
#chatinput {
line-height:22px;
padding: 0 4px;
position: absolute;
bottom: 0;
left: 0;
background: rgba(8, 48, 78, 0.9);
width: 700px;
border: 1px solid #20A8B1;
z-index: 3001;
}
#chat .invisibleseparator {
color: rgba(8, 48, 78, 0.0);
overflow: hidden;
width: 0px;
}
#chatinput span {
font-family: Verdana,sans-serif;
display: inline-block;
font-size: 12.6px;
width: 84px;
color: red;
padding: 0 4px 0 1px;
width: 85px;
}
#chatinput input {
background: transparent;
font-size: 12.6px;
font-family: Verdana,sans-serif;
color: #EEEEEE;
width: 558px
}
/* sidebar ************************************************************/
#sidebar > * {
border-bottom: 1px solid #20A8B1;
}
#sidebartoggle .toggle {
border-bottom: 10px solid transparent;
border-top: 10px solid transparent;
}
#sidebartoggle .open {
border-right: 10px solid #FFCE00;
}
#sidebartoggle .close {
border-left: 10px solid #FFCE00;
}
/* player stats */
#playerstat {
height: 30px;
}
h2 {
color: #ffce00;
font-size: 21px;
padding: 0 4px;
margin: 0;
cursor:help;
}
h2 span {
display: inline-block;
overflow: hidden;
text-overflow: "~";
vertical-align: top;
white-space: nowrap;
width: 205px;
}
h2 div {
float: right;
height: 100%;
overflow: hidden;
}
h2 sup, h2 sub {
display: block;
font-size: 11px;
margin-bottom: -1px;
}
/* gamestats */
#gamestat, #gamestat span {
height: 22px;
line-height: 22px;
}
#gamestat span {
display: block;
float: left;
font-weight: bold;
cursor:help;
}
#gamestat .res {
background: #005684;
text-align: right;
}
#gamestat .enl {
background: #017f01;
}
/* geosearch input, and others */
input {
background-color: rgba(0, 0, 0, 0.3);
color: #ffce00;
height: 22px;
line-height: 22px;
padding: 0 4px;
font-size: 14px;
border:0;
font-family:inherit;
}
::-webkit-input-placeholder {
font-style: italic;
}
:-moz-placeholder {
font-style: italic;
}
::-moz-placeholder {
font-style: italic;
}
/* portal title and image */
h3 {
font-size: 17px;
padding: 0 4px;
margin:0;
height: 25px;
width: 100%;
overflow:hidden;
text-overflow: "~";
white-space: nowrap;
}
.imgpreview {
height: 200px;
overflow: hidden;
}
.imgpreview img {
cursor: help;
}
#level {
font-size: 40px;
position: absolute;
right: 10px;
text-shadow: -1px -1px #000, 1px -1px #000, -1px 1px #000, 1px 1px #000, 0 0 5px #fff;
top: 100px;
}
/* portal mods */
.mods {
margin-bottom: 1px;
margin-top: 5px;
height: 75px;
}
.mods span {
background-color: rgba(0, 0, 0, 0.3);
/* cant use inline-block because Webkits implementation is buggy and
* introduces additional margins in random cases. No clear necessary,
* as thats solved by setting height on .mods. */
display: block;
float:left;
height: 63.7px;
margin-left: 4px;
overflow: hidden;
padding: 2px;
text-align: center;
width: 63.7px;
cursor:help;
border: 1px solid #666;
}
.mods span[title=""] {
cursor: auto;
}
.res .mods span, .res .meter {
border: 1px solid #0076b6;
}
.enl .mods span, .enl .meter {
border: 1px solid #017f01;
}
/* random details list */
#randdetails {
margin: 0 4px;
margin-top: 11px;
}
aside {
display: inline-block;
width: 140px;
}
aside span {
overflow: hidden;
text-overflow: "~";
white-space: nowrap;
width: 74px;
}
aside:nth-child(odd) {
margin-right: 4px;
text-align: right;
}
aside:nth-child(even) {
margin-left: 4px;
text-align: left;
}
aside:nth-child(even) span {
float: right;
padding-left: 4px;
text-align: left;
}
aside:nth-child(odd) span {
float: left;
padding-right: 4px;
text-align: right;
}
#randdetails tt {
font-family: inherit;
cursor: help;
}
/* resonators */
#resodetails {
white-space: nowrap;
margin: 16px 0;
-moz-column-gap: 10px;
-moz-column-width: 141px;
-webkit-column-gap: 10px;
-webkit-column-width: 141px;
}
.meter {
background: #000;
cursor: help;
display: inline-block;
height: 14px;
padding: 1px;
width: 58px;
}
.meter-text {
display: inline-block;
height: 18px;
margin: 0 4px;
overflow: hidden;
text-overflow: "~";
vertical-align: top;
white-space: nowrap;
width: 75px;
}
.meter-text.left {
text-align: right;
}
.meter span {
display: block;
height: 14px;
}
.meter-rel {
position: relative;
left: 0;
top: 0;
}
.meter-level {
position: absolute;
top: -2px;
left: 25px;
text-shadow: 0.0em 0.0em 0.3em #808080;
}
/* links below resos */
.linkdetails {
text-align: center;
margin-bottom: 10px;
}
.linkdetails aside {
margin: 0 4px;
width: 140px;
}
#toolbox {
padding: 4px;
font-size:90%;
}
#toolbox > a {
padding: 5px;
}
#spacer {
height: 10px;
}
/* a common portal display takes this much space (prevents moving
* content when first selecting a portal) */
#portaldetails {
min-height: 553px;
}
/* update status */
#updatestatus {
background-color: rgba(8, 48, 78, 1);
border-bottom: 0;
border-top: 1px solid #20A8B1;
border-left: 1px solid #20A8B1;
bottom: 0;
color: #ffce00;
font-size:13px;
padding: 4px;
position: fixed;
right: 0;
z-index:3002;
}
/* preview */
#largepreview {
left: 50%;
position: fixed;
top: 50%;
z-index: 2000;
}
#largepreview img {
box-shadow: 0 0 40px #000;
}
#largepreview img {
border: 2px solid #f8ff5e;
}

2723
dist/total-conversion-build.user.js vendored Normal file

File diff suppressed because it is too large Load Diff

10
main.js
View File

@ -1,10 +1,10 @@
// ==UserScript== // ==UserScript==
// @id ingress-intel-total-conversion@breunigs // @id ingress-intel-total-conversion@breunigs
// @name intel map total conversion // @name intel map total conversion
// @version 0.4-@@BUILDDATE@@ // @version 0.51-@@BUILDDATE@@
// @namespace https://github.com/breunigs/ingress-intel-total-conversion // @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/total-conversion-build.user.js // @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/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. // @description total conversion for the ingress intel map.
// @include http://www.ingress.com/intel* // @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel* // @match http://www.ingress.com/intel*
@ -62,7 +62,7 @@ document.getElementsByTagName('head')[0].innerHTML = ''
document.getElementsByTagName('body')[0].innerHTML = '' document.getElementsByTagName('body')[0].innerHTML = ''
+ '<div id="map">Loading, please wait</div>' + '<div id="map">Loading, please wait</div>'
+ '<div id="chatcontrols" style="display:none">' + '<div id="chatcontrols" style="display:none">'
+ ' <a>◢◣</a><a>automated</a><a>public</a><a class="active">faction</a>' + ' <a><span class="toggle expand"></span></a><a>automated</a><a>public</a><a class="active">faction</a>'
+ '</div>' + '</div>'
+ '<div id="chat" style="display:none">' + '<div id="chat" style="display:none">'
+ ' <div id="chatfaction"></div>' + ' <div id="chatfaction"></div>'
@ -70,7 +70,7 @@ document.getElementsByTagName('body')[0].innerHTML = ''
+ ' <div id="chatautomated"></div>' + ' <div id="chatautomated"></div>'
+ '</div>' + '</div>'
+ '<form id="chatinput" style="display:none"><time></time><span>tell faction:</span><input type="text"/></form>' + '<form id="chatinput" style="display:none"><time></time><span>tell faction:</span><input type="text"/></form>'
+ '<a id="sidebartoggle">◣<br>◤</a>' + '<a id="sidebartoggle"><span class="toggle close"></span></a>'
+ '<div id="scrollwrapper">' // enable scrolling for small screens + '<div id="scrollwrapper">' // enable scrolling for small screens
+ ' <div id="sidebar" style="display: none">' + ' <div id="sidebar" style="display: none">'
+ ' <div id="playerstat">t</div>' + ' <div id="playerstat">t</div>'

9
pack-release.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
./build.py
cp iitc-debug.user.js dist/total-conversion-build.user.js
cp style.css dist/style.css
cp external/leaflet_google.js dist/leaflet_google.js
cp external/autolink.js dist/autolink.js
echo 'Change path of style.css to dist/style.css'

View File

@ -1,3 +1,17 @@
/* update notice */
body:after {
background: none repeat scroll 0 0 #FFFFFF;
color: red;
content: "Please update your total conversion script or it will not work properly anymore.";
display: block;
font-weight: bold;
left: 381px;
padding: 2px;
position: absolute;
top: 0;
z-index: 9001;
}
/* general rules ******************************************************/ /* general rules ******************************************************/
html, body, #map { html, body, #map {
@ -18,12 +32,6 @@ body {
width: 340px; width: 340px;
bottom: 45px; bottom: 45px;
z-index: 1001; z-index: 1001;
/*
* NOTE: following 2 items are needed for back compatability
* between new css and old plugin versions (0.4) as of 2013-02-11 UTC.
*/
background-color: rgba(8, 48, 78, 1);
} }
#sidebar { #sidebar {
@ -92,6 +100,13 @@ a:hover {
cursor: help; cursor: help;
} }
.toggle {
display: block;
height: 0;
width: 0;
}
/* chat ***************************************************************/ /* chat ***************************************************************/
#chatcontrols { #chatcontrols {
@ -117,6 +132,7 @@ a:hover {
height: 24px; height: 24px;
line-height: 24px; line-height: 24px;
border: 1px solid #20A8B1; border: 1px solid #20A8B1;
vertical-align: top;
} }
#chatcontrols a:first-child { #chatcontrols a:first-child {
@ -134,6 +150,22 @@ a:hover {
border-left-color: #FFCE00 border-left-color: #FFCE00
} }
#chatcontrols .toggle {
border-left: 10px solid transparent;
border-right: 10px solid transparent;
margin: 6px auto auto;
}
#chatcontrols .expand {
border-bottom: 10px solid #FFCE00;
}
#chatcontrols .shrink {
border-top: 10px solid #FFCE00;
}
#chat { #chat {
position: absolute; position: absolute;
width: 708px; width: 708px;
@ -265,6 +297,20 @@ summary {
} }
#sidebartoggle .toggle {
border-bottom: 10px solid transparent;
border-top: 10px solid transparent;
}
#sidebartoggle .open {
border-right: 10px solid #FFCE00;
}
#sidebartoggle .close {
border-left: 10px solid #FFCE00;
}
/* player stats */ /* player stats */
#playerstat { #playerstat {
height: 30px; height: 30px;

View File

@ -1,10 +1,10 @@
// ==UserScript== // ==UserScript==
// @id ingress-intel-total-conversion@breunigs // @id ingress-intel-total-conversion@breunigs
// @name intel map total conversion // @name intel map total conversion
// @version 0.4-2013-02-09-151927 // @version 0.51-2013-02-11-193316
// @namespace https://github.com/breunigs/ingress-intel-total-conversion // @namespace https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/total-conversion-build.user.js // @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/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. // @description total conversion for the ingress intel map.
// @include http://www.ingress.com/intel* // @include http://www.ingress.com/intel*
// @match http://www.ingress.com/intel* // @match http://www.ingress.com/intel*
@ -28,6 +28,19 @@ for(var x in scr) {
break; break;
} }
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.');
}
// FIXME: handle nia takedown in progress
throw('Couldnt retrieve player data. Are you logged in?');
}
for(var i = 0; i < d.length; i++) { for(var i = 0; i < d.length; i++) {
if(!d[i].match('var PLAYER = ')) continue; if(!d[i].match('var PLAYER = ')) continue;
eval(d[i].match(/^var /, 'window.')); eval(d[i].match(/^var /, 'window.'));
@ -36,21 +49,20 @@ for(var i = 0; i < d.length; i++) {
// player information is now available in a hash like this: // player information is now available in a hash like this:
// window.PLAYER = {"ap": "123", "energy": 123, "available_invites": 123, "nickname": "somenick", "team": "ALIENS||RESISTANCE"}; // window.PLAYER = {"ap": "123", "energy": 123, "available_invites": 123, "nickname": "somenick", "team": "ALIENS||RESISTANCE"};
// remove complete page. We only wanted the user-data and the pages // 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 // security context so we can access the API easily. Setup as much as
// possible without requiring scripts. // possible without requiring scripts.
document.getElementsByTagName('head')[0].innerHTML = '' document.getElementsByTagName('head')[0].innerHTML = ''
//~ + '<link rel="stylesheet" type="text/css" href="http://0.0.0.0:8000/style.css"/>' //~ + '<link rel="stylesheet" type="text/css" href="http://0.0.0.0:8000/style.css"/>'
+ '<title>Ingress Intel Map</title>' + '<title>Ingress Intel Map</title>'
+ '<link rel="stylesheet" type="text/css" href="http://breunigs.github.com/ingress-intel-total-conversion/style.css?2013-02-09-151927"/>' + '<link rel="stylesheet" type="text/css" href="http://breunigs.github.com/ingress-intel-total-conversion/dist/style.css?2013-02-11-193316"/>'
+ '<link rel="stylesheet" type="text/css" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.css"/>' + '<link rel="stylesheet" type="text/css" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.css"/>'
+ '<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Coda"/>'; + '<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Coda"/>';
document.getElementsByTagName('body')[0].innerHTML = '' document.getElementsByTagName('body')[0].innerHTML = ''
+ '<div id="map">Loading, please wait</div>' + '<div id="map">Loading, please wait</div>'
+ '<div id="chatcontrols" style="display:none">' + '<div id="chatcontrols" style="display:none">'
+ ' <a>expand</a><a>automated</a><a>public</a><a class="active">faction</a>' + ' <a><span class="toggle expand"></span></a><a>automated</a><a>public</a><a class="active">faction</a>'
+ '</div>' + '</div>'
+ '<div id="chat" style="display:none">' + '<div id="chat" style="display:none">'
+ ' <div id="chatfaction"></div>' + ' <div id="chatfaction"></div>'
@ -58,6 +70,7 @@ document.getElementsByTagName('body')[0].innerHTML = ''
+ ' <div id="chatautomated"></div>' + ' <div id="chatautomated"></div>'
+ '</div>' + '</div>'
+ '<form id="chatinput" style="display:none"><time></time><span>tell faction:</span><input type="text"/></form>' + '<form id="chatinput" style="display:none"><time></time><span>tell faction:</span><input type="text"/></form>'
+ '<a id="sidebartoggle"><span class="toggle close"></span></a>'
+ '<div id="scrollwrapper">' // enable scrolling for small screens + '<div id="scrollwrapper">' // enable scrolling for small screens
+ ' <div id="sidebar" style="display: none">' + ' <div id="sidebar" style="display: none">'
+ ' <div id="playerstat">t</div>' + ' <div id="playerstat">t</div>'
@ -65,11 +78,11 @@ document.getElementsByTagName('body')[0].innerHTML = ''
+ ' <input id="geosearch" placeholder="Search location…" type="text"/>' + ' <input id="geosearch" placeholder="Search location…" type="text"/>'
+ ' <div id="portaldetails"></div>' + ' <div id="portaldetails"></div>'
+ ' <input id="redeem" placeholder="Redeem code…" type="text"/>' + ' <input id="redeem" placeholder="Redeem code…" type="text"/>'
+ ' <div id="toolbox"></div>' + ' <div id="toolbox"><a onmouseover="setPermaLink(this)">permalink</a></div>'
+ ' <div id="spacer"></div>' + ' <div id="spacer"></div>'
+ ' <div id="updatestatus"></div>' + ' </div>'
+ ' </div>'; + '</div>'
+ '</div>'; + '<div id="updatestatus"></div>';
// putting everything in a wrapper function that in turn is placed in a // putting everything in a wrapper function that in turn is placed in a
// script tag on the website allows us to execute in the sites context // script tag on the website allows us to execute in the sites context
@ -130,19 +143,31 @@ var RANGE_INDICATOR_COLOR = 'red';
var RESO_NRG = [0, 1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000]; var RESO_NRG = [0, 1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000];
var MAX_XM_PER_LEVEL = [0, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]; var MAX_XM_PER_LEVEL = [0, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000];
var MIN_AP_FOR_LEVEL = [0, 10000, 30000, 70000, 150000, 300000, 600000, 1200000]; var MIN_AP_FOR_LEVEL = [0, 10000, 30000, 70000, 150000, 300000, 600000, 1200000];
var HACK_RANGE = 35; // in meters, max. distance from portal to be able to access it var HACK_RANGE = 40; // in meters, max. distance from portal to be able to access it
var OCTANTS = ['E', 'NE', 'N', 'NW', 'W', 'SW', 'S', 'SE']; var OCTANTS = ['E', 'NE', 'N', 'NW', 'W', 'SW', 'S', 'SE'];
var DEFAULT_PORTAL_IMG = 'http://commondatastorage.googleapis.com/ingress/img/default-portal-image.png'; var DEFAULT_PORTAL_IMG = 'http://commondatastorage.googleapis.com/ingress/img/default-portal-image.png';
var DESTROY_RESONATOR = 75; //AP for destroying portal
var DESTROY_LINK = 187; //AP for destroying link
var DESTROY_FIELD = 750; //AP for destroying field
// OTHER MORE-OR-LESS CONSTANTS ////////////////////////////////////// // OTHER MORE-OR-LESS CONSTANTS //////////////////////////////////////
var NOMINATIM = 'http://nominatim.openstreetmap.org/search?format=json&limit=1&q='; var NOMINATIM = 'http://nominatim.openstreetmap.org/search?format=json&limit=1&q=';
var DEG2RAD = Math.PI / 180; var DEG2RAD = Math.PI / 180;
var TEAM_NONE = 0, TEAM_RES = 1, TEAM_ENL = 2; var TEAM_NONE = 0, TEAM_RES = 1, TEAM_ENL = 2;
var TEAM_TO_CSS = ['none', 'res', 'enl']; var TEAM_TO_CSS = ['none', 'res', 'enl'];
var TYPE_UNKNOWN = 0, TYPE_PORTAL = 1, TYPE_LINK = 2, TYPE_FIELD = 3, TYPE_PLAYER = 4, TYPE_CHAT = 5, TYPE_RESONATOR = 6;
// make PLAYER variable available in site context // make PLAYER variable available in site context
var PLAYER = window.PLAYER; var PLAYER = window.PLAYER;
var CHAT_SHRINKED = 60; var CHAT_SHRINKED = 60;
// Minimum zoom level resonator will display
var RESONATOR_DISPLAY_ZOOM_LEVEL = 17;
// Constants for resonator positioning
var SLOT_TO_LAT = [0, Math.sqrt(2)/2, 1, Math.sqrt(2)/2, 0, -Math.sqrt(2)/2, -1, -Math.sqrt(2)/2];
var SLOT_TO_LNG = [1, Math.sqrt(2)/2, 0, -Math.sqrt(2)/2, -1, -Math.sqrt(2)/2, 0, Math.sqrt(2)/2];
var EARTH_RADIUS=6378137;
// STORAGE /////////////////////////////////////////////////////////// // STORAGE ///////////////////////////////////////////////////////////
// global variables used for storage. Most likely READ ONLY. Proper // global variables used for storage. Most likely READ ONLY. Proper
// way would be to encapsulate them in an anonymous function and write // way would be to encapsulate them in an anonymous function and write
@ -163,6 +188,7 @@ var portalsLayers, linksLayer, fieldsLayer;
window.portals = {}; window.portals = {};
window.links = {}; window.links = {};
window.fields = {}; window.fields = {};
window.resonators = {};
// plugin framework. Plugins may load earlier than iitc, so dont // plugin framework. Plugins may load earlier than iitc, so dont
// overwrite data // overwrite data
@ -171,6 +197,55 @@ if(typeof window.plugin !== 'function') window.plugin = function() {};
// PLUGIN HOOKS ////////////////////////////////////////////////////////
// Plugins may listen to any number of events by specifying the name of
// the event to listen to and handing a function that should be exe-
// cuted when an event occurs. Callbacks will receive additional data
// the event created as their first parameter. The value is always a
// hash that contains more details.
//
// For example, this line will listen for portals to be added and print
// the data generated by the event to the console:
// window.addHook('portalAdded', function(data) { console.log(data) });
//
// Boot hook: booting is handled differently because IITC may not yet
// be available. Have a look at the plugins in plugins/. All
// code before “// PLUGIN START” and after “// PLUGIN END” os
// required to successfully boot the plugin.
//
// Heres more specific information about each event:
// portalAdded: called when a portal has been received and is about to
// be added to its layer group. Note that this does NOT
// mean it is already visible or will be, shortly after.
// If a portal is added to a hidden layer it may never be
// shown at all. Injection point is in
// code/map_data.js#renderPortal near the end. Will hand
// the Leaflet CircleMarker for the portal in "portal" var.
window._hooks = {}
window.VALID_HOOKS = ['portalAdded'];
window.runHooks = function(event, data) {
if(VALID_HOOKS.indexOf(event) === -1) throw('Unknown event type: ' + event);
if(!_hooks[event]) return;
$.each(_hooks[event], function(ind, callback) {
callback(data);
});
}
window.addHook = function(event, callback) {
if(VALID_HOOKS.indexOf(event) === -1) throw('Unknown event type: ' + event);
if(typeof callback !== 'function') throw('Callback must be a function.');
if(!_hooks[event])
_hooks[event] = [callback];
else
_hooks[event].push(callback);
}
// MAP DATA ////////////////////////////////////////////////////////// // MAP DATA //////////////////////////////////////////////////////////
// these functions handle how and which entities are displayed on the // these functions handle how and which entities are displayed on the
@ -242,8 +317,16 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
// to be in the foreground, or they cannot be clicked. See // to be in the foreground, or they cannot be clicked. See
// https://github.com/Leaflet/Leaflet/issues/185 // https://github.com/Leaflet/Leaflet/issues/185
var ppp = []; var ppp = [];
var p2f = {};
$.each(m, function(qk, val) { $.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;
fieldArray = window.portals[vertex.guid].options.portalV2.linkedFields;
fieldArray.splice($.inArray(guid, fieldArray), 1);
});
}
window.removeByGuid(guid); window.removeByGuid(guid);
}); });
@ -265,17 +348,37 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
ppp.push(ent); // delay portal render ppp.push(ent); // delay portal render
} else if(ent[2].edge !== undefined) } else if(ent[2].edge !== undefined) {
renderLink(ent); renderLink(ent);
else if(ent[2].capturedRegion !== undefined) } else if(ent[2].capturedRegion !== undefined) {
$.each(ent[2].capturedRegion, function(ind, vertex) {
if(p2f[vertex.guid] === undefined)
p2f[vertex.guid] = new Array();
p2f[vertex.guid].push(ent[0]);
});
renderField(ent); renderField(ent);
else } else {
throw('Unknown entity: ' + JSON.stringify(ent)); throw('Unknown entity: ' + JSON.stringify(ent));
}
}); });
}); });
$.each(ppp, function(ind, portal) {
if(portal[2].portalV2['linkedFields'] === undefined) {
portal[2].portalV2['linkedFields'] = [];
}
if(p2f[portal[0]] !== undefined) {
$.merge(p2f[portal[0]], portal[2].portalV2['linkedFields']);
portal[2].portalV2['linkedFields'] = uniqueArray(p2f[portal[0]]);
}
});
$.each(ppp, function(ind, portal) { renderPortal(portal); }); $.each(ppp, function(ind, portal) { renderPortal(portal); });
if(portals[selectedPortal]) portals[selectedPortal].bringToFront(); if(portals[selectedPortal]) {
try {
portals[selectedPortal].bringToFront();
} catch(e) { /* portal is now visible, catch Leaflet error */ }
}
if(portalInUrlAvailable) { if(portalInUrlAvailable) {
renderPortalDetails(urlPortal); renderPortalDetails(urlPortal);
@ -294,13 +397,16 @@ window.cleanUp = function() {
var minlvl = getMinPortalLevel(); var minlvl = getMinPortalLevel();
for(var i = 0; i < portalsLayers.length; i++) { for(var i = 0; i < portalsLayers.length; i++) {
// i is also the portal level // i is also the portal level
portalsLayers[i].eachLayer(function(portal) { portalsLayers[i].eachLayer(function(item) {
var itemGuid = item.options.guid;
// check if 'item' is a portal
if(getTypeByGuid(itemGuid) != TYPE_PORTAL) return true;
// portal must be in bounds and have a high enough level. Also dont // portal must be in bounds and have a high enough level. Also dont
// remove if it is selected. // remove if it is selected.
if(portal.options.guid == window.selectedPortal || if(itemGuid == window.selectedPortal ||
(b.contains(portal.getLatLng()) && i >= minlvl)) return; (b.contains(item.getLatLng()) && i >= minlvl)) return true;
cnt[0]++; cnt[0]++;
portalsLayers[i].removeLayer(portal); portalsLayers[i].removeLayer(item);
}); });
} }
linksLayer.eachLayer(function(link) { linksLayer.eachLayer(function(link) {
@ -316,31 +422,30 @@ window.cleanUp = function() {
console.log('removed out-of-bounds: '+cnt[0]+' portals, '+cnt[1]+' links, '+cnt[2]+' fields'); console.log('removed out-of-bounds: '+cnt[0]+' portals, '+cnt[1]+' links, '+cnt[2]+' fields');
} }
// removes given entity from map // removes given entity from map
window.removeByGuid = function(guid) { window.removeByGuid = function(guid) {
// portals end in “.11” or “.12“, links in “.9", fields in “.b” switch(getTypeByGuid(guid)) {
// .11 == portals case TYPE_PORTAL:
// .12 == portals
// .9 == links
// .b == fields
// .c == player/creator
// .d == chat messages
switch(guid.slice(33)) {
case '11':
case '12':
if(!window.portals[guid]) return; if(!window.portals[guid]) return;
var p = window.portals[guid]; var p = window.portals[guid];
for(var i = 0; i < portalsLayers.length; i++) for(var i = 0; i < portalsLayers.length; i++)
portalsLayers[i].removeLayer(p); portalsLayers[i].removeLayer(p);
break; break;
case '9': case TYPE_LINK:
if(!window.links[guid]) return; if(!window.links[guid]) return;
linksLayer.removeLayer(window.links[guid]); linksLayer.removeLayer(window.links[guid]);
break; break;
case 'b': case TYPE_FIELD:
if(!window.fields[guid]) return; if(!window.fields[guid]) return;
fieldsLayer.removeLayer(window.fields[guid]); fieldsLayer.removeLayer(window.fields[guid]);
break; break;
case TYPE_RESONATOR:
if(!window.resonators[guid]) return;
var r = window.resonators[guid];
for(var i = 1; i < portalsLayers.length; i++)
portalsLayers[i].removeLayer(r);
break;
default: default:
console.warn('unknown GUID type: ' + guid); console.warn('unknown GUID type: ' + guid);
//window.debug.printStackTrace(); //window.debug.printStackTrace();
@ -351,59 +456,148 @@ window.removeByGuid = function(guid) {
// renders a portal on the map from the given entity // renders a portal on the map from the given entity
window.renderPortal = function(ent) { window.renderPortal = function(ent) {
removeByGuid(ent[0]);
if(Object.keys(portals).length >= MAX_DRAWN_PORTALS && ent[0] != selectedPortal) if(Object.keys(portals).length >= MAX_DRAWN_PORTALS && ent[0] != selectedPortal)
return; return removeByGuid(ent[0]);
var latlng = [ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6];
// needs to be checked before, so the portal isnt added to the
// details list and other places
//if(!getPaddedBounds().contains(latlng)) return;
// hide low level portals on low zooms // hide low level portals on low zooms
var portalLevel = getPortalLevel(ent[2]); var portalLevel = getPortalLevel(ent[2]);
if(portalLevel < getMinPortalLevel() && ent[0] != selectedPortal) return; if(portalLevel < getMinPortalLevel() && ent[0] != selectedPortal)
return removeByGuid(ent[0]);
// pre-load player names for high zoom levels
if(map.getZoom() >= PRECACHE_PLAYER_NAMES_ZOOM) {
if(ent[2].captured && ent[2].captured.capturingPlayerId)
getPlayerName(ent[2].captured.capturingPlayerId);
if(ent[2].resonatorArray && ent[2].resonatorArray.resonators)
$.each(ent[2].resonatorArray.resonators, function(ind, reso) {
if(reso) getPlayerName(reso.ownerGuid);
});
}
var team = getTeam(ent[2]); var team = getTeam(ent[2]);
// do nothing if portal did not change
var layerGroup = portalsLayers[parseInt(portalLevel)];
var old = findEntityInLeaflet(layerGroup, window.portals, ent[0]);
if(old) {
var oo = old.options;
var u = oo.team !== team;
u = u || oo.level !== portalLevel;
// nothing for the portal changed, so dont update. Let resonators
// manage themselves if they want to be updated.
if(!u) return renderResonators(ent);
removeByGuid(ent[0]);
}
// there were changes, remove old portal
removeByGuid(ent[0]);
var latlng = [ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6];
// 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 p = L.circleMarker(latlng, { var p = L.circleMarker(latlng, {
radius: 7, radius: lvRadius,
color: ent[0] == selectedPortal ? COLOR_SELECTED_PORTAL : COLORS[team], color: ent[0] == selectedPortal ? COLOR_SELECTED_PORTAL : COLORS[team],
opacity: 1, opacity: 1,
weight: 3, weight: lvWeight,
fillColor: COLORS[team], fillColor: COLORS[team],
fillOpacity: 0.5, fillOpacity: 0.5,
clickable: true, clickable: true,
level: portalLevel, level: portalLevel,
team: team,
details: ent[2], details: ent[2],
guid: ent[0]}); guid: ent[0]});
p.on('remove', function() { delete window.portals[this.options.guid]; }); p.on('remove', function() {
var portalGuid = this.options.guid
// remove attached resonators, skip if
// all resonators have already removed by zooming
if(isResonatorsShow()) {
for(var i = 0; i <= 7; i++)
removeByGuid(portalResonatorGuid(portalGuid,i));
}
delete window.portals[portalGuid];
if(window.selectedPortal === portalGuid) {
window.unselectOldPortal();
window.map.removeLayer(window.portalAccessIndicator);
window.portalAccessIndicator = null;
}
});
p.on('add', function() { p.on('add', function() {
// enable for debugging
if(window.portals[this.options.guid]) throw('duplicate portal detected');
window.portals[this.options.guid] = this; window.portals[this.options.guid] = this;
// handles the case where a selected portal gets removed from the // handles the case where a selected portal gets removed from the
// map by hiding all portals with said level // map by hiding all portals with said level
if(window.selectedPortal != this.options.guid) if(window.selectedPortal != this.options.guid)
window.portalResetColor(this); window.portalResetColor(this);
}); });
p.on('click', function() { window.renderPortalDetails(ent[0]); }); p.on('click', function() { window.renderPortalDetails(ent[0]); });
p.on('dblclick', function() { p.on('dblclick', function() {
window.renderPortalDetails(ent[0]); window.renderPortalDetails(ent[0]);
window.map.setView(latlng, 17); window.map.setView(latlng, 17);
}); });
window.renderResonators(ent);
window.runHooks('portalAdded', {portal: p});
// portalLevel contains a float, need to round down // portalLevel contains a float, need to round down
p.addTo(portalsLayers[parseInt(portalLevel)]); p.addTo(layerGroup);
}
window.renderResonators = function(ent) {
var portalLevel = getPortalLevel(ent[2]);
if(portalLevel < getMinPortalLevel() && ent[0] != selectedPortal) return;
if(!isResonatorsShow()) return;
for(var i=0; i < ent[2].resonatorArray.resonators.length; i++) {
var rdata = ent[2].resonatorArray.resonators[i];
if(rdata == null) continue;
if(window.resonators[portalResonatorGuid(ent[0],i)]) continue;
// offset in meters
var dn = rdata.distanceToPortal*SLOT_TO_LAT[rdata.slot];
var de = rdata.distanceToPortal*SLOT_TO_LNG[rdata.slot];
// Coordinate offset in radians
var dLat = dn/EARTH_RADIUS;
var dLon = de/(EARTH_RADIUS*Math.cos(Math.PI/180*(ent[2].locationE6.latE6/1E6)));
// OffsetPosition, decimal degrees
var lat0 = ent[2].locationE6.latE6/1E6 + dLat * 180/Math.PI;
var lon0 = ent[2].locationE6.lngE6/1E6 + dLon * 180/Math.PI;
var Rlatlng = [lat0, lon0];
var r = L.circleMarker(Rlatlng, {
radius: 3,
// #AAAAAA outline seems easier to see the fill opacity
color: '#AAAAAA',
opacity: 1,
weight: 1,
fillColor: COLORS_LVL[rdata.level],
fillOpacity: rdata.energyTotal/RESO_NRG[rdata.level],
clickable: false,
level: rdata.level,
details: rdata,
pDetails: ent[2],
guid: portalResonatorGuid(ent[0],i) });
r.on('remove', function() { delete window.resonators[this.options.guid]; });
r.on('add', function() { window.resonators[this.options.guid] = this; });
r.addTo(portalsLayers[parseInt(portalLevel)]);
}
}
// append portal guid with -resonator-[slot] to get guid for resonators
window.portalResonatorGuid = function(portalGuid, slot) {
return portalGuid + '-resonator-' + slot;
}
window.isResonatorsShow = function() {
return map.getZoom() >= RESONATOR_DISPLAY_ZOOM_LEVEL;
} }
window.portalResetColor = function(portal) { window.portalResetColor = function(portal) {
@ -412,8 +606,12 @@ window.portalResetColor = function(portal) {
// renders a link on the map from the given entity // renders a link on the map from the given entity
window.renderLink = function(ent) { window.renderLink = function(ent) {
removeByGuid(ent[0]); if(Object.keys(links).length >= MAX_DRAWN_LINKS)
if(Object.keys(links).length >= MAX_DRAWN_LINKS) return; return removeByGuid(ent[0]);
// assume that links never change. If they do, they will have a
// different ID.
if(findEntityInLeaflet(linksLayer, links, ent[0])) return;
var team = getTeam(ent[2]); var team = getTeam(ent[2]);
var edge = ent[2].edge; var edge = ent[2].edge;
@ -433,14 +631,23 @@ window.renderLink = function(ent) {
if(!getPaddedBounds().intersects(poly.getBounds())) return; if(!getPaddedBounds().intersects(poly.getBounds())) return;
poly.on('remove', function() { delete window.links[this.options.guid]; }); poly.on('remove', function() { delete window.links[this.options.guid]; });
poly.on('add', function() { window.links[this.options.guid] = this; }); poly.on('add', function() {
poly.addTo(linksLayer).bringToBack(); // enable for debugging
if(window.links[this.options.guid]) throw('duplicate link detected');
window.links[this.options.guid] = this;
this.bringToBack();
});
poly.addTo(linksLayer);
} }
// renders a field on the map from a given entity // renders a field on the map from a given entity
window.renderField = function(ent) { window.renderField = function(ent) {
window.removeByGuid(ent[0]); if(Object.keys(fields).length >= MAX_DRAWN_FIELDS)
if(Object.keys(fields).length >= MAX_DRAWN_FIELDS) return; 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 team = getTeam(ent[2]); var team = getTeam(ent[2]);
var reg = ent[2].capturedRegion; var reg = ent[2].capturedRegion;
@ -455,13 +662,40 @@ window.renderField = function(ent) {
stroke: false, stroke: false,
clickable: false, clickable: false,
smoothFactor: 10, smoothFactor: 10,
vertices: ent[2].capturedRegion,
lastUpdate: ent[1],
guid: ent[0]}); guid: ent[0]});
if(!getPaddedBounds().intersects(poly.getBounds())) return; if(!getPaddedBounds().intersects(poly.getBounds())) return;
poly.on('remove', function() { delete window.fields[this.options.guid]; }); poly.on('remove', function() { delete window.fields[this.options.guid]; });
poly.on('add', function() { window.fields[this.options.guid] = this; }); poly.on('add', function() {
poly.addTo(fieldsLayer).bringToBack(); // enable for debugging
if(window.fields[this.options.guid]) console.warn('duplicate field detected');
window.fields[this.options.guid] = this;
this.bringToBack();
});
poly.addTo(fieldsLayer);
}
// looks for the GUID in either the layerGroup or entityHash, depending
// on which is faster. Will either return the Leaflet entity or null, if
// it does not exist.
// For example, to find a field use the function like this:
// field = findEntityInLeaflet(fieldsLayer, fields, 'asdasdasd');
window.findEntityInLeaflet = function(layerGroup, entityHash, guid) {
// fast way
if(map.hasLayer(layerGroup)) return entityHash[guid] || null;
// slow way in case the layer is currently hidden
var ent = null;
layerGroup.eachLayer(function(entity) {
if(entity.options.guid !== guid) return true;
ent = entity;
return false;
});
return ent;
} }
@ -517,7 +751,7 @@ window.renderUpdateStatus = function() {
t += ' <span style="color:red" class="help" title="Can only render so much before it gets unbearably slow. Not all entities are shown. Zoom in or increase the limit (search for MAX_DRAWN_*).">RENDER LIMIT</span> ' t += ' <span style="color:red" class="help" title="Can only render so much before it gets unbearably slow. Not all entities are shown. Zoom in or increase the limit (search for MAX_DRAWN_*).">RENDER LIMIT</span> '
if(window.failedRequestCount > 0) if(window.failedRequestCount > 0)
t += ' <span style="color:red">' + window.failedRequestCount + ' requests failed</span>.' t += ' <span style="color:red">' + window.failedRequestCount + ' failed</span>.'
t += '<br/>('; t += '<br/>(';
var minlvl = getMinPortalLevel(); var minlvl = getMinPortalLevel();
@ -677,6 +911,13 @@ window.rangeLinkClick = function() {
window.reportPortalIssue = function(info) { 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. 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 d = window.portals[window.selectedPortal].options.details;
var info = 'Your Nick: ' + PLAYER.nickname + ' '
+ 'Portal: ' + d.portalV2.descriptiveText.TITLE + ' '
+ 'Location: ' + d.portalV2.descriptiveText.ADDRESS
+' (lat ' + (d.locationE6.latE6/1E6) + '; lng ' + (d.locationE6.lngE6/1E6) + ')';
//codename, approx addr, portalname //codename, approx addr, portalname
if(prompt(t, info) !== null) if(prompt(t, info) !== null)
location.href = 'https://support.google.com/ingress?hl=en'; location.href = 'https://support.google.com/ingress?hl=en';
@ -718,8 +959,51 @@ window.scrollBottom = function(elm) {
} }
window.zoomToAndShowPortal = function(guid, latlng) { window.zoomToAndShowPortal = function(guid, latlng) {
renderPortalDetails(guid);
map.setView(latlng, 17); map.setView(latlng, 17);
// if the data is available, render it immediately. Otherwise defer
// until it becomes available.
if(window.portals[guid])
renderPortalDetails(guid);
else
urlPortal = guid;
}
// translates guids to entity types
window.getTypeByGuid = function(guid) {
// portals end in “.11” or “.12“, links in “.9", fields in “.b”
// .11 == portals
// .12 == portals
// .9 == links
// .b == fields
// .c == player/creator
// .d == chat messages
//
// others, not used in web:
// .5 == resources (burster/resonator)
// .6 == XM
// .4 == media items, maybe all droppped resources (?)
// resonator guid is [portal guid]-resonator-[slot]
switch(guid.slice(33)) {
case '11':
case '12':
return TYPE_PORTAL;
case '9':
return TYPE_LINK;
case 'b':
return TYPE_FIELD;
case 'c':
return TYPE_PLAYER;
case 'd':
return TYPE_CHAT;
default:
if(guid.slice(-11,-2) == 'resonator') return TYPE_RESONATOR;
return TYPE_UNKNOWN;
}
} }
String.prototype.capitalize = function() { String.prototype.capitalize = function() {
@ -733,6 +1017,24 @@ if (typeof String.prototype.startsWith !== 'function') {
}; };
} }
window.prettyEnergy = function(nrg) {
return nrg> 1000 ? Math.round(nrg/1000) + 'k': nrg;
}
window.setPermaLink = function(elm) {
var c = map.getCenter();
var lat = Math.round(c.lat*1E6);
var lng = Math.round(c.lng*1E6);
var qry = 'latE6='+lat+'&lngE6='+lng+'&z=' + map.getZoom();
$(elm).attr('href', 'http://www.ingress.com/intel?' + qry);
}
window.uniqueArray = function(arr) {
return $.grep(arr, function(v, i) {
return $.inArray(v, arr) === i;
});
}
@ -763,16 +1065,18 @@ window.setupLargeImagePreview = function() {
window.setupStyles = function() { window.setupStyles = function() {
$('head').append('<style>' + $('head').append('<style>' +
[ '#map { margin-right: '+(SIDEBAR_WIDTH+2)+'px } ', [ 'body:after { display: none } ',
'#largepreview.enl img { border:2px solid '+COLORS[TEAM_ENL]+'; } ', '#largepreview.enl img { border:2px solid '+COLORS[TEAM_ENL]+'; } ',
'#largepreview.res img { border:2px solid '+COLORS[TEAM_RES]+'; } ', '#largepreview.res img { border:2px solid '+COLORS[TEAM_RES]+'; } ',
'#largepreview.none img { border:2px solid '+COLORS[TEAM_NONE]+'; } ', '#largepreview.none img { border:2px solid '+COLORS[TEAM_NONE]+'; } ',
'#chatcontrols { bottom: '+(CHAT_SHRINKED+24)+'px; }', '#chatcontrols { bottom: '+(CHAT_SHRINKED+24)+'px; }',
'#chat { height: '+CHAT_SHRINKED+'px; } ', '#chat { height: '+CHAT_SHRINKED+'px; } ',
'#updatestatus { width:'+(SIDEBAR_WIDTH-2*4)+'px; } ', '.leaflet-right { margin-right: '+(SIDEBAR_WIDTH+1)+'px } ',
'#sidebar { width:'+(SIDEBAR_WIDTH + HIDDEN_SCROLLBAR_ASSUMED_WIDTH + 2 /*border*/)+'px; } ', '#updatestatus { width:'+(SIDEBAR_WIDTH-2*4+1)+'px; } ',
'#sidebar { width:'+(SIDEBAR_WIDTH + HIDDEN_SCROLLBAR_ASSUMED_WIDTH + 1 /*border*/)+'px; } ',
'#sidebartoggle { right:'+SIDEBAR_WIDTH+'px; } ',
'#scrollwrapper { width:'+(SIDEBAR_WIDTH + 2*HIDDEN_SCROLLBAR_ASSUMED_WIDTH)+'px; right:-'+(2*HIDDEN_SCROLLBAR_ASSUMED_WIDTH-2)+'px } ', '#scrollwrapper { width:'+(SIDEBAR_WIDTH + 2*HIDDEN_SCROLLBAR_ASSUMED_WIDTH)+'px; right:-'+(2*HIDDEN_SCROLLBAR_ASSUMED_WIDTH-2)+'px } ',
'#sidebar input, h2, #updatestatus { width:'+(SIDEBAR_WIDTH - 2*4)+'px !important } ', '#sidebar input, h2 { width:'+(SIDEBAR_WIDTH - 2*4)+'px !important } ',
'#sidebar > *, #gamestat span, .imgpreview img { width:'+SIDEBAR_WIDTH+'px; }'].join("\n") '#sidebar > *, #gamestat span, .imgpreview img { width:'+SIDEBAR_WIDTH+'px; }'].join("\n")
+ '</style>'); + '</style>');
} }
@ -829,7 +1133,22 @@ window.setupMap = function() {
map.attributionControl.setPrefix(''); map.attributionControl.setPrefix('');
// listen for changes and store them in cookies // listen for changes and store them in cookies
map.on('moveend', window.storeMapPosition); map.on('moveend', window.storeMapPosition);
map.on('zoomend', window.storeMapPosition); map.on('zoomend', function() {
window.storeMapPosition;
// remove all resonators if zoom out to < RESONATOR_DISPLAY_ZOOM_LEVEL
if(isResonatorsShow()) return;
for(var i = 1; i < portalsLayers.length; i++) {
portalsLayers[i].eachLayer(function(item) {
var itemGuid = item.options.guid;
// check if 'item' is a resonator
if(getTypeByGuid(itemGuid) != TYPE_RESONATOR) return true;
portalsLayers[i].removeLayer(item);
});
}
console.log('Remove all resonators');
});
$("[name='leaflet-base-layers']").change(function () { $("[name='leaflet-base-layers']").change(function () {
writeCookie('ingress.intelmap.type', $(this).parent().index()); writeCookie('ingress.intelmap.type', $(this).parent().index());
}); });
@ -890,6 +1209,24 @@ window.setupPlayerStat = function() {
); );
} }
window.setupSidebarToggle = function() {
$('#sidebartoggle').on('click', function() {
var toggle = $('#sidebartoggle');
var sidebar = $('#scrollwrapper');
if(sidebar.is(':visible')) {
sidebar.hide().css('z-index', 1);
$('.leaflet-right').css('margin-right','0');
toggle.html('<span class="toggle open"></span>');
toggle.css('right', '0');
} else {
sidebar.css('z-index', 1001).show();
$('.leaflet-right').css('margin-right', SIDEBAR_WIDTH+1+'px');
toggle.html('<span class="toggle close"></span>');
toggle.css('right', SIDEBAR_WIDTH+1+'px');
}
});
}
// BOOTING /////////////////////////////////////////////////////////// // BOOTING ///////////////////////////////////////////////////////////
@ -900,6 +1237,7 @@ function boot() {
window.setupGeosearch(); window.setupGeosearch();
window.setupRedeem(); window.setupRedeem();
window.setupLargeImagePreview(); window.setupLargeImagePreview();
window.setupSidebarToggle();
window.updateGameScore(); window.updateGameScore();
window.setupPlayerStat(); window.setupPlayerStat();
window.chat.setup(); window.chat.setup();
@ -928,10 +1266,10 @@ function asyncLoadScript(a){return function(b,c){var d=document.createElement("s
// modified version of https://github.com/shramov/leaflet-plugins. Also // modified version of https://github.com/shramov/leaflet-plugins. Also
// contains the default Ingress map style. // contains the default Ingress map style.
var LLGMAPS = 'http://breunigs.github.com/ingress-intel-total-conversion/external/leaflet_google.js'; var LLGMAPS = 'http://breunigs.github.com/ingress-intel-total-conversion/dist/leaflet_google.js';
var JQUERY = 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js'; var JQUERY = 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js';
var LEAFLET = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.js'; var LEAFLET = 'http://cdn.leafletjs.com/leaflet-0.5/leaflet.js';
var AUTOLINK = 'http://breunigs.github.com/ingress-intel-total-conversion/external/autolink.js'; var AUTOLINK = 'http://breunigs.github.com/ingress-intel-total-conversion/dist/autolink.js';
// after all scripts have loaded, boot the actual app // after all scripts have loaded, boot the actual app
load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS).onError(function (err) { load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS).onError(function (err) {
@ -1397,7 +1735,7 @@ window.chat.renderMsg = function(msg, nick, time, team) {
var t = '<time title="'+tb+'" data-timestamp="'+time+'">'+ta+'</time>'; var t = '<time title="'+tb+'" data-timestamp="'+time+'">'+ta+'</time>';
var s = 'style="color:'+COLORS[team]+'"'; var s = 'style="color:'+COLORS[team]+'"';
var title = nick.length >= 8 ? 'title="'+nick+'" class="help"' : ''; var title = nick.length >= 8 ? 'title="'+nick+'" class="help"' : '';
return '<p>'+t+'<mark '+s+'>'+nick+'</mark><span>'+msg+'</span></p>'; return '<p>'+t+'<span class="invisibleseparator"> &lt;</span><mark '+s+'>'+nick+'</mark><span class="invisibleseparator">&gt; </span><span>'+msg+'</span></p>';
} }
@ -1410,14 +1748,16 @@ window.chat.getActive = function() {
window.chat.toggle = function() { window.chat.toggle = function() {
var c = $('#chat, #chatcontrols'); var c = $('#chat, #chatcontrols');
if(c.hasClass('expand')) { if(c.hasClass('expand')) {
$('#chatcontrols a:first').text('expand'); $('#chatcontrols a:first').html('<span class="toggle expand"></span>');
c.removeClass('expand'); c.removeClass('expand');
var div = $('#chat > div:visible'); var div = $('#chat > div:visible');
div.data('ignoreNextScroll', true); div.data('ignoreNextScroll', true);
div.scrollTop(99999999); // scroll to bottom div.scrollTop(99999999); // scroll to bottom
$('.leaflet-control').css('margin-left', '13px');
} else { } else {
$('#chatcontrols a:first').text('shrink'); $('#chatcontrols a:first').html('<span class="toggle shrink"></span>');
c.addClass('expand'); c.addClass('expand');
$('.leaflet-control').css('margin-left', '720px');
chat.needMoreMessages(); chat.needMoreMessages();
} }
} }
@ -1635,12 +1975,12 @@ window.chat.postMsg = function() {
// returns displayable text+link about portal range // returns displayable text+link about portal range
window.getRangeText = function(d) { window.getRangeText = function(d) {
var range = getPortalRange(d); var range = getPortalRange(d);
return 'range: ' return ['range',
+ '<a onclick="window.rangeLinkClick()">' '<a onclick="window.rangeLinkClick()">'
+ (range > 1000 + (range > 1000
? Math.round(range/1000) + 'km' ? Math.round(range/1000) + 'km'
: Math.round(range) + 'm') : Math.round(range) + 'm')
+ '</a>'; + '</a>'];
} }
// generates description text from details for portal // generates description text from details for portal
@ -1694,30 +2034,31 @@ window.getModDetails = function(d) {
} }
window.getEnergyText = function(d) { window.getEnergyText = function(d) {
var nrg = getPortalEnergy(d); var currentNrg = getCurrentPortalEnergy(d);
return 'energy: ' + (nrg > 1000 ? Math.round(nrg/1000) +'k': nrg); var totalNrg = getTotalPortalEnergy(d);
var inf = currentNrg + ' / ' + totalNrg;
var fill = prettyEnergy(currentNrg) + ' / ' + prettyEnergy(totalNrg)
return ['energy', '<tt title="'+inf+'">' + fill + '</tt>'];
} }
window.getAvgResoDistText = function(d) { window.getAvgResoDistText = function(d) {
var avgDist = Math.round(10*getAvgResoDist(d))/10; var avgDist = Math.round(10*getAvgResoDist(d))/10;
return '⌀ res dist: ' + avgDist + 'm'; return ['⌀ res dist', avgDist + 'm'];
}
window.getReportIssueInfoText = function(d) {
return ('Your Nick: '+PLAYER.nickname+' '
+ 'Portal: '+d.portalV2.descriptiveText.TITLE+' '
+ 'Location: '+d.portalV2.descriptiveText.ADDRESS
+' (lat '+(d.locationE6.latE6/1E6)+'; lng '+(d.locationE6.lngE6/1E6)+')'
).replace(/['"]/, '');
} }
window.getResonatorDetails = function(d) { window.getResonatorDetails = function(d) {
console.log('rendering reso details');
var resoDetails = ''; var resoDetails = '';
var slotsFilled = 0; // octant=slot: 0=E, 1=NE, 2=N, 3=NW, 4=W, 5=SW, 6=S, SE=7
$.each(d.resonatorArray.resonators, function(ind, reso) { // resos in the display should be ordered like this:
// N NE Since the view is displayed in columns, they
// NW E need to be ordered like this: N, NW, W, SW, NE,
// W SE E, SE, S, i.e. 2 3 4 5 1 0 7 6
// SW S
$.each([2, 3, 4, 5, 1, 0, 7, 6], function(ind, slot) {
var isLeft = slot >= 2 && slot <= 5;
var reso = d.resonatorArray.resonators[slot];
if(!reso) { if(!reso) {
resoDetails += renderResonatorDetails(slotsFilled++, 0); resoDetails += renderResonatorDetails(slot, 0, 0, null, null, isLeft);
return true; return true;
} }
@ -1725,9 +2066,11 @@ window.getResonatorDetails = function(d) {
var v = parseInt(reso.energyTotal); var v = parseInt(reso.energyTotal);
var nick = window.getPlayerName(reso.ownerGuid); var nick = window.getPlayerName(reso.ownerGuid);
var dist = reso.distanceToPortal; var dist = reso.distanceToPortal;
// if array order and slot order drift apart, at least the octant
// naming will still be correct.
slot = parseInt(reso.slot);
slotsFilled++; resoDetails += renderResonatorDetails(slot, l, v, dist, nick, isLeft);
resoDetails += renderResonatorDetails(parseInt(reso.slot), l, v, dist, nick);
}); });
return resoDetails; return resoDetails;
} }
@ -1736,15 +2079,15 @@ window.getResonatorDetails = function(d) {
// not work with raw details-hash. Needs digested infos instead: // not work with raw details-hash. Needs digested infos instead:
// slot: which slot this resonator occupies. Starts with 0 (east) and // slot: which slot this resonator occupies. Starts with 0 (east) and
// rotates clockwise. So, last one is 7 (southeast). // rotates clockwise. So, last one is 7 (southeast).
window.renderResonatorDetails = function(slot, level, nrg, dist, nick) { window.renderResonatorDetails = function(slot, level, nrg, dist, nick, isLeft) {
if(level == 0) { if(level === 0) {
var meter = '<span class="meter" style="cursor:auto"></span>'; var meter = '<span class="meter" title="octant:\t' + OCTANTS[slot] + '"></span>';
} else { } else {
var max = RESO_NRG[level]; var max = RESO_NRG[level];
var fillGrade = nrg/max*100; var fillGrade = nrg/max*100;
var inf = 'energy:\t\t' + nrg + ' / ' + max + ' (' + Math.round(fillGrade) + '%)' + '\n' var inf = 'energy:\t\t' + nrg + ' / ' + max + ' (' + Math.round(fillGrade) + '%)\n'
+ 'level:\t\t' + level +'\n' + 'level:\t\t' + level + '\n'
+ 'distance:\t' + dist + 'm\n' + 'distance:\t' + dist + 'm\n'
+ 'owner:\t\t' + nick + '\n' + 'owner:\t\t' + nick + '\n'
+ 'octant:\t' + OCTANTS[slot]; + 'octant:\t' + OCTANTS[slot];
@ -1757,12 +2100,41 @@ window.renderResonatorDetails = function(slot, level, nrg, dist, nick) {
var fill = '<span style="'+style+'"></span>'; var fill = '<span style="'+style+'"></span>';
var meter = '<span class="meter meter-rel" title="'+inf+'">' var meter = '<span class="meter meter-rel" title="'+inf+'">' + fill + lbar + '</span>';
+ fill + lbar + '</span>';
} }
var cls = slot <= 3 ? 'left' : 'right'; var cls = isLeft ? 'left' : 'right';
var text = '<span class="meter-text '+cls+'">'+(nick||'')+'</span>'; var text = '<span class="meter-text '+cls+'">'+(nick||'')+'</span>';
return (slot <= 3 ? text+meter : meter+text) + '<br/>'; return (isLeft ? text+meter : meter+text) + '<br/>';
}
// calculate AP gain from destroying portal
// so far it counts only resonators + links
window.getDestroyAP = function(d) {
var resoCount = 0;
$.each(d.resonatorArray.resonators, function(ind, reso) {
if(!reso) return true;
resoCount += 1;
});
var linkCount = d.portalV2.linkedEdges ? d.portalV2.linkedEdges.length : 0;
var fieldCount = d.portalV2.linkedFields ? d.portalV2.linkedFields.length : 0;
var resoAp = resoCount * DESTROY_RESONATOR;
var linkAp = linkCount * DESTROY_LINK;
var fieldAp = fieldCount * DESTROY_FIELD;
var sum = resoAp + linkAp + fieldAp;
function tt(text) {
var t = 'Destroy:\n';
t += resoCount + '×\tResonators\t= ' + digits(resoAp) + '\n';
t += linkCount + '×\tLinks\t\t= ' + digits(linkAp) + '\n';
t += fieldCount + '×\tFields\t\t= ' + digits(fieldAp) + '\n';
t += 'Sum: ' + digits(sum) + ' AP';
return '<tt title="'+t+'">' + digits(text) + '</tt>';
}
return [tt('AP Gain'), tt(sum)];
} }
@ -1977,22 +2349,23 @@ window.renderPortalDetails = function(guid) {
links[link.isOrigin ? 'outgoing' : 'incoming']++; links[link.isOrigin ? 'outgoing' : 'incoming']++;
}); });
function linkExpl(t) { return '<tt title="↳ incoming links\n↴ outgoing links\n• is meant to be the portal.">'+t+'</tt>'; } function linkExpl(t) { return '<tt title="↳ incoming links\n↴ outgoing links\n• is meant to be the portal.">'+t+'</tt>'; }
var linksText = linkExpl('links')+':'+linkExpl(' ↳ ' + links.incoming+'&nbsp;&nbsp;•&nbsp;&nbsp;'+links.outgoing+' ↴'); var linksText = [linkExpl('links'), linkExpl(' ↳ ' + links.incoming+'&nbsp;&nbsp;•&nbsp;&nbsp;'+links.outgoing+' ↴')];
var player = d.captured && d.captured.capturingPlayerId var player = d.captured && d.captured.capturingPlayerId
? getPlayerName(d.captured.capturingPlayerId) ? getPlayerName(d.captured.capturingPlayerId)
: null; : null;
var playerText = player ? 'owner: ' + player : null; var playerText = player ? ['owner', player] : null;
var time = d.captured ? unixTimeToString(d.captured.capturedTime) : null; var time = d.captured ? unixTimeToString(d.captured.capturedTime) : null;
var sinceText = time ? 'since: ' + time : null; var sinceText = time ? ['since', time] : null;
var linkedFields = ['fields', d.portalV2.linkedFields.length];
// collect and html-ify random data // collect and html-ify random data
var randDetails = [playerText, sinceText, getRangeText(d), getEnergyText(d), linksText, getAvgResoDistText(d)]; var randDetails = [playerText, sinceText, getRangeText(d), getEnergyText(d), linksText, getAvgResoDistText(d), linkedFields, getDestroyAP(d)];
randDetails = randDetails.map(function(detail) { randDetails = randDetails.map(function(detail) {
if(!detail) return ''; if(!detail) return '';
detail = detail.split(':'); detail = '<aside>'+detail[0]+'<span>'+detail[1]+'</span></aside>';
detail = '<aside>'+detail.shift()+'<span>'+detail.join(':')+'</span></aside>';
return detail; return detail;
}).join('\n'); }).join('\n');
@ -2026,7 +2399,7 @@ window.renderPortalDetails = function(guid) {
+ '<div id="resodetails">'+getResonatorDetails(d)+'</div>' + '<div id="resodetails">'+getResonatorDetails(d)+'</div>'
+ '<div class="linkdetails">' + '<div class="linkdetails">'
+ '<aside><a href="'+perma+'">portal link</a></aside>' + '<aside><a href="'+perma+'">portal link</a></aside>'
+ '<aside><a onclick="window.reportPortalIssue(\''+getReportIssueInfoText(d)+'\')">report issue</a></aside>' + '<aside><a onclick="window.reportPortalIssue()">report issue</a></aside>'
+ '</div>' + '</div>'
); );
} }
@ -2188,6 +2561,21 @@ window.setPlayerName = function(guid, nick) {
} }
window.loadPlayerNamesForPortal = function(portal_details) {
if(map.getZoom() < PRECACHE_PLAYER_NAMES_ZOOM) return;
var e = portal_details;
if(e.captured && e.captured.capturingPlayerId)
getPlayerName(e.captured.capturingPlayerId);
if(!e.resonatorArray || !e.resonatorArray.resonators) return;
$.each(e.resonatorArray.resonators, function(ind, reso) {
if(reso) getPlayerName(reso.ownerGuid);
});
}
// DEBUGGING TOOLS /////////////////////////////////////////////////// // DEBUGGING TOOLS ///////////////////////////////////////////////////
// meant to be used from browser debugger tools and the like. // meant to be used from browser debugger tools and the like.
@ -2273,7 +2661,21 @@ window.getPortalLevel = function(d) {
return hasReso ? Math.max(1, lvl/8) : 0; return hasReso ? Math.max(1, lvl/8) : 0;
} }
window.getPortalEnergy = function(d) { window.getTotalPortalEnergy = function(d) {
var nrg = 0;
$.each(d.resonatorArray.resonators, function(ind, reso) {
if(!reso) return true;
var level = parseInt(reso.level);
var max = RESO_NRG[level];
nrg += max;
});
return nrg;
}
// For backwards compatibility
window.getPortalEnergy = window.getTotalPortalEnergy;
window.getCurrentPortalEnergy = function(d) {
var nrg = 0; var nrg = 0;
$.each(d.resonatorArray.resonators, function(ind, reso) { $.each(d.resonatorArray.resonators, function(ind, reso) {
if(!reso) return true; if(!reso) return true;