// ==UserScript== // @id iitc-plugin-portalsinpolygons@hayeswise // @name IITC plugin: Portals-in-Polygons // @category Layer // @version 1.2017.02.24 // @namespace https://github.com/hayeswise/ingress-intel-total-conversion // @description Display a list of portals in, on on the perimeter of, polygons and circles, and on lines. Use the layer group check boxes to filter the portals. // @updateURL https://github.com/hayeswise/iitc-shadowops/raw/master/dist/plugins/wise-portalsinpolygons.meta.js // @downloadURL https://github.com/hayeswise/iitc-shadowops/raw/master/dist/plugins/wise-portalsinpolygons.user.js // @include https://*.ingress.com/intel* // @include http://*.ingress.com/intel* // @match https://*.ingress.com/intel* // @match http://*.ingress.com/intel* // @require https://rawgit.com/hayeswise/Leaflet.PointInPolygon/v1.0.0/wise-leaflet-pip.js // @author Hayeswise // @grant none // ==/UserScript== // MIT License, Copyright (c) 2017 Brian Hayes ("Hayeswise") // For more information, visit https://github.com/hayeswise/iitc-wise-portalsinpolygon /* * Thanks to: * IITC - Ingress Intel Total Conversion - https://iitc.me/ and https://github.com/iitc-project/ingress-intel-total-conversion * License: https://github.com/iitc-project/ingress-intel-total-conversion/blob/master/LICENSE * Leaflet.Geodesic by Kevin Brasier (a.k.a. Fragger) * License: https://github.com/Fragger/Leaflet.Geodesic/blob/master/LICENSE * Leaflet.Geodesc has been extended by the IITC project. See the IITC distribution of the L.Geodesc.js in GitHub. * Dan Sunday's Winding Number and isLeft C++ implementation - http://geomalgorithms.com/. * Copyright and License: http://geomalgorithms.com/a03-_inclusion.html */ /** * Greasemonkey/Tampermonkey information about the plugin. * @typedef ScriptInfo * @type {Object} * @property {String} version This is set to GM_info.script.version. * @property {String} name This is set to GM_info.script.name. * @property {String} description This is set to GM_info.script.description. */ /** * Plugin information which includes the Greasemonkey/Tampermonkey information about the plugin. * @typedef PluginInfo * @type {Object} * @property {ScriptInfo} script Greasemonkey/Tampermonkey information about the plugin. */ /** * The IITC map object (a Leaflet map). * @external "window.map" * @see {@link https://iitc.me/ Ingress Intel Total Conversion} */ /** * The IITC portals object (used as a map) that contains a list of the cached * portal information for the portals in the current and surrounding view. * @type {Object<string, Object>} Assoiciative array of portal information keyed by the portal's guid. * @external "window.portals" * @see {@link https://iitc.me/ Ingress Intel Total Conversion} */ /** * The map data render class which handles rendering into Leaflet the JSON data from the servers. Needed to access * `window.Render.prototype.bringPortalsToFront`. * @external "window.Render" * @see {@link https://iitc.me/ Ingress Intel Total Conversion} */ /** * The "show list of portals" plugin object, properties, and methods. * @external "window.plugin.portalslist" * @see {@link http://leafletjs.com/ "show list of portals"} plugin source code for further information. */ /** * Required Plugins helper. * @module {function} "window.helper.requiredPlugins" */ ;(function () { "use strict"; /** * Information about a plugin. The `pluginKey` is the property name of the * plugin in the `window.plugin` associative array. The `name` value is used * in messaging about the plugins (e.g., if it is missing). * @typedef PluginMetaData * @type {Object} * @property {String} pluginKey The property name of the plugin in * `window.plugin`. * @property {String} name A title or short name of the plugin. * @example * { * pluginKey: "drawTools", * name: "draw tools" * } */ // Aggregate helpers in the window.helper object if (typeof window.helper !== "object") { window.helper = {}; } window.helper.requiredPlugins = {}; /** * Required Plugins namespace. * @alias "window.helper.requiredPlugins" * @variation 2 */ var self = window.helper.requiredPlugins; self.spacename = "helper.requiredPlugins"; self.version = "0.1.0"; /** * Returns true if all the prerequisite plugins are installed. * @param {PluginMetaData[]} prerequisites An array of * `RequiredPluginMetaData`. * @returns {boolean} Returns `true` if all the prerequisite plugins are * installed; otherwise, returns `false`. * @example * window.plugin.myPlugin.requiredPlugins = [{ * pluginKey: window.plugin.drawTools, * name: "draw tools" * }, { * pluginKey: window.plugin.myotherplugin, * name: "My Other Plugin" * }] * ... * if (window.helper.requiredPlugins.areMissing(window.plugin.myPlugin.requiredPlugins)) { * return; * } */ self.areMissing = function (prerequisites) { var areMissing; areMissing = prerequisites.some(function (metadata) { return (typeof window.plugin[metadata.pluginKey] === "undefined"); }); return areMissing; }; /** * Checks if the prerequisite/required plugins are installed. * @param {PluginMetaData[]} requiredPlugins An array of plugin meta-data on * the required plugins. * @returns {PluginMetaData[]} * @example * window.plugin.myPlugin.requiredPlugins = [{ * pluginKey: window.plugin.drawTools, * name: "draw tools" * }, { * pluginKey: window.plugin.myotherplugin, * name: "My Other Plugin" * }] * ... * var missing = window.helper.requiredPlugins.missingPluginNames(window.plugin.myPlugin.requiredPlugins); * if (missing.length > 0) { * msg = 'IITC plugin "' + pluginName + '" requires IITC plugin' + ((missing.length === 1) ? ' ' : 's ') + * ((missing.length === 1) ? missing[0] : (missing.slice(0,-1).join(", ") + " and " + missing[missing.length - 1])) + '.'; * console.warn(msg); * alert(msg); * } */ self.missingPluginNames = function (requiredPlugins) { var missing = []; requiredPlugins.forEach(function (metadata) { if (metadata.pluginKey === undefined) { missing.push('"' + metadata.name + '"'); } }); return missing; }; /** * Checks if the pre-requisite plugins are installed. If one or more requisites are not installed, an alert is * displayed. * @param {RequiredPluginMetaData[]} requiredPlugins An array of plugin meta-data on the required plugins. * @param {string} pluginName The name of the plugin requiring the required plugins. Recommend using * `plugin_info.script.name`. * @returns {boolean} * @example * window.plugin.myPlugin.requiredPlugins = [{ * pluginKey: window.plugin.drawTools, * name: "draw tools" * }, { * pluginKey: window.plugin.myotherplugin, * name: "My Other Plugin" * }] * ... * if (!window.helper.requiredPlugins.alertIfNotInstalled(window.plugin.myPlugin.requiredPlugins, plugin_info.script.name) { * return; * } */ self.alertIfNotInstalled = function (requiredPlugins, pluginName) { var missing = [], msg; missing = self.missingPluginNames(requiredPlugins); if (missing.length > 0) { msg = 'IITC plugin "' + pluginName + '" requires IITC plugin' + ((missing.length === 1) ? ' ' : 's ') + ((missing.length === 1) ? missing[0] : (missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1])) + '.'; window.console.warn(msg); alert(msg); } return (missing.length === 0); }; }()); /** * Toolbox Control Section helper. * @module {function} "window.helper.ToolboxControlSection" */ ;(function () { "use strict"; // Aggregate helpers in the window.helper object if (typeof window.helper !== "object") { window.helper = {}; } /** * ToolboxControlSection Class. Provides a standardized way of adding toolbox controls and grouping controls in * the same "family". */ /** * Creates a new ToolboxControlSection. * * @class * @param {String|Element|Text|Array|jQuery} content A object suitable for passing to `jQuery.append()`: a * DOM element, text node, array of elements and text nodes, HTML string, or jQuery object to insert at the end of * each element in the set of matched elements. * @param {String} controlSectionClass The class name for a section of controls, typically in a `div` tag. * @param {String} [controlClass] An optional class name of a simple control or collection of controls. * @property {String} defaultStyle Global CSS for the toolbox control section. Set * using `setStyle()`. * @property {String} style Global CSS for the toolbox control section. Set * using `setStyle()`. */ window.helper.ToolboxControlSection = function (content, controlSectionClass, controlClass) { this.controlSectionClass = controlSectionClass; this.controlClass = controlClass; this.merged = false; this.jQueryObj = jQuery('<div>').append(content).addClass(controlSectionClass); }; // Properties var self = window.helper.ToolboxControlSection; self.version = "0.1.0"; self.defaultStyle = "div.wise-toolbox-control-section {color:#00C5FF;text-align:center;width:fit-content;border-top: 1px solid #20A8B1;border-bottom: 1px solid #20A8B1;}"; self.style = undefined; /** * See jQuery `.attr()` function. * * @returns {String} * @todo Consider removing this. */ self.prototype.attr = function (attributeNameOrAttributes, valueOrFunction) { if (typeof valueOrFunction === 'undefined') { return this.jQueryObj.attr(attributeNameOrAttributes); } else { return this.jQueryObj.attr(attributeNameOrAttributes, valueOrFunction); } }; /** * Appends toolbox controls with the same toolbox control section class and toolbox control class. * <p> * Merge * ``` * <div class="myControlFamily"> * ...this control... * </div> * ``` * with * ``` * <div class="myControlFamily"> * ...other control... * </div> * ``` * to get * ``` * <div class="myControlFamily"> * ...this control... * ...other control... * </div> * ``` */ self.prototype.mergeWithFamily = function () { var controlFamily, that; if (!this.merged) { that = this; controlFamily = jQuery('.' + this.controlSectionClass); if (controlFamily.length > 0) { controlFamily.each(function () { var jQobj = jQuery(this); jQobj.css("border-style", "none"); that.jQueryObj.append(jQobj.removeClass(that.controlSectionClass).addClass(that.controlSectionClass + "-moved")); // remove oringal section so any subsequent merges have a single control section to deal with }); this.merged = true; } if (typeof this.controlClass !== 'undefined') { controlFamily = jQuery(':not(.' + this.controlSectionClass + ') .' + this.controlClass); if (controlFamily.length > 0) { controlFamily.each(function () { that.jQueryObj.append(jQuery(this)); }); this.merged = true; } } } return this.jQueryObj; }; /** * Sets the documents's styling. Will not add the style if previously used. * @param {String} [styling] CSS styles. */ self.prototype.setStyle = function (styling) { if (typeof styling === "undefined") { styling = self.defaultStyle; } if (typeof self.style === 'undefined' || (self.style !== styling)) { self.style = styling; jQuery("<style>") .prop("type", "text/css") .html(styling) .appendTo("head"); } }; /** * Override valueOf so that we get the desired behavior of getting the jQuery object when we access an object * directly. * @returns {Object} jQuery object. * @example * $("#toolbox").append(new ToolboxControlSection(html, "myfamily-control-section", "myfamily-control").mergeWithFamily(); */ self.prototype.valueOf = function () { return this.jQueryObj; }; }()); /** * Portals-in-Polygon IITC plugin. The plugin and its members can be accessed via * `window.plugin.portalsinpolygons`. * @module {function} "window.plugin.portalsinpolygons" */ /** * Closure function for Portals-in-Polygon. * <p> * Standard IITC wrapper pattern used to create the plugin's closure when * "installed" using `document.createElement("script".appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');'));` * @param {PluginInfo} plugin_info Plugin information object provided the standard IITC PLUGINEND code. */ ;function wrapper(plugin_info) { "use strict";// ensure plugin framework is there, even if iitc is not yet loaded if(typeof window.plugin !== 'function') window.plugin = function() {}; //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) plugin_info.buildName = 'wise'; plugin_info.dateTimeVersion = '20170226.80142'; plugin_info.pluginId = 'wise-portalsinpolygons'; //END PLUGIN AUTHORS NOTE // PLUGIN START /////////////////////////////////////////////////////////////// window.plugin.portalsinpolygons = function () {}; /** * Portals-in-Polygon namespace. `self` is set to `window.plugin.portalsinpolygons`. * @alias "window.plugin.portalsinpolygons" * @variation 2 */ var self = window.plugin.portalsinpolygons; self.spacename = "portalsinpolygons"; // Configuration self.title = "Portals-in-Polygon"; self.version = ""; /** * An array of objects describing the required plugins. * @type {RequiredPluginMetaData[]} Array of required plugin meta-data. */ self.requiredPlugins = [{ pluginKey: "drawTools", name: "draw tools" }, { pluginKey: "portalslist", name: "show list of portals" }]; /** * A assoicative array of layer chooser names. * Used when calling `window.isLayerGroupDisplayed(<String> name)`. * @example * window.isLayerGroupDisplayed(self.layerChooserName[portal.options.data.level]) * @type {Object.<string, string>} */ self.layerChooserName = { 0: "Unclaimed/Placeholder Portals", 1: "Level 1 Portals", 2: "Level 2 Portals", 3: "Level 3 Portals", 4: "Level 4 Portals", 5: "Level 5 Portals", 6: "Level 6 Portals", 7: "Level 7 Portals", 8: "Level 8 Portals", Resistance: "Resistance", Enlightened: "Enlightened", Neutral: "Unclaimed/Placeholder Portals" }; /** * Returns a list of portals contained in the geodesic polygon. An optional * associative array of portals (like window.portals) can be provided to * restict the base list (e.g., to find if bookmarked portals are in the * polygoon). * @param {Object<string, Object>} [portals] Optional. An associative array * of IITC portal objects keyed by the portal guid. * Defaults to `window.portals`. * @member external:L.Polyline.portalsIn * @returns {Object<string, Object>} An associative array of portal * objects in the polygon. */ L.Polyline.prototype.portalsIn = function (portals) { var fname = "L.Polynline.prototype.portalsIn"; var containedPortals, keys, rectangularBounds, point, portal, wn; console.log (fname + ": Start"); portals =(typeof portals === "undefined") ? window.portals : portals; keys = Object.keys(portals); rectangularBounds = this.getBounds(); containedPortals = new Map(); console.log ("---"); console.log(["Index","Title","GUID","Lng(X)","Lat(Y)","WindingNumber"].join(",")); for (var i = 0; i < keys.length; i++) { portal = portals[keys[i]]; point = L.latLng(portal.options.data.latE6 / 1E6, portal.options.data.lngE6 / 1E6); if (this.contains(point)) { containedPortals[portal.options.guid] = portal; console.log ([i, '"' + portal.options.data.title + '"', portal.options.guid, portal.options.data.lngE6 / 1E6, portal.options.data.latE6 / 1E6, wn].join(",")); } } console.log ("---"); console.log (fname + ": End"); return containedPortals; }; /** * Bring portals to the front of the draw layers so that you can click on * them after drawing a circle or polygon over the portals. * <br> * Thanks to Zaso's "Bring Portals To Front" at * <a href="http://www.giacintogarcea.com/ingress/iitc/bring-portals-to-front-by-zaso.meta.js"> Zaso Items</a>. */ self.bringPortalsToFront = function(){ window.Render.prototype.bringPortalsToFront(); // See IITC code }; /** * A getPortalsCallback function returns returns an associative array of IITC portals (typically a subset * of `window.portals`). * * @callback getPortalsCallback * @returns {Object<string,Object>} An associative array of IITC portals. * @see {@link displayPortals} * @see {@link displayContainedPortals} */ /** * Displays portals. The portals are filtered based on selections in the layer chooser. * <br> * This function is generalized version of the `window.plugin.portalslist.displayPL` function. * @param {getPortalsCallback} [getPortalsFn] Optional. An callback function that returns an associative array of IITC * portals. If the function is not provided or set to undefined, the portals in the current map bounds will be * used. * @param {String} [title="Portal List"] Optional. A title for the portal list dialog. The default is * "Portal list". */ self.displayPortals = function (getPortalsFn, title) { var fname = self.spacename + ".displayPortals"; var formattedPortals, list, msg, portals, type; // Check parameters. type = typeof getPortalsFn; if (type !== 'function') { if (type === 'undefined') { getPortalsFn = self.getPortalsInMapBounds; } else { msg = "Unexpected parameter type, '" + type + "', for function " + fname + ", parameter getPortalsFn."; throw new TypeError (msg, plugin_info.name); } } title = (typeof title === 'undefined' ? "Portal list" : title); if (!self.mapZoomHasPortals()) { console.warn("Map is zoomed too far out to get sufficient portal data (e.g., the portal name)."); list = $('<table class="noPortals"><tr><td>Please zoom to get additional portal data like the portal title.</td></tr></table>'); } else { // plugins (e.g. bookmarks) can insert fields before the standard ones - so we need to search for the 'level' column window.plugin.portalslist.sortBy = window.plugin.portalslist.fields.map(function (f) { return f.title; }).indexOf('Level'); window.plugin.portalslist.sortOrder = -1; window.plugin.portalslist.enlP = 0; window.plugin.portalslist.resP = 0; window.plugin.portalslist.neuP = 0; window.plugin.portalslist.filter = 0; // Get portals and format them for display. portals = getPortalsFn.call(this); formattedPortals = self.formattedPortalList(portals); if (formattedPortals.length > 0) { list = window.plugin.portalslist.portalTable(window.plugin.portalslist.sortBy, window.plugin.portalslist.sortOrder, window.plugin.portalslist.filter); } else { list = $('<table class="noPortals"><tr><td>Nothing to show!</td></tr></table>'); } } // Display table of portals. if (window.useAndroidPanes()) { $('<div id="portalslist" class="mobile">').append(list).appendTo(document.body); } else { dialog({ html: $('<div id="portalslist">').append(list), dialogClass: 'ui-dialog-portalslist', title: title + ': ' + window.plugin.portalslist.listPortals.length + ' ' + (window.plugin.portalslist.listPortals.length == 1 ? 'portal' : 'portals'), id: 'portalsinpolygons-list', width: 700 } ); } }; /** * Displays the portals contain in, and on the perimeter, of drawn polygons * and on any lines. */ self.displayContainedPortals = function () { self.displayPortals(self.getContainedPortals, "Portals-in-Polygons"); }; var list; self.outputAsJson = function() { //self.displayPortals(self.getContainedPortals, "outPut-as-Json"); window.plugin.portalslist.sortBy = window.plugin.portalslist.fields.map(function (f) { return f.title; }).indexOf('Level'); window.plugin.portalslist.sortOrder = -1; window.plugin.portalslist.enlP = 0; window.plugin.portalslist.resP = 0; window.plugin.portalslist.neuP = 0; window.plugin.portalslist.filter = 0; // Get portals and format them for display. portals = self.getContainedPortals.call(this); var formattedPortals = self.formattedPortalList(portals); var node = {}; node.maps = {}; node.maps.idOthers = {}; node.maps.idOthers.label = "Others"; node.maps.idOthers.state = window.plugin.portalslist.listPortals.length; node.maps.idOthers.bkmrk = {}; node.portals = {}; node.portals.idOthers = {}; node.maps.idOthers.label = "Others"; node.portals.idOthers = {}; node.portals.idOthers.bkmrk = {}; for (var idx = 0; idx < window.plugin.portalslist.listPortals.length; ++idx) { var tee = { guid: window.plugin.portalslist.listPortals[idx].sortValues[0], latlng: String(window.plugin.portalslist.listPortals[idx].portal._latlng.lat) + "," + String(window.plugin.portalslist.listPortals[idx].portal._latlng.lng), label: window.plugin.portalslist.listPortals[idx].sortValues[1], } node.portals.idOthers.bkmrk[String(tee.latlng)] = tee; } // console.log('============'); // console.log(JSON.stringify(node)); var ls = "<textarea readonly style='width:700'>" + JSON.stringify(node) + '</textarea>'; //console.log(window.plugin.portalslist.listPortals); // console.log('============'); // if (formattedPortals.length > 0) { // list = window.plugin.portalslist.portalTable(window.plugin.portalslist.sortBy, //window.plugin.portalslist.sortOrder, window.plugin.portalslist.filter); // } else { // list = $('<table class="noPortals"><tr><td>Nothing to show!</td></tr></table>'); // } // console.log(list); // console.log('============'); // console.log(typeof list); // //alert(list.stringify()); // // Display table of portals. /* if (window.useAndroidPanes()) { $('<div id="portalslist" class="mobile">').append(list).appendTo(document.body); } else { */ dialog({ html: $('<div id="portalslist">').append(ls), dialogClass: 'ui-dialog-portalslist', title: JSON + ': ' + window.plugin.portalslist.listPortals.length + ' ' + (window.plugin.portalslist.listPortals.length == 1 ? 'portal' : 'portals'), id: 'portalsinpolygons-list', width: 700 } ); // } } /** * Gets and formats the portal information that will be used in the portal list display. * <br> * This function is based on a modified version of the * `window.plugin.portalslist.getPortals` function. * @param {Object} portals An associative array of IITC portals. * @returns {Array<{portal:{Object}, values:{Array}, sortValues:{Array}>} Returns an array of * formatted portals. */ self.formattedPortalList = function (portals) { //filter : 0 = All, 1 = Neutral, 2 = Res, 3 = Enl, -x = all but x var fname = self.spacename + "formattedPortalList"; var guids, // {String[]} msg, // {String} portalList = []; guids = Object.keys(portals); console.log ("---"); console.log(["Index","Title","GUID","Lng(X)","Lat(Y)"].join(",")); guids.forEach(function (guid, i, array) { var portal = portals[guid]; console.log ([i, '"' + portal.options.data.title + '"', portal.options.guid, portal.options.data.lngE6 / 1E6, portal.options.data.latE6 / 1E6].join(",")); switch (portal.options.team) { case window.TEAM_RES: window.plugin.portalslist.resP++; break; case window.TEAM_ENL: window.plugin.portalslist.enlP++; break; default: window.plugin.portalslist.neuP++; } // cache values and DOM nodes var obj = { portal: portal, values: [], sortValues: [] }; var row = document.createElement('tr'); row.className = window.TEAM_TO_CSS[portal.options.team]; obj.row = row; var cell = row.insertCell(-1); cell.className = 'alignR'; window.plugin.portalslist.fields.forEach(function (field, i) { cell = row.insertCell(-1); var value = field.value(portal); if (typeof value === 'undefined') { value = "[unknown]"; } obj.values.push(value); obj.sortValues.push(field.sortValue && !!value ? field.sortValue(value, portal) : value); if (field.format) { field.format(cell, portal, value); } else { cell.textContent = value; } }); portalList.push(obj); }); console.log ("---"); window.plugin.portalslist.listPortals = portalList; return portalList; }; /** * Returns an array of IITC portals contained in the polygons and circles * drawn on the map.<br> * Checks for layers of type L.Polygon, which includes L.GeodesicPolygon * and L.GeodesicCircle, and L.Polyline, which in L.GeodesicPolyline. * @param {(keepPortalCallback|true|false)} [keepPortalFn = window.plugin.portalsinpolygons.isPortalDisplayed] If a * callback function is * provided, it will be called and passed the IITC portal object. If keepPortalFn is not a function and is set to * something falsy, the portals will not be filtered. If keepPortalCallback is not provided, explicitly * undefined, or something truthy, then the default filtering will be * performed which is to filter portals based on the layer group selections of "Unclaimed Portals", * "Level 1 Portals" to "Level 8 Portals", "Enlightened" and "Resistance". * @returns {Object} A collection of IITC portals. */ self.getContainedPortals = function(keepPortalFn) { var fname = self.spacename + ".getContainedPortals"; console.log (fname + ": Start"); var enclosures, enclosureData, data = [], layers, // Leaflet Layer[] type, portals; // Check parameter type = typeof keepPortalFn; if (type !== 'function') { if (type === 'undefined') { keepPortalFn = self.isPortalDisplayed; } else if (!keepPortalFn) { keepPortalFn = function (portal) {return true;}; } else { keepPortalFn = self.isPortalDisplayed; } } // Loop through all map elements looking for polygons and circles (and in the future polylines). layers = window.plugin.drawTools.drawnItems.getLayers(); console.log(fname + ": layers.length=" + layers.length); enclosures = layers.filter(function(layer, i, array) { return (layer instanceof L.Polygon || layer instanceof L.Polyline); }); portals = enclosures.reduce(function(collectedPortals, layer, currentIndex, array){ var morePortals; var desc = [currentIndex, self.getLayerClassName(layer)]; // start debug var latLngs = layer.getLatLngs(); latLngs.forEach(function(latLng, i, array) { data.push([].concat(desc).concat([latLng.lng, latLng.lat])); }); // end debug morePortals = (typeof layer.portalsIn === 'function') ? layer.portalsIn() : {}; //return collectedPortals.concat(morePortals); return jQuery.extend(collectedPortals, morePortals); }, Object.create(null)); // start debug console.log(fname + ": Number of enclosures: " + enclosures.length); console.log ("---"); console.log(["LayerNumber","LayerClassName","Lng(X)","Lat(Y)"].join(",")); data.forEach(function(elem) { console.log (elem.join(",")); }); console.log ("---"); // end debug // Filter out unwanted portals portals = Object.keys(portals).reduce(function(collectedPortals, guid, currentIndex, array) { var portal = portals[guid]; if (keepPortalFn(portal)) { collectedPortals[portal.options.guid] = portal; } return collectedPortals; }, Object.create(null)); console.log (fname + ": End"); return portals; }; /** * Returns a string representation of the layer class (e.g., "L.GeodesicPolygon" and "L.Marker"). * @param {L.Layer} layer An object whose class extends L.Layer. * @returns {String} A string representation of the layer class. */ self.getLayerClassName = function getLayerClassName (layer) { if (layer instanceof L.GeodesicCircle) { return "L.GeodesicCircle"; } else if (layer instanceof L.GeodesicPolygon) { return "L.GeodesicPolygon"; } else if (layer instanceof L.GeodesicPolyline) { return "L.GeodesicPolyline"; } else if (layer instanceof L.Circle) { return "L.Circle"; } else if (layer instanceof L.Marker) { return "L.Marker"; } else if (layer instanceof L.Polygon) { return "L.Polygon"; } else if (layer instanceof L.Polyline) { return "L.Polyline"; } else { return "New or Unknown Layer Type"; } }; /** * Returns a set of guids belonging to the portals filtered by the layer group selections of * "Unclaimed Portals", "Level 1 Portals" to "Level 8 Portals", "Enlightened" and "Resistance". * @param {Object} portals An associative array of IITC portal objects. * @returns {string[]} An array of portal guids. */ self.getPortalGuidsFilteredByLayerGroup = function (portals) { var guids; guids = Object.keys(portals); guids = guids.filter(function (guid, i, array) { var keep; var portal = portals[guid]; keep = (window.isLayerGroupDisplayed(self.layerChooserName[portal.options.data.level]) && ((portal.options.data.team === "R" && window.isLayerGroupDisplayed(self.layerChooserName.Resistance)) || (portal.options.data.team === "E" && window.isLayerGroupDisplayed(self.layerChooserName.Enlightened)) || (portal.options.data.team === "N" && window.isLayerGroupDisplayed(self.layerChooserName.Neutral)))); return keep; }); return guids; }; /** * A keepPortalCallback function returns true if the the provided portal passes the test implemented by the * callback function. The callback is used to determine if the portal should be displayed in the list of portals. * * @callback keepPortalCallback * @param {Object} portal An IITC portal object. * @returns {boolean} True if the portal should be kept. False if the portal should be ignored. * @see {@link getPortalsInMapBounds} */ /** * Returns the portals within the displayed map boundaries. * @param {(keepPortalCallback|true|false)} [keepPortalFn = self.isPortalDisplayed] If a callback function is * provided, it will be called and passed the IITC portal object. If keepPortalFn is not a function and is set to * something falsy, the portals will not be filtered. If keepPortalCallback is not provided, explicitly * undefined, or something truthy, then the default filtering will be * performed which is to filter portals based on the layer group selections of "Unclaimed Portals", * "Level 1 Portals" to "Level 8 Portals", "Enlightened" and "Resistance". * @returns {Object} An associative array of IITC portal objects (a subset of `window.portals`). */ self.getPortalsInMapBounds = function (keepPortalFn) { var displayBounds, type, boundedPortals; // Check parameter type = typeof keepPortalFn; if (type !== 'function') { if (type === 'undefined') { keepPortalFn = self.isPortalDisplayed; } else if (!keepPortalFn) { keepPortalFn = function (portal) {return true;}; } else { keepPortalFn = self.isPortalDisplayed; } } displayBounds = window.map.getBounds(); // the bounds could contain larger than life lat and lngs if zoomed out far. displayBounds.getSouthWest().wrap(); displayBounds.getNorthEast().wrap(); boundedPortals = Object.keys(window.portals).reduce(function (collectedPortals, guid, currentIndex, array) { var portal; portal = window.portals[guid]; // var exp = {latLng: portal.getLatLng(), contains: displayBounds.contains(portal.getLatLng()), keep:keepPortalFn(portal)}; // console.log("exp="+JSON.stringify(exp)); if (displayBounds.contains(portal.getLatLng()) && keepPortalFn(portal)) { collectedPortals[guid] = portal; } return collectedPortals; }, Object.create(null)); return boundedPortals; }; /** * Returns the DOM elements containing the plugin controls to be appended to the IITC toolbox. * <br> * Controls from other plugins with class "wise-toolbox-control" or "wise-toolbox-control-section" will be grouped * into one subsection (same div tag). * @returns {Object} DOM elements. */ self.getToolboxControls = function () { var controlsHtml, pluginControl, portalsToFrontControl, displayPortalsControl, listPortalsInPolygonControl,outputAsJson; portalsToFrontControl = '<span style="white-space:nowrap"><a id="portalsinpolygons-portalsToFront" onclick="window.plugin.portalsinpolygons.bringPortalsToFront();false;" title="Bring portals to the front draw layer so that you can click on them after drawing a circle or polygon over them.">Portals To Front</a></span>'; listPortalsInPolygonControl = '<span style="white-space:nowrap"><a id="portalsinpolygons-portalsInPolygons" onclick="window.plugin.portalsinpolygons.displayContainedPortals();false;" title="Display a list of portals in polygons, circles, and on lines. Use the layer group check boxes to filter the portals.">Portals in Polygons</a></span>'; displayPortalsControl = '<span style="white-space:nowrap"><a id="portalsinpolygons-portalsOnMap" onclick="window.plugin.portalsinpolygons.displayPortals();false;" title="Display a list of portals.">Portals on Map</a></span>'; outputAsJson = '<span style="white-space:nowrap"><a id="portalsinpolygons-outputAsJson" onclick="window.plugin.portalsinpolygons.outputAsJson();false;" title="Output Portals details in Json format">Output as Json</a></span>'; controlsHtml = listPortalsInPolygonControl + ' ● ' + displayPortalsControl + ' ● ' + portalsToFrontControl + ' ● ' + outputAsJson; pluginControl = new window.helper.ToolboxControlSection(controlsHtml, "wise-toolbox-control-section", "wise-toolbox-control"); pluginControl.attr("id", self.spacename + ".controls"); pluginControl.setStyle(); pluginControl = pluginControl.mergeWithFamily(); return pluginControl; }; /** * Returns the portal if it is displayed based on the the layer group selections of "Unclaimed Portals", * "Level 1 Portals" to "Level 8 Portals", "Enlightened" and "Resistance". Returns null if it is not * displayed. * @param {Object} portal An IITC portal object. * @returns {(Object|null)} The IITC portal object or null. */ self.isPortalDisplayed = function (portal) { var keep; keep = (window.isLayerGroupDisplayed(self.layerChooserName[portal.options.level]) && ((portal.options.data.team === "R" && window.isLayerGroupDisplayed(self.layerChooserName.Resistance)) || (portal.options.data.team === "E" && window.isLayerGroupDisplayed(self.layerChooserName.Enlightened)) || (portal.options.data.team === "N" && window.isLayerGroupDisplayed(self.layerChooserName.Neutral)))); return keep; }; /** * Checks if there is sufficient portal data for the current map zoom. When the zoom is set very far, * `window.portals` will only contain placeholder data and may not contain the portal title and other * information. * @todo it might be easier to check if one of the portals has the data your are looking for (e.g., check if portal.options.data.title exists). * @returns {boolean} True if there is sufficient portal data; otherwise, returns false. */ self.mapZoomHasPortals = function() { var zoom = map.getZoom(); zoom = getDataZoomForMapZoom(zoom); var tileParams = getMapZoomTileParameters(zoom); return tileParams.hasPortals; }; /** * Setup function called by IITC. */ self.setup = function init() { var fname = self.spacename + ".setup"; var controls; self.version = (!!plugin_info ? plugin_info.script.version : "unknown"); console.log (fname + ": Start, version " + self.version); if (!window.helper.requiredPlugins.alertIfNotInstalled(self.requiredPlugins, plugin_info.script.name)) { return; } // Standard sytling for "wise" family of toolbox controls $("<style>") .prop("type", "text/css") .html("div.wise-toolbox-control-section {color:#00C5FF;text-align:center;width:fit-content;border-top: 1px solid #20A8B1;border-bottom: 1px solid #20A8B1;}") .appendTo("head"); // Add controls to IITC right hand side toolbox. controls = self.getToolboxControls(); $("#toolbox").append(controls); // Delete setup function so that it is not run again. console.log (fname + ": End"); delete self.setup; }; /* * Set the required setup function that is called or handled by PLUGINEND * code provided IITC build script. The function will be called if IITC is * already loaded and, if not, saved for later execution. */ var setup = self.setup; // PLUGIN END ///////////////////////////////////////////////////////////////// setup.info = plugin_info; //add the script info data to the function as a property if(!window.bootPlugins) window.bootPlugins = []; window.bootPlugins.push(setup); // if IITC has already booted, immediately run the 'setup' function if(window.iitcLoaded && typeof setup === 'function') setup(); } // wrapper end // inject code into site 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);