iitcp
Version:
IITC Plugin creator and developer tools
1,758 lines (1,448 loc) • 219 kB
JavaScript
/**
* @fileOverview Externs for IITC
* with thanks to Filip Wieland
* @see http://ingress-intel-total-conversion.readthedocs.io
* @externs
*/
/**
* @const {string}
*/
window.iitcBuildDate;
/**
* Stores info about the IITC plugin
* @const {object}
*/
window.script_info;
/**
* @const {string}
*/
window.script_info.buildName;
/**
* @const {string}
*/
window.script_info.dateTimeVersion;
/**
* Stores info about the IITC plugin from the header
* @const {object}
*/
window.script_info.script;
/**
* @const {string}
*/
window.script_info.script.version;
/**
* @const {string}
*/
window.script_info.script.name;
/**
* @const {string}
*/
window.script_info.script.description;
/**
* 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;
/**
* Minimum area to zoom ratio that field MU's will display, default 0.001
* @const {number}
* @deprecated IITC no longer shows MU of fields due to intel changes.
*/
window.FIELD_MU_DISPLAY_AREA_ZOOM_RATIO;
/**
* 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;
/**
* Array for calculating direction of resonator
* @const {Array<number>}
* @deprecated resonator position is no longer provided by NIA
*/
window.SLOT_TO_LAT;
/**
* Array for calculating direction of resonator
* @const {Array<number>}
* @deprecated resonator position is no longer provided by NIA
*/
window.SLOT_TO_LNG;
/**
* 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 noop function/namespace.
* @nosideeffects
*/
window.artifact = function() {};
/**
* Initiate artifacts
*/
window.artifact.setup = function() {};
/**
* 2 minute random period so not all users refresh at once
* @type {number}
*/
window.artifact.REFRESH_JITTER;
/**
* 60 minutes on success
* @type {number}
*/
window.artifact.REFRESH_SUCCESS;
/**
* 2 minute retry on failure
* @type {number}
*/
window.artifact.REFRESH_FAILURE;
/**
* Flag to show if artifacts refresh is in idle state
* @type {boolean}
*/
window.artifact.idle;
/**
* Leaflet layer group for displaying artifacts
* @type {object}
*/
window.artifact._layer;
/**
* Call to NIA for artict partals
*/
window.artifact.requestData = function() {};
/**
* Resume refresh after timeout
*/
window.artifact.idleResume = function() {};
/**
* Success callback after fetching artifact data
* sets timeout to refresh the data later
* @param {object} data
*/
window.artifact.handleSuccess = function(data) {};
/**
* Error callback after fetching artifact data
* no useful data on failure - do nothing
* sets timeout to refresh the data later
* @param {object} data
*/
window.artifact.handleFailure = function(data) {};
/**
* Checks status of result from fetching artifact data
* Runs hook when result processed
* @param {object} data
*/
window.artifact.processData = function(data) {};
/**
* Resets cache of artifact data
*/
window.artifact.clearData = function() {};
/**
* Stores cache of portals with artifacts
* @type {object}
*/
window.artifact.portalInfo;
/**
* Stores cache of artifact types
* @type {object}
*/
window.artifact.artifactTypes;
/**
* Stores cache of artifacts
* @type {Array<Array>}
*/
window.artifact.entities;
/**
* Caches the fresh artifact data
* @param {object} portals an object, keyed from the portal GUID, containing the portal entity array
*/
window.artifact.processResult = function (portals) {};
/**
* @returns {Array<string>} Array of artifact types
*/
window.artifact.getArtifactTypes = function() {};
/**
* @param {string} type artifact name to check
* @returns {boolean} true if type exists
*/
window.artifact.isArtifact = function(type) {};
/**
* used to render portals that would otherwise be below the visible level
* @returns {Array<Array>} window.artifact.entities
*/
window.artifact.getArtifactEntities = function() {};
/**
* Get an array of portal guids with artifacts
* @returns {Array<string>} Artifact portal guids
*/
window.artifact.getInterestingPortals = function() {};
/**
* quick test for portal being relevant to artifacts - of any type
* @param {string} guid guid of portalto test
* @returns {boolean} true if portal has an artifact
*/
window.artifact.isInterestingPortal = function(guid) {
return guid in artifact.portalInfo;
}
/**
* get the artifact data for a specified artifact id (e.g. 'jarvis'), if it exists - otherwise returns something 'false'y
* @param {string} guid guid of portal to check
* @param {string} artifactId the ID of a specific artifact
* @returns {(boolean|undefined|null)} true if it exists - otherwise returns something 'false'y
*/
window.artifact.getPortalData = function(guid,artifactId) {};
/**
* Updates the leaflet artifact layer
*/
window.artifact.updateLayer = function() {};
/**
* Opens a dialog containing the known artifacts
*/
window.artifact.showArtifactList = function() {};
/**
* sets up a click event listener on the portal image in the side bar that opens a dialog showing a larger image.
* Run once.
*/
window.setupLargeImagePreview = function() {};
/**
* adds listeners to the layer chooser such that a long press hides all custom layers except the long pressed one.
* Run once.
*/
window.setupLayerChooserSelectOne = function() {};
/**
* Setup the function to record the on/off status of overlay layerGroups.
* Run once.
*/
window.setupLayerChooserStatusRecorder = function() {};
/**
* Enable/disable layers that aren't visible due to zoom level
*/
window.layerChooserSetDisabledStates = function() {};
/**
* Adds a style tag to te document.head
* Run once.
*/
window.setupStyles = function() {};
/**
* Initiates the Leaflet Map.
* Run Once
*/
window.setupMap = function() {};
/**
* Adds a base layer to the map. done separately from the above, so that plugins that add base layers can be the default.
*/
window.setMapBaseLayer = function() {};
/**
* Stores info about the signed in agent
* @const {object}
*/
window.PLAYER;
/**
* Agent's current AP
* @const {string}
*/
window.PLAYER.ap;
/**
* Agent's current XM
* @const {number}
*/
window.PLAYER.energy;
/**
* Agent's level
* @const {number}
*/
window.PLAYER.level;
/**
* Minimum AP for the agent's current level
* @const {string}
*/
window.PLAYER.min_ap_for_current_level;
/**
* Minimum AP for the agent's next level
* @const {string}
*/
window.PLAYER.min_ap_for_next_level;
/**
* A regex for testing if the agent's nickname appears in a text string
* @const {RegExp}
*/
window.PLAYER.nickMatcher;
/**
* The agent's in game name (IGN) or nickname
* @const {string}
*/
window.PLAYER.nickname;
/**
* The agent's faction ('ENLIGHTENED' or 'RESISTANCE')
* @const {string}
*/
window.PLAYER.team;
/**
* The same as the agent's current level
* @const {number}
*/
window.PLAYER.verified_level;
/**
* The maximum amount of XM the agent can have
* @const {string}
*/
window.PLAYER.xm_capacity;
/**
* renders player details into the website. Since the player info is included as inline script in the original site, the data is static and cannot be updated.
* Run once.
*/
window.setupPlayerStat = function() {};
/**
* Add click event listener to side bar toggle.
* Run once.
*/
window.setupSidebarToggle = function() {};
/**
* Set up tool tips on an element (deault element is document).
* @param {?HTMLElement} element default is document
*/
window.setupTooltips = function(element) {};
/**
* Implements a tap and hold functionality. If you click/tap and release, it will trigger a normal click event. But if you click/tap and hold for 1s (default), it will trigger a taphold event instead.
*/
window.setupTaphold = function() {};
/**
* Initiates QR Library
*/
window.setupQRLoadLib = function() {};
/**
* Leaflet control for selecting the visibility of layers.
* It also has API methods for layers
* @type {object} Leaflet control
*/
window.layerChooser;
/**
* hook some additional code into the LayerControl so it's easy for the mobile app to interface with it.
* WARNING: does depend on internals of the L.Control.Layers code.
* @returns {object} The layer
*/
window.layerChooser.getLayers = function() {};
/**
* Show or hide a layer
* @param {string} id The ID of the layer to toggle
* @param {?boolean} show true to show (default), false to hide
* @returns {boolean} true if the layer exists
*/
window.layerChooser.showLayer = function(id,show) {};
/**
* Initiates the layerchooser API methods
*/
window.setupLayerChooserApi = function() {};
/**
* An array of functions 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>|undefined)}
*/
window.bootPlugins;
/**
* A noop function/namespace for the chat.
* @nosideeffects
*/
window.chat = function() {};
/**
* @type {Array<object>}
*/
window.chat.commTabs = [];
window.chat.handleTabCompletion = function() {};
/**
* Generates an object to post in order to receive chat messages
* @param {string} channel 'all' / 'faction' / 'alerts'
* @param {object} storageHash object storing last timestamps
* @param {?boolean} getOlderMsgs flag for fetching older messages
* @returns {object}
*/
window.chat.genPostData = function(channel, storageHash, getOlderMsgs) {};
/**
* Fetch faction chat messages. It will retry once if there is an error
* @param {boolean} getOlderMsgs flag for fetching older messages
* @param {?boolean} isRetry flag to tell if it's a retry
*/
window.chat.requestFaction = function(getOlderMsgs, isRetry) {};
/**
* Handles faction chat response
* @param {object} data the response from NIA
* @param {?boolean} olderMsgs flag if fetching older messages
*/
window.chat.handleFaction = function(data, olderMsgs) {};
/**
* calls window.chat.renderData
* @param {?boolean} oldMsgsWereAdded
*/
window.chat.renderFaction = function(oldMsgsWereAdded) {};
/**
* Fetch public chat messages. It will retry once if there is an error
* @param {boolean} getOlderMsgs flag for fetching older messages
* @param {?boolean} isRetry flag to tell if it's a retry
*/
window.chat.requestPublic = function(getOlderMsgs, isRetry) {};
/**
* Handles public chat response
* @param {object} data the response from NIA
* @param {?boolean} olderMsgs flag if fetching older messages
*/
window.chat.handlePublic = function(data, olderMsgs) {};
/**
* calls window.chat.renderData
* @param {?boolean} oldMsgsWereAdded
*/
window.chat.renderPublic = function(oldMsgsWereAdded) {};
/**
* Fetch alert messages. It will retry once if there is an error
* @param {boolean} getOlderMsgs flag for fetching older messages
* @param {?boolean} isRetry flag to tell if it's a retry
*/
window.chat.requestAlerts = function(getOlderMsgs, isRetry) {};
/**
* Handles alerts response
* @param {object} data the response from NIA
* @param {?boolean} olderMsgs flag if fetching older messages
*/
window.chat.handleAlerts = function(data, olderMsgs) {};
/**
* calls window.chat.renderData
* @param {?boolean} oldMsgsWereAdded
*/
window.chat.renderAlerts = function(oldMsgsWereAdded) {};
/**
* Event handler for when agent names are clicked
* Runs hook 'nicknameClicked' and adds name in chat input
* @param {Event} event
* @param {string} nickname Name of clicked agent
* @returns {boolean} false
*/
window.chat.nicknameClicked = function(event, nickname) {};
/**
* stores provided chat data in a hash
* @param {object} newData the response object from NIA
* @param {object} storageHash the hash for this channel
* @param {boolean} isPublicChannel flag for public channel
* @param {?boolean} isOlderMsgs flag for older messages
*/
window.chat.writeDataToHash = function(newData, storageHash, isPublicChannel, isOlderMsgs) {};
/**
* Override portal names that are used over and over, such as 'US Post Office'
* @param {object} Plext markup
* @returns {string} possibly ammended portal name
*/
window.chat.getChatPortalName = function(markup) {}
/**
* 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.
* @param {object} data from hash
* @param {string} element the element ID
* @param {boolean} likelyWereOldMsgs Set to true if it is likely that old data has been added. Only required for scrolling.
*/
window.chat.renderData = function(data, element, likelyWereOldMsgs) {};
/**
* Returns a string for innerHTML to represent a divider in the comm
* @param {string} text Text to insert in the divider (usually a date string)
* @returns {string}
*/
window.chat.renderDivider = function(text) {};
/**
* converts a message into an html string between TR tags
* @param {string} msg The message to render
* @param {string} nick The agent name of the originator of the message
* @param {number} time timestamp of message in ms
* @param {string} team faction of the originator
* @param {?boolean} msgToPlayer If the message mentions the user
* @param {?boolean} systemNarrowcast If it is not player generated
*/
window.chat.renderMsg = function(msg, nick, time, team, msgToPlayer, systemNarrowcast) {};
/**
* Adds a nickname to the chat input
* @param {string} nick nickname to add
*/
window.chat.addNickname= function(nick) {};
/**
* Find the active chat channel
* @returns {string} the name of the chat channel
*/
window.chat.getActive = function() {};
/**
* convert tab text to a legitimate channel
* @param {string} tab the tab name
* @returns {string} 'all' / 'faction' / 'alerts'
*/
window.chat.tabToChannel = function(tab) {};
/**
* Expands or contracts the chat pane
*/
window.chat.toggle = function() {};
/**
* called by plugins (or other things?) that need to monitor COMM data streams when the user is not viewing them
* @param {string} instance a unique string identifying the plugin requesting background COMM
* @param {string} channel either 'all', 'faction' or (soon) 'alerts' - others possible in the future
* @param {boolean} flag true for data wanted, false for not wanted
*/
window.chat.backgroundChannelData = function(instance,channel,flag) {};
/**
* Starts a call to request the latest chat data for the open tab
*/
window.chat.request = function() {};
/**
* checks if there are enough messages in the selected chat tab and loads more if not.
*/
window.chat.needMoreMessages = function() {};
/**
* Select a tab
* @param {string} tab the tab to select, only "all", "faction" and "alerts" are valid
*/
window.chat.chooseTab = function(tab) {};
/**
* Show a tab
* @param {string} name the tab to select, only "all", "faction" and "alerts" are valid
*/
window.chat.show = function(name) {};
/**
* an event handler for clicking on the tab selector
* @param {DOMEvent} event
*/
window.chat.chooser = function(event) {};
/**
* Contains the logic to keep the correct scroll position.
* If scrolled down completely, keep it that way so new messages can be seen easily. If scrolled up, only need to fix scroll position when old messages are added. New messages added at the bottom don’t change the view and enabling this would make the chat scroll down for every added message, even if the user wants to read old stuff.
* @param {*} box jquery element
* @param {number} scrollBefore in pixels
* @param {?boolean} isOldMsgs flag for old messages
*/
window.chat.keepScrollPosition = function(box, scrollBefore, isOldMsgs) {};
/**
* Initiate the chat and related event handlers
*/
window.chat.setup = function() {};
/**
* Set the time on the left of the chat input and setTimeout to update it
*/
window.chat.setupTime = function() {};
/**
* Sets up event handlers for chat input
*/
window.chat.setupPosting = function() {};
/**
* Gets text from chat input and sends it to all or faction depending on the tab
*/
window.chat.postMsg = function() {};
// MAP DATA CACHE ///////////////////////////////////
// cache for map data tiles.
/**
* cache for map data tiles.
* @constructor
*/
window.DataCache = function() {};
/**
* Store an item in the cache
* @param {string} qk key to store in the cache for this data
* @param {(object|Array)} data a value that will be JSON.stringified
* @param {?number} freshTime number of milliseconds this will be fresh for, default 180000 (3 mins)
*/
window.DataCache.prototype.store = function(qk,data,freshTime) {};
/**
* Remove an item in the cache
* @param {string} qk key for the data to remove
*/
window.DataCache.prototype.remove = function(qk) {};
/**
* Get the data stored in a cache
* @param {string} qk key of data to get
* @returns {(object|Array|undefined)} the stored data or undefined
*/
window.DataCache.prototype.get = function(qk) {};
/**
* Get the time data was stored in a cache
* @param {string} qk key of time to get
* @returns {number} the stored time in ms or 0
*/
window.DataCache.prototype.getTime = function(qk) {}
/**
* check if the data has expired
* @param {string} qk key to check
* @returns {(boolean|undefined)} true if fresh, false if not or undefined if not there
*/
window.DataCache.prototype.isFresh = function(qk) {};
/**
* Sets a setInterval to clear expired data
* @param {number} period The number of seconds for the interval
*/
window.DataCache.prototype.startExpireInterval = function(period) {};
/**
* clears the interval set to remove expired data
*/
window.DataCache.prototype.stopExpireInterval = function() {};
/**
* Iterate through the cache expiring old data
*/
window.DataCache.prototype.runExpire = function() {};
/**
* Get the size of the cache for debugging purposes
* @returns {string} Number of items, bytes & Kb
*/
window.DataCache.prototype.debug = function() {};
/**
* 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;
/**
* The global ID of onscreen dialogs.
* Starts at 0.
* @type {?number}
*/
window.DIALOG_ID;
/**
* Controls how quickly the slide toggle animation
* should play for dialog collapsing and expanding.
* default 100ms
* @type {number}
*/
window.DIALOG_SLIDE_DURATION;
/**
*
* @typedef {Array<{text:string,click:function(){}}>}
* {text:string,click:function(){}}>
*
*/
var DialogButtonsArrayOptions;
/**
*
* @typedef {Object<string, function(event:DOMEvent):void>}
*/
var DialogButtonsObjectOptions;
/**
* Dialog buttons options
* @typedef {({*:string,function(){}}|undefined)}
*/
var DialogButtonsOptions
/**
* Options param for dialog function
* @typedef {{text:(string|undefined), html:(string|undefined), title:(string|undefined), modal:(boolean|undefined), id:(string|undefined), appendTo:(string|undefined), autoOpen:(boolean|undefined), classes:(Object<string, string>|undefined), buttons:(Object<string, function(){}>|Array<{{text:string, click:function(){}}}>|undefined), closeOnEscape:(boolean|undefined), draggable:(boolean|undefined), height:(number|string|undefined), width:(number|string|undefined), maxHeight:(number|undefined), maxWidth:(number|undefined), minHeight:(number|undefined), minWidth:(number|undefined), position:{{ my:string, at:string, of:(Element|window) }},resizable:(boolean|undefined)}}
*/
var DialogOptions;
/**
* Creates a dialog and puts it onscreen. Takes one argument: options, a JS object.
*
* == Common options
*
* (text|html): The text or HTML to display in the dialog. Text is auto-converted to HTML.
*
* title: The dialog's title.
*
* modal: Whether to open a modal dialog. Implies draggable=false; dialogClass='ui-dialog-modal'.
* Please note that modal dialogs hijack the entire screen and should only be used in very
* specific cases. (If IITC is running on mobile, modal will always be true).
* id: A unique ID for this dialog. If a dialog with id `id' is already open and dialog() is called
* again, it will be automatically closed.
*
* == Callbacks
*
* closeCallback: A callback to run on close. Takes no arguments.
* collapseCallback: A callback to run on dialog collapse. Takes no arguments.
* expandCallback: A callback to run on dialog expansion. Takes no arguments.
* collapseExpandCallback: A callback to run on both collapse and expand (overrides collapseCallback
* and expandCallback, takes a boolean argument `collapsing' - true if collapsing;
* false if expanding)
* focusCallback: A callback to run when the dialog gains focus.
* blurCallback: A callback to run when the dialog loses focus.
*
* See http://docs.jquery.com/UI/API/1.8/Dialog for a list of all the options. If you previously
* applied a class to your dialog after creating it with alert(), dialogClass may be particularly
* useful.
* @param {{text:(string|undefined), html:(string|undefined), title:(string|undefined), modal:(boolean|undefined), id:(string|undefined), appendTo:(string|undefined), text:(string|undefined), autoOpen:(boolean|undefined), classes:(Object<string, string>|undefined), buttons:(Object<string, function>|Array<Object>|undefined)}} options dialog options
* @returns {object} the created dialog object
*/
window.dialog = function(options) {};
/**
* Creates an alert dialog with default settings.
* If you want more configurability, use window.dialog instead.
* @param {string} text text for the body of the dialog
* @param {?boolean} isHTML flag to indicate if the text is HTML
* @param {function} closeCallback callback to run when dialog closes
* @returns {object} the created dialog object
*/
window.alert = function(text, isHTML, closeCallback) {};
/**
* Initiates dialogs
*/
window.setupDialogs = function() {};
// decode the on-network array entity format into an object format closer to that used before
// makes much more sense as an object, means that existing code didn't need to change, and it's what the
// stock intel site does internally too (the array format is only on the network)
// anonymous wrapper function
(function(){
window.decodeArray = function(){};
function parseMod(arr) {
if(arr == null) { return null; }
return {
owner: arr[0],
name: arr[1],
rarity: arr[2],
stats: arr[3],
};
}
function parseResonator(arr) {
if(arr == null) { return null; }
return {
owner: arr[0],
level: arr[1],
energy: arr[2],
};
}
function parseArtifactBrief(arr) {
if (arr === null) return null;
// array index 0 is for fragments at the portal. index 1 is for target portals
// each of those is two dimensional - not sure why. part of this is to allow for multiple types of artifacts,
// with their own targets, active at once - but one level for the array is enough for that
// making a guess - first level is for different artifact types, second index would allow for
// extra data for that artifact type
function decodeArtifactArray(arr) {
var result = {};
for (var i=0; i<arr.length; i++) {
// we'll use the type as the key - and store any additional array values as the value
// that will be an empty array for now, so only object keys are useful data
result[arr[i][0]] = arr[i].slice(1);
}
return result;
}
return {
fragment: decodeArtifactArray(arr[0]),
target: decodeArtifactArray(arr[1]),
};
}
function parseArtifactDetail(arr) {
if (arr == null) { return null; }
// empty artifact data is pointless - ignore it
if (arr.length == 3 && arr[0] == "" && arr[1] == "" && arr[2].length == 0) { return null; }
return {
type: arr[0],
displayName: arr[1],
fragments: arr[2],
};
}
//there's also a 'placeholder' portal - generated from the data in links/fields. only has team/lat/lng
var CORE_PORTA_DATA_LENGTH = 4;
function corePortalData(a) {
return {
// a[0] == type (always 'p')
team: a[1],
latE6: a[2],
lngE6: a[3]
}
};
var SUMMARY_PORTAL_DATA_LENGTH = 14;
function summaryPortalData(a) {
return {
level: a[4],
health: a[5],
resCount: a[6],
image: a[7],
title: a[8],
ornaments: a[9],
mission: a[10],
mission50plus: a[11],
artifactBrief: parseArtifactBrief(a[12]),
timestamp: a[13]
};
};
var DETAILED_PORTAL_DATA_LENGTH = SUMMARY_PORTAL_DATA_LENGTH+4;
window.decodeArray.portalSummary = function(a) {
if (!a) return undefined;
if (a[0] != 'p') throw 'Error: decodeArray.portalSUmmary - not a portal';
if (a.length == CORE_PORTA_DATA_LENGTH) {
return corePortalData(a);
}
// NOTE: allow for either summary or detailed portal data to be passed in here, as details are sometimes
// passed into code only expecting summaries
if (a.length != SUMMARY_PORTAL_DATA_LENGTH && a.length != DETAILED_PORTAL_DATA_LENGTH) {
console.warn('Portal summary length changed - portal details likely broken!');
debugger;
}
return $.extend(corePortalData(a), summaryPortalData(a));
}
window.decodeArray.portalDetail = function(a) {
if (!a) return undefined;
if (a[0] != 'p') throw 'Error: decodeArray.portalDetail - not a portal';
if (a.length != DETAILED_PORTAL_DATA_LENGTH) {
console.warn('Portal detail length changed - portal details may be wrong');
debugger;
}
//TODO look at the array values, make a better guess as to which index the mods start at, rather than using the hard-coded SUMMARY_PORTAL_DATA_LENGTH constant
// the portal details array is just an extension of the portal summary array
// to allow for niantic adding new items into the array before the extended details start,
// use the length of the summary array
return $.extend(corePortalData(a), summaryPortalData(a),{
mods: a[SUMMARY_PORTAL_DATA_LENGTH+0].map(parseMod),
resonators:a[SUMMARY_PORTAL_DATA_LENGTH+1].map(parseResonator),
owner: a[SUMMARY_PORTAL_DATA_LENGTH+2],
artifactDetail: parseArtifactDetail(a[SUMMARY_PORTAL_DATA_LENGTH+3]),
});
}
})();
;
// ENTITY DETAILS TOOLS //////////////////////////////////////////////
// hand any of these functions the details-hash of an entity (i.e.
// portal, link, field) and they will return useful data.
// given the entity detail data, returns the team the entity belongs
// to. Uses TEAM_* enum values.
window.getTeam = function(details) {
return teamStringToId(details.team);
}
window.teamStringToId = function(teamStr) {
var team = TEAM_NONE;
if(teamStr === 'ENLIGHTENED') team = TEAM_ENL;
if(teamStr === 'RESISTANCE') team = TEAM_RES;
if(teamStr === 'E') team = TEAM_ENL;
if(teamStr === 'R') team = TEAM_RES;
return team;
}
;
// as of 2014-08-14, Niantic have returned to minifying the javascript. This means we no longer get the nemesis object
// and it's various member objects, functions, etc.
// so we need to extract some essential parameters from the code for IITC to use
window.extractFromStock = function() {
window.niantic_params = {}
// extract the former nemesis.dashboard.config.CURRENT_VERSION from the code
var reVersion = new RegExp('"X-CSRFToken".*[a-z].v="([a-f0-9]{40})";');
var minified = new RegExp('^[a-zA-Z$][a-zA-Z$0-9]?$');
for (var topLevel in window) {
if (minified.test(topLevel)) {
// a minified object - check for minified prototype entries
var topObject = window[topLevel];
if (topObject && topObject.prototype) {
// the object has a prototype - iterate through the properties of that
for (var secLevel in topObject.prototype) {
if (minified.test(secLevel)) {
// looks like we've found an object of the format "XX.prototype.YY"...
var item = topObject.prototype[secLevel];
if (item && typeof(item) == "function") {
// a function - test it against the relevant regular expressions
var funcStr = item.toString();
var match = reVersion.exec(funcStr);
if (match) {
console.log('Found former CURRENT_VERSION in '+topLevel+'.prototype.'+secLevel);
niantic_params.CURRENT_VERSION = match[1];
}
}
}
}
} //end 'if .prototype'
if (topObject && Array.isArray && Array.isArray(topObject)) {
// find all non-zero length arrays containing just numbers
if (topObject.length>0) {
var justInts = true;
for (var i=0; i<topObject.length; i++) {
if (typeof(topObject[i]) !== 'number' || topObject[i] != parseInt(topObject[i])) {
justInts = false;
break;
}
}
if (justInts) {
// current lengths are: 17: ZOOM_TO_LEVEL, 14: TILES_PER_EDGE
// however, slightly longer or shorter are a possibility in the future
if (topObject.length >= 12 && topObject.length <= 18) {
// a reasonable array length for tile parameters
// need to find two types:
// a. portal level limits. decreasing numbers, starting at 8
// b. tiles per edge. increasing numbers. current max is 36000, 9000 was the previous value - 18000 is a likely possibility too
if (topObject[0] == 8) {
// check for tile levels
var decreasing = true;
for (var i=1; i<topObject.length; i++) {
if (topObject[i-1] < topObject[i]) {
decreasing = false;
break;
}
}
if (decreasing) {
console.log ('int array '+topLevel+' looks like ZOOM_TO_LEVEL: '+JSON.stringify(topObject));
window.niantic_params.ZOOM_TO_LEVEL = topObject;
}
} // end if (topObject[0] == 8)
// 2015-06-25 - changed to top value of 64000, then to 32000 - allow for them to restore it just in case
if (topObject[topObject.length-1] >= 9000 && topObject[topObject.length-1] <= 64000) {
var increasing = true;
for (var i=1; i<topObject.length; i++) {
if (topObject[i-1] > topObject[i]) {
increasing = false;
break;
}
}
if (increasing) {
console.log ('int array '+topLevel+' looks like TILES_PER_EDGE: '+JSON.stringify(topObject));
window.niantic_params.TILES_PER_EDGE = topObject;
}
} //end if (topObject[topObject.length-1] == 9000) {
}
}
}
}
}
}
if (niantic_params.CURRENT_VERSION === undefined) {
dialog({
title: 'IITC Broken',
html: '<p>IITC failed to extract the required parameters from the intel site</p>'
+'<p>This can happen after Niantic update the standard intel site. A fix will be needed from the IITC developers.</p>',
});
console.log('Discovered parameters');
console.log(JSON.stringify(window.niantic_params,null,2));
throw('Error: IITC failed to extract CURRENT_VERSION string - cannot continue');
}
}
;
// GAME STATUS ///////////////////////////////////////////////////////
// MindUnit display
window.updateGameScore = function(data) {
if(!data) {
// move the postAjax call onto a very short timer. this way, if it throws an exception, it won't prevent IITC booting
setTimeout (function() { window.postAjax('getGameScore', {}, window.updateGameScore); }, 1);
return;
}
if (data && data.result) {
var e = parseInt(data.result[0]); //enlightened score in result[0]
var r = parseInt(data.result[1]); //resistance score in result[1]
var s = r+e;
var rp = r/s*100, ep = e/s*100;
r = digits(r), e = digits(e);
var rs = '<span class="res" style="width:'+rp+'%;">'+Math.round(rp)+'% </span>';
var es = '<span class="enl" style="width:'+ep+'%;"> '+Math.round(ep)+'%</span>';
$('#gamestat').html(rs+es).one('click', function() { window.updateGameScore() });
// help cursor via “#gamestat span”
$('#gamestat').attr('title', 'Resistance:\t'+r+' MindUnits\nEnlightened:\t'+e+' MindUnits');
} else if (data && data.error) {
console.warn('game score failed to load: '+data.error);
} else {
console.warn('game score failed to load - unknown reason');
}
// TODO: idle handling - don't refresh when IITC is idle!
window.setTimeout('window.updateGameScore', REFRESH_GAME_SCORE*1000);
}
;
// PLUGIN HOOKS ////////////////////////////////////////////////////////
// Plugins may listen to any number of events by specifying the name of
// the event to listen to and handing a function that should be exe-
// cuted when an event occurs. Callbacks will receive additional data
// the event created as their first parameter. The value is always a
// hash that contains more details.
//
// For example, this line will listen for portals to be added and print
// the data generated by the event to the console:
// window.addHook('portalAdded', function(data) { console.log(data) });
//
// Boot hook: booting is handled differently because IITC may not yet
// be available. Have a look at the plugins in plugins/. All
// code before “// PLUGIN START” and after “// PLUGIN END” is
// required to successfully boot the plugin.
//
// Here’s more specific information about each event:
// portalSelected: called when portal on map is selected/unselected.
// Provide guid of selected and unselected portal.
// mapDataRefreshStart: called when we start refreshing map data
// mapDataEntityInject: called just as we start to render data. has callback to inject cached entities into the map render
// mapDataRefreshEnd: called when we complete the map data load
// portalAdded: called when a portal has been received and is about to
// be added to its layer group. Note that this does NOT
// mean it is already visible or will be, shortly after.
// If a portal is added to a hidden layer it may never be
// shown at all. Injection point is in
// code/map_data.js#renderPortal near the end. Will hand
// the Leaflet CircleMarker for the portal in "portal" var.
// linkAdded: called when a link is about to be added to the map
// fieldAdded: called when a field is about to be added to the map
// portalRemoved: called when a portal has been removed
// linkRemoved: called when a link has been removed
// fieldRemoved: called when a field has been removed
// portalDetailsUpdated: fired after the details in the sidebar have
// been (re-)rendered Provides data about the portal that
// has been selected.
// publicChatDataAvailable: this hook runs after data for any of the
// public chats has been received and processed, but not
// yet been displayed. The data hash contains both the un-
// processed raw ajax response as well as the processed
// chat data that is going to be used for display.
// factionChatDataAvailable: this hook runs after data for the faction
// chat has been received and processed, but not yet been
// displayed. The data hash contains both the unprocessed
// raw ajax response as well as the processed chat data
// that is going to be used for display.
// requestFinished: DEPRECATED: best to use mapDataRefreshEnd instead
// called after each map data request finished. Argument is
// {success: boolean} indicated the request success or fail.
// iitcLoaded: called after IITC and all plugins loaded
// portalDetailLoaded: called when a request to load full portal detail
// completes. guid, success, details parameters
// paneChanged called when the current pane has changed. On desktop,
// this only selects the current chat pane; on mobile, it
// also switches between map, info and other panes defined
// by plugins
// artifactsUpdated: called when the set of artifacts (including targets)
// has changed. Parameters names are old, new.
window._hooks = {}
window.VALID_HOOKS = [
'portalSelected', 'portalDetailsUpdated', 'artifactsUpdated',
'mapDataRefreshStart', 'mapDataEntityInject', 'mapDataRefreshEnd',
'portalAdded', 'linkAdded', 'fieldAdded',
'portalRemoved', 'linkRemoved', 'fieldRemoved',
'publicChatDataAvailable', 'factionChatDataAvailable',
'requestFinished', 'nicknameClicked',
'geoSearch', 'search', 'iitcLoaded',
'portalDetailLoaded', 'paneChanged'];
window.runHooks = function(event, data) {
if(VALID_HOOKS.indexOf(event) === -1) throw('Unknown event type: ' + event);
if(!_hooks[event]) return true;
var interrupted = false;
$.each(_hooks[event], function(ind, callback) {
try {
if (callback(data) === false) {
interrupted = true;
return false; //break from $.each
}
} catch(err) {
console.error('error running hook '+event+', error: '+err);
debugger;
}
});
return !interrupted;
}
// helper method to allow plugins to create new hooks
window.pluginCreateHook = function(event) {
if($.inArray(event, window.VALID_HOOKS) < 0) {
window.VALID_HOOKS.push(event);
}
}
window.addHook = function(event, callback) {
if(VALID_HOOKS.indexOf(event) === -1) {
console.error('addHook: Unknown event type: ' + event + ' - ignoring');
debugger;
return;
}
if(typeof callback !== 'function') throw('Callback must be a function.');
if(!_hooks[event])
_hooks[event] = [callback];
else
_hooks[event].push(callback);
}
// callback must the SAME function to be unregistered.
window.removeHook = function(event, callback) {
if (typeof callback !== 'function') throw('Callback must be a function.');
if (_hooks[event]) {
var index = _hooks[event].indexOf(callback);
if(index == -1)
console.warn('Callback wasn\'t registered for this event.');
else
_hooks[event].splice(index, 1);
}
}
;
// IDLE HANDLING /////////////////////////////////////////////////////
window.idleTime = 0; // in seconds
window._idleTimeLimit = MAX_IDLE_TIME;
var IDLE_POLL_TIME = 10;
var idlePoll = function() {
var wasIdle = isIdle();
window.idleTime += IDLE_POLL_TIME;
var hidden = (document.hidden || document.webkitHidden || document.mozHidden || document.msHidden || false);
if (hidden) {
window._idleTimeLimit = window.REFRESH; // set a small time limit before entering idle mode
}
if (!wasIdle && isIdle()) {
console.log('idlePoll: entering idle mode');
}
}
setInterval(idlePoll, IDLE_POLL_TIME*1000);
window.idleReset = function () {
// update immediately when the user comes back
if(isIdle()) {
console.log ('idleReset: leaving idle mode');
window.idleTime = 0;
$.each(window._onResumeFunctions, function(ind, f) {
f();
});
}
window.idleTime = 0;
window._idleTimeLimit = MAX_IDLE_TIME;
};
window.idleSet = function() {
var wasIdle = isIdle();
window._idleTimeLimit = 0; // a zero time here will cause idle to start immediately
if (!wasIdle && isIdle()) {
console.log ('idleSet: entering idle mode');
}
}
// only reset idle on mouse move where the coordinates are actually different.
// some browsers send the event when not moving!
var _lastMouseX=-1, _lastMouseY=-1;
var idleMouseMove = function(e) {
var dX = _lastMouseX-e.clientX;
var dY = _lastMouseY-e.clientY;
var deltaSquared = dX*dX + dY*dY;
// only treat movements over 3 pixels as enough to reset us
if (deltaSquared > 3*3) {
_lastMouseX = e.clientX;
_lastMouseY = e.clientY;
idleReset();
}
}
window.setupIdle = function() {
$('body').keypress(idleReset);
$('body').mousemove(idleMouseMove);
}
window.isIdle = function() {
return window.idleTime >= window._idleTimeLimit;
}
window._onResumeFunctions = [];
// add your function here if you want to be notified when the user
// resumes from being idle
window.addResumeFunction = function(f) {
window._onResumeFunctions.push(f);
}
;
// LOCATION HANDLING /////////////////////////////////////////////////
// i.e. setting initial position and storing new position after moving
// retrieves current position from map and stores it cookies
window.storeMapPosition = function() {
var m = window.map.getCenter();
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.
window.getPosition = function() {
if(getURLParam('latE6') && getURLParam('lngE6')) {
console.log("mappos: reading email URL params");
var lat = parseInt(getURLParam('latE6'))/1E6 || 0.0;
var lng = parseInt(getURLParam('lngE6'))/1E6 || 0.0;
var z = parseInt(getURLParam('z')) || 17;
return {center: new L.LatLng(lat, lng), zoom: z};
}
if(getURLParam('ll')) {
console.log("mappos: reading stock Intel URL params");
var lat = parseFloat(getURLParam('ll').split(",")[0]) || 0.0;
var lng = parseFloat(getURLParam('ll').split(",")[1]) || 0.0;
var z = parseInt(getURLParam('z')) || 17;
return {center: new L.LatLng(lat, lng), zoom: z};
}
if(getURLParam('pll')) {
console.log("mappos: reading stock Intel URL portal params");
var lat = parseFloat(getURLParam('pll').split(",")[0]) || 0.0;
var lng = parseFloat(getURLParam('pll').split(",")[1]) || 0.0;
var z = parseInt(getURLParam('z')) || 17;
return {center: new L.LatLng(lat, lng), zoom: