diff --git a/README.md b/README.md
index c6b6c2a5..4a6d476e 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ Features
- hack range (yellow circle) and link range (large red circle) for portals. Click on the range link in the sidebar to zoom to link range.
- double clicking a portal zooms in and focuses it
- display of XM and AP rewards for redeemed passcodes
+- [extend it even more with the use of plugins](https://github.com/breunigs/ingress-intel-total-conversion/plugins)
Install
@@ -70,9 +71,7 @@ Please do!
(Obviously, Resistance folks must send in complete patches while Enlightenment gals and guys may just open feature request ☺)
-
-Contributors
-------------
+**So far, these people have contributed:**
[Bananeweizen](https://github.com/Bananeweizen),
[cmrn](https://github.com/cmrn),
@@ -83,8 +82,10 @@ Contributors
[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),
+[vita10gy](https://github.com/vita10gy),
[Xelio](https://github.com/Xelio),
[ZauberNerd](https://github.com/ZauberNerd),
[jonatkins](https://github.com/jonatkins)
@@ -115,9 +116,10 @@ Attribution & License
This project is licensed under the permissive ISC license. Parts imported from other projects remain under their respective licenses:
-- [load.js by Chris O'Hara; MIT](https://github.com/chriso/load.js)
- [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
diff --git a/code/boot.js b/code/boot.js
index 57e6ebe9..e0397ebe 100644
--- a/code/boot.js
+++ b/code/boot.js
@@ -36,7 +36,7 @@ window.setupStyles = function() {
'.leaflet-right { margin-right: '+(SIDEBAR_WIDTH+1)+'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; } ',
+ '#sidebartoggle { right:'+(SIDEBAR_WIDTH+1)+'px; } ',
'#scrollwrapper { width:'+(SIDEBAR_WIDTH + 2*HIDDEN_SCROLLBAR_ASSUMED_WIDTH)+'px; right:-'+(2*HIDDEN_SCROLLBAR_ASSUMED_WIDTH-2)+'px } ',
'#sidebar input, h2 { width:'+(SIDEBAR_WIDTH - 2*4)+'px !important } ',
'#sidebar > *, #gamestat span, .imgpreview img { width:'+SIDEBAR_WIDTH+'px; }'].join("\n")
@@ -83,7 +83,7 @@ window.setupMap = function() {
map.addLayer(linksLayer, true);
addLayers['Links'] = linksLayer;
- map.addControl(new L.Control.Layers({
+ window.layerChooser = new L.Control.Layers({
'OSM Cloudmade Midnight': views[0],
'OSM Cloudmade Minimal': views[1],
'OSM Mapnik': views[2],
@@ -91,7 +91,9 @@ window.setupMap = function() {
'Google Roads': views[4],
'Google Satellite': views[5],
'Google Hybrid': views[6]
- }, addLayers));
+ }, addLayers);
+
+ map.addControl(window.layerChooser);
map.attributionControl.setPrefix('');
// listen for changes and store them in cookies
map.on('moveend', window.storeMapPosition);
@@ -111,8 +113,10 @@ window.setupMap = function() {
console.log('Remove all resonators');
});
- $("[name='leaflet-base-layers']").change(function () {
- writeCookie('ingress.intelmap.type', $(this).parent().index());
+
+ map.on('baselayerchange', function () {
+ var selInd = $('[name=leaflet-base-layers]:checked').parent().index();
+ writeCookie('ingress.intelmap.type', selInd);
});
// map update status handling
@@ -153,11 +157,11 @@ window.setupPlayerStat = function() {
var cls = PLAYER.team === 'ALIENS' ? 'enl' : 'res';
- var t = 'Level:\t\t' + level + '\n'
- + 'XM:\t\t\t' + PLAYER.energy + ' / ' + xmMax + '\n'
- + 'AP:\t\t\t' + digits(ap) + '\n'
+ var t = 'Level:\t' + level + '\n'
+ + 'XM:\t' + PLAYER.energy + ' / ' + xmMax + '\n'
+ + 'AP:\t' + digits(ap) + '\n'
+ (level < 8 ? 'level up in:\t' + lvlUpAp + ' AP' : 'Congrats! (neeeeerd)')
- + '\n\Invites:\t\t'+PLAYER.available_invites;
+ + '\n\Invites:\t'+PLAYER.available_invites;
+ '\n\nNote: your player stats can only be updated by a full reload (F5)';
$('#playerstat').html(''
@@ -189,10 +193,57 @@ window.setupSidebarToggle = function() {
});
}
+window.setupTooltips = function() {
+ $(document).tooltip({
+ // disable show/hide animation
+ show: { effect: "hide", duration: 0 } ,
+ hide: false,
+ open: function(event, ui) {
+ ui.tooltip.delay(300).fadeIn(0);
+ },
+ content: function() {
+ var title = $(this).attr('title');
+
+ // check if it should be converted to a table
+ if(!title.match(/\t/)) {
+ return title.replace(/\n/g, ' ');
+ }
+
+ var data = [];
+ var columnCount = 0;
+
+ // parse data
+ var rows = title.split('\n');
+ $.each(rows, function(i, row) {
+ data[i] = row.split('\t');
+ if(data[i].length > columnCount) columnCount = data[i].length;
+ });
+
+ // build the table
+ var tooltip = '
';
+ $.each(data, function(i, row) {
+ tooltip += '';
+ $.each(data[i], function(k, cell) {
+ var attributes = '';
+ if(k === 0 && data[i].length < columnCount) {
+ attributes = ' colspan="'+(columnCount - data[i].length + 1)+'"';
+ }
+ tooltip += ''+cell+' ';
+ });
+ tooltip += ' ';
+ });
+ tooltip += '
';
+ return tooltip;
+ }
+ });
+}
+
// BOOTING ///////////////////////////////////////////////////////////
function boot() {
+ window.debug.console.overwriteNativeIfRequired();
+
console.log('loading done, booting');
window.setupStyles();
window.setupMap();
@@ -202,6 +253,7 @@ function boot() {
window.setupSidebarToggle();
window.updateGameScore();
window.setupPlayerStat();
+ window.setupTooltips();
window.chat.setup();
// read here ONCE, so the URL is only evaluated one time after the
// necessary data has been loaded.
@@ -230,10 +282,11 @@ function asyncLoadScript(a){return function(b,c){var d=document.createElement("s
// contains the default Ingress map style.
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 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';
// after all scripts have loaded, boot the actual app
-load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS).onError(function (err) {
+load(JQUERY, LEAFLET, AUTOLINK).then(LLGMAPS, JQUERYUI).onError(function (err) {
alert('Could not all resources, the script likely won’t work.\n\nIf this happend the first time for you, it’s 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);
diff --git a/code/chat.js b/code/chat.js
index 77969845..d570decc 100644
--- a/code/chat.js
+++ b/code/chat.js
@@ -1,22 +1,14 @@
window.chat = function() {};
-window.chat._lastNicksForAutocomplete = [[], []];
-window.chat.addNickForAutocomplete = function(nick, isFaction) {
- var r = chat._lastNicksForAutocomplete[isFaction ? 0 : 1];
- if(r.indexOf(nick) !== -1) return;
- r.push(nick);
- if(r.length >= 15)
- r.shift();
-}
-
window.chat.handleTabCompletion = function() {
var el = $('#chatinput input');
var curPos = el.get(0).selectionStart;
var text = el.val();
var word = text.slice(0, curPos).replace(/.*\b([a-z0-9-_])/, '$1').toLowerCase();
- var list = window.chat._lastNicksForAutocomplete;
- list = list[1].concat(list[0]);
+ var list = $('#chat > div:visible mark');
+ list = list.map(function(ind, mark) { return $(mark).text(); } );
+ list = uniqueArray(list);
var nick = null;
for(var i = 0; i < list.length; i++) {
@@ -43,27 +35,24 @@ window.chat.handleTabCompletion = function() {
// timestamp and clear management
//
-window.chat._oldFactionTimestamp = -1;
-window.chat._newFactionTimestamp = -1;
-window.chat._oldPublicTimestamp = -1;
-window.chat._newPublicTimestamp = -1;
-
-window.chat.getOldestTimestamp = function(public) {
- return chat['_old'+(public ? 'Public' : 'Faction')+'Timestamp'];
+window.chat.getTimestamps = function(isFaction) {
+ var storage = isFaction ? chat._factionData : chat._publicData;
+ return $.map(storage, function(v, k) { return [v[0]]; });
}
-window.chat.getNewestTimestamp = function(public) {
- return chat['_new'+(public ? 'Public' : 'Faction')+'Timestamp'];
+window.chat.getOldestTimestamp = function(isFaction) {
+ var t = Math.min.apply(null, chat.getTimestamps(isFaction));
+ return t === Infinity ? -1 : t;
}
-window.chat.clearIfRequired = function(elm) {
- if(!elm.data('needsClearing')) return;
- elm.data('ignoreNextScroll', true).data('needsClearing', false).html('');
+window.chat.getNewestTimestamp = function(isFaction) {
+ var t = Math.max.apply(null, chat.getTimestamps(isFaction));
+ return t === -1*Infinity ? -1 : t;
}
window.chat._oldBBox = null;
-window.chat.genPostData = function(public, getOlderMsgs) {
- if(typeof public !== 'boolean') throw('Need to know if public or faction chat.');
+window.chat.genPostData = function(isFaction, getOlderMsgs) {
+ if(typeof isFaction !== 'boolean') throw('Need to know if public or faction chat.');
chat._localRangeCircle.setLatLng(map.getCenter());
var b = map.getBounds().extend(chat._localRangeCircle.getBounds());
@@ -78,35 +67,30 @@ window.chat.genPostData = function(public, getOlderMsgs) {
// need to reset these flags now because clearing will only occur
// after the request is finished – i.e. there would be one almost
// useless request.
- chat._displayedFactionGuids = [];
- chat._displayedPublicGuids = [];
- chat._displayedPlayerActionTime = {};
- chat._oldFactionTimestamp = -1;
- chat._newFactionTimestamp = -1;
- chat._oldPublicTimestamp = -1;
- chat._newPublicTimestamp = -1;
+ chat._factionData = {};
+ chat._publicData = {};
}
chat._oldBBox = bbs;
var ne = b.getNorthEast();
var sw = b.getSouthWest();
var data = {
- desiredNumItems: public ? CHAT_PUBLIC_ITEMS : CHAT_FACTION_ITEMS,
+ desiredNumItems: isFaction ? CHAT_FACTION_ITEMS : CHAT_PUBLIC_ITEMS ,
minLatE6: Math.round(sw.lat*1E6),
minLngE6: Math.round(sw.lng*1E6),
maxLatE6: Math.round(ne.lat*1E6),
maxLngE6: Math.round(ne.lng*1E6),
minTimestampMs: -1,
maxTimestampMs: -1,
- factionOnly: !public
+ factionOnly: isFaction
}
if(getOlderMsgs) {
// ask for older chat when scrolling up
- data = $.extend(data, {maxTimestampMs: chat.getOldestTimestamp(public)});
+ data = $.extend(data, {maxTimestampMs: chat.getOldestTimestamp(isFaction)});
} else {
// ask for newer chat
- var min = chat.getNewestTimestamp(public);
+ var min = chat.getNewestTimestamp(isFaction);
// the inital request will have both timestamp values set to -1,
// thus we receive the newest desiredNumItems. After that, we will
// only receive messages with a timestamp greater or equal to min
@@ -130,322 +114,219 @@ window.chat.genPostData = function(public, getOlderMsgs) {
//
-// requesting faction
+// faction
//
-window.chat._requestOldFactionRunning = false;
-window.chat.requestOldFaction = function(isRetry) {
- if(chat._requestOldFactionRunning) return;
+window.chat._requestFactionRunning = false;
+window.chat.requestFaction = function(getOlderMsgs, isRetry) {
+ if(chat._requestFactionRunning && !isRetry) return;
if(isIdle()) return renderUpdateStatus();
- chat._requestOldFactionRunning = true;
+ chat._requestFactionRunning = true;
- var d = chat.genPostData(false, true);
+ var d = chat.genPostData(true, getOlderMsgs);
var r = window.postAjax(
'getPaginatedPlextsV2',
d,
- chat.handleOldFaction,
+ chat.handleFaction,
isRetry
- ? function() { window.chat._requestOldFactionRunning = false; }
- : function() { window.chat.requestOldFaction(true) }
- );
-
- requests.add(r);
-}
-
-window.chat._requestNewFactionRunning = false;
-window.chat.requestNewFaction = function(isRetry) {
- if(chat._requestNewFactionRunning) return;
- if(window.isIdle()) return renderUpdateStatus();
- chat._requestNewFactionRunning = true;
-
- var d = chat.genPostData(false, false);
- var r = window.postAjax(
- 'getPaginatedPlextsV2',
- d,
- chat.handleNewFaction,
- isRetry
- ? function() { window.chat._requestNewFactionRunning = false; }
- : function() { window.chat.requestNewFaction(true) }
+ ? function() { window.chat._requestFactionRunning = false; }
+ : function() { window.chat.requestFaction(getOlderMsgs, true) }
);
requests.add(r);
}
-//
-// handle faction
-//
+window.chat._factionData = {};
+window.chat.handleFaction = function(data, textStatus, jqXHR) {
+ chat._requestFactionRunning = false;
-window.chat.handleOldFaction = function(data, textStatus, jqXHR) {
- chat._requestOldFactionRunning = false;
- chat.handleFaction(data, textStatus, jqXHR, true);
-}
-
-window.chat.handleNewFaction = function(data, textStatus, jqXHR) {
- chat._requestNewFactionRunning = false;
- chat.handleFaction(data, textStatus, jqXHR, false);
-}
-
-
-
-window.chat._displayedFactionGuids = [];
-window.chat.handleFaction = function(data, textStatus, jqXHR, isOldMsgs) {
if(!data || !data.result) {
window.failedRequestCount++;
return console.warn('faction chat error. Waiting for next auto-refresh.');
}
- var c = $('#chatfaction');
- chat.clearIfRequired(c);
-
if(data.result.length === 0) return;
- chat._newFactionTimestamp = data.result[0][1];
- chat._oldFactionTimestamp = data.result[data.result.length-1][1];
+ var old = chat.getOldestTimestamp(true);
+ chat.writeDataToHash(data, chat._factionData, false);
+ var oldMsgsWereAdded = old !== chat.getOldestTimestamp(true);
- var scrollBefore = scrollBottom(c);
- chat.renderPlayerMsgsTo(true, data, isOldMsgs, chat._displayedFactionGuids);
- chat.keepScrollPosition(c, scrollBefore, isOldMsgs);
+ window.chat.renderFaction(oldMsgsWereAdded);
if(data.result.length >= CHAT_FACTION_ITEMS) chat.needMoreMessages();
}
-
+window.chat.renderFaction = function(oldMsgsWereAdded) {
+ chat.renderData(chat._factionData, 'chatfaction', oldMsgsWereAdded);
+}
//
-// requesting public
+// public
//
-window.chat._requestOldPublicRunning = false;
-window.chat.requestOldPublic = function(isRetry) {
- if(chat._requestOldPublicRunning) return;
+window.chat._requestPublicRunning = false;
+window.chat.requestPublic = function(getOlderMsgs, isRetry) {
+ if(chat._requestPublicRunning && !isRetry) return;
if(isIdle()) return renderUpdateStatus();
- chat._requestOldPublicRunning = true;
+ chat._requestPublicRunning = true;
- var d = chat.genPostData(true, true);
+ var d = chat.genPostData(false, getOlderMsgs);
var r = window.postAjax(
'getPaginatedPlextsV2',
d,
- chat.handleOldPublic,
+ chat.handlePublic,
isRetry
- ? function() { window.chat._requestOldPublicRunning = false; }
- : function() { window.chat.requestOldPublic(true) }
+ ? function() { window.chat._requestPublicRunning = false; }
+ : function() { window.chat.requestPublic(getOlderMsgs, true) }
);
requests.add(r);
}
-window.chat._requestNewPublicRunning = false;
-window.chat.requestNewPublic = function(isRetry) {
- if(chat._requestNewPublicRunning) return;
- if(window.isIdle()) return renderUpdateStatus();
- chat._requestNewPublicRunning = true;
+window.chat._publicData = {};
+window.chat.handlePublic = function(data, textStatus, jqXHR) {
+ chat._requestPublicRunning = false;
- var d = chat.genPostData(true, false);
- var r = window.postAjax(
- 'getPaginatedPlextsV2',
- d,
- chat.handleNewPublic,
- isRetry
- ? function() { window.chat._requestNewPublicRunning = false; }
- : function() { window.chat.requestNewPublic(true) }
- );
-
- requests.add(r);
-}
-
-
-//
-// handle public
-//
-
-
-window.chat.handleOldPublic = function(data, textStatus, jqXHR) {
- chat._requestOldPublicRunning = false;
- chat.handlePublic(data, textStatus, jqXHR, true);
-}
-
-window.chat.handleNewPublic = function(data, textStatus, jqXHR) {
- chat._requestNewPublicRunning = false;
- chat.handlePublic(data, textStatus, jqXHR, false);
-}
-
-window.chat._displayedPublicGuids = [];
-window.chat._displayedPlayerActionTime = {};
-window.chat.handlePublic = function(data, textStatus, jqXHR, isOldMsgs) {
if(!data || !data.result) {
window.failedRequestCount++;
return console.warn('public chat error. Waiting for next auto-refresh.');
}
- var ca = $('#chatautomated');
- var cp = $('#chatpublic');
- chat.clearIfRequired(ca);
- chat.clearIfRequired(cp);
-
if(data.result.length === 0) return;
- chat._newPublicTimestamp = data.result[0][1];
- chat._oldPublicTimestamp = data.result[data.result.length-1][1];
+ var old = chat.getOldestTimestamp(false);
+ chat.writeDataToHash(data, chat._publicData, true);
+ var oldMsgsWereAdded = old !== chat.getOldestTimestamp(false);
-
- var scrollBefore = scrollBottom(ca);
- chat.handlePublicAutomated(data);
- chat.keepScrollPosition(ca, scrollBefore, isOldMsgs);
-
-
- var scrollBefore = scrollBottom(cp);
- chat.renderPlayerMsgsTo(false, data, isOldMsgs, chat._displayedPublicGuids);
- chat.keepScrollPosition(cp, scrollBefore, isOldMsgs);
+ switch(chat.getActive()) {
+ case 'public': window.chat.renderPublic(oldMsgsWereAdded); break;
+ case 'compact': window.chat.renderCompact(oldMsgsWereAdded); break;
+ case 'full': window.chat.renderFull(oldMsgsWereAdded); break;
+ }
if(data.result.length >= CHAT_PUBLIC_ITEMS) chat.needMoreMessages();
}
-
-window.chat.handlePublicAutomated = function(data) {
- $.each(data.result, function(ind, json) { // newest first!
- var time = json[1];
-
- // ignore player messages
- var t = json[2].plext.plextType;
- if(t !== 'SYSTEM_BROADCAST' && t !== 'SYSTEM_NARROWCAST') return true;
-
- var tmpmsg = '', nick = null, pguid, team;
-
- // each automated message is composed of many text chunks. loop
- // over them to gather all necessary data.
- $.each(json[2].plext.markup, function(ind, part) {
- switch(part[0]) {
- case 'PLAYER':
- pguid = part[1].guid;
- var lastAction = window.chat._displayedPlayerActionTime[pguid];
- // ignore older messages about player
- if(lastAction && lastAction[0] > time) return false;
-
- nick = part[1].plain;
- team = part[1].team === 'ALIENS' ? TEAM_ENL : TEAM_RES;
- window.setPlayerName(pguid, nick); // free nick name resolves
- if(ind > 0) tmpmsg += nick; // don’t repeat nick directly
- break;
-
- case 'TEXT':
- tmpmsg += part[1].plain;
- break;
-
- case 'PORTAL':
- var latlng = [part[1].latE6/1E6, part[1].lngE6/1E6];
- var js = 'window.zoomToAndShowPortal(\''+part[1].guid+'\', ['+latlng[0]+', '+latlng[1]+'])';
- tmpmsg += ''+part[1].name+' ';
- break;
- }
- });
-
- // nick will only be set if we don’t have any info about that
- // player yet.
- if(nick) {
- tmpmsg = chat.renderMsg(tmpmsg, nick, time, team);
- window.chat._displayedPlayerActionTime[pguid] = [time, tmpmsg];
- };
- });
-
- if(chat.getActive() === 'automated')
- window.chat.renderAutomatedMsgsTo();
+window.chat.renderPublic = function(oldMsgsWereAdded) {
+ // only keep player data
+ var data = $.map(chat._publicData, function(entry) {
+ if(!entry[1]) return [entry];
+ });
+ chat.renderData(data, 'chatpublic', oldMsgsWereAdded);
}
-window.chat.renderAutomatedMsgsTo = function() {
- var x = window.chat._displayedPlayerActionTime;
- // we don’t care about the GUIDs anymore
- var vals = $.map(x, function(v, k) { return [v]; });
- // sort them old to new
- vals = vals.sort(function(a, b) { return a[0]-b[0]; });
-
- var prevTime = null;
- var msgs = $.map(vals, function(v) {
- var nowTime = new Date(v[0]).toLocaleDateString();
- if(prevTime && prevTime !== nowTime)
- var val = chat.renderDivider(nowTime) + v[1];
- else
- var val = v[1];
-
- prevTime = nowTime;
- return val;
- }).join('\n');
-
- $('#chatautomated').html(msgs);
+window.chat.renderCompact = function(oldMsgsWereAdded) {
+ var data = {};
+ $.each(chat._publicData, function(guid, entry) {
+ // skip player msgs
+ if(!entry[1]) return true;
+ var pguid = entry[3];
+ // ignore if player has newer data
+ if(data[pguid] && data[pguid][0] > entry[0]) return true;
+ data[pguid] = entry;
+ });
+ // data keys are now player guids instead of message guids. However,
+ // it is all the same to renderData.
+ chat.renderData(data, 'chatcompact', oldMsgsWereAdded);
}
-
+window.chat.renderFull = function(oldMsgsWereAdded) {
+ // only keep automatically generated data
+ var data = $.map(chat._publicData, function(entry) {
+ if(entry[1]) return [entry];
+ });
+ chat.renderData(data, 'chatfull', oldMsgsWereAdded);
+}
//
// common
//
-
-window.chat.renderPlayerMsgsTo = function(isFaction, data, isOldMsgs, dupCheckArr) {
- var msgs = '';
- var prevTime = null;
-
- $.each(data.result.reverse(), function(ind, json) { // oldest first!
- if(json[2].plext.plextType !== 'PLAYER_GENERATED') return true;
-
+window.chat.writeDataToHash = function(newData, storageHash, skipSecureMsgs) {
+ $.each(newData.result, function(ind, json) {
// avoid duplicates
- if(dupCheckArr.indexOf(json[0]) !== -1) return true;
- dupCheckArr.push(json[0]);
+ if(json[0] in storageHash) return true;
+
+ var skipThisEntry = false;
var time = json[1];
var team = json[2].plext.team === 'ALIENS' ? TEAM_ENL : TEAM_RES;
- var msg, nick, pguid;
+ var auto = json[2].plext.plextType !== 'PLAYER_GENERATED';
+ var msg = '', nick = '', pguid;
$.each(json[2].plext.markup, function(ind, markup) {
- if(markup[0] === 'SENDER') {
+ switch(markup[0]) {
+ case 'SENDER': // user generated messages
nick = markup[1].plain.slice(0, -2); // cut “: ” at end
pguid = markup[1].guid;
- window.setPlayerName(pguid, nick); // free nick name resolves
- if(!isOldMsgs) window.chat.addNickForAutocomplete(nick, isFaction);
- }
+ break;
- if(markup[0] === 'TEXT') {
- msg = markup[1].plain.autoLink();
- msg = msg.replace(window.PLAYER['nickMatcher'], '$1 ');
- }
+ case 'PLAYER': // automatically generated messages
+ pguid = markup[1].guid;
+ nick = markup[1].plain;
+ team = markup[1].team === 'ALIENS' ? TEAM_ENL : TEAM_RES;
+ if(ind > 0) msg += nick; // don’t repeat nick directly
+ break;
- if(!isFaction && markup[0] === 'SECURE') {
- nick = null;
- return false; // aka break
+ case 'TEXT':
+ var tmp = $('
').text(markup[1].plain).html().autoLink();
+ msg += tmp.replace(window.PLAYER['nickMatcher'], '$1 ');
+ break;
+
+ case 'PORTAL':
+ var latlng = [markup[1].latE6/1E6, markup[1].lngE6/1E6];
+ var js = 'window.zoomToAndShowPortal(\''+markup[1].guid+'\', ['+latlng[0]+', '+latlng[1]+'])';
+ msg += ''+markup[1].name+' ';
+ break;
+
+ case 'SECURE':
+ if(skipSecureMsgs) {
+ skipThisEntry = true;
+ return false; // breaks $.each
+ }
}
});
+ if(skipThisEntry) return true;
- if(!nick) return true; // aka next
+ // format: timestamp, autogenerated, HTML message, player guid
+ storageHash[json[0]] = [json[1], auto, chat.renderMsg(msg, nick, time, team), pguid];
- var nowTime = new Date(time).toLocaleDateString();
- if(prevTime && prevTime !== nowTime)
- msgs += chat.renderDivider(nowTime);
+ window.setPlayerName(pguid, nick); // free nick name resolves
+ });
+}
- msgs += chat.renderMsg(msg, nick, time, team);
- prevTime = nowTime;
+// 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.
+window.chat.renderData = function(data, element, likelyWereOldMsgs) {
+ var elm = $('#'+element);
+ if(elm.is(':hidden')) return;
+
+ // discard guids and sort old to new
+ var vals = $.map(data, function(v, k) { return [v]; });
+ vals = vals.sort(function(a, b) { return a[0]-b[0]; });
+
+ // render to string with date separators inserted
+ var msgs = '';
+ var prevTime = null;
+ $.each(vals, function(ind, msg) {
+ var nextTime = new Date(msg[0]).toLocaleDateString();
+ if(prevTime && prevTime !== nextTime)
+ msgs += chat.renderDivider(nextTime);
+ msgs += msg[2];
+ prevTime = nextTime;
});
- var addTo = isFaction ? $('#chatfaction') : $('#chatpublic');
-
- // if there is a change of day between two requests, handle the
- // divider insertion here.
- if(isOldMsgs) {
- var ts = addTo.find('time:first').data('timestamp');
- var nextTime = new Date(ts).toLocaleDateString();
- if(prevTime && prevTime !== nextTime && ts)
- msgs += chat.renderDivider(nextTime);
- }
-
- if(isOldMsgs)
- addTo.prepend(msgs);
- else
- addTo.append(msgs);
+ var scrollBefore = scrollBottom(elm);
+ elm.html(msgs);
+ chat.keepScrollPosition(elm, scrollBefore, likelyWereOldMsgs);
}
window.chat.renderDivider = function(text) {
- return '─ '+text+' ──────────────────────────────────────────────────────────────────────────── ';
+ return '─ '+text+' ────────────────────────────────────────────────────────────────────────── ';
}
@@ -486,27 +367,36 @@ window.chat.toggle = function() {
window.chat.request = function() {
console.log('refreshing chat');
- chat.requestNewFaction();
- chat.requestNewPublic();
+ chat.requestFaction(false);
+ chat.requestPublic(false);
}
// checks if there are enough messages in the selected chat tab and
// loads more if not.
window.chat.needMoreMessages = function() {
+ var activeTab = chat.getActive();
+ if(activeTab === 'debug') return;
+
var activeChat = $('#chat > :visible');
- if(scrollBottom(activeChat) !== 0 || activeChat.scrollTop() !== 0) return;
- console.log('no scrollbar in active chat, requesting more msgs');
- if($('#chatcontrols a:last.active').length)
- chat.requestOldFaction();
+
+ var hasScrollbar = scrollBottom(activeChat) !== 0 || activeChat.scrollTop() !== 0;
+ var nearTop = activeChat.scrollTop() <= CHAT_REQUEST_SCROLL_TOP;
+ if(hasScrollbar && !nearTop) return;
+
+ console.log('No scrollbar or near top in active chat. Requesting more data.');
+
+ if(activeTab === 'faction')
+ chat.requestFaction(true);
else
- chat.requestOldPublic();
+ chat.requestPublic(true);
}
window.chat.chooser = function(event) {
var t = $(event.target);
var tt = t.text();
+
var span = $('#chatinput span');
$('#chatcontrols .active').removeClass('active');
@@ -520,24 +410,26 @@ window.chat.chooser = function(event) {
case 'faction':
span.css('color', '');
span.text('tell faction:');
- elm = $('#chatfaction');
break;
case 'public':
span.css('cssText', 'color: red !important');
span.text('broadcast:');
- elm = $('#chatpublic');
break;
- case 'automated':
+ case 'compact':
+ case 'full':
span.css('cssText', 'color: #bbb !important');
span.text('tell Jarvis:');
- chat.renderAutomatedMsgsTo();
- elm = $('#chatautomated');
break;
+
+ default:
+ throw('chat.chooser was asked to handle unknown button: ' + tt);
}
+ var elm = $('#chat' + tt);
elm.show();
+ eval('chat.render' + tt.capitalize() + '(false);');
if(elm.data('needsScrollTop')) {
elm.data('ignoreNextScroll', true);
elm.scrollTop(elm.data('needsScrollTop'));
@@ -580,7 +472,10 @@ window.chat.setup = function() {
$('#chatcontrols, #chat, #chatinput').show();
$('#chatcontrols a:first').click(window.chat.toggle);
- $('#chatcontrols a:not(:first)').click(window.chat.chooser);
+ $('#chatcontrols a').each(function(ind, elm) {
+ if($.inArray($(elm).text(), ['full', 'compact', 'public', 'faction']) !== -1)
+ $(elm).click(window.chat.chooser);
+ });
$('#chatinput').click(function() {
@@ -593,15 +488,15 @@ window.chat.setup = function() {
$('#chatfaction').scroll(function() {
var t = $(this);
if(t.data('ignoreNextScroll')) return t.data('ignoreNextScroll', false);
- if(t.scrollTop() < 200) chat.requestOldFaction();
- if(scrollBottom(t) === 0) chat.requestNewFaction();
+ if(t.scrollTop() < CHAT_REQUEST_SCROLL_TOP) chat.requestFaction(true);
+ if(scrollBottom(t) === 0) chat.requestFaction(false);
});
- $('#chatpublic, #chatautomated').scroll(function() {
+ $('#chatpublic, #chatfull, #chatcompact').scroll(function() {
var t = $(this);
if(t.data('ignoreNextScroll')) return t.data('ignoreNextScroll', false);
- if(t.scrollTop() < 200) chat.requestOldPublic();
- if(scrollBottom(t) === 0) chat.requestNewPublic();
+ if(t.scrollTop() < CHAT_REQUEST_SCROLL_TOP) chat.requestPublic(true);
+ if(scrollBottom(t) === 0) chat.requestPublic(false);
});
chat.request();
@@ -636,38 +531,38 @@ window.chat.setupTime = function() {
window.chat.setupPosting = function() {
$('#chatinput input').keydown(function(event) {
-try{
-
- var kc = (event.keyCode ? event.keyCode : event.which);
- if(kc === 13) { // enter
- chat.postMsg();
- event.preventDefault();
- } else if (kc === 9) { // tab
- event.preventDefault();
- window.chat.handleTabCompletion();
+ try {
+ var kc = (event.keyCode ? event.keyCode : event.which);
+ if(kc === 13) { // enter
+ chat.postMsg();
+ event.preventDefault();
+ } else if (kc === 9) { // tab
+ event.preventDefault();
+ window.chat.handleTabCompletion();
+ }
+ } catch(error) {
+ console.log(error);
+ debug.printStackTrace();
}
-
-
-} catch(error) {
- console.log(error);
- debug.printStackTrace();
-}
});
$('#chatinput').submit(function(event) {
- chat.postMsg();
event.preventDefault();
+ chat.postMsg();
});
}
window.chat.postMsg = function() {
var c = chat.getActive();
- if(c === 'automated') return alert('Jarvis: A strange game. The only winning move is not to play. How about a nice game of chess?');
+ if(c === 'full' || c === 'compact')
+ return alert('Jarvis: A strange game. The only winning move is not to play. How about a nice game of chess?');
var msg = $.trim($('#chatinput input').val());
if(!msg || msg === '') return;
+ if(c === 'debug') return new Function (msg)();
+
var public = c === 'public';
var latlng = map.getCenter();
@@ -676,11 +571,15 @@ window.chat.postMsg = function() {
lngE6: Math.round(latlng.lng*1E6),
factionOnly: !public};
+ var errMsg = 'Your message could not be delivered. You can copy&' +
+ 'paste it here and try again if you want:\n\n' + msg;
+
window.postAjax('sendPlext', data,
- function() { if(public) chat.requestNewPublic(); else chat.requestNewFaction(); },
+ function(response) {
+ if(response.error) alert(errMsg);
+ if(public) chat.requestPublic(false); else chat.requestFaction(false); },
function() {
- alert('Your message could not be delivered. You can copy&' +
- 'paste it here and try again if you want:\n\n'+msg);
+ alert(errMsg);
}
);
diff --git a/code/debugging.js b/code/debugging.js
index 0a3ce734..b3f12ecc 100644
--- a/code/debugging.js
+++ b/code/debugging.js
@@ -42,3 +42,68 @@ window.debug.forceSync = function() {
updateGameScore();
requestData();
}
+
+window.debug.console = function() {
+ $('#debugconsole').text();
+}
+
+window.debug.console.create = function() {
+ if($('#debugconsole').length) return;
+ $('#chatcontrols').append('debug ');
+ $('#chatcontrols a:last').click(function() {
+ $('#chatinput span').css('cssText', 'color: #bbb !important').text('debug:');
+ $('#chat > div').hide();
+ $('#debugconsole').show();
+ $('#chatcontrols .active').removeClass('active');
+ $(this).addClass('active');
+ });
+ $('#chat').append('
');
+}
+
+window.debug.console.renderLine = function(text, errorType) {
+ debug.console.create();
+ switch(errorType) {
+ case 'error': var color = '#FF424D'; break;
+ case 'warning': var color = '#FFDE42'; break;
+ case 'alert': var color = '#42FF90'; break;
+ default: var color = '#eee';
+ }
+ if(typeof text !== 'string' && typeof text !== 'number') text = JSON.stringify(text);
+ var d = new Date();
+ var ta = d.toLocaleTimeString(); // print line instead maybe?
+ var tb = d.toLocaleString();
+ var t = ''+ta+' ';
+ var s = 'style="color:'+color+'"';
+ var l = ''+t+''+errorType+' '+text+'
';
+ $('#debugconsole').prepend(l);
+}
+
+window.debug.console.log = function(text) {
+ debug.console.renderLine(text, 'notice');
+}
+
+window.debug.console.warn = function(text) {
+ debug.console.renderLine(text, 'warning');
+}
+
+window.debug.console.error = function(text) {
+ debug.console.renderLine(text, 'error');
+}
+
+window.debug.console.alert = function(text) {
+ debug.console.renderLine(text, 'alert');
+}
+
+window.debug.console.overwriteNative = function() {
+ window.debug.console.create();
+ window.console = function() {}
+ window.console.log = window.debug.console.log;
+ window.console.warn = window.debug.console.warn;
+ window.console.error = window.debug.console.error;
+ window.alert = window.debug.console.alert;
+}
+
+window.debug.console.overwriteNativeIfRequired = function() {
+ if(!window.console || L.Browser.mobile)
+ window.debug.console.overwriteNative();
+}
diff --git a/code/game_status.js b/code/game_status.js
index 6027bfe3..fe29b868 100644
--- a/code/game_status.js
+++ b/code/game_status.js
@@ -15,7 +15,7 @@ window.updateGameScore = function(data) {
var es = ' '+Math.round(ep)+'% ';
$('#gamestat').html(rs+es).one('click', function() { window.updateGameScore() });
// help cursor via “#gamestat span”
- $('#gamestat').attr('title', 'Resistance:\t\t'+r+' MindUnits\nEnlightenment:\t'+e+' MindUnits');
+ $('#gamestat').attr('title', 'Resistance:\t'+r+' MindUnits\nEnlightenment:\t'+e+' MindUnits');
window.setTimeout('window.updateGameScore', REFRESH_GAME_SCORE*1000);
}
diff --git a/code/location.js b/code/location.js
index 7258ff83..2860a9a5 100644
--- a/code/location.js
+++ b/code/location.js
@@ -5,11 +5,17 @@
// retrieves current position from map and stores it cookies
window.storeMapPosition = function() {
var m = window.map.getCenter();
- writeCookie('ingress.intelmap.lat', m['lat']);
- writeCookie('ingress.intelmap.lng', m['lng']);
+
+ if(m['lat'] >= -90 && m['lat'] <= 90)
+ writeCookie('ingress.intelmap.lat', m['lat']);
+
+ if(m['lng'] >= -180 && m['lng'] <= 180)
+ writeCookie('ingress.intelmap.lng', m['lng']);
+
writeCookie('ingress.intelmap.zoom', window.map.getZoom());
}
+
// either retrieves the last shown position from a cookie, from the
// URL or if neither is present, via Geolocation. If that fails, it
// returns a map that shows the whole world.
@@ -28,6 +34,10 @@ window.getPosition = function() {
var lat = parseFloat(readCookie('ingress.intelmap.lat')) || 0.0;
var lng = parseFloat(readCookie('ingress.intelmap.lng')) || 0.0;
var z = parseInt(readCookie('ingress.intelmap.zoom')) || 17;
+
+ if(lat < -90 || lat > 90) lat = 0.0;
+ if(lng < -180 || lng > 180) lng = 0.0;
+
return {center: new L.LatLng(lat, lng), zoom: z > 18 ? 18 : z};
}
diff --git a/code/map_data.js b/code/map_data.js
index 893ecc3a..072c8259 100644
--- a/code/map_data.js
+++ b/code/map_data.js
@@ -75,7 +75,7 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
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 = window.portals[vertex.guid].options.details.portalV2.linkedFields;
fieldArray.splice($.inArray(guid, fieldArray), 1);
});
}
@@ -225,9 +225,14 @@ window.renderPortal = function(ent) {
var oo = old.options;
var u = oo.team !== team;
u = u || oo.level !== portalLevel;
- // nothing for the portal changed, so don’t update. Let resonators
- // manage themselves if they want to be updated.
- if(!u) return renderResonators(ent);
+ // nothing changed that requires re-rendering the portal.
+ if(!u) {
+ // let resos handle themselves if they need to be redrawn
+ renderResonators(ent, old);
+ // update stored details for portal details in sidebar.
+ old.options.details = ent[2];
+ return;
+ }
// there were changes, remove old portal
removeByGuid(ent[0]);
}
@@ -237,7 +242,6 @@ 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);
@@ -261,7 +265,7 @@ window.renderPortal = function(ent) {
// all resonators have already removed by zooming
if(isResonatorsShow()) {
for(var i = 0; i <= 7; i++)
- removeByGuid(portalResonatorGuid(portalGuid,i));
+ removeByGuid(portalResonatorGuid(portalGuid, i));
}
delete window.portals[portalGuid];
if(window.selectedPortal === portalGuid) {
@@ -287,7 +291,7 @@ window.renderPortal = function(ent) {
window.map.setView(latlng, 17);
});
- window.renderResonators(ent);
+ window.renderResonators(ent, null);
window.runHooks('portalAdded', {portal: p});
@@ -295,18 +299,29 @@ window.renderPortal = function(ent) {
p.addTo(layerGroup);
}
-window.renderResonators = function(ent) {
- var portalLevel = getPortalLevel(ent[2]);
- if(portalLevel < getMinPortalLevel() && ent[0] != selectedPortal) return;
-
+window.renderResonators = function(ent, portalLayer) {
if(!isResonatorsShow()) return;
- for(var i=0; i < ent[2].resonatorArray.resonators.length; i++) {
+ var portalLevel = getPortalLevel(ent[2]);
+ if(portalLevel < getMinPortalLevel() && ent[0] != selectedPortal) return;
+ var portalLatLng = [ent[2].locationE6.latE6/1E6, ent[2].locationE6.lngE6/1E6];
+
+ var layerGroup = portalsLayers[parseInt(portalLevel)];
+ var reRendered = false;
+ for(var i = 0; i < ent[2].resonatorArray.resonators.length; i++) {
var rdata = ent[2].resonatorArray.resonators[i];
- if(rdata == null) continue;
+ // skip if resonator didn't change
+ if(portalLayer) {
+ var oldRes = findEntityInLeaflet(layerGroup, window.resonators, portalResonatorGuid(ent[0], i));
+ if(oldRes && isSameResonator(oldRes.options.details, rdata)) continue;
+ }
- if(window.resonators[portalResonatorGuid(ent[0],i)]) continue;
+ // skip and remove old resonator if no new resonator
+ if(rdata === null) {
+ if(oldRes) removeByGuid(oldRes.options.guid);
+ continue;
+ }
// offset in meters
var dn = rdata.distanceToPortal*SLOT_TO_LAT[rdata.slot];
@@ -320,7 +335,11 @@ window.renderResonators = function(ent) {
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, {
+
+ var resoGuid = portalResonatorGuid(ent[0], i);
+
+ // the resonator
+ var reso = L.circleMarker(Rlatlng, {
radius: 3,
// #AAAAAA outline seems easier to see the fill opacity
color: '#AAAAAA',
@@ -328,17 +347,40 @@ window.renderResonators = function(ent) {
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) });
+ guid: resoGuid // need this here as well for add/remove events
+ });
- r.on('remove', function() { delete window.resonators[this.options.guid]; });
- r.on('add', function() { window.resonators[this.options.guid] = this; });
+ // line connecting reso to portal
+ var conn = L.polyline([Rlatlng, portalLatLng], {
+ weight: 2,
+ color: '#FFFFFF',
+ opacity: 0.2,
+ dashArray: '10,4',
+ fill: false,
+ clickable: false});
+
+
+ // put both in one group, so they can be handled by the same logic.
+ var r = L.layerGroup([reso, conn]);
+ r.options = {
+ level: rdata.level,
+ details: rdata,
+ pDetails: ent[2],
+ guid: resoGuid
+ };
+
+ // However, LayerGroups (and FeatureGroups) don’t fire add/remove
+ // events, thus this listener will be attached to the resonator. It
+ // doesn’t matter to which element these are bound since Leaflet
+ // will add/remove all elements of the LayerGroup at once.
+ reso.on('remove', function() { delete window.resonators[this.options.guid]; });
+ reso.on('add', function() { window.resonators[this.options.guid] = r; });
r.addTo(portalsLayers[parseInt(portalLevel)]);
+ reRendered = true;
}
+ // if there is any resonator re-rendered, bring portal to front
+ if(reRendered && portalLayer) portalLayer.bringToFront();
}
// append portal guid with -resonator-[slot] to get guid for resonators
@@ -350,8 +392,17 @@ window.isResonatorsShow = function() {
return map.getZoom() >= RESONATOR_DISPLAY_ZOOM_LEVEL;
}
+window.isSameResonator = function(oldRes, newRes) {
+ if(!oldRes && !newRes) return true;
+ if(typeof oldRes !== typeof newRes) return false;
+ if(oldRes.level !== newRes.level) return false;
+ if(oldRes.energyTotal !== newRes.energyTotal) return false;
+ if(oldRes.distanceToPortal !== newRes.distanceToPortal) return false;
+ return true;
+}
+
window.portalResetColor = function(portal) {
- portal.setStyle({color: portal.options.fillColor});
+ portal.setStyle({color: COLORS[getTeam(portal.options.details)]});
}
// renders a link on the map from the given entity
diff --git a/code/portal_detail_display.js b/code/portal_detail_display.js
index 98b33592..703af772 100644
--- a/code/portal_detail_display.js
+++ b/code/portal_detail_display.js
@@ -4,13 +4,14 @@
// methods that highlight the portal in the map view.
window.renderPortalDetails = function(guid) {
- var d = window.portals[guid].options.details;
- if(!d) {
+ if(!window.portals[guid]) {
unselectOldPortal();
urlPortal = guid;
return;
}
+ var d = window.portals[guid].options.details;
+
var update = selectPortal(guid);
// collect some random data that’s not worth to put in an own method
diff --git a/code/portal_detail_display_tools.js b/code/portal_detail_display_tools.js
index 6bcbe96e..d0ead99d 100644
--- a/code/portal_detail_display_tools.js
+++ b/code/portal_detail_display_tools.js
@@ -1,4 +1,3 @@
-
// PORTAL DETAILS DISPLAY ////////////////////////////////////////////
// hand any of these functions the details-hash of a portal, and they
// will return pretty, displayable HTML or parts thereof.
@@ -56,10 +55,10 @@ window.getModDetails = function(d) {
}
});
- var t = ''+mods[0]+' '
- + ''+mods[1]+' '
- + ''+mods[2]+' '
- + ''+mods[3]+' '
+ var t = ''+mods[0]+' '
+ + ''+mods[1]+' '
+ + ''+mods[2]+' '
+ + ''+mods[3]+' '
return t;
}
@@ -117,10 +116,10 @@ window.renderResonatorDetails = function(slot, level, nrg, dist, nick, isLeft) {
var max = RESO_NRG[level];
var fillGrade = nrg/max*100;
- var inf = 'energy:\t\t' + nrg + ' / ' + max + ' (' + Math.round(fillGrade) + '%)\n'
- + 'level:\t\t' + level + '\n'
+ var inf = 'energy:\t' + nrg + ' / ' + max + ' (' + Math.round(fillGrade) + '%)\n'
+ + 'level:\t' + level + '\n'
+ 'distance:\t' + dist + 'm\n'
- + 'owner:\t\t' + nick + '\n'
+ + 'owner:\t' + nick + '\n'
+ 'octant:\t' + OCTANTS[slot];
var style = 'width:'+fillGrade+'%; background:'+COLORS_LVL[level]+';';
@@ -154,13 +153,16 @@ window.getDestroyAP = function(d) {
var resoAp = resoCount * DESTROY_RESONATOR;
var linkAp = linkCount * DESTROY_LINK;
var fieldAp = fieldCount * DESTROY_FIELD;
- var sum = resoAp + linkAp + fieldAp;
+ var sum = resoAp + linkAp + fieldAp + CAPTURE_PORTAL + 8*DEPLOY_RESONATOR + COMPLETION_BONUS;
function tt(text) {
- var t = 'Destroy:\n';
+ var t = 'Destroy & Capture:\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 += linkCount + '×\tLinks\t= ' + digits(linkAp) + '\n';
+ t += fieldCount + '×\tFields\t= ' + digits(fieldAp) + '\n';
+ t += '1×\tCapture\t= ' + CAPTURE_PORTAL + '\n';
+ t += '8×\tDeploy\t= ' + DEPLOY_RESONATOR + '\n';
+ t += '1×\tBonus\t= ' + COMPLETION_BONUS + '\n';
t += 'Sum: ' + digits(sum) + ' AP';
return '' + digits(text) + ' ';
}
diff --git a/code/request_handling.js b/code/request_handling.js
index 042fc397..6fe0acd3 100644
--- a/code/request_handling.js
+++ b/code/request_handling.js
@@ -25,10 +25,8 @@ window.requests.abort = function() {
window.activeRequests = [];
window.failedRequestCount = 0;
- window.chat._requestOldPublicRunning = false;
- window.chat._requestNewPublicRunning = false;
- window.chat._requestOldFactionRunning = false;
- window.chat._requestNewFactionRunning = false;
+ window.chat._requestPublicRunning = false;
+ window.chat._requestFactionRunning = false;
renderUpdateStatus();
}
diff --git a/dist/images/draw-circle.png b/dist/images/draw-circle.png
new file mode 100644
index 00000000..0cb17b8f
Binary files /dev/null and b/dist/images/draw-circle.png differ
diff --git a/dist/images/draw-marker-icon.png b/dist/images/draw-marker-icon.png
new file mode 100644
index 00000000..f26437fc
Binary files /dev/null and b/dist/images/draw-marker-icon.png differ
diff --git a/dist/images/draw-polygon.png b/dist/images/draw-polygon.png
new file mode 100644
index 00000000..579102dd
Binary files /dev/null and b/dist/images/draw-polygon.png differ
diff --git a/dist/images/draw-polyline.png b/dist/images/draw-polyline.png
new file mode 100644
index 00000000..18c9a27d
Binary files /dev/null and b/dist/images/draw-polyline.png differ
diff --git a/dist/images/draw-rectangle.png b/dist/images/draw-rectangle.png
new file mode 100644
index 00000000..8f1cbc85
Binary files /dev/null and b/dist/images/draw-rectangle.png differ
diff --git a/dist/images/marker-icon.png b/dist/images/marker-icon.png
new file mode 100644
index 00000000..519b1c57
Binary files /dev/null and b/dist/images/marker-icon.png differ
diff --git a/dist/images/marker-icon@2x.png b/dist/images/marker-icon@2x.png
new file mode 100644
index 00000000..0865fd0c
Binary files /dev/null and b/dist/images/marker-icon@2x.png differ
diff --git a/dist/images/marker-shadow.png b/dist/images/marker-shadow.png
new file mode 100644
index 00000000..d1e773c7
Binary files /dev/null and b/dist/images/marker-shadow.png differ
diff --git a/dist/leaflet.draw.0.1.6.css b/dist/leaflet.draw.0.1.6.css
new file mode 100644
index 00000000..c5ad833c
--- /dev/null
+++ b/dist/leaflet.draw.0.1.6.css
@@ -0,0 +1,118 @@
+/* Leaflet controls */
+
+.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(images/draw-polyline.png);
+}
+
+.leaflet-control-draw-polygon {
+ background-image: url(images/draw-polygon.png);
+}
+
+.leaflet-control-draw-rectangle {
+ background-image: url(images/draw-rectangle.png);
+}
+
+.leaflet-control-draw-circle {
+ background-image: url(images/draw-circle.png);
+}
+
+.leaflet-control-draw-marker {
+ background-image: url(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; }
+}
\ No newline at end of file
diff --git a/dist/leaflet.draw.0.1.6.js b/dist/leaflet.draw.0.1.6.js
new file mode 100644
index 00000000..49bb67b5
--- /dev/null
+++ b/dist/leaflet.draw.0.1.6.js
@@ -0,0 +1,6 @@
+/*
+ Copyright (c) 2012, Smartrak, Jacob Toye
+ Leaflet.draw is an open-source JavaScript library for drawing shapes/markers on leaflet powered maps.
+ https://github.com/jacobtoye/Leaflet.draw
+*/
+(function(e,t){L.drawVersion="0.1.6",L.Util.extend(L.LineUtil,{segmentsIntersect:function(e,t,n,r){return this._checkCounterclockwise(e,n,r)!==this._checkCounterclockwise(t,n,r)&&this._checkCounterclockwise(e,t,n)!==this._checkCounterclockwise(e,t,r)},_checkCounterclockwise:function(e,t,n){return(n.y-e.y)*(t.x-e.x)>(t.y-e.y)*(n.x-e.x)}}),L.Polyline.include({intersects:function(){var e=this._originalPoints,t=e?e.length:0,n,r,i,s,o,u;if(this._tooFewPointsForIntersection())return!1;for(n=t-1;n>=3;n--){i=e[n-1],s=e[n];if(this._lineSegmentsIntersectsRange(i,s,n-2))return!0}return!1},newLatLngIntersects:function(e,t){return this._map?this.newPointIntersects(this._map.latLngToLayerPoint(e),t):!1},newPointIntersects:function(e,t){var n=this._originalPoints,r=n?n.length:0,i=n?n[r-1]:null,s=r-2;return this._tooFewPointsForIntersection(1)?!1:this._lineSegmentsIntersectsRange(i,e,s,t?1:0)},_tooFewPointsForIntersection:function(e){var t=this._originalPoints,n=t?t.length:0;return n+=e||0,!this._originalPoints||n<=3},_lineSegmentsIntersectsRange:function(e,t,n,r){var i=this._originalPoints,s,o;r=r||0;for(var u=n;u>r;u--){s=i[u-1],o=i[u];if(L.LineUtil.segmentsIntersect(e,t,s,o))return!0}return!1}}),L.Polygon.include({intersects:function(){var e,t=this._originalPoints,n,r,i,s;return this._tooFewPointsForIntersection()?!1:(e=L.Polyline.prototype.intersects.call(this),e?!0:(n=t.length,r=t[0],i=t[n-1],s=n-2,this._lineSegmentsIntersectsRange(i,r,s,1)))}}),L.Handler.Draw=L.Handler.extend({includes:L.Mixin.Events,initialize:function(e,t){this._map=e,this._container=e._container,this._overlayPane=e._panes.overlayPane,this._popupPane=e._panes.popupPane,t&&t.shapeOptions&&(t.shapeOptions=L.Util.extend({},this.options.shapeOptions,t.shapeOptions)),L.Util.extend(this.options,t)},enable:function(){this.fire("activated"),this._map.fire("drawing",{drawingType:this.type}),L.Handler.prototype.enable.call(this)},disable:function(){this._map.fire("drawing-disabled",{drawingType:this.type}),L.Handler.prototype.disable.call(this)},addHooks:function(){this._map&&(L.DomUtil.disableTextSelection(),this._label=L.DomUtil.create("div","leaflet-draw-label",this._popupPane),this._singleLineLabel=!1,L.DomEvent.addListener(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._popupPane.removeChild(this._label),delete this._label,L.DomEvent.removeListener(this._container,"keyup",this._cancelDrawing))},_updateLabelText:function(e){e.subtext=e.subtext||"",e.subtext.length===0&&!this._singleLineLabel?(L.DomUtil.addClass(this._label,"leaflet-draw-label-single"),this._singleLineLabel=!0):e.subtext.length>0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._label,"leaflet-draw-label-single"),this._singleLineLabel=!1),this._label.innerHTML=(e.subtext.length>0?''+e.subtext+" "+" ":"")+""+e.text+" "},_updateLabelPosition:function(e){L.DomUtil.setPosition(this._label,e)},_cancelDrawing:function(e){e.keyCode===27&&this.disable()}}),L.Polyline.Draw=L.Handler.Draw.extend({Poly:L.Polyline,type:"polyline",options:{allowIntersection:!0,drawError:{color:"#b00b00",message:"Error: shape edges cannot cross!",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),guidelineDistance:20,shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!1,clickable:!0},zIndexOffset:2e3},initialize:function(e,t){t&&t.drawError&&(t.drawError=L.Util.extend({},this.options.drawError,t.drawError)),L.Handler.Draw.prototype.initialize.call(this,e,t)},addHooks:function(){L.Handler.Draw.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._updateLabelText(this._getLabelText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Handler.Draw.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("click",this._onClick),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mousemove",this._onMouseMove)},_finishShape:function(){if(!this.options.allowIntersection&&this._poly.newLatLngIntersects(this._poly.getLatLngs()[0],!0)){this._showErrorLabel();return}if(!this._shapeIsValid()){this._showErrorLabel();return}this._map.fire("draw:poly-created",{poly:new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions)}),this.disable()},_shapeIsValid:function(){return!0},_onMouseMove:function(e){var t=e.layerPoint,n=e.latlng,r=this._markers.length;this._currentLatLng=n,this._updateLabelPosition(t),r>0&&(this._updateLabelText(this._getLabelText()),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[r-1].getLatLng()),t)),this._mouseMarker.setLatLng(n),L.DomEvent.preventDefault(e.originalEvent)},_onClick:function(e){var t=e.target.getLatLng(),n=this._markers.length;if(n>0&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t)){this._showErrorLabel();return}this._errorShown&&this._hideErrorLabel(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),this._poly.getLatLngs().length===2&&this._map.addLayer(this._poly),this._updateMarkerHandler(),this._vertexAdded(t)},_updateMarkerHandler:function(){this._markers.length>1&&this._markers[this._markers.length-1].on("click",this._finishShape,this),this._markers.length>2&&this._markers[this._markers.length-2].off("click",this._finishShape)},_createMarker:function(e){var t=new L.Marker(e,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset*2});return this._markerGroup.addLayer(t),t},_drawGuide:function(e,t){var n=Math.floor(Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))),r,i,s,o;this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));for(r=this.options.guidelineDistance;r1e3?(t/1e3).toFixed(2)+" km":Math.ceil(t)+" m",this._markers.length===1?e={text:"Click to continue drawing line.",subtext:n}:e={text:"Click last point to finish line.",subtext:n}),e},_showErrorLabel:function(){this._errorShown=!0,L.DomUtil.addClass(this._label,"leaflet-error-draw-label"),L.DomUtil.addClass(this._label,"leaflet-flash-anim"),L.Handler.Draw.prototype._updateLabelText.call(this,{text:this.options.drawError.message}),this._updateGuideColor(this.options.drawError.color),this._poly.setStyle({color:this.options.drawError.color}),this._clearHideErrorTimeout(),this._hideErrorTimeout=setTimeout(L.Util.bind(this._hideErrorLabel,this),this.options.drawError.timeout)},_hideErrorLabel:function(){this._errorShown=!1,this._clearHideErrorTimeout(),L.DomUtil.removeClass(this._label,"leaflet-error-draw-label"),L.DomUtil.removeClass(this._label,"leaflet-flash-anim"),this._updateLabelText(this._getLabelText()),this._updateGuideColor(this.options.shapeOptions.color),this._poly.setStyle({color:this.options.shapeOptions.color})},_clearHideErrorTimeout:function(){this._hideErrorTimeout&&(clearTimeout(this._hideErrorTimeout),this._hideErrorTimeout=null)},_vertexAdded:function(e){this._markers.length===1?this._measurementRunningTotal=0:this._measurementRunningTotal+=e.distanceTo(this._markers[this._markers.length-2].getLatLng())},_cleanUpShape:function(){this._markers.length>0&&this._markers[this._markers.length-1].off("click",this._finishShape)}}),L.Polygon.Draw=L.Polyline.Draw.extend({Poly:L.Polygon,type:"polygon",options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!1}},_updateMarkerHandler:function(){this._markers.length===1&&this._markers[0].on("click",this._finishShape,this)},_getLabelText:function(){var e;return this._markers.length===0?e="Click to start drawing shape.":this._markers.length<3?e="Click to continue drawing shape.":e="Click first point to close this shape.",{text:e}},_shapeIsValid:function(){return this._markers.length>=3},_vertexAdded:function(e){},_cleanUpShape:function(){this._markers.length>0&&this._markers[0].off("click",this._finishShape)}}),L.SimpleShape={},L.SimpleShape.Draw=L.Handler.Draw.extend({addHooks:function(){L.Handler.Draw.prototype.addHooks.call(this),this._map&&(this._map.dragging.disable(),this._container.style.cursor="crosshair",this._updateLabelText({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Handler.Draw.prototype.removeHooks.call(this),this._map&&(this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this),L.DomEvent.off(document,"mouseup",this._onMouseUp),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_onMouseDown:function(e){this._isDrawing=!0,this._startLatLng=e.latlng,L.DomEvent.on(document,"mouseup",this._onMouseUp,this).preventDefault(e.originalEvent)},_onMouseMove:function(e){var t=e.layerPoint,n=e.latlng;this._updateLabelPosition(t),this._isDrawing&&(this._updateLabelText({text:"Release mouse to finish drawing."}),this._drawShape(n))},_onMouseUp:function(e){this._shape&&this._fireCreatedEvent(),this.disable()}}),L.Circle.Draw=L.SimpleShape.Draw.extend({type:"circle",options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0}},_initialLabelText:"Click and drag to draw circle.",_drawShape:function(e){this._shape?this._shape.setRadius(this._startLatLng.distanceTo(e)):(this._shape=new L.Circle(this._startLatLng,this._startLatLng.distanceTo(e),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){this._map.fire("draw:circle-created",{circ:new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions)})}}),L.Rectangle.Draw=L.SimpleShape.Draw.extend({type:"rectangle",options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0}},_initialLabelText:"Click and drag to draw rectangle.",_drawShape:function(e){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,e)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,e),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){this._map.fire("draw:rectangle-created",{rect:new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions)})}}),L.Marker.Draw=L.Handler.Draw.extend({type:"marker",options:{icon:new L.Icon.Default,zIndexOffset:2e3},addHooks:function(){L.Handler.Draw.prototype.addHooks.call(this),this._map&&(this._updateLabelText({text:"Click map to place marker."}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Handler.Draw.prototype.removeHooks.call(this),this._map&&(this._marker&&(this._marker.off("click",this._onClick),this._map.off("click",this._onClick).removeLayer(this._marker),delete this._marker),this._map.off("mousemove",this._onMouseMove))},_onMouseMove:function(e){var t=e.layerPoint,n=e.latlng;this._updateLabelPosition(t),this._marker?this._marker.setLatLng(n):(this._marker=new L.Marker(n,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset}),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_onClick:function(e){this._map.fire("draw:marker-created",{marker:new L.Marker(this._marker.getLatLng(),{icon:this.options.icon})}),this.disable()}}),L.Map.mergeOptions({drawControl:!1}),L.Control.Draw=L.Control.extend({options:{position:"topleft",polyline:{title:"Draw a polyline"},polygon:{title:"Draw a polygon"},rectangle:{title:"Draw a rectangle"},circle:{title:"Draw a circle"},marker:{title:"Add a marker"}},initialize:function(e){L.Util.extend(this.options,e)},onAdd:function(e){var t="leaflet-control-draw",n="leaflet-bar",r=n+"-part",i=L.DomUtil.create("div",t+" "+n),s=[];return this.handlers={},this.options.polyline&&(this.handlers.polyline=new L.Polyline.Draw(e,this.options.polyline),s.push(this._createButton(this.options.polyline.title,t+"-polyline "+r,i,this.handlers.polyline.enable,this.handlers.polyline)),this.handlers.polyline.on("activated",this._disableInactiveModes,this)),this.options.polygon&&(this.handlers.polygon=new L.Polygon.Draw(e,this.options.polygon),s.push(this._createButton(this.options.polygon.title,t+"-polygon "+r,i,this.handlers.polygon.enable,this.handlers.polygon)),this.handlers.polygon.on("activated",this._disableInactiveModes,this)),this.options.rectangle&&(this.handlers.rectangle=new L.Rectangle.Draw(e,this.options.rectangle),s.push(this._createButton(this.options.rectangle.title,t+"-rectangle "+r,i,this.handlers.rectangle.enable,this.handlers.rectangle)),this.handlers.rectangle.on("activated",this._disableInactiveModes,this)),this.options.circle&&(this.handlers.circle=new L.Circle.Draw(e,this.options.circle),s.push(this._createButton(this.options.circle.title,t+"-circle "+r,i,this.handlers.circle.enable,this.handlers.circle)),this.handlers.circle.on("activated",this._disableInactiveModes,this)),this.options.marker&&(this.handlers.marker=new L.Marker.Draw(e,this.options.marker),s.push(this._createButton(this.options.marker.title,t+"-marker "+r,i,this.handlers.marker.enable,this.handlers.marker)),this.handlers.marker.on("activated",this._disableInactiveModes,this)),L.DomUtil.addClass(s[0],r+"-top"),L.DomUtil.addClass(s[s.length-1],r+"-bottom"),i},_createButton:function(e,t,n,r,i){var s=L.DomUtil.create("a",t,n);return s.href="#",s.title=e,L.DomEvent.on(s,"click",L.DomEvent.stopPropagation).on(s,"mousedown",L.DomEvent.stopPropagation).on(s,"dblclick",L.DomEvent.stopPropagation).on(s,"click",L.DomEvent.preventDefault).on(s,"click",r,i),s},_disableInactiveModes:function(){for(var e in this.handlers)this.handlers.hasOwnProperty(e)&&this.handlers[e].enabled()&&this.handlers[e].disable()}}),L.Map.addInitHook(function(){this.options.drawControl&&(this.drawControl=new L.Control.Draw,this.addControl(this.drawControl))})})(this);
\ No newline at end of file
diff --git a/dist/style.css b/dist/style.css
index 6fb01ecd..43abdb69 100644
--- a/dist/style.css
+++ b/dist/style.css
@@ -36,10 +36,10 @@ body {
#sidebartoggle {
display: block;
padding: 20px 5px;
- margin-top: -31px;
+ margin-top: -31px; /* -(toggle height / 2) */
line-height: 10px;
position: absolute;
- top: 50%;
+ top: 340px; /* (sidebar height / 2) */
z-index: 3001;
background-color: rgba(8, 48, 78, 0.9);
color: #FFCE00;
@@ -561,10 +561,6 @@ aside:nth-child(odd) span {
padding: 5px;
}
-#spacer {
- height: 10px;
-}
-
/* a common portal display takes this much space (prevents moving
* content when first selecting a portal) */
@@ -575,7 +571,7 @@ aside:nth-child(odd) span {
/* update status */
#updatestatus {
- background-color: rgba(8, 48, 78, 1);
+ background-color: rgba(8, 48, 78, 0.9);
border-bottom: 0;
border-top: 1px solid #20A8B1;
border-left: 1px solid #20A8B1;
diff --git a/external/leaflet.draw.0.1.6.css b/external/leaflet.draw.0.1.6.css
new file mode 100644
index 00000000..c5ad833c
--- /dev/null
+++ b/external/leaflet.draw.0.1.6.css
@@ -0,0 +1,118 @@
+/* Leaflet controls */
+
+.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(images/draw-polyline.png);
+}
+
+.leaflet-control-draw-polygon {
+ background-image: url(images/draw-polygon.png);
+}
+
+.leaflet-control-draw-rectangle {
+ background-image: url(images/draw-rectangle.png);
+}
+
+.leaflet-control-draw-circle {
+ background-image: url(images/draw-circle.png);
+}
+
+.leaflet-control-draw-marker {
+ background-image: url(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; }
+}
\ No newline at end of file
diff --git a/external/leaflet.draw.0.1.6.js b/external/leaflet.draw.0.1.6.js
new file mode 100644
index 00000000..49bb67b5
--- /dev/null
+++ b/external/leaflet.draw.0.1.6.js
@@ -0,0 +1,6 @@
+/*
+ Copyright (c) 2012, Smartrak, Jacob Toye
+ Leaflet.draw is an open-source JavaScript library for drawing shapes/markers on leaflet powered maps.
+ https://github.com/jacobtoye/Leaflet.draw
+*/
+(function(e,t){L.drawVersion="0.1.6",L.Util.extend(L.LineUtil,{segmentsIntersect:function(e,t,n,r){return this._checkCounterclockwise(e,n,r)!==this._checkCounterclockwise(t,n,r)&&this._checkCounterclockwise(e,t,n)!==this._checkCounterclockwise(e,t,r)},_checkCounterclockwise:function(e,t,n){return(n.y-e.y)*(t.x-e.x)>(t.y-e.y)*(n.x-e.x)}}),L.Polyline.include({intersects:function(){var e=this._originalPoints,t=e?e.length:0,n,r,i,s,o,u;if(this._tooFewPointsForIntersection())return!1;for(n=t-1;n>=3;n--){i=e[n-1],s=e[n];if(this._lineSegmentsIntersectsRange(i,s,n-2))return!0}return!1},newLatLngIntersects:function(e,t){return this._map?this.newPointIntersects(this._map.latLngToLayerPoint(e),t):!1},newPointIntersects:function(e,t){var n=this._originalPoints,r=n?n.length:0,i=n?n[r-1]:null,s=r-2;return this._tooFewPointsForIntersection(1)?!1:this._lineSegmentsIntersectsRange(i,e,s,t?1:0)},_tooFewPointsForIntersection:function(e){var t=this._originalPoints,n=t?t.length:0;return n+=e||0,!this._originalPoints||n<=3},_lineSegmentsIntersectsRange:function(e,t,n,r){var i=this._originalPoints,s,o;r=r||0;for(var u=n;u>r;u--){s=i[u-1],o=i[u];if(L.LineUtil.segmentsIntersect(e,t,s,o))return!0}return!1}}),L.Polygon.include({intersects:function(){var e,t=this._originalPoints,n,r,i,s;return this._tooFewPointsForIntersection()?!1:(e=L.Polyline.prototype.intersects.call(this),e?!0:(n=t.length,r=t[0],i=t[n-1],s=n-2,this._lineSegmentsIntersectsRange(i,r,s,1)))}}),L.Handler.Draw=L.Handler.extend({includes:L.Mixin.Events,initialize:function(e,t){this._map=e,this._container=e._container,this._overlayPane=e._panes.overlayPane,this._popupPane=e._panes.popupPane,t&&t.shapeOptions&&(t.shapeOptions=L.Util.extend({},this.options.shapeOptions,t.shapeOptions)),L.Util.extend(this.options,t)},enable:function(){this.fire("activated"),this._map.fire("drawing",{drawingType:this.type}),L.Handler.prototype.enable.call(this)},disable:function(){this._map.fire("drawing-disabled",{drawingType:this.type}),L.Handler.prototype.disable.call(this)},addHooks:function(){this._map&&(L.DomUtil.disableTextSelection(),this._label=L.DomUtil.create("div","leaflet-draw-label",this._popupPane),this._singleLineLabel=!1,L.DomEvent.addListener(this._container,"keyup",this._cancelDrawing,this))},removeHooks:function(){this._map&&(L.DomUtil.enableTextSelection(),this._popupPane.removeChild(this._label),delete this._label,L.DomEvent.removeListener(this._container,"keyup",this._cancelDrawing))},_updateLabelText:function(e){e.subtext=e.subtext||"",e.subtext.length===0&&!this._singleLineLabel?(L.DomUtil.addClass(this._label,"leaflet-draw-label-single"),this._singleLineLabel=!0):e.subtext.length>0&&this._singleLineLabel&&(L.DomUtil.removeClass(this._label,"leaflet-draw-label-single"),this._singleLineLabel=!1),this._label.innerHTML=(e.subtext.length>0?''+e.subtext+" "+" ":"")+""+e.text+" "},_updateLabelPosition:function(e){L.DomUtil.setPosition(this._label,e)},_cancelDrawing:function(e){e.keyCode===27&&this.disable()}}),L.Polyline.Draw=L.Handler.Draw.extend({Poly:L.Polyline,type:"polyline",options:{allowIntersection:!0,drawError:{color:"#b00b00",message:"Error: shape edges cannot cross!",timeout:2500},icon:new L.DivIcon({iconSize:new L.Point(8,8),className:"leaflet-div-icon leaflet-editing-icon"}),guidelineDistance:20,shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!1,clickable:!0},zIndexOffset:2e3},initialize:function(e,t){t&&t.drawError&&(t.drawError=L.Util.extend({},this.options.drawError,t.drawError)),L.Handler.Draw.prototype.initialize.call(this,e,t)},addHooks:function(){L.Handler.Draw.prototype.addHooks.call(this),this._map&&(this._markers=[],this._markerGroup=new L.LayerGroup,this._map.addLayer(this._markerGroup),this._poly=new L.Polyline([],this.options.shapeOptions),this._updateLabelText(this._getLabelText()),this._mouseMarker||(this._mouseMarker=L.marker(this._map.getCenter(),{icon:L.divIcon({className:"leaflet-mouse-marker",iconAnchor:[20,20],iconSize:[40,40]}),opacity:0,zIndexOffset:this.options.zIndexOffset})),this._mouseMarker.on("click",this._onClick,this).addTo(this._map),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Handler.Draw.prototype.removeHooks.call(this),this._clearHideErrorTimeout(),this._cleanUpShape(),this._map.removeLayer(this._markerGroup),delete this._markerGroup,delete this._markers,this._map.removeLayer(this._poly),delete this._poly,this._mouseMarker.off("click",this._onClick),this._map.removeLayer(this._mouseMarker),delete this._mouseMarker,this._clearGuides(),this._map.off("mousemove",this._onMouseMove)},_finishShape:function(){if(!this.options.allowIntersection&&this._poly.newLatLngIntersects(this._poly.getLatLngs()[0],!0)){this._showErrorLabel();return}if(!this._shapeIsValid()){this._showErrorLabel();return}this._map.fire("draw:poly-created",{poly:new this.Poly(this._poly.getLatLngs(),this.options.shapeOptions)}),this.disable()},_shapeIsValid:function(){return!0},_onMouseMove:function(e){var t=e.layerPoint,n=e.latlng,r=this._markers.length;this._currentLatLng=n,this._updateLabelPosition(t),r>0&&(this._updateLabelText(this._getLabelText()),this._clearGuides(),this._drawGuide(this._map.latLngToLayerPoint(this._markers[r-1].getLatLng()),t)),this._mouseMarker.setLatLng(n),L.DomEvent.preventDefault(e.originalEvent)},_onClick:function(e){var t=e.target.getLatLng(),n=this._markers.length;if(n>0&&!this.options.allowIntersection&&this._poly.newLatLngIntersects(t)){this._showErrorLabel();return}this._errorShown&&this._hideErrorLabel(),this._markers.push(this._createMarker(t)),this._poly.addLatLng(t),this._poly.getLatLngs().length===2&&this._map.addLayer(this._poly),this._updateMarkerHandler(),this._vertexAdded(t)},_updateMarkerHandler:function(){this._markers.length>1&&this._markers[this._markers.length-1].on("click",this._finishShape,this),this._markers.length>2&&this._markers[this._markers.length-2].off("click",this._finishShape)},_createMarker:function(e){var t=new L.Marker(e,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset*2});return this._markerGroup.addLayer(t),t},_drawGuide:function(e,t){var n=Math.floor(Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))),r,i,s,o;this._guidesContainer||(this._guidesContainer=L.DomUtil.create("div","leaflet-draw-guides",this._overlayPane));for(r=this.options.guidelineDistance;r1e3?(t/1e3).toFixed(2)+" km":Math.ceil(t)+" m",this._markers.length===1?e={text:"Click to continue drawing line.",subtext:n}:e={text:"Click last point to finish line.",subtext:n}),e},_showErrorLabel:function(){this._errorShown=!0,L.DomUtil.addClass(this._label,"leaflet-error-draw-label"),L.DomUtil.addClass(this._label,"leaflet-flash-anim"),L.Handler.Draw.prototype._updateLabelText.call(this,{text:this.options.drawError.message}),this._updateGuideColor(this.options.drawError.color),this._poly.setStyle({color:this.options.drawError.color}),this._clearHideErrorTimeout(),this._hideErrorTimeout=setTimeout(L.Util.bind(this._hideErrorLabel,this),this.options.drawError.timeout)},_hideErrorLabel:function(){this._errorShown=!1,this._clearHideErrorTimeout(),L.DomUtil.removeClass(this._label,"leaflet-error-draw-label"),L.DomUtil.removeClass(this._label,"leaflet-flash-anim"),this._updateLabelText(this._getLabelText()),this._updateGuideColor(this.options.shapeOptions.color),this._poly.setStyle({color:this.options.shapeOptions.color})},_clearHideErrorTimeout:function(){this._hideErrorTimeout&&(clearTimeout(this._hideErrorTimeout),this._hideErrorTimeout=null)},_vertexAdded:function(e){this._markers.length===1?this._measurementRunningTotal=0:this._measurementRunningTotal+=e.distanceTo(this._markers[this._markers.length-2].getLatLng())},_cleanUpShape:function(){this._markers.length>0&&this._markers[this._markers.length-1].off("click",this._finishShape)}}),L.Polygon.Draw=L.Polyline.Draw.extend({Poly:L.Polygon,type:"polygon",options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!1}},_updateMarkerHandler:function(){this._markers.length===1&&this._markers[0].on("click",this._finishShape,this)},_getLabelText:function(){var e;return this._markers.length===0?e="Click to start drawing shape.":this._markers.length<3?e="Click to continue drawing shape.":e="Click first point to close this shape.",{text:e}},_shapeIsValid:function(){return this._markers.length>=3},_vertexAdded:function(e){},_cleanUpShape:function(){this._markers.length>0&&this._markers[0].off("click",this._finishShape)}}),L.SimpleShape={},L.SimpleShape.Draw=L.Handler.Draw.extend({addHooks:function(){L.Handler.Draw.prototype.addHooks.call(this),this._map&&(this._map.dragging.disable(),this._container.style.cursor="crosshair",this._updateLabelText({text:this._initialLabelText}),this._map.on("mousedown",this._onMouseDown,this).on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Handler.Draw.prototype.removeHooks.call(this),this._map&&(this._map.dragging.enable(),this._container.style.cursor="",this._map.off("mousedown",this._onMouseDown,this).off("mousemove",this._onMouseMove,this),L.DomEvent.off(document,"mouseup",this._onMouseUp),this._shape&&(this._map.removeLayer(this._shape),delete this._shape)),this._isDrawing=!1},_onMouseDown:function(e){this._isDrawing=!0,this._startLatLng=e.latlng,L.DomEvent.on(document,"mouseup",this._onMouseUp,this).preventDefault(e.originalEvent)},_onMouseMove:function(e){var t=e.layerPoint,n=e.latlng;this._updateLabelPosition(t),this._isDrawing&&(this._updateLabelText({text:"Release mouse to finish drawing."}),this._drawShape(n))},_onMouseUp:function(e){this._shape&&this._fireCreatedEvent(),this.disable()}}),L.Circle.Draw=L.SimpleShape.Draw.extend({type:"circle",options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0}},_initialLabelText:"Click and drag to draw circle.",_drawShape:function(e){this._shape?this._shape.setRadius(this._startLatLng.distanceTo(e)):(this._shape=new L.Circle(this._startLatLng,this._startLatLng.distanceTo(e),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){this._map.fire("draw:circle-created",{circ:new L.Circle(this._startLatLng,this._shape.getRadius(),this.options.shapeOptions)})}}),L.Rectangle.Draw=L.SimpleShape.Draw.extend({type:"rectangle",options:{shapeOptions:{stroke:!0,color:"#f06eaa",weight:4,opacity:.5,fill:!0,fillColor:null,fillOpacity:.2,clickable:!0}},_initialLabelText:"Click and drag to draw rectangle.",_drawShape:function(e){this._shape?this._shape.setBounds(new L.LatLngBounds(this._startLatLng,e)):(this._shape=new L.Rectangle(new L.LatLngBounds(this._startLatLng,e),this.options.shapeOptions),this._map.addLayer(this._shape))},_fireCreatedEvent:function(){this._map.fire("draw:rectangle-created",{rect:new L.Rectangle(this._shape.getBounds(),this.options.shapeOptions)})}}),L.Marker.Draw=L.Handler.Draw.extend({type:"marker",options:{icon:new L.Icon.Default,zIndexOffset:2e3},addHooks:function(){L.Handler.Draw.prototype.addHooks.call(this),this._map&&(this._updateLabelText({text:"Click map to place marker."}),this._map.on("mousemove",this._onMouseMove,this))},removeHooks:function(){L.Handler.Draw.prototype.removeHooks.call(this),this._map&&(this._marker&&(this._marker.off("click",this._onClick),this._map.off("click",this._onClick).removeLayer(this._marker),delete this._marker),this._map.off("mousemove",this._onMouseMove))},_onMouseMove:function(e){var t=e.layerPoint,n=e.latlng;this._updateLabelPosition(t),this._marker?this._marker.setLatLng(n):(this._marker=new L.Marker(n,{icon:this.options.icon,zIndexOffset:this.options.zIndexOffset}),this._marker.on("click",this._onClick,this),this._map.on("click",this._onClick,this).addLayer(this._marker))},_onClick:function(e){this._map.fire("draw:marker-created",{marker:new L.Marker(this._marker.getLatLng(),{icon:this.options.icon})}),this.disable()}}),L.Map.mergeOptions({drawControl:!1}),L.Control.Draw=L.Control.extend({options:{position:"topleft",polyline:{title:"Draw a polyline"},polygon:{title:"Draw a polygon"},rectangle:{title:"Draw a rectangle"},circle:{title:"Draw a circle"},marker:{title:"Add a marker"}},initialize:function(e){L.Util.extend(this.options,e)},onAdd:function(e){var t="leaflet-control-draw",n="leaflet-bar",r=n+"-part",i=L.DomUtil.create("div",t+" "+n),s=[];return this.handlers={},this.options.polyline&&(this.handlers.polyline=new L.Polyline.Draw(e,this.options.polyline),s.push(this._createButton(this.options.polyline.title,t+"-polyline "+r,i,this.handlers.polyline.enable,this.handlers.polyline)),this.handlers.polyline.on("activated",this._disableInactiveModes,this)),this.options.polygon&&(this.handlers.polygon=new L.Polygon.Draw(e,this.options.polygon),s.push(this._createButton(this.options.polygon.title,t+"-polygon "+r,i,this.handlers.polygon.enable,this.handlers.polygon)),this.handlers.polygon.on("activated",this._disableInactiveModes,this)),this.options.rectangle&&(this.handlers.rectangle=new L.Rectangle.Draw(e,this.options.rectangle),s.push(this._createButton(this.options.rectangle.title,t+"-rectangle "+r,i,this.handlers.rectangle.enable,this.handlers.rectangle)),this.handlers.rectangle.on("activated",this._disableInactiveModes,this)),this.options.circle&&(this.handlers.circle=new L.Circle.Draw(e,this.options.circle),s.push(this._createButton(this.options.circle.title,t+"-circle "+r,i,this.handlers.circle.enable,this.handlers.circle)),this.handlers.circle.on("activated",this._disableInactiveModes,this)),this.options.marker&&(this.handlers.marker=new L.Marker.Draw(e,this.options.marker),s.push(this._createButton(this.options.marker.title,t+"-marker "+r,i,this.handlers.marker.enable,this.handlers.marker)),this.handlers.marker.on("activated",this._disableInactiveModes,this)),L.DomUtil.addClass(s[0],r+"-top"),L.DomUtil.addClass(s[s.length-1],r+"-bottom"),i},_createButton:function(e,t,n,r,i){var s=L.DomUtil.create("a",t,n);return s.href="#",s.title=e,L.DomEvent.on(s,"click",L.DomEvent.stopPropagation).on(s,"mousedown",L.DomEvent.stopPropagation).on(s,"dblclick",L.DomEvent.stopPropagation).on(s,"click",L.DomEvent.preventDefault).on(s,"click",r,i),s},_disableInactiveModes:function(){for(var e in this.handlers)this.handlers.hasOwnProperty(e)&&this.handlers[e].enabled()&&this.handlers[e].disable()}}),L.Map.addInitHook(function(){this.options.drawControl&&(this.drawControl=new L.Control.Draw,this.addControl(this.drawControl))})})(this);
\ No newline at end of file
diff --git a/images/draw-circle.png b/images/draw-circle.png
new file mode 100644
index 00000000..0cb17b8f
Binary files /dev/null and b/images/draw-circle.png differ
diff --git a/images/draw-marker-icon.png b/images/draw-marker-icon.png
new file mode 100644
index 00000000..f26437fc
Binary files /dev/null and b/images/draw-marker-icon.png differ
diff --git a/images/draw-polygon.png b/images/draw-polygon.png
new file mode 100644
index 00000000..579102dd
Binary files /dev/null and b/images/draw-polygon.png differ
diff --git a/images/draw-polyline.png b/images/draw-polyline.png
new file mode 100644
index 00000000..18c9a27d
Binary files /dev/null and b/images/draw-polyline.png differ
diff --git a/images/draw-rectangle.png b/images/draw-rectangle.png
new file mode 100644
index 00000000..8f1cbc85
Binary files /dev/null and b/images/draw-rectangle.png differ
diff --git a/images/marker-icon.png b/images/marker-icon.png
new file mode 100644
index 00000000..519b1c57
Binary files /dev/null and b/images/marker-icon.png differ
diff --git a/images/marker-icon@2x.png b/images/marker-icon@2x.png
new file mode 100644
index 00000000..0865fd0c
Binary files /dev/null and b/images/marker-icon@2x.png differ
diff --git a/images/marker-shadow.png b/images/marker-shadow.png
new file mode 100644
index 00000000..d1e773c7
Binary files /dev/null and b/images/marker-shadow.png differ
diff --git a/json_examples/chat_public_decay_msg.js b/json_examples/chat_public_decay_msg.js
new file mode 100644
index 00000000..d7366372
--- /dev/null
+++ b/json_examples/chat_public_decay_msg.js
@@ -0,0 +1,49 @@
+[
+ "431a30b634394956a16e62954222a37d.d",
+ 1360634878999,
+ {
+ "plext": {
+ "text": "Control Field @In the fishing village ( Lomse (Oktyabr'skaya ulitsa, 1, Kaliningrad, Kaliningrad Oblast, Russia) has decayed -108 MUs",
+ "markup": [
+ [
+ "TEXT",
+ {
+ "plain": "Control Field @"
+ }
+ ],
+ [
+ "PORTAL",
+ {
+ "name": "In the fishing village ( Lomse",
+ "plain": "In the fishing village ( Lomse (Oktyabr'skaya ulitsa, 1, Kaliningrad, Kaliningrad Oblast, Russia)",
+ "team": "ALIENS",
+ "latE6": 54705182,
+ "address": "Oktyabr'skaya ulitsa, 1, Kaliningrad, Kaliningrad Oblast, Russia",
+ "lngE6": 20514959,
+ "guid": "6af16d09fb574c989b7fa09e718585a7.12"
+ }
+ ],
+ [
+ "TEXT",
+ {
+ "plain": " has decayed -"
+ }
+ ],
+ [
+ "TEXT",
+ {
+ "plain": "108"
+ }
+ ],
+ [
+ "TEXT",
+ {
+ "plain": " MUs"
+ }
+ ]
+ ],
+ "plextType": "SYSTEM_BROADCAST",
+ "team": "NEUTRAL"
+ }
+ }
+]
diff --git a/main.js b/main.js
index 96f0a63c..5400cfc1 100644
--- a/main.js
+++ b/main.js
@@ -62,12 +62,14 @@ document.getElementsByTagName('head')[0].innerHTML = ''
document.getElementsByTagName('body')[0].innerHTML = ''
+ 'Loading, please wait
'
+ ''
+ ''
+ '
'
+ '
'
- + '
'
+ + '
'
+ + '
'
+ '
'
+ ''
+ ''
@@ -79,7 +81,6 @@ document.getElementsByTagName('body')[0].innerHTML = ''
+ '
'
+ ' '
+ ' '
- + '
'
+ ' '
+ ''
+ '
';
@@ -96,47 +97,55 @@ function wrapper() {
L_PREFER_CANVAS = false;
// CONFIG OPTIONS ////////////////////////////////////////////////////
-var REFRESH = 30; // refresh view every 30s (base time)
-var ZOOM_LEVEL_ADJ = 5; // add 5 seconds per zoom level
-var REFRESH_GAME_SCORE = 5*60; // refresh game score every 5 minutes
-var MAX_IDLE_TIME = 4; // stop updating map after 4min idling
-var PRECACHE_PLAYER_NAMES_ZOOM = 17; // zoom level to start pre-resolving player names
-var HIDDEN_SCROLLBAR_ASSUMED_WIDTH = 20;
-var SIDEBAR_WIDTH = 300;
+window.REFRESH = 30; // refresh view every 30s (base time)
+window.ZOOM_LEVEL_ADJ = 5; // add 5 seconds per zoom level
+window.REFRESH_GAME_SCORE = 5*60; // refresh game score every 5 minutes
+window.MAX_IDLE_TIME = 4; // stop updating map after 4min idling
+window.PRECACHE_PLAYER_NAMES_ZOOM = 17; // zoom level to start pre-resolving player names
+window.HIDDEN_SCROLLBAR_ASSUMED_WIDTH = 20;
+window.SIDEBAR_WIDTH = 300;
// chat messages are requested for the visible viewport. On high zoom
// levels this gets pretty pointless, so request messages in at least a
// X km radius.
-var CHAT_MIN_RANGE = 6;
+window.CHAT_MIN_RANGE = 6;
// this controls how far data is being drawn outside the viewport. Set
// it 0 to only draw entities that intersect the current view. A value
// of one will render an area twice the size of the viewport (or some-
// thing like that, Leaflet doc isn’t 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.
-var VIEWPORT_PAD_RATIO = 0.3;
+window.VIEWPORT_PAD_RATIO = 0.3;
// how many items to request each query
-var CHAT_PUBLIC_ITEMS = 200
-var CHAT_FACTION_ITEMS = 50
+window.CHAT_PUBLIC_ITEMS = 200;
+window.CHAT_FACTION_ITEMS = 50;
+// how many pixels to the top before requesting new data
+window.CHAT_REQUEST_SCROLL_TOP = 200;
+window.CHAT_SHRINKED = 60;
// Leaflet will get very slow for MANY items. It’s better to display
// only some instead of crashing the browser.
-var MAX_DRAWN_PORTALS = 1000;
-var MAX_DRAWN_LINKS = 400;
-var MAX_DRAWN_FIELDS = 200;
+window.MAX_DRAWN_PORTALS = 1000;
+window.MAX_DRAWN_LINKS = 400;
+window.MAX_DRAWN_FIELDS = 200;
+// Minimum zoom level resonator will display
+window.RESONATOR_DISPLAY_ZOOM_LEVEL = 17;
-
-var COLOR_SELECTED_PORTAL = '#f00';
-var COLORS = ['#FFCE00', '#0088FF', '#03FE03']; // none, res, enl
-var COLORS_LVL = ['#000', '#FECE5A', '#FFA630', '#FF7315', '#E40000', '#FD2992', '#EB26CD', '#C124E0', '#9627F4'];
-var COLORS_MOD = {VERY_RARE: '#F78AF6', RARE: '#AD8AFF', COMMON: '#84FBBD'};
+window.COLOR_SELECTED_PORTAL = '#f00';
+window.COLORS = ['#FFCE00', '#0088FF', '#03FE03']; // 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'};
// circles around a selected portal that show from where you can hack
// it and how far the portal reaches (i.e. how far links may be made
// from this portal)
-var ACCESS_INDICATOR_COLOR = 'orange';
-var RANGE_INDICATOR_COLOR = 'red';
+window.ACCESS_INDICATOR_COLOR = 'orange';
+window.RANGE_INDICATOR_COLOR = 'red';
+
+
+window.DEFAULT_PORTAL_IMG = 'http://commondatastorage.googleapis.com/ingress/img/default-portal-image.png';
+window.NOMINATIM = 'http://nominatim.openstreetmap.org/search?format=json&limit=1&q=';
// INGRESS CONSTANTS /////////////////////////////////////////////////
// http://decodeingress.me/2012/11/18/ingress-portal-levels-and-link-range/
@@ -145,28 +154,22 @@ 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 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 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
+var CAPTURE_PORTAL = 500; //AP for capturing a portal
+var DEPLOY_RESONATOR = 125; //AP for deploying a resonator
+var COMPLETION_BONUS = 250; //AP for deploying all resonators on portal
// OTHER MORE-OR-LESS CONSTANTS //////////////////////////////////////
-var NOMINATIM = 'http://nominatim.openstreetmap.org/search?format=json&limit=1&q=';
-var DEG2RAD = Math.PI / 180;
var TEAM_NONE = 0, TEAM_RES = 1, TEAM_ENL = 2;
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
-var PLAYER = window.PLAYER;
-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;
+var DEG2RAD = Math.PI / 180;
// STORAGE ///////////////////////////////////////////////////////////
// global variables used for storage. Most likely READ ONLY. Proper
diff --git a/pack-release.sh b/pack-release.sh
index 8ffd8790..d19213e7 100755
--- a/pack-release.sh
+++ b/pack-release.sh
@@ -3,7 +3,7 @@
./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
+cp external/* dist/
+cp -r images/ dist/images
echo 'Change path of style.css to dist/style.css'
diff --git a/plugins/README.md b/plugins/README.md
index 310077f6..739f840a 100644
--- a/plugins/README.md
+++ b/plugins/README.md
@@ -10,17 +10,25 @@ Available Plugins
-----------------
- [**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 or orange 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.
+- [**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.
Hacking
-------
-Plugins may be developed in the same way as the total conversion script. Plugins may provide features tailored to specific needs and are allowed to change things as they see fit. You can provide them separately oder submit a pull request to have them managed in this repository. There are currently no hooks that allow integration with the main script, but I will add those if the need arises. Simply open a bug report.
+Plugins may be developed in the same way as the total conversion script. Plugins may provide features tailored to specific needs and are allowed to change things as they see fit. You can provide them separately or submit a pull request to have them managed in this repository.
+If you think a hook in the main script is required, simply open a bug report.
You can use the guess player level script as an example to get you started. Just update the names and the part between `// PLUGIN START` and `// PLUGIN END` and you should be able to develop your plugin. The other code ensures your plugin is executed after the main script.
If you happen the write general purpose functions for your plugin, consider adding them to the main script instead. For example, if you write a `getResoCountFromPortal(details)` function it may be very well added to `code/portal_info.js`.
+External Dependencies
+---------------------
+
+If you have external dependencies put them into `external/` and add a version number to their filename. I will put them in `dist/` once required. Don’t forget to add a note about author and license in main `README.md`.
+
Available Hooks
---------------
diff --git a/plugins/draw-tools.user.js b/plugins/draw-tools.user.js
new file mode 100644
index 00000000..34a5f6e0
--- /dev/null
+++ b/plugins/draw-tools.user.js
@@ -0,0 +1,211 @@
+// ==UserScript==
+// @id iitc-plugin-draw-tools@breunigs
+// @name iitc: draw tools
+// @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/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*
+// ==/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 ////////////////////////////////////////////////////////
+
+var DRAW_TOOLS_SHAPE_OPTIONS = {
+ color: '#BB56FF',
+ fill: false,
+ opacity: 1,
+ weight: 4,
+ clickable: false
+};
+
+// use own namespace for plugin
+window.plugin.drawTools = function() {};
+
+window.plugin.drawTools.loadExternals = function() {
+ var base = 'http://breunigs.github.com/ingress-intel-total-conversion/dist';
+ //~ var base = 'http://0.0.0.0:8000/dist';
+ $('head').append(' ');
+ load(base+'/leaflet.draw.0.1.6.js').thenRun(window.plugin.drawTools.boot);
+ // overwrite default Leaflet Marker icon.
+ L.Icon.Default.imagePath = base + '/images';
+}
+
+
+window.plugin.drawTools.addStyles = function() {
+ $('head').append('');
+}
+
+// renders buttons which are not originally part of Leaflet.draw, such
+// as the clear-drawings button.
+window.plugin.drawTools.addCustomButtons = function() {
+ $('.leaflet-control-draw .leaflet-bar-part-bottom').removeClass('leaflet-bar-part-bottom');
+
+ var undo = $('⎌ ')
+ .click(function() {
+ var last = null;
+ window.plugin.drawTools.drawnItems.eachLayer(function(l) {
+ last = l;
+ });
+ if(last) window.plugin.drawTools.drawnItems.removeLayer(last);
+ }
+ );
+
+ var clear = $('✗ ')
+ .click(function() {
+ window.plugin.drawTools.drawnItems.clearLayers();
+ }
+ );
+
+ $('.leaflet-control-draw').append(undo).append(clear);
+}
+
+// renders the draw control buttons in the top left corner
+window.plugin.drawTools.addDrawControl = function() {
+ var drawControl = new L.Control.Draw({
+ rectangle: false,
+ polygon: false,
+
+ polyline: {
+ shapeOptions: DRAW_TOOLS_SHAPE_OPTIONS
+ },
+
+ circle: {
+ shapeOptions: DRAW_TOOLS_SHAPE_OPTIONS
+ }
+ });
+ map.addControl(drawControl);
+ plugin.drawTools.addCustomButtons();
+}
+
+// hacks into circle to render the radius of the circle while drawing
+// and to allow the center of the circle to snap to a nearby portal.
+window.plugin.drawTools.enhanceCircle = function() {
+ // replace _onMouseMove function to also display the radius of the
+ // circle
+ L.Circle.Draw.prototype._onMouseMove = function (e) {
+ var layerPoint = e.layerPoint,
+ latlng = e.latlng;
+
+ this._updateLabelPosition(layerPoint);
+ if (this._isDrawing) {
+ var dist = this._startLatLng.distanceTo(latlng);
+ dist = dist > 1000
+ ? (dist / 1000).toFixed(2) + ' km'
+ : Math.ceil(dist) + ' m';
+ this._updateLabelText({
+ text: 'Release mouse to finish drawing. ',
+ subtext: 'radius: ' +dist }
+ );
+ this._drawShape(latlng);
+ }
+ }
+
+ // replace _onMouseDown to implement snapping
+ L.Circle.Draw.prototype._onMouseDown = function (e) {
+ this._isDrawing = true;
+
+ var snapTo = window.plugin.drawTools.getSnapLatLng(e.containerPoint);
+ this._startLatLng = snapTo || e.latlng;
+
+ L.DomEvent
+ .on(document, 'mouseup', this._onMouseUp, this)
+ .preventDefault(e.originalEvent);
+ }
+}
+
+// hacks into PolyLine to implement snapping and to remove the polyline
+// markers when they are not required anymore for finishing the line.
+// Otherwise they get in the way and make it harder to create a closed
+// polyline.
+window.plugin.drawTools.enhancePolyLine = function() {
+ // hack in snapping
+ L.Polyline.Draw.prototype._onClickOld = L.Polyline.Draw.prototype._onClick;
+ L.Polyline.Draw.prototype._onClick = function(e) {
+ var cp = map.latLngToContainerPoint(e.target.getLatLng());
+ var snapTo = window.plugin.drawTools.getSnapLatLng(cp);
+ if(snapTo) e.target._latlng = snapTo;
+ return this._onClickOld(e);
+ }
+
+ // remove polyline markers because they get in the way
+ L.Polyline.Draw.prototype._updateMarkerHandler = function() {
+ if(!this._markers) return;
+
+ if(this._markers.length >= 2)
+ this._markerGroup.removeLayer(this._markers.shift());
+
+ if(this._markers.length >= 1)
+ this._markers[this._markers.length - 1].on('click', this._finishShape, this);
+ }
+}
+
+// given a container point it tries to find the most suitable portal to
+// snap to. It takes the CircleMarker’s radius and weight into account.
+// Will return null if nothing to snap to or a LatLng instance.
+window.plugin.drawTools.getSnapLatLng = function(containerPoint) {
+ var candidates = [];
+ $.each(window.portals, function(guid, portal) {
+ var ll = portal.getLatLng();
+ var pp = map.latLngToContainerPoint(ll);
+ var size = portal.options.weight + portal.options.radius;
+ var dist = pp.distanceTo(containerPoint);
+ if(dist > size) return true;
+ candidates.push([dist, ll]);
+ });
+
+ if(candidates.length === 0) return;
+ candidates = candidates.sort(function(a, b) { return a[0]-b[0]; });
+ return candidates[0][1];
+}
+
+window.plugin.drawTools.boot = function() {
+ plugin.drawTools.enhanceCircle();
+ plugin.drawTools.enhancePolyLine();
+ plugin.drawTools.addStyles();
+ plugin.drawTools.addDrawControl();
+
+ window.plugin.drawTools.drawnItems = new L.LayerGroup();
+ var drawnItems = window.plugin.drawTools.drawnItems;
+ map.on('draw:poly-created', function (e) {
+ drawnItems.addLayer(e.poly);
+ });
+
+
+ map.on('draw:circle-created', function (e) {
+ drawnItems.addLayer(e.circ);
+ });
+
+ map.on('draw:marker-created', function (e) {
+ drawnItems.addLayer(e.marker);
+ e.marker.dragging.enable();
+ });
+
+ window.layerChooser.addOverlay(drawnItems, 'Drawn Items');
+ map.addLayer(drawnItems);
+}
+
+
+var setup = window.plugin.drawTools.loadExternals;
+
+// PLUGIN END //////////////////////////////////////////////////////////
+
+if(window.iitcLoaded && typeof setup === 'function') {
+ setup();
+} else {
+ if(window.bootPlugins)
+ window.bootPlugins.push(setup);
+ else
+ window.bootPlugins = [setup];
+}
+} // wrapper end
+// inject code into site context
+var script = document.createElement('script');
+script.appendChild(document.createTextNode('('+ wrapper +')();'));
+(document.body || document.head || document.documentElement).appendChild(script);
diff --git a/plugins/show-portal-weakness.user.js b/plugins/show-portal-weakness.user.js
new file mode 100644
index 00000000..48e60682
--- /dev/null
+++ b/plugins/show-portal-weakness.user.js
@@ -0,0 +1,132 @@
+// ==UserScript==
+// @id iitc-plugin-show-portal-weakness@vita10gy
+// @name iitc: show portal weakness
+// @version 0.2
+// @namespace https://github.com/breunigs/ingress-intel-total-conversion
+// @updateURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-portal-weekness.user.js
+// @downloadURL https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/show-portal-weekness.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*
+// ==/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.portalWeakness = function() {};
+
+window.plugin.portalWeakness.getPortalWeaknessFactor = function(d)
+{
+ var portal_weakness = 0;
+ if(getTeam(d) != 0)
+ {
+ if(window.getTotalPortalEnergy(d)> 0 && window.getCurrentPortalEnergy(d) < window.getTotalPortalEnergy(d))
+ {
+ portal_weakness = 1 - (window.getPortalEnergy(d)/window.getTotalPortalEnergy(d));
+ }
+ //Ding the portal for every missing sheild.
+ $.each(d.portalV2.linkedModArray, function(ind, mod)
+ {
+ if(mod == null)
+ {
+ portal_weakness += .05;
+ }
+ });
+ //Ding the portal for every missing resonator.
+ var resCount = 0;
+ $.each(d.resonatorArray.resonators, function(ind, reso)
+ {
+ if(reso == null) {
+ portal_weakness += .125;
+ }
+ else {
+ resCount++;
+ }
+ });
+ if(portal_weakness<0) {
+ portal_weakness = 0;
+ }
+ if(portal_weakness>1)
+ {
+ portal_weakness = 1;
+ }
+ }
+ return(Math.round(portal_weakness*100)/100);
+}
+
+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))
+ {
+ portal_weakness = 1 - (window.getCurrentPortalEnergy(d)/window.getTotalPortalEnergy(d));
+ }
+ //Ding the portal for every missing sheild.
+ $.each(d.portalV2.linkedModArray, function(ind, mod)
+ {
+ if(mod == null)
+ {
+ portal_weakness += .03;
+ }
+ });
+ //Ding the portal for every missing resonator.
+ var resCount = 0;
+ $.each(d.resonatorArray.resonators, function(ind, reso)
+ {
+ if(reso == null) {
+ portal_weakness += .125;
+ }
+ else {
+ resCount++;
+ }
+ });
+ if(portal_weakness<0) {
+ portal_weakness = 0;
+ }
+ 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)
+ {
+ params["dashArray"] = "4,6";
+ }
+ data.portal.setStyle(params);
+ }
+ }
+}
+
+var setup = function() {
+ window.addHook('portalAdded', window.plugin.portalWeakness.portalAdded);
+ window.COLOR_SELECTED_PORTAL = '#f0f';
+}
+
+// PLUGIN END //////////////////////////////////////////////////////////
+
+if(window.iitcLoaded && typeof setup === 'function') {
+ setup();
+} else {
+ if(window.bootPlugins)
+ window.bootPlugins.push(setup);
+ else
+ window.bootPlugins = [setup];
+}
+} // wrapper end
+// inject code into site context
+var script = document.createElement('script');
+script.appendChild(document.createTextNode('('+ wrapper +')();'));
+(document.body || document.head || document.documentElement).appendChild(script);
diff --git a/style.css b/style.css
index bec15a62..46ffb18c 100644
--- a/style.css
+++ b/style.css
@@ -2,7 +2,7 @@
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.";
+ content: "Please update your total conversion script or it will not work properly anymore. \a Google \"ingress total conversion\".";
display: block;
font-weight: bold;
left: 381px;
@@ -10,6 +10,8 @@ body:after {
position: absolute;
top: 0;
z-index: 9001;
+ white-space: pre;
+ text-align: center;
}
/* general rules ******************************************************/
@@ -50,10 +52,10 @@ body {
#sidebartoggle {
display: block;
padding: 20px 5px;
- margin-top: -31px;
+ margin-top: -31px; /* -(toggle height / 2) */
line-height: 10px;
position: absolute;
- top: 50%;
+ top: 340px; /* (sidebar height / 2) */
z-index: 3001;
background-color: rgba(8, 48, 78, 0.9);
color: #FFCE00;
@@ -189,7 +191,7 @@ em {
top: 25px;
}
-#chatpublic, #chatautomated {
+#chatpublic, #chatfull, #chatcompact, /* FIXME DEPRECATED: */#chatautomated {
display: none;
}
@@ -224,6 +226,7 @@ time {
display: inline-block;
width: 44px;
color: #bbb;
+ overflow: hidden;
}
mark {
@@ -447,7 +450,7 @@ h3 {
border: 1px solid #666;
}
-.mods span[title=""] {
+.mods span:not([title]) {
cursor: auto;
}
@@ -575,10 +578,6 @@ aside:nth-child(odd) span {
padding: 5px;
}
-#spacer {
- height: 10px;
-}
-
/* a common portal display takes this much space (prevents moving
* content when first selecting a portal) */
@@ -589,7 +588,7 @@ aside:nth-child(odd) span {
/* update status */
#updatestatus {
- background-color: rgba(8, 48, 78, 1);
+ background-color: rgba(8, 48, 78, 0.9);
border-bottom: 0;
border-top: 1px solid #20A8B1;
border-left: 1px solid #20A8B1;
@@ -617,3 +616,23 @@ aside:nth-child(odd) span {
#largepreview img {
border: 2px solid #f8ff5e;
}
+
+/* tooltips */
+.ui-tooltip {
+ max-width: 300px;
+ position: absolute;
+ z-index: 9999;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ color: #222;
+ font: 13px/15px "Helvetica Neue", Arial, Helvetica, sans-serif;
+ padding: 2px 4px;
+}
+
+td {
+ padding: 0;
+}
+
+td + td {
+ padding-left: 4px;
+}