### Install Project Dependencies Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Code-Style Run this command in the project root to install all necessary development dependencies. ```bash npm install ``` -------------------------------- ### Call initMPE within Plugin Setup Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Multi-Projects-Extension This snippet shows how to correctly call the `initMPE` function within the plugin's setup function. This ensures MPE integration is initialized when the plugin loads. ```javascript var setup = function () { window.plugin.myPlugin.initMPE (); } ``` -------------------------------- ### Filters API Example Usage Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Example configuration for the Filters API to hide intel entities based on properties. ```javascript { portal: true, data: ['not', { history: { scoutControlled: false }, ornaments: ['some', 'sc5_p'] }] } ``` -------------------------------- ### Registering and Running the Plugin Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin Adds the plugin's setup function to the `window.bootPlugins` array for deferred execution and immediately calls it if IITC has already loaded. This ensures the plugin runs correctly regardless of script load order. ```javascript // Make sure window.bootPlugins exists and is an array if (!window.bootPlugins) window.bootPlugins = []; // Add our startup hook window.bootPlugins.push(setup); // If IITC has already booted, immediately run the 'setup' function if (window.iitcLoaded && typeof setup === 'function') setup(); ``` -------------------------------- ### Example of Keyword Spacing Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Code-Style Demonstrates correct spacing around keywords like 'if' and 'else', and proper brace style with opening braces on the same line. ```javascript if (true) { doStuff(); } else { dontDoStuff(); } ``` -------------------------------- ### Create a Hello World IITC Plugin Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin A template for a basic IITC plugin, including the required userscript header, wrapper function, and setup registration. This script must be injected into the page context to interact with IITC internals. ```javascript // ==UserScript== // @id hello-iitc // @name IITC Plugin: Hello World // @category Misc // @version 0.1.0 // @namespace https://tempuri.org/iitc/hello // @description Hello, World plugin for IITC // @match https://intel.ingress.com/* // @grant none // ==/UserScript== // Wrapper function that will be stringified and injected // into the document. Because of this, normal closure rules // do not apply here. function wrapper(plugin_info) { // Make sure that window.plugin exists. IITC defines it as a no-op function, // and other plugins assume the same. if(typeof window.plugin !== 'function') window.plugin = function() {}; // Name of the IITC build for first-party plugins plugin_info.buildName = 'hello'; // Datetime-derived version of the plugin plugin_info.dateTimeVersion = '2015-08-29-103500'; // ID/name of the plugin plugin_info.pluginId = 'hello'; // ESLint directives for identifiers that are provided externally by the main // IITC script or third-party libraries. This prevents editor warnings like // "no-undef" and "no-unused-vars" in this file. /* global IITC:readonly, L:readonly */ /* exported setup, changelog */ // The changelog will be shown in the "About IITC" dialog. // Use sematinc versioning: major.minor.patch // Always add the newest entry on top. var changelog = [ /* { version: '0.1.1', changes: [ 'This is a patch', 'Hello again', ], }, */ { version: '0.1.0', changes: [ 'My first IITC plugin', 'Hello IITC', ], } ]; // The entry point for this plugin. function setup() { alert('Hello, IITC!'); } // Add an info property for IITC's plugin system setup.info = plugin_info; // Add the changelog property to the info object for IITC's plugin system if (typeof changelog !== 'undefined') setup.info.changelog = changelog; // Make sure window.bootPlugins exists and is an array if (!window.bootPlugins) window.bootPlugins = []; // Add our startup hook window.bootPlugins.push(setup); // If IITC has already booted, immediately run the 'setup' function if (window.iitcLoaded && typeof setup === 'function') setup(); } // Create a script element to hold our content script var script = document.createElement('script'); var info = {}; // GM_info is defined by the assorted monkey-themed browser extensions // and holds information parsed from the script header. if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) { info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description }; } // Create a text node and our IIFE inside of it var textContent = document.createTextNode('('+ wrapper +')('+ JSON.stringify(info) +')'); // Add some content to the script element script.appendChild(textContent); // Finally, inject it... wherever. (document.body || document.head || document.documentElement).appendChild(script); ``` -------------------------------- ### Example of Spaced Comment Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Code-Style Shows the required format for comments, ensuring a space after the comment delimiters. ```javascript // this is a comment ``` -------------------------------- ### Example of jQuery and Vanilla JS Usage Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Code-Style Illustrates the use of jQuery for DOM manipulation, with a preference for vanilla JavaScript when possible. Note the use of single quotes for JavaScript strings and double quotes for HTML content. ```javascript $('body').append('
Plugin content here
', id: 'my-plugin-dialog' }); }; window.plugin.myPlugin.onPortalSelected = function(data) { if (data.selectedPortalGuid) { console.log('Portal selected:', data.selectedPortalGuid); } }; window.plugin.myPlugin.onIitcLoaded = function() { console.log('IITC fully loaded, safe to access all features'); }; // Register plugin setup.info = plugin_info; if (typeof changelog !== 'undefined') setup.info.changelog = changelog; if (!window.bootPlugins) window.bootPlugins = []; window.bootPlugins.push(setup); if (window.iitcLoaded && typeof setup === 'function') setup(); } // Inject into page context var script = document.createElement('script'); var info = {}; if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) { info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description }; } script.appendChild(document.createTextNode('(' + wrapper + ')(' + JSON.stringify(info) + ')')); (document.body || document.head || document.documentElement).appendChild(script); ``` -------------------------------- ### Example of Trailing Whitespace Check Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Code-Style This command can be used to find lines with trailing whitespace in a file, which should be removed before submitting changes. ```bash grep -nE "[[:space:]]+" «filename» ``` -------------------------------- ### Add Declarative Message Filter Rule Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Use this to add a rule for filtering messages in channels. This example hides all messages except those from the Resistance team. ```javascript IITC.comm.declarativeMessageFilter.addRule({ id: "hideExceptResistanceTeam", conditions: [ { field: "player.team", value: "Resistance", invert: true }, ] }); ``` -------------------------------- ### Implement MPE in a Plugin Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Multi-Projects-Extension This code demonstrates how to integrate the Multi Projects Extension (MPE) into a third-party plugin. It includes setting up MPE configuration, defining storage keys, and handling pre/post actions for project switching. Ensure MPE plugin is present before calling initMPE. ```javascript //-------------------------------------------- // This is an example to implement MPE: //-------------------------------------------- // A function for myPlugin window.plugin.myPlugin.initMPE = function(){ // Not launch che code if the MPE plugin there isn't. if(!window.plugin.mpe){ return; } // The MPE function to set a MultiProjects type window.plugin.mpe.setMultiProjects({ namespace: 'myplugin', title: 'Label for My Plugin', // Font awesome css class fa: 'fa-star', // Function to change a localstorage key func_setKey: function(newKey){ window.plugin.myPlugin.KEY_STORAGE = newKey; }, // Native value of localstorage key defaultKey: 'myPlugin-storage', // This function is run before the localstorage key change func_pre: function(){}, // This function is run after the localstorage key change func_post: function(){ // Code to: // hide/remove elements from DOM, layers, variables, etc... // load data from window.localStorage[window.plugin.myPlugin.KEY_STORAGE] // show/add/draw elements in the DOM, layers, variables, etc... } }); } // Insert in the plugin setup window.plugin.myPlugin.initMPE(); ``` -------------------------------- ### Set Android SDK Path Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-Mobile-(Android-app) Configure the ANDROID_HOME environment variable required for the build process. ```bash export ANDROID_HOME=/path/to/android_sdk ``` -------------------------------- ### Integrate with Bookmarks Plugin Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt Demonstrates how to query, add, toggle, and listen for events within the IITC bookmarks plugin. ```javascript // Check if a portal is bookmarked var bookmarkInfo = window.plugin.bookmarks.findByGuid(guid); if (bookmarkInfo) { console.log('Folder:', bookmarkInfo.id_folder); console.log('Bookmark ID:', bookmarkInfo.id_bookmark); } // Add a portal to bookmarks programmatically window.plugin.bookmarks.addPortalBookmarkByGuid(portalGuid, true); // Add via marker reference var marker = window.portals[guid]; window.plugin.bookmarks.addPortalBookmarkByMarker(marker, true); // Toggle bookmark status (add if not bookmarked, remove if bookmarked) window.plugin.bookmarks.switchStarPortal(guid); // Listen for bookmark changes window.addHook('pluginBkmrksEdit', function(data) { console.log('Bookmark action:', data.action); // 'add', 'remove', 'sort' console.log('Target:', data.target); // 'portal', 'folder', 'map' if (data.guid) { console.log('Affected portal:', data.guid); } }); // Listen for sync completion window.addHook('pluginBkmrksSyncEnd', function(data) { console.log('Bookmarks synced'); }); ``` -------------------------------- ### Check API Availability (Good Practice) Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Check for the existence of the method directly instead of relying on build dates or versions. ```javascript if (typeof IITC !== 'undefined' && typeof IITC.toolbox !== 'undefined') { /* ... */ } ``` -------------------------------- ### Build IITC Mobile via Script Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-Mobile-(Android-app) Execute the build script to generate the mobile APK and necessary assets. ```bash ./build.py mobile ``` -------------------------------- ### Assigning Plugin Info to Entry Point Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin Attaches the plugin's metadata object to the entry point function. IITC expects this structure to access plugin details. ```javascript setup.info = plugin_info; ``` -------------------------------- ### Plugin Framework Initialization Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin Ensures the IITC plugin framework is available before attempting to register the plugin. This handles cases where the plugin script might load before the IITC framework. ```javascript if(typeof window.plugin !== 'function') window.plugin = function() {}; ``` -------------------------------- ### Managing Portal Highlighters Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt Register custom visual styles for portals and manage active highlighting states. ```javascript // Register a simple highlighter that colors portals by team window.addPortalHighlighter('My Custom Highlighter', function(data) { var portal = data.portal; var team = portal.options.data.team; if (team === 'RESISTANCE') { portal.setStyle({ fillColor: 'blue', fillOpacity: 0.7 }); } else if (team === 'ENLIGHTENED') { portal.setStyle({ fillColor: 'green', fillOpacity: 0.7 }); } else { portal.setStyle({ fillColor: 'gray', fillOpacity: 0.5 }); } }); // Advanced highlighter with selection callbacks window.addPortalHighlighter('High Level Portals', { highlight: function(data) { var portal = data.portal; var level = portal.options.data.level; if (level >= 7) { portal.setStyle({ fillColor: 'gold', fillOpacity: 0.8, weight: 3 }); } }, setSelected: function(isSelected) { // Called when this highlighter is selected/deselected console.log('Highlighter active:', isSelected); } }); // Programmatically change the active highlighter window.changePortalHighlights('My Custom Highlighter'); // Reset all portals to default styling window.resetHighlightedPortals(); ``` -------------------------------- ### Configure Zed Editor for ESLint Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Code-Style Create this JSON file in the project root to enable ESLint auto-formatting within the Zed editor for JavaScript files. ```json { "languages": { "JavaScript": { "formatter": { "code_actions": { "source.fixAll.eslint": true } } } } } ``` -------------------------------- ### Registering and Triggering Hooks Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt Use the hook system to respond to map events or trigger custom events from plugins. ```javascript // Register a callback for when a portal is selected window.addHook('portalSelected', function(data) { console.log('Portal selected:', data.selectedPortalGuid); console.log('Previously selected:', data.unselectedPortalGuid); }); // Register for map data refresh events window.addHook('mapDataRefreshEnd', function() { console.log('Map data has finished loading'); // Process all currently visible portals Object.keys(window.portals).forEach(function(guid) { var portal = window.portals[guid]; console.log('Portal:', portal.options.data.title); }); }); // Register for portal additions window.addHook('portalAdded', function(data) { var portal = data.portal; var portalData = portal.options.data; console.log('New portal added:', portalData.title, 'Level:', portalData.level); }); // Remove a hook when no longer needed var myCallback = function(data) { console.log('Portal details updated'); }; window.addHook('portalDetailsUpdated', myCallback); // Later... window.removeHook('portalDetailsUpdated', myCallback); // Trigger custom hooks from plugins window.runHooks('myCustomEvent', { customData: 'value' }); ``` -------------------------------- ### Plugin Entry Point Function Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin Defines the main entry point for the plugin's logic. This function will be called by IITC when the plugin is loaded and ready to execute. ```javascript function setup() { alert('Hello, IITC!'); } ``` -------------------------------- ### Accessing Portal Details Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt Retrieve, cache, and request detailed portal information from the server. ```javascript // Get cached portal details (returns undefined if not cached) var details = window.portalDetail.get(guid); if (details) { console.log('Portal title:', details.title); console.log('Portal level:', details.level); console.log('Resonators:', details.resonators); } // Request portal details from server (returns a Promise) window.portalDetail.request(guid) .done(function(details) { console.log('Loaded portal details:', details.title); console.log('Health:', details.health + '%'); console.log('Owner:', details.owner); console.log('Mods:', details.mods); }) .fail(function() { console.log('Failed to load portal details'); }); // Check if cached details are fresh if (window.portalDetail.isFresh(guid)) { console.log('Details are fresh, no need to re-request'); } // Store custom portal details in cache window.portalDetail.store(guid, portalData, freshTime); // Listen for portal detail load events window.addHook('portalDetailLoaded', function(data) { if (data.success) { console.log('Portal loaded:', data.guid); console.log('Details:', data.details); } else { console.log('Failed to load portal:', data.guid); } }); ``` -------------------------------- ### Plugin Icon Configuration Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Contributing-to-IITC‐CE This configuration line is required in build settings to correctly reference plugin icons. Ensure your plugin includes the appropriate icon reference for consistency. ```python 'url_icon_base': 'https://iitc.app/extras/plugin-icons/{}.svg', ``` -------------------------------- ### Portal Highlighter API Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt The portal highlighter system allows plugins to register custom visual styles for portals on the map. ```APIDOC ## Portal Highlighter API ### Description Allows plugins to define custom visual styles for portals based on criteria and manage active highlighters. ### Methods - **window.addPortalHighlighter(name, highlighter)**: Registers a new portal highlighter. - **window.changePortalHighlights(name)**: Programmatically sets the active highlighter. - **window.resetHighlightedPortals()**: Resets all portals to default styling. ### Parameters - **name** (string) - Required - The unique name of the highlighter. - **highlighter** (function|object) - Required - A function to apply styles or an object containing 'highlight' and 'setSelected' methods. ``` -------------------------------- ### Create Dialogs and Alerts Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt Display modal or non-modal dialogs using the dialog API, or use the alert convenience method for simple notifications. ```javascript // Create a simple dialog window.dialog({ title: 'My Plugin Dialog', html: 'Hello from my plugin!
', buttons: { 'OK': function() { $(this).dialog('close'); }, 'Cancel': function() { $(this).dialog('close'); } } }); // Create a dialog with callbacks window.dialog({ id: 'my-plugin-dialog', // Unique ID prevents duplicates title: 'Settings', html: '', width: 400, closeCallback: function() { var value = $('#setting-input').val(); console.log('Dialog closed, value:', value); }, focusCallback: function() { console.log('Dialog gained focus'); }, blurCallback: function() { console.log('Dialog lost focus'); } }); // Create a modal dialog (blocks interaction with rest of page) window.dialog({ title: 'Important Message', html: 'This requires your attention!
', modal: true, buttons: { 'I Understand': function() { $(this).dialog('close'); } } }); // Simple alert dialog window.alert('Operation completed successfully!'); // Alert with HTML content window.alert('Warning: Something happened!', true); // Alert with close callback window.alert('Processing complete', false, function() { console.log('User dismissed the alert'); }); ``` -------------------------------- ### Check Plugin Git History Source: https://github.com/iitc-ce/ingress-intel-total-conversion/blob/master/CLAUDE.md Command to identify changes in a specific plugin since the last release. ```bash git log --oneline [last-release-commit]..HEAD -- plugins/[plugin-name].js ``` -------------------------------- ### Check API Availability (Bad Practice) Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Avoid checking IITC build dates to determine API availability, as this is less reliable. ```javascript if (iitcBuildDate <= '2023-11-20-071719') { /* ... */ } ``` -------------------------------- ### Map Data Structure Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Provides details on portal visibility, link lengths, loading status, and request counts. Use this structure to understand the current state of the map. ```javascript { portalLevels: { hasPortals: boolean, // Whether portals are visible at current zoom minLinkLength: number, // Minimum link length in meters formattedLength: string // Pre-formatted length (e.g., "1.2km", "500m") }, mapStatus: { short: string, // Short status text (e.g., "done", "loading") long: string | null, // Detailed status description progress: number, // Progress value (0-1, or -1 for unknown) progressPercent: number | null // Pre-calculated percentage (0-100) }, requests: { active: number, // Number of active requests failed: number, // Number of failed requests hasActive: boolean, // Whether there are active requests hasFailed: boolean // Whether there are failed requests } } ``` -------------------------------- ### Check Core Git History Source: https://github.com/iitc-ce/ingress-intel-total-conversion/blob/master/CLAUDE.md Command to identify changes in the core directory since the last release. ```bash git log --oneline [last-release-commit]..HEAD -- core/ ``` -------------------------------- ### Portal Detail API Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt Provides methods for caching, retrieving, and requesting detailed portal information from the server. ```APIDOC ## Portal Detail API ### Description Manages the caching and retrieval of detailed portal information, including resonators, mods, and owner data. ### Methods - **window.portalDetail.get(guid)**: Retrieves cached portal details. - **window.portalDetail.request(guid)**: Requests portal details from the server (returns a Promise). - **window.portalDetail.isFresh(guid)**: Checks if cached details are still valid. - **window.portalDetail.store(guid, data, freshTime)**: Manually stores portal data in the cache. ### Parameters - **guid** (string) - Required - The unique identifier for the portal. - **data** (object) - Required - The portal data object to store. - **freshTime** (number) - Required - The timestamp or duration for cache validity. ``` -------------------------------- ### Listen for Draw Tools Events Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt Use window.addHook to listen for various events fired by the draw tools plugin, such as layer creation, deletion, or editing. This allows other plugins to react to user drawing actions. ```javascript window.addHook('pluginDrawTools', function(data) { switch (data.event) { case 'layerCreated': console.log('New shape created:', data.layer); break; case 'layersDeleted': console.log('Shapes deleted'); break; case 'layersEdited': console.log('Shapes edited'); break; case 'import': console.log('Shapes imported'); break; case 'clear': console.log('All shapes cleared'); break; } }); ``` -------------------------------- ### Select chat tabs by data-channel Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Use the data-channel attribute instead of text content to reliably select chat tabs after the channel naming convention change. ```javascript [...document.querySelectorAll('#chatcontrols a')].filter(el=>el.textContent == 'all') ``` ```javascript [...document.querySelectorAll('#chatcontrols a')].filter(el=>el.dataset.channel == 'all') ``` -------------------------------- ### Plugin Information Properties Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin Sets essential metadata for the plugin, such as build name, version, and ID. These properties are used by IITC for internal tracking and display. ```javascript // Name of the IITC build for first-party plugins plugin_info.buildName = 'hello'; // Datetime-derived version of the plugin plugin_info.dateTimeVersion = '20150829103500'; // ID/name of the plugin plugin_info.pluginId = 'hello'; ``` -------------------------------- ### Hook System API Source: https://context7.com/iitc-ce/ingress-intel-total-conversion/llms.txt The hook system allows plugins to register callbacks for specific map events and trigger custom events. ```APIDOC ## Hook System API ### Description The hook system is the primary mechanism for plugins to respond to IITC events such as portal selection, map data loading, and chat updates. ### Methods - **window.addHook(hookName, callback)**: Registers a callback function for a specific event. - **window.removeHook(hookName, callback)**: Removes a previously registered callback. - **window.runHooks(hookName, data)**: Triggers a custom event with associated data. ### Parameters - **hookName** (string) - Required - The name of the event to listen for or trigger. - **callback** (function) - Required - The function to execute when the event occurs. - **data** (object) - Optional - Data payload passed to the hook callbacks. ``` -------------------------------- ### Add Toolbox Button (New API) Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Use the Toolbox API to add buttons to the plugin toolbox, ensuring better compatibility. ```javascript IITC.toolbox.addButton({ id: 'mybtn', label: 'Test Button', action: window.plugin.myplugin.openDialog }); ``` -------------------------------- ### Plugin Author Warning Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin A standard warning for plugin authors not using the IITC build environment. Leaving these lines in might interfere with IITC's 'About' page or update checks. ```javascript //PLUGIN AUTHORS: writing a plugin outside of the IITC build environment? if so, delete these lines!! //(leaving them in place might break the 'About IITC' page or break update checks) ``` -------------------------------- ### Set Progress Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Updates the map loading progress. Use this to provide real-time feedback on the loading status, with -1 indicating an unknown progress. ```javascript window.app.setProgress(progress); ``` -------------------------------- ### Render chat channels Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Replace specific channel rendering functions with the unified IITC.comm.renderChannel method. ```javascript window.chat.renderPublic(oldMsgsWereAdded) ``` ```javascript IITC.comm.renderChannel('all', oldMsgsWereAdded) ``` -------------------------------- ### Wrapper Function for Plugin Injection Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/Your-First-Plugin This function serves as a wrapper for the plugin code, ensuring it runs in the correct context when injected into the page. It receives plugin information as a parameter. ```javascript function wrapper(plugin_info) { /* ... */ } ``` -------------------------------- ### Portal Data Structure Source: https://github.com/iitc-ce/ingress-intel-total-conversion/wiki/IITC-plugin-migration-guide Defines the structure for individual portal data, including its attributes and resonator information. ```APIDOC ## Portal Data Structure ### Description This structure represents the data for a single Ingress portal, including its basic attributes and details about its resonators. ### Data Structure ```javascript { guid: "portal-guid", team: "E" | "R" | "N", level: 0-8, title: "Portal Name", health: 0-100, isLoading: boolean, isNeutral: boolean, resonators: null | Array