iitcp
Version:
IITC Plugin creator and developer tools
1,007 lines (854 loc) • 23.3 kB
JavaScript
/**
* @fileOverview Externs for IITC
* with thanks to Filip Wieland
* @see http://ingress-intel-total-conversion.readthedocs.io
* @externs
*/
window.PLAYER = {};
/**
* @const {string}
*/
window.PLAYER.ap;
/**
* @const {number}
*/
window.PLAYER.energy;
/**
* @const {number}
*/
window.PLAYER.level;
/**
* @const {string}
*/
window.PLAYER.min_ap_for_current_level;
/**
* @const {string}
*/
window.PLAYER.min_ap_for_next_level;
/**
* @const {RegExp}
*/
window.PLAYER.nickMatcher;
/**
* @const {string}
*/
window.PLAYER.nickname;
/**
* @const {string}
*/
window.PLAYER.team;
/**
* @const {number}
*/
window.PLAYER.verified_level;
/**
* @const {string}
*/
window.PLAYER.xm_capacity;
/**
* Controls how often the map should refresh, in seconds, default 30.
* @const {number}
*/
window.REFRESH;
/**
* Controls the extra refresh delay per zoom level, in seconds, default 5.
* @const {number}
*/
window.ZOOM_LEVEL_ADJ;
/**
* Wait this long before refreshing the view after the map has been moved, in seconds, default 2.5
* @const {number}
*/
window.ON_MOVE_REFRESH;
/**
* “limit on refresh time since previous refresh, limiting repeated move refresh rate” (?), in seconds, default 10
* @const {number}
*/
window.MINIMUM_OVERRIDE_REFRESH;
/**
* Controls how long to wait between refreshing the global score, in seconds, default 15*60 (15 mins)
* @const {number}
*/
window.REFRESH_GAME_SCORE;
/**
* Controls how long, at most, can the map be inactive before refreshing, in secods, default 15*60 (15 mins)
* @const {number}
*/
window.MAX_IDLE_TIME;
/**
* How much space to leave for scrollbars, in pixels, default 20.
* @const {number}
*/
window.HIDDEN_SCROLLBAR_ASSUMED_WIDTH;
/**
* How wide should the sidebar be, in pixels, default 300.
* @const {number}
*/
window.SIDEBAR_WIDTH;
/**
* Controls requesting chat data if chat is expanded based on the pixel distance from the line currently in view and the top of history, in pixels, default 200
* @const {number}
*/
window.CHAT_REQUEST_SCROLL_TOP;
/**
* Controls height of chat when chat is collapsed, in pixels, default 60
* @const {number}
*/
window.CHAT_SHRINKED;
/**
* Point tolerance(?) for displaying MUs, in unknown units, default 60
* @const {number}
* @deprecated IITC no longer shows MU of fields due to intel changes.
*/
window.FIELD_MU_DISPLAY_POINT_TOLERANCE;
/**
* What colour should the selected portal be, string(css hex code), default ‘#f0f’ (hot pink)
* @const {string}
*/
window.COLOR_SELECTED_PORTAL;
/**
* Colour hex values for teams used in portals, player names, etc.
* @const {Array<string>}
*/
window.COLORS;
/**
* Colour hex values for levels, consistent with Ingress, with index 0 being white for neutral portals.
* @const {Array<string>}
*/
window.COLORS_LVL;
/**
* Colour hex values for displaying mods, consistent with Ingress. Very Rare also used for AXA shields and Ultra Links.
* @const {object}
*/
window.COLORS_MOD;
/**
* Colour hex values for displaying mods
* @type {string}
*/
window.COLORS_MOD.VERY_RARE;
/**
* Colour hex values for displaying mods
* @type {string}
*/
window.COLORS_MOD.RARE;
/**
* Colour hex values for displaying mods
* @type {string}
*/
window.COLORS_MOD.COMMON;
/**
* Mod type dict for displaying mod names.
* @const {object}
*/
window.MOD_TYPE;
/**
* Mod type dict for displaying mod name
* @const {string}
*/
window.MOD_TYPE.RES_SHIELD;
/**
* Mod type dict for displaying mod name
* @const {string}
*/
window.MOD_TYPE.MULTIHACK;
/**
* Mod type dict for displaying mod name
* @const {string}
*/
window.MOD_TYPE.FORCE_AMP;
/**
* Mod type dict for displaying mod name
* @const {string}
*/
window.MOD_TYPE.HEATSINK;
/**
* Mod type dict for displaying mod name
* @const {string}
*/
window.MOD_TYPE.TURRET;
/**
* Mod type dict for displaying mod name
* @const {string}
*/
window.MOD_TYPE.LINK_AMPLIFIER;
/**
* What colour should the hacking range circle be (the small circle that appears around a selected portal, marking a ~40 metre radius), string(css colour value), default ‘orange’
* @const {string}
*/
window.ACCESS_INDICATOR_COLOR;
/**
* What colour should the linkable range circle be, string(css colour value), default ‘red’
* @const {string}
*/
window.RANGE_INDICATOR_COLOR;
/**
* “min zoom for intel map - should match that used by stock intel”, in (leaflet zoom levels?), default 3
* @const {number}
*/
window.MIN_ZOOM;
/**
* URL to blank portal image png.
* @const {string}
*/
window.DEFAULT_PORTAL_IMG;
/**
* URL to call the Nominatim (geocoder?) service, string.
* @const {string}
*/
window.NOMINATIM;
/**
* Resonator energy per level, 1-based array, XM
* @const {Array<number>}
*/
window.RESO_NRG;
/**
* Maximum radius around a portal from which the portal is hackable, metres.
* @const {number}
*/
window.HACK_RANGE;
/**
* Resonator octant cardinal directions
* @const {Array<string>}
*/
window.OCTANTS;
/**
* Resonator octant arrows
* @const {Array<string>}
*/
window.OCTANT_ARROW;
/**
* AP value for performing in-game action
* @const {number}
*/
window.DESTROY_RESONATOR;
/**
* AP value for performing in-game action
* @const {number}
*/
window.DESTROY_LINK;
/**
* AP value for performing in-game action
* @const {number}
*/
window.DESTROY_FIELD;
/**
* AP value for performing in-game action
* @const {number}
*/
window.CAPTURE_PORTAL;
/**
* AP value for performing in-game action
* @const {number}
*/
window.DEPLOY_RESONATOR;
/**
* AP value for performing in-game action
* @const {number}
*/
window.UPGRADE_ANOTHERS_RESONATOR;
/**
* AP value for performing in-game action
* refers to the extra AP for deploying the last resonator on a portal.
* @const {number}
*/
window.COMPLETION_BONUS;
/**
* Maximum portal level.
* @const {number}
*/
window.MAX_PORTAL_LEVEL;
/**
* How many resonators of a given level can one deploy
* 1-based array where the index is the resonator level.
* @const {Array<number>}
*/
window.MAX_RESO_PER_PLAYER;
/**
* Faction. NONE is 0, RES is 1, ENL is 2.
* @const {number}
*/
window.TEAM_NONE;
/**
* Faction. NONE is 0, RES is 1, ENL is 2.
* @const {number}
*/
window.TEAM_RES;
/**
* Faction. NONE is 0, RES is 1, ENL is 2.
* @const {number}
*/
window.TEAM_ENL;
/**
* Maps team to its CSS class.
* ['none', 'res', 'enl']
* @const {Array<string>}
*/
window.TEAM_TO_CSS;
/**
* The Earth’s approximate radius at the equator in metres.
* @const {number}
*/
window.EARTH_RADIUS;
/**
* Constant for converting degrees to radians
* Math.PI / 180
* @const {number}
*/
window.DEG2RAD;
/**
* stores the id of the timeout that kicks off the next refresh (ie value returned by setTimeout())
* @type {number}
*/
window.refreshTimeout;
/**
* Portal GUID if the original URL had it.
* @type {?string}
*/
window.urlPortal;
/**
* Portal lng/lat if the orignial URL had it.
* @type {?string}
*/
window.urlPortalLL;
/**
* Stores the ID of the selected portal, or is null if there is none.
* @type {?string}
*/
window.selectedPortal;
/**
* Reference to the linking range indicator of the selected portal. This is a Leaflet layer.
* @type {?object}
*/
window.portalRangeIndicator;
/**
* Reference to the hacking range indicator of the selected portal. This is a Leaflet layer.
* @type {?object}
*/
window.portalAccessIndicator;
/**
* Bool, true if the map is currently being moved. More precisely, this is true between the movestart and moveend events of the Leaflet map.
* @type {boolean}
*/
window.mapRunsUserAction;
/**
* References to Leaflet objects for portals. These are indexed by the entity ID in an object
* @type {object<string, object>}
*/
window.portals;
/**
* References to Leaflet objects for links. These are indexed by the entity ID in an object
* @type {object<string, object>}
*/
window.links;
/**
* References to Leaflet objects for fields. These are indexed by the entity ID in an object
* @type {object<string, object>}
*/
window.fields;
/**
* From when NIA provided resonator data
* @type {object}
* @deprecated
*/
window.resonators;
/**
* An object, where the keys are layer names and their values are bools true if the layer is enabled. Should mirror the layer selector UI.
*
* Note: The variable comment states that “you should use :function:`window.isLayerGroupDisplayed(name)` to check the [layer] status”
* @type {object<string, boolean>}
*/
window.overlayStatus;
/**
* A noop function/namespace/”plugin framework”.
* @nosideeffects
*/
window.plugin = function() {};
/**
* A list of hooks that should be called after IITC has finished booting. Mostly used to initialise plugins. Note: These will not run if some blacklisted plugins are detected.
* @type {?Array<function>}
*/
window.bootPlugins;
/**
* Object to store all open dialogs. { Element.id: Element}
* @type {object}
*/
window.DIALOGS;
/**
* Count of open dialogs
* @type {number}
*/
window.DIALOG_COUNT;
/**
* The HTML Element of the dialog in focus
* @type {?HTMLElement}
*/
window.DIALOG_FOCUS;
/**
*
* @type {?number}
*/
window.DIALOG_ID;
/**
* The time in ms it takes for the animation of minimising dialogs
* @type {number}
*/
window.DIALOG_SLIDE_DURATION;
/**
* @type {boolean}
*/
window.IS_DEVICE_MOBILE;
/**
* @type {boolean}
*/
window.IS_DEVICE_TABLET;
/**
* @type {boolean}
*/
window.IS_VERSION_MOBILE;
/**
* Object storing the map info
* @type {object}
*/
window.MAP_PARAMS;
/**
* Store map center lat
* @type {number}
*/
window.MAP_PARAMS.lat;
/**
* Store map center lng
* @type {number}
*/
window.MAP_PARAMS.lng;
/**
* Store map zoom
* @type {number}
*/
window.MAP_PARAMS.zoom;
/**
* Controls what is displayed on map tiles
* @type {object}
*/
window.TILE_PARAMS;
/**
* @type {Array<number>}
*/
window.TILE_PARAMS.ZOOM_TO_LINK_LENGTH;
/**
* @type {Array<number>}
*/
window.TILE_PARAMS.ZOOM_TO_LEVEL;
/**
* @type {Array<number>}
*/
window.TILE_PARAMS.TILES_PER_EDGE;
/**
* Array of names of IITC hooks
* @type {Array<string>}
*/
window.VALID_HOOKS;
/**
* Calls each callback for a given hook
* @param {string} event the name of the hook
* @param {*} data any info to supply to each callback
* @return {boolean} True if all callbacks were called without error
*/
window.runHooks = function(event, data) {};
/**
* helper method to allow plugins to create new hooks
* @param {string} event the name of the hook to create
*/
window.pluginCreateHook = function(event) {};
/**
* Provide a callback to be called when a hook is run
* @param {string} event the name of the hook
* @param {funcion(?*)} callback will be called when the hook runs
*/
window.addHook = function(event, callback) {};
/**
* Remove a function from the hook
* callback must the SAME function to be unregistered.
* @param {string} event the name of the hook
* @param {function(?*)} callback the callback that was originaly provided
*/
window.removeHook = function(event, callback) {};
/**
* Launches the abouth IITC Dialog
* @nosideeffects
*/
window.aboutIITC = function() {};
/**
* @type {Array<any>}
*/
window.activeRequests;
/**
* @type {number}
*/
window.failedRequestCount;
/**
* @type {number}
*/
window.statusTotalMapTiles;
/**
* @type {number}
*/
window.statusCachedMapTiles;
/**
* @type {number}
*/
window.statusSuccessMapTiles;
/**
* @type {number}
*/
window.statusStaleMapTiles;
/**
* @type {number}
*/
window.statusErrorMapTiles;
/**
* @nosideeffects
*/
window.requests = function() {};
/**
* Returns the number of Layers in a LayerGroup
* @param {*} layerGroup Leaflet layerGroup object
* @returns {number}
*/
window.layerGroupLength = function(layerGroup) {};
/**
* retrieves parameter from the URL?query=string.
* @param {string} param the key you are looking for
* @returns \{{{type}}\} {{description}}{{}}
* @returns {string}
*/
window.getURLParam = function(param) {};
// read cookie by name.
// http://stackoverflow.com/a/5639455/1684530 by cwolves
/**
* read cookie by name.
* @see http://stackoverflow.com/a/5639455/1684530
* @param {string} name the cookie you want
* @returns {string}
*/
window.readCookie = function(name) {};
/**
* Add a cookie
* @param {string} name name of cookie
* @param {string} val value of cookie
*/
window.writeCookie = function(name, val) {};
/**
* Expire a cookie
* @param {string} name The name of the cookie to expire
*/
window.eraseCookie = function(name) {};
/**
* certain values were stored in cookies, but we're better off using localStorage instead - make it easy to convert
* @param {string} name name of cookie to convert
*/
window.convertCookieToLocalStorage = function(name) {};
/**
* add thousand separators to given number.
* U+2009 - Thin Space. Recommended for use as a thousands separator...
* @see http://stackoverflow.com/a/1990590/1684530 by Doug Neiner.
* @see https://en.wikipedia.org/wiki/Space_(punctuation)#Table_of_spaces
* @param {(number|string)} d the number to separate
* @returns {string}
*/
window.digits = function(d) {};
/**
* The age old left pad problem
* @param {number} number the number to left pad
* @param {number} pad number of chars to be
* @returns {string}
*/
window.zeroPad = function(number, pad) {};
// converts javascript timestamps to HH:mm:ss format if it was today;
//
/**
* converts javascript timestamps to HH:mm:ss format if it was today,
* otherwise it returns YYYY-MM-DD
* @param {(string|number)} time unix epoch milliseconds
* @param {boolean} full use tru to return both date and time
* @returns {string}
*/
window.unixTimeToString = function(time, full) {};
// converts a javascript time to a precise date and time (optionally with millisecond precision)
// formatted in ISO-style YYYY-MM-DD hh:mm:ss.mmm - but using local timezone
/**
* converts a javascript time to a precise date and time (optionally with millisecond precision) formatted in ISO-style YYYY-MM-DD hh:mm:ss.mmm - but using local timezone
* @param {(number|string)} time unix epoch milliseconds
* @param {boolean} millisecond flag to include milliseconds in return value
* @returns {string}
*/
window.unixTimeToDateTimeString = function(time, millisecond) {};
/**
* Converts Unix milliseconds to HH:mm
* @param {(number|string)} time unix epoch milliseconds
* @returns {string}
*/
window.unixTimeToHHmm = function(time) {};
/**
* Converts seconds into a human readable amount of d,h,m,s
* @param {number} seconds number of seconds
* @param {number} maxTerms level of accuracy, d/h/m/s
* @returns {string}
*/
window.formatInterval = function(seconds, maxTerms) {};
/**
* Moves the map to show the range the portal can link to
*/
window.rangeLinkClick = function() {};
/**
* Show portal links for sharing
* @param {number} lat Latitude of portal
* @param {number} lng Longitude of portal
* @param {?string} name Name of portal
*/
window.showPortalPosLinks = function(lat, lng, name) {};
/**
* @returns {boolean} true if touch screen
*/
window.isTouchDevice = function() {};
/**
* If android, copy text
* @param {string} text text to copy
* @returns {boolean} true if not android copied
*/
window.androidCopy = function(text) {};
/**
* Attempts to create a link to share for android
* @returns {boolean} true if not android
*/
window.androidPermalink = function() {};
/**
* Get Minimum portal level for current zoom
* @returns {number} Min portal level
*/
window.getMinPortalLevel = function() {};
// returns number of pixels left to scroll down before reaching the
// bottom. Works similar to the native scrollTop function.
window.scrollBottom = function(elm) {
if (typeof elm === 'string') elm = $(elm);
return elm.get(0).scrollHeight - elm.innerHeight() - elm.scrollTop();
};
window.zoomToAndShowPortal = function(guid, latlng) {
map.setView(latlng, 17);
// if the data is available, render it immediately. Otherwise defer
// until it becomes available.
if (window.portals[guid]) renderPortalDetails(guid);
else urlPortal = guid;
};
window.selectPortalByLatLng = function(lat, lng) {
if (lng === undefined && lat instanceof Array) {
lng = lat[1];
lat = lat[0];
} else if (lng === undefined && lat instanceof L.LatLng) {
lng = lat.lng;
lat = lat.lat;
}
for (var guid in window.portals) {
var latlng = window.portals[guid].getLatLng();
if (latlng.lat == lat && latlng.lng == lng) {
renderPortalDetails(guid);
return;
}
}
// not currently visible
urlPortalLL = [lat, lng];
map.setView(urlPortalLL, 17);
};
String.prototype.capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
};
// http://stackoverflow.com/a/646643/1684530 by Bergi and CMS
if (typeof String.prototype.startsWith !== 'function') {
String.prototype.startsWith = function(str) {
return this.slice(0, str.length) === str;
};
}
// escape a javascript string, so quotes and backslashes are escaped with a backslash
// (for strings passed as parameters to html onclick="..." for example)
window.escapeJavascriptString = function(str) {
return (str + '').replace(/[\\"']/g, '\\$&');
};
//escape special characters, such as tags
window.escapeHtmlSpecialChars = function(str) {
var div = document.createElement(div);
var text = document.createTextNode(str);
div.appendChild(text);
return div.innerHTML;
};
window.prettyEnergy = function(nrg) {
return nrg > 1000 ? Math.round(nrg / 1000) + ' k' : nrg;
};
window.setPermaLink = function(elm) {
var c = map.getCenter();
var lat = Math.round(c.lat * 1e6) / 1e6;
var lng = Math.round(c.lng * 1e6) / 1e6;
var qry = 'll=' + lat + ',' + lng + '&z=' + map.getZoom();
$(elm).attr('href', '/intel?' + qry);
};
window.uniqueArray = function(arr) {
return $.grep(arr, function(v, i) {
return $.inArray(v, arr) === i;
});
};
window.genFourColumnTable = function(blocks) {
var t = $.map(blocks, function(detail, index) {
if (!detail) return '';
var title = detail[2]
? ' title="' + escapeHtmlSpecialChars(detail[2]) + '"'
: '';
if (index % 2 === 0)
return (
'<tr><td' +
title +
'>' +
detail[1] +
'</td><th' +
title +
'>' +
detail[0] +
'</th>'
);
else
return (
' <th' +
title +
'>' +
detail[0] +
'</th><td' +
title +
'>' +
detail[1] +
'</td></tr>'
);
}).join('');
if (t.length % 2 === 1) t + '<td></td><td></td></tr>';
return t;
};
// converts given text with newlines (\n) and tabs (\t) to a HTML
// table automatically.
window.convertTextToTableMagic = function(text) {
// check if it should be converted to a table
if (!text.match(/\t/)) return text.replace(/\n/g, '<br>');
var data = [];
var columnCount = 0;
// parse data
var rows = text.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 table = '<table>';
$.each(data, function(i, row) {
table += '<tr>';
$.each(data[i], function(k, cell) {
var attributes = '';
if (k === 0 && data[i].length < columnCount) {
attributes = ' colspan="' + (columnCount - data[i].length + 1) + '"';
}
table += '<td' + attributes + '>' + cell + '</td>';
});
table += '</tr>';
});
table += '</table>';
return table;
};
// Given 3 sets of points in an array[3]{lat, lng} returns the area of the triangle
window.calcTriArea = function(p) {
return Math.abs(
(p[0].lat * (p[1].lng - p[2].lng) +
p[1].lat * (p[2].lng - p[0].lng) +
p[2].lat * (p[0].lng - p[1].lng)) /
2
);
};
// Update layerGroups display status to window.overlayStatus and localStorage 'ingress.intelmap.layergroupdisplayed'
window.updateDisplayedLayerGroup = function(name, display) {
overlayStatus[name] = display;
localStorage['ingress.intelmap.layergroupdisplayed'] = JSON.stringify(
overlayStatus
);
};
// Read layerGroup status from window.overlayStatus if it was added to map,
// read from cookie if it has not added to map yet.
// return 'defaultDisplay' if both overlayStatus and cookie didn't have the record
window.isLayerGroupDisplayed = function(name, defaultDisplay) {
if (typeof overlayStatus[name] !== 'undefined') return overlayStatus[name];
convertCookieToLocalStorage('ingress.intelmap.layergroupdisplayed');
var layersJSON = localStorage['ingress.intelmap.layergroupdisplayed'];
if (!layersJSON) return defaultDisplay;
var layers = JSON.parse(layersJSON);
// keep latest overlayStatus
overlayStatus = $.extend(layers, overlayStatus);
if (typeof overlayStatus[name] === 'undefined') return defaultDisplay;
return overlayStatus[name];
};
window.addLayerGroup = function(name, layerGroup, defaultDisplay) {
if (defaultDisplay === undefined) defaultDisplay = true;
if (isLayerGroupDisplayed(name, defaultDisplay)) map.addLayer(layerGroup);
layerChooser.addOverlay(layerGroup, name);
};
window.removeLayerGroup = function(layerGroup) {
if (!layerChooser._layers[layerGroup._leaflet_id])
throw 'Layer was not found';
// removing the layer will set it's default visibility to false (store if layer gets added again)
var name = layerChooser._layers[layerGroup._leaflet_id].name;
var enabled = isLayerGroupDisplayed(name);
map.removeLayer(layerGroup);
layerChooser.removeLayer(layerGroup);
updateDisplayedLayerGroup(name, enabled);
};
window.clampLat = function(lat) {
// the map projection used does not handle above approx +- 85 degrees north/south of the equator
if (lat > 85.051128) lat = 85.051128;
else if (lat < -85.051128) lat = -85.051128;
return lat;
};
window.clampLng = function(lng) {
if (lng > 179.999999) lng = 179.999999;
else if (lng < -180.0) lng = -180.0;
return lng;
};
window.clampLatLng = function(latlng) {
return new L.LatLng(clampLat(latlng.lat), clampLng(latlng.lng));
};
window.clampLatLngBounds = function(bounds) {
return new L.LatLngBounds(
clampLatLng(bounds.getSouthWest()),
clampLatLng(bounds.getNorthEast())
);
};
window.getGenericMarkerSvg = function(color) {
var markerTemplate = '@@INCLUDESTRING:images/marker-icon.svg.template@@';
return markerTemplate.replace(/%COLOR%/g, color);
};
window.getGenericMarkerIcon = function(color, className) {
return L.divIcon({
iconSize: new L.Point(25, 41),
iconAnchor: new L.Point(12, 41),
html: getGenericMarkerSvg(color),
className: className || 'leaflet-iitc-divicon-generic-marker'
});
};
window.createGenericMarker = function(ll, color, options) {
options = options || {};
var markerOpt = $.extend(
{
icon: getGenericMarkerIcon(color || '#a24ac3')
},
options
);
return L.marker(ll, markerOpt);
};
// Fix Leaflet: handle touchcancel events in Draggable
L.Draggable.prototype._onDownOrig = L.Draggable.prototype._onDown;
L.Draggable.prototype._onDown = function(e) {
L.Draggable.prototype._onDownOrig.apply(this, arguments);
if (e.type === 'touchstart') {
L.DomEvent.on(document, 'touchcancel', this._onUp, this);
}
};