// ==UserScript== // @author DanielOnDiordna // @name Unique Portal History // @category Layer // @version 2.1.0.20220711.235400 // @updateURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/DanielOnDiordna/uniqueportalhistory.meta.js // @downloadURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/DanielOnDiordna/uniqueportalhistory.user.js // @description [danielondiordna-2.1.0.20220711.235400] Show your personal unique portal history for Visited, Captured or Scout Controlled portals with layers. Choose your own colors. Place bookmarks. Invert results! Add three extra Portals List plugin columns. Requires CORE subscription. // @id uniqueportalhistory@DanielOnDiordna // @namespace https://softspot.nl/ingress/ // @depends portalhistorysupport@DanielOnDiordna // @antiFeatures scraper // @match https://intel.ingress.com/* // @grant none // ==/UserScript== function wrapper(plugin_info) { // ensure plugin framework is there, even if iitc is not yet loaded if(typeof window.plugin !== 'function') window.plugin = function() {}; // use own namespace for plugin window.plugin.uniqueportalhistory = function() {}; var self = window.plugin.uniqueportalhistory; self.id = 'uniqueportalhistory'; self.title = 'Unique Portal History'; self.version = '2.1.0.20220711.235400'; self.author = 'DanielOnDiordna'; self.changelog = ` Changelog: version 2.1.0.20220711.235400 - made compatible with IITC-CE Beta 0.32.1.20211217.151857 version 2.0.1.20210724.002500 - prevent double plugin setup on hook iitcLoaded version 2.0.1.20210517.233500 - added bookmarks anywhere for cached history portals (if plugin portalhistorysupport is installed) version 2.0.0.20210313.210300 - removed all IITC core injection functions (moved to separate and required Portal History Support plugin) - removed history storage, this is now cached and returned from Portal History Support plugin - removed highlighter functions (moved to separate optional History Highlighter plugin) - removed getPortalHistoryDetails function - replace variable scanned with scoutControlled - added rescaling markers when zooming in or out version 1.0.1.20210222.233700 - rewritten some IITC core injection function - added decodeArray.portalDetail rewrite, to support new history bitarray - fixed all portals zoom level detection version 1.0.0.20210220.134700 - renamed plugin from Portal Agent Status to Unique Portal History - improved IITC core modifications (needed for older versions of IITC and also for latest version of IITC) - applied portalAdded code to add history if it is missing (for older versions of IITC) - added option to keep showing layers when zooming out - added caching option, which will make it possible to display markers on linked portals without zooming in after reload of IITC and show total counts - see sortable columns in Portals List plugin - removed single team color and added both teams, there are now 5 layers to choose from - added highlighters to hide all or only all captured portals, with or without ENL and RES portals version 0.0.3.20210211.164200 version 0.0.3.20210211.182800 - fixed log.log debug error on IITC-CE - added extra code injections - added same team target color setting - integrated Spectrum Colorpicker 1.8.1 plugin code, no need for the separate plugin - bookmarks menu version 0.0.2.20210209.235900 version 0.0.2.20210210.001100 - a lot of changes, a complete rewrite - added a menu to select inversion of results version 0.0.1.20210206.120400 - first release - used plugin wrapper and userscript header formatting to match IITC-CE coding `; self.namespace = 'window.plugin.' + self.id + '.'; self.pluginname = 'plugin-' + self.id; self.localstoragesettings = self.pluginname + '-settings'; self.settings = {}; self.settings.invertresults = true; self.settings.showsameteamcolor = true; self.settings.showneutralcolor = true; self.settings.color = { visited: 'purple', capturedenl: '#FF0000', capturedres: '#FF0000', capturedneutral: '#000000', scoutControlled: 'yellow' }; self.settings.replacebookmarks = true; self.settings.bookmarkscolor = 'yellow'; self.settings.showwhenzoomedout = false; self.capturedlayers = { capturedenl: window.TEAM_ENL, capturedres: window.TEAM_RES, capturedneutral: window.TEAM_NONE }; self.bookmarktimerlist = []; self.bookmarkrestorecolor = undefined; self.lastportalscale = undefined; self.markerformatting = { visited: { stroke: true, color: 'settingscolor', radius: 15.0, weight: 2, opacity: 0.5, fill: false, fillOpacity: 0.0 }, capturedenl: { stroke: true, color: 'portalcolor', radius: 'portalradius', weight: 'portalweight', opacity: 1.0, fill: true, fillOpacity: 1.0, fillColor: 'settingscolor' }, capturedres: { stroke: true, color: 'portalcolor', radius: 'portalradius', weight: 'portalweight', opacity: 1.0, fill: true, fillOpacity: 1.0, fillColor: 'settingscolor' }, capturedneutral: { stroke: true, color: 'portalcolor', radius: 'portalradius', weight: 'portalweight', opacity: 1.0, fill: true, fillOpacity: 1.0, fillColor: 'settingscolor' }, scoutControlled: { stroke: true, color: 'settingscolor', radius: 19.0, weight: 3, opacity: 1.0, fill: false, fillOpacity: 0.0 } }; self.basiclayers = { visited: 1, captured: 1, scoutControlled: 1 }; self.toggle_layernames = { visited: 'Visited', capturedenl: 'Captured ENL', capturedres: 'Captured RES', capturedneutral: 'Captured neutral', scoutControlled: 'Scout Controlled' }; self.toggle_layernamesinverted = { visited: 'Never Visited', capturedenl: 'Never Captured ENL', capturedres: 'Never Captured RES', capturedneutral: 'Never Captured neutral', scoutControlled: 'Not Scout Controlled' }; self.toggle_layers = { visited: undefined, capturedenl: undefined, capturedres: undefined, capturedneutral: undefined, scoutControlled: undefined, }; self.layers = { visited: undefined, capturedenl: undefined, capturedres: undefined, capturedneutral: undefined, scoutControlled: undefined }; self.layermarkers = { visited: {}, capturedenl: {}, capturedres: {}, capturedneutral: {}, scoutControlled: {} }; self.colorpickeroptions = { flat: false, showInput: true, showButtons: true, showPalette: true, showSelectionPalette: true, allowEmpty: false, hideAfterPaletteSelect: true, palette: [ ['#004000','#008000','#00C000'], ['#00FF00','#80FF80','#C0FFC0'], ['#000040','#000080','#0000C0','rgb(254, 254, 51)'], ['#4040FF','#8080FF','#C0C0FF','#0000FF'], ['#6A3400','#964A00','#C05F00','#00FFFF'], ['#E27000','#FF8309','#FFC287','#FF0000'], ['#a24ac3','#514ac3','#4aa8c3','#51c34a'], ['#c1c34a','#c38a4a','#c34a4a','#c34a6f'], ['#000000','#666666','#bbbbbb','#ffffff'] ]}; self.requestlist = []; self.requestguid = undefined; self.requestlisttimer = 0; self.requestlisttimeout = 0; self.requestdelay = 100; self.requestbookmarkfoldername = ''; self.restoresettings = function() { if (typeof localStorage[self.localstoragesettings] != 'string' || localStorage[self.localstoragesettings] == '') return; try { var settings = JSON.parse(localStorage[self.localstoragesettings]); if (typeof settings === 'object' && settings instanceof Object && !(settings instanceof Array)) { // expect an object for (const i in self.settings) { if (i in settings && typeof settings[i] === typeof self.settings[i]) { // only accept settings from default template of same type if (typeof self.settings[i] === 'object' && self.settings[i] instanceof Object && !(self.settings[i] instanceof Array)) { // 1 sublevel is supported for (const a in self.settings[i]) { if (a in settings[i] && typeof settings[i][a] === typeof self.settings[i][a]) { self.settings[i][a] = settings[i][a]; } } } else { self.settings[i] = settings[i]; } } } } } catch(e) { return false; } }; self.storesettings = function() { localStorage[self.localstoragesettings] = JSON.stringify(self.settings); }; self.moveHistoryToNewPlugin = function() { let localstoragehistory = self.pluginname + '-history'; if (typeof localStorage[localstoragehistory] == 'undefined' || !window.plugin.portalhistorysupport) return; // no old values found, or required plugin not found if (typeof localStorage[localstoragehistory] == 'string' && localStorage[localstoragehistory] != '') { try { var storagehistoryraw = JSON.parse(localStorage[localstoragehistory]); if (typeof storagehistoryraw == 'object' && storagehistoryraw instanceof Object && !(storagehistoryraw instanceof Array)) { for (const guid in storagehistoryraw) { // convert history data to new plugin cache if (storagehistoryraw[guid] > 0) window.plugin.portalhistorysupport.addcache(guid,storagehistoryraw[guid]); } } delete localStorage[localstoragehistory]; // delete permanently } catch(e) { } } }; self.historySortString = function(history,layerkey,level) { if (!history) return ''; let sortstring = (history[layerkey]?layerkey[0]:' '); for (const layerkey in self.basiclayers) { sortstring += (history[layerkey]?layerkey[0]:'z'); // descending } sortstring += level; return sortstring; }; self.setupPortalsList = function() { if (!window.plugin.portalslist) return; let colpos = 0; for (colpos = 0; colpos < window.plugin.portalslist.fields.length; colpos++) { // find column Portal Name if (window.plugin.portalslist.fields[colpos].title == 'Portal Name') { break; } } if (colpos >= window.plugin.portalslist.fields.length) colpos = 0; // default first colum if column name not found // insert extra columns at colpos: for (const layerkey in self.basiclayers) { window.plugin.portalslist.fields.splice(colpos,0,{ title: layerkey[0], // first character value: function(portal) { return (portal.options.data.history && portal.options.data.history[layerkey]); }, sortValue: function(value, portal) { return self.historySortString(portal.options.data.history,layerkey,portal.options.data.level); }, format: function(cell, portal, value) { $(cell) .append($('') .html((portal.options.data.history && portal.options.data.history[layerkey]?layerkey[0]:(!portal.options.data.history || portal.options.data.history._raw == undefined?'?':''))) .attr({ "class": "value", "style": "font-family: courier", })); }, defaultOrder: -1 // descending }); colpos++; } }; self.setupLayers = function() { for (const layername in self.toggle_layers) { // use separate layer togglers and actual drawing layers, to enable show/hide layers when zooming in/out: self.toggle_layers[layername] = new L.LayerGroup(); window.addLayerGroup((self.settings.invertresults?self.toggle_layernamesinverted[layername]:self.toggle_layernames[layername]), self.toggle_layers[layername], true); // create drawing layers: self.layers[layername] = new L.FeatureGroup(); // setup initial drawing layers visibility: if (self.layeractive(layername)) self.showlayer(layername); } // toggle drawing layers: window.map.on('layeradd', function(obj) { for (const layername in self.toggle_layers) { if (obj.layer == self.toggle_layers[layername]) { self.showlayer(layername); } } if (window.layerChooser._layers[obj.layer._leaflet_id] && window.layerChooser._layers[obj.layer._leaflet_id].overlay) { // if overlay layer is activated, then bring the layers to front, a moment/event later with a timeout: window.setTimeout(self.layersbringToFront,0); } }); window.map.on('layerremove', function(obj) { for (const layername in self.toggle_layers) { if (obj.layer == self.toggle_layers[layername]) { self.hidelayer(layername); } } }); }; self.getMarkerStyle = function(portaloptions, layerkey, selected) { let portalstyle = window.getMarkerStyleOptions(portaloptions); let scaleRadius = window.portalMarkerScale(); if (selected) portalstyle.color = window.COLOR_SELECTED_PORTAL; let layername = self.gettogglelayername(layerkey,portaloptions.team); let markerformatting = self.markerformatting[layername]; var styleOptions = { radius: markerformatting.radius, stroke: markerformatting.stroke, color: markerformatting.color, weight: markerformatting.weight, opacity: markerformatting.opacity, dashArray: null, fill: markerformatting.fill, fillColor: markerformatting.fillColor, fillOpacity: markerformatting.fillOpacity, clickable: (layerkey == 'captured') }; if (typeof styleOptions.radius == 'number') styleOptions.radius = styleOptions.radius * scaleRadius; // convert values: for (const id in styleOptions) { switch (styleOptions[id]) { case 'settingscolor': styleOptions[id] = self.settings.color[layername]; break; case 'portalcolor': case 'portalradius': case 'portalweight': styleOptions[id] = portalstyle[id]; break; } } return styleOptions; }; self.createMarker = function(latlng, dataOptions, layerkey) { // dataOptions = { guid: guid, data: data.portal.options } let styleOptions = self.getMarkerStyle(dataOptions.data, layerkey, dataOptions.guid == window.selectedPortal); var options = L.extend({}, dataOptions, styleOptions); var marker = L.circleMarker(latlng, options); marker.on('click', function() { window.renderPortalDetails(dataOptions.guid); }); marker.on('dblclick', function() { window.renderPortalDetails(dataOptions.guid); window.map.setView(latlng, 17); }); return marker; }; self.gettogglelayername = function(layerkey,team) { let layername; if (layerkey == 'captured') { switch (team) { case window.TEAM_ENL: layername = layerkey + 'enl'; break; case window.TEAM_RES: layername = layerkey + 'res'; break; case window.TEAM_NONE: layername = layerkey + 'neutral'; break; } } else if (layerkey in self.toggle_layers) { layername = layerkey } return layername; }; self.zoomlevelhasportals = function() { return window.getMapZoomTileParameters(window.getDataZoomForMapZoom(window.map.getZoom())).hasPortals; }; self.onportalAdded = function(data) { // data = {portal: marker, previousData: previousData} if (!data.portal.options.data.history) return; // draw nothing if there is no history available (will never happen, but just in case) if (!self.zoomlevelhasportals() && !self.settings.showwhenzoomedout) return; let guid = data.portal.options.guid; let latlng = data.portal.getLatLng(); let dataOptions = { guid: guid, data: data.portal.options }; for (const layerkey in self.basiclayers) { //console.log('onportalAdded',layerkey,data.portal.options.data.history); if (data.portal.options.data.history && data.portal.options.data.history._raw != undefined) if ((!self.settings.invertresults && data.portal.options.data.history[layerkey]) || (self.settings.invertresults && !data.portal.options.data.history[layerkey])) { let marker = self.createMarker(latlng, dataOptions, layerkey); let layername = self.gettogglelayername(layerkey,data.portal.options.team); self.layers[layername].addLayer(marker); self.layermarkers[layername][guid] = marker; } } }; self.onportalRemoved = function(data) { // data = {portal: p, data: p.options.data } let guid = data.portal.options.guid; for (const layername in self.layers) { if (guid in self.layermarkers[layername]) { self.layers[layername].removeLayer(self.layermarkers[layername][guid]); delete self.layermarkers[layername][guid]; } } }; self.onzoomlevelschange = function() { if (self.lastportalscale == window.portalMarkerScale()) return; console.log('onzoomlevelschange: resize markers'); for (const layername in self.layers) { let layerkey; switch (layername) { case 'visited': case 'scoutControlled': layerkey = layername; break; default: layerkey = 'captured'; } for (const guid in self.layermarkers[layername]) { let styleOptions = self.getMarkerStyle(window.portals[guid].options,layerkey,guid === window.selectedPortal); self.layermarkers[layername][guid].setStyle(styleOptions); } } } self.onportalSelected = function(data) { let layerkey = 'captured'; // draw on captured layer // restore portal color for unselected captured portal marker: if (data.unselectedPortalGuid && (data.unselectedPortalGuid != data.selectedPortalGuid)) { let styleOptions = self.getMarkerStyle(window.portals[data.unselectedPortalGuid].options,layerkey,false); let layername = self.gettogglelayername(layerkey,window.portals[data.unselectedPortalGuid].options.team); if (self.layermarkers[layername][data.unselectedPortalGuid]) self.layermarkers[layername][data.unselectedPortalGuid].setStyle(styleOptions); } // apply portal selected color to captured portal marker: if (data.selectedPortalGuid) { let styleOptions = self.getMarkerStyle(window.portals[data.selectedPortalGuid].options,layerkey,true); let layername = self.gettogglelayername(layerkey,window.portals[data.selectedPortalGuid].options.team); if (self.layermarkers[layername][data.selectedPortalGuid]) self.layermarkers[layername][data.selectedPortalGuid].setStyle(styleOptions); } }; self.layersbringToFront = function() { for (const layername in self.layers) { if (self.layeractive(layername) && self.layers[layername]._map) { // only if layer is visible // console.log('layersbringToFront',layername,self.layers[layername]); self.layers[layername].bringToFront(); } } }; self.showlayer = function(layername) { // only show layer if map is at zoom level all portals if (((!self.settings.showwhenzoomedout && self.zoomlevelhasportals()) || self.settings.showwhenzoomedout) && self.layers[layername] && !self.layers[layername]._map) window.map.addLayer(self.layers[layername]); //self.layersbringToFront(); self.updatemenu(); }; self.hidelayer = function(layername) { if (self.layers[layername] && self.layers[layername]._map) window.map.removeLayer(self.layers[layername]); self.updatemenu(); }; self.displaytogglelayer = function(layername,display) { if (display && !map.hasLayer(self.toggle_layers[layername])) window.map.addLayer(self.toggle_layers[layername]); else if (!display && window.map.hasLayer(self.toggle_layers[layername])) window.map.removeLayer(self.toggle_layers[layername]); }; self.layeractive = function(findlayername) { var overlayLayers = window.layerChooser.getLayers().overlayLayers; for (let cnt = overlayLayers.length -1; cnt >= 0; cnt--) { let layername = overlayLayers[cnt].name; // compare with self.toggle_layernames and self.toggle_layernamesinverted if (layername == self.toggle_layernames[findlayername] || layername == self.toggle_layernamesinverted[findlayername]) { //console.log('layeractive',findlayername,layername,layers[cnt].active); return overlayLayers[cnt].active; } } return false; }; self.updateMarkers = function(layername) { let markerformatting = self.markerformatting[layername]; let styleOptions = {}; for (const id in markerformatting) { if (markerformatting[id] == 'settingscolor') styleOptions[id] = self.settings.color[layername]; } for (const guid in self.layermarkers[layername]) { self.layermarkers[layername][guid].setStyle(styleOptions); } }; self.invertresults = function() { // replace layer choosers with new names: for (const layername in self.toggle_layernames) { // copy current layer visibility status, to force same initial status when creating the new layer: let oldlayername = (!self.settings.invertresults?self.toggle_layernamesinverted[layername]:self.toggle_layernames[layername]); let enabled = window.isLayerGroupDisplayed(oldlayername); let newlayername = (self.settings.invertresults?self.toggle_layernamesinverted[layername]:self.toggle_layernames[layername]); if (window.isLayerGroupDisplayed(newlayername) != enabled) { if (typeof window.updateDisplayedLayerGroup == "function") { // IITC 0.32.1 Release window.updateDisplayedLayerGroup(newlayername,enabled); // force start status } else if (typeof window.layerChooser._storeOverlayState == "function") { // IITC 0.32.1 Beta window.layerChooser._storeOverlayState(newlayername,enabled); // force start status } } window.removeLayerGroup(self.toggle_layers[layername]); window.addLayerGroup((self.settings.invertresults?self.toggle_layernamesinverted[layername]:self.toggle_layernames[layername]), self.toggle_layers[layername], enabled); // remove all markers: self.layers[layername].clearLayers(); self.layermarkers[layername] = {}; } // draw all new markers: for (const guid in window.portals) { let data = {portal: window.portals[guid], previousData: undefined}; self.onportalAdded(data); } }; self.onzoomend = function() { let ZOOM_LEVEL_ALL_PORTALS = self.zoomlevelhasportals(); for (const layername in self.layers) { if (!ZOOM_LEVEL_ALL_PORTALS && !self.settings.showwhenzoomedout) // hide layers window.map.removeLayer(self.layers[layername]); else if (self.layeractive(layername)) // show if enabled window.map.addLayer(self.layers[layername]); } }; self.about = function() { let html = '
' + 'Thank you for choosing this plugin.
' + '
' + 'You can visualize your unique history with 5 toggle layers:
' + '- Visited: draw a small circle around the portal.
' + '- Captured: draw a filled portal, with a separate layer for ENL, RES and Neutral portals.
' + '- Scout Controlled: a larger circle around the portal.
' + '
' + 'You can toggle these 5 layers individually from the menu or on the layer selector.
' + 'You can also choose your own colors for every layer.
' + 'You can invert the results. The layer selector names will change accordingly to Never Visited, Never Captured (ENL, RES and Neutral) and Not Scout Controlled.
' + 'When zooming out to "link" levels, the layers will be hidden, unless Show markers when zooming out is enabled.
' + '
' + 'The Portals List plugin (if enabled) will show extra columns for visit, capture and scout controlled (v c s columns) and can be sorted.
' + '
' + 'You can draw (and remove) Bookmarks (if plugin is enabled) for groups of portals which are never visited or captured. Bookmarks are automatically created in named folders. With the Bookmarks add-on you can draw colored bookmarks.
' + 'Also an option to add bookmarks for portals anywhere for cached history portals (if plugin portalhistorysupport is installed).
' + '' + self.title + ' version ' + self.version + ' by ' + self.author + '' + '
'; window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-' + self.pluginname, width: 'auto', title: self.title + ' - About' }).dialog('option', 'buttons', { '< Main menu': function() { self.menu(); }, 'Changelog': function() { alert(self.changelog); }, 'Close': function() { $(this).dialog('close'); }, }); }; self.clearbookmarktimerlist = function() { for (let cnt = self.bookmarktimerlist.length - 1; cnt >= 0; cnt--) { window.clearTimeout(self.bookmarktimerlist[cnt]); } self.bookmarktimerlist = []; self.requestlist = []; clearTimeout(self.requestlisttimer); self.requestlisttimer = 0; clearTimeout(self.requestlisttimeout); self.requestlisttimeout = 0; self.requestbookmarkfoldername = ''; if (self.bookmarkrestorecolor) window.plugin.bookmarksAddon.settings.color = self.bookmarkrestorecolor; self.bookmarkrestorecolor = undefined; $('#dialog-' + self.pluginname + '-dialog-bookmarks').dialog('close'); }; self.createNewBookmarkFolder = function(foldername) { for (const ID in window.plugin.bookmarks.bkmrksObj.portals) { if (window.plugin.bookmarks.bkmrksObj.portals[ID].label == foldername) // folder already exists return ID; } let ID = window.plugin.bookmarks.generateID(); window.plugin.bookmarks.bkmrksObj.portals[ID] = {"label":foldername,"state":1,"bkmrk":{}}; window.plugin.bookmarks.saveStorage(); window.plugin.bookmarks.refreshBkmrks(); window.runHooks('pluginBkmrksEdit', {"target": 'portals', "action": "add", "id": ID}); return ID; }; self.createOrMoveBookMarkInFolder = function(portalguid,foldername) { if (!(portalguid in window.portals) || !window.portals[portalguid].options.data.title) { console.log("portal details missing",portalguid); return; } if (!foldername) foldername = "Others"; // default // find existing bookmark and folder ids: let bookmarkid, oldfolderid, newfolderid; for (const ID in window.plugin.bookmarks.bkmrksObj.portals) { if (window.plugin.bookmarks.bkmrksObj.portals[ID].label == foldername) newfolderid = ID; for (const bkmrkID in window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk) { if (window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk[bkmrkID].guid == portalguid) { oldfolderid = ID; bookmarkid = bkmrkID; } } } if (!newfolderid) newfolderid = self.createNewBookmarkFolder(foldername); // create new folder if (!bookmarkid) { // create new bookmark let portal = window.portals[portalguid]; let latlng = portal.getLatLng(); let label = portal.options.data.title; window.plugin.bookmarks.addPortalBookmark(portalguid,latlng.lat + ',' + latlng.lng,label); // default added to idOthers let ID = window.plugin.bookmarks.KEY_OTHER_BKMRK; for (const bkmrkID in window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk) { if (window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk[bkmrkID].guid == portalguid) { oldfolderid = ID; bookmarkid = bkmrkID; } } } if (newfolderid != oldfolderid) { // move when needed var Bkmrk = window.plugin.bookmarks.bkmrksObj.portals[oldfolderid].bkmrk[bookmarkid]; delete window.plugin.bookmarks.bkmrksObj.portals[oldfolderid].bkmrk[bookmarkid]; window.plugin.bookmarks.bkmrksObj.portals[newfolderid].bkmrk[bookmarkid] = Bkmrk; window.plugin.bookmarks.saveStorage(); window.plugin.bookmarks.refreshBkmrks(); window.runHooks('pluginBkmrksEdit', {"target": "bookmarks", "action": "sort"}); } }; self.addbookmarkanywhererequestnext = function() { self.requestguid = self.requestlist.shift(); $('#' + self.id + '_count_addbookmarks').html(parseInt($('#' + self.id + '_count_addbookmarks').text()) + 1); self.requestlisttimer = setTimeout(function() { let guid = self.requestguid; self.requestlisttimeout = setTimeout(function() { // retry self.requestlisttimeout = 0; console.log("request timeout",guid); self.requestlist.push(guid); self.addbookmarkanywhererequestnext(); },1000); // timeout window.portalDetail.request(guid); },self.requestdelay); }; self.addbookmarkanywhererequestloaded = function(data) { let guid = data.guid; if (self.requestguid != guid) return; self.requestguid = undefined; clearTimeout(self.requestlisttimeout); self.requestlisttimeout = 0; if (data.success) { self.createOrMoveBookMarkInFolder(guid,self.requestbookmarkfoldername); } else { // retry console.log("load portal details failure",data); self.requestlist.push(guid); } if (self.requestlist.length == 0) { console.log("all requests finished"); self.clearbookmarktimerlist(); } else { self.addbookmarkanywhererequestnext(); } }; self.addbookmarksanywherecached = function(bookmarkgroup,never) { if (!window.plugin.portalhistorysupport) return; if (Object.keys(self.requestlist).length > 0) return; // busy let bookmarkportalguids = self.getBookmarkPortalGuids(); let addbookmarks = []; let addbookmarkrequests = []; let bookmarkfoldername = (never?'Never ':'') + bookmarkgroup; for (const guid in window.plugin.portalhistorysupport.cache[window.PLAYER.nickname]) { let bitarray = window.plugin.portalhistorysupport.cache[window.PLAYER.nickname][guid]; let history = window.plugin.portalhistorysupport.decodeHistory(bitarray); if ((bookmarkgroup == "Visited but never Captured" && history.visited && !history.captured) || (!never && history[bookmarkgroup]) || (never && !history[bookmarkgroup])) { if (guid in bookmarkportalguids && self.settings.replacebookmarks) { // bookmark for portal exists and is visible window.plugin.bookmarks.switchStarPortal(guid); // remove addbookmarks.push(guid); } else if (!(guid in bookmarkportalguids)) { // no bookmark for portal exists if (guid in window.portals && window.portals[guid].options.data.title) { // portal already loaded addbookmarks.push(guid); } else { addbookmarkrequests.push(guid); } } } } self.clearbookmarktimerlist(); if ((addbookmarks.length + addbookmarkrequests.length) > 20) // only show a dialog when drawing bookmarks takes a long time window.dialog({ html: '
Adding 0/' + (addbookmarks.length + addbookmarkrequests.length) + ' bookmarks. Please be patient...
' + bookmarkfoldername + '
', id: self.pluginname + '-dialog-bookmarks', dialogClass: 'ui-dialog-' + self.pluginname, title: self.title + ' - Bookmarks', width: 'auto', closeCallback: function() { self.clearbookmarktimerlist(); } }).dialog('option', 'buttons', { 'Cancel': function() { $(this).dialog('close'); }, }); if (window.plugin.bookmarksAddon) { self.bookmarkrestorecolor = window.plugin.bookmarksAddon.settings.color; window.plugin.bookmarksAddon.settings.color = self.settings.bookmarkscolor; } for (let cnt = 0; cnt < addbookmarks.length; cnt++) { let guid = addbookmarks[cnt]; self.bookmarktimerlist.push(window.setTimeout(function() { // use a timeout to make sure the scanner does not appear to hang $('#' + self.id + '_count_addbookmarks').html(parseInt($('#' + self.id + '_count_addbookmarks').text()) + 1); self.createOrMoveBookMarkInFolder(guid,bookmarkfoldername); if (cnt + 1 >= addbookmarks.length) { if (addbookmarkrequests.length == 0) { self.clearbookmarktimerlist(); } else { self.requestlist = addbookmarkrequests; self.addbookmarkanywhererequestnext(); } } },0)); } if (addbookmarks.length == 0) { if (addbookmarkrequests.length == 0) { self.clearbookmarktimerlist(); } else { self.requestlist = addbookmarkrequests; self.addbookmarkanywhererequestnext(); } } }; self.addbookmarksvisible = function(bookmarkgroup,never) { let bookmarkportalguids = self.getBookmarkPortalGuids(); let displayBounds = window.map.getBounds(); let addbookmarks = []; let bookmarkfoldername = (never?'Never ':'') + bookmarkgroup; for (const guid in window.portals) { let portal = window.portals[guid]; if (displayBounds.contains(portal.getLatLng())) { if (portal.options.data.history && portal.options.data.history._raw != undefined) { let addbookmark = false; switch (bookmarkgroup) { case 'visited': case 'captured': case 'scoutControlled': addbookmark = portal.options.data.history[bookmarkgroup]; if (never) addbookmark = !addbookmark; break; case 'capturedenl': if (window.portals[guid].options.team == window.TEAM_ENL) { addbookmark = portal.options.data.history.captured; if (never) addbookmark = !addbookmark; } break; case 'capturedres': if (window.portals[guid].options.team == window.TEAM_RES) { addbookmark = portal.options.data.history.captured; if (never) addbookmark = !addbookmark; } break; case 'capturedneutral': if (window.portals[guid].options.team == window.TEAM_NONE) { addbookmark = portal.options.data.history.captured; if (never) addbookmark = !addbookmark; } break; } if (addbookmark) { if (guid in bookmarkportalguids && self.settings.replacebookmarks) { // bookmark for portal exists and is visible window.plugin.bookmarks.switchStarPortal(guid); // remove addbookmarks.push(guid); } else if (!(guid in bookmarkportalguids)) { // no bookmark for portal exists addbookmarks.push(guid); } } } } } self.clearbookmarktimerlist(); if (addbookmarks.length > 20) // only show a dialog when drawing bookmarks takes a long time window.dialog({ html: '
Adding /' + addbookmarks.length + ' bookmarks. Please be patient...
' + bookmarkfoldername + '
', id: self.pluginname + '-dialog-bookmarks', dialogClass: 'ui-dialog-' + self.pluginname, title: self.title + ' - Bookmarks', width: 'auto', closeCallback: function() { self.clearbookmarktimerlist(); } }).dialog('option', 'buttons', { 'Cancel': function() { $(this).dialog('close'); }, }); if (window.plugin.bookmarksAddon) { self.bookmarkrestorecolor = window.plugin.bookmarksAddon.settings.color; window.plugin.bookmarksAddon.settings.color = self.settings.bookmarkscolor; } for (let cnt = 0; cnt < addbookmarks.length; cnt++) { let guid = addbookmarks[cnt]; self.bookmarktimerlist.push(window.setTimeout(function() { // use a timeout to make sure the scanner does not appear to hang $('#' + self.id + '_count_addbookmarks').html(cnt + 1); self.createOrMoveBookMarkInFolder(guid,bookmarkfoldername); if (cnt + 1 >= addbookmarks.length) { self.clearbookmarktimerlist(); } },0)); } }; self.removebookmarksvisible = function(bookmarkgroup) { let bookmarkportalguids = self.getBookmarkPortalGuids(); let displayBounds = window.map.getBounds(); for (const guid in bookmarkportalguids) { switch (bookmarkgroup) { case 'all': if (bookmarkportalguids[guid] == true) // bookmark for portal exists and is visible window.plugin.bookmarks.switchStarPortal(guid); // remove break; case 'visited': case 'captured': if (bookmarkportalguids[guid] == true) { // bookmark for portal exists and is visible let portal = window.portals[guid]; if (portal.options.data.history && portal.options.data.history._raw != undefined) { if (portal.options.data.history[bookmarkgroup]) window.plugin.bookmarks.switchStarPortal(guid); // remove } } break; } } }; self.removeallbookmarks = function() { let bookmarkportalguids = self.getBookmarkPortalGuids(); for (const guid in bookmarkportalguids) { window.plugin.bookmarks.switchStarPortal(guid); // remove } }; self.bookmarks = function() { let html = '
If you install and enable the Bookmarks plugin, you can auto create bookmarks for your ' + self.title + '.
'; if (window.plugin.bookmarks) { html = '
' + (!window.plugin.bookmarksAddon?'':' Bookmarks color
') + '' + '
' + '
' + 'Draw bookmarks on visible portals:
' + 'Never Visited ()' + 'Never Captured ()' + 'Never Captured ENL ()' + 'Never Captured RES ()' + 'Never Captured Neutral ()' + 'Not Scout Controlled ()' + (window.plugin.portalhistorysupport ? 'Draw bookmarks for cached portals:
' + 'Visited ()' + 'Captured ()' + 'Visited but never Captured ()' + 'Scout Controlled ()' : '') + 'Remove bookmarks for visible portals:
' + 'Remove Visited bookmarks ()' + 'Remove Captured bookmarks ()' + 'Remove all visible bookmarks ()' + 'Remove ALL bookmarks ()' + '
'; } window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-' + self.pluginname, title: self.title + ' - Bookmarks', width: 'auto' }).dialog('option', 'buttons', { '< Main menu': function() { self.menu(); }, 'Bookmarks plugin': function() { if (window.plugin.bookmarks) window.plugin.bookmarks.manualOpt(); }, 'Close': function() { $(this).dialog('close'); }, }); self.updatemenu(); if (window.plugin.bookmarksAddon) { $('#' + self.id + '_colorbookmarks').spectrum($.extend({}, self.colorpickeroptions, { change: function(color) { self.settings.bookmarkscolor = color.toHexString(); self.storesettings(); }, color: self.settings.bookmarkscolor, })); } }; self.getBookmarkPortalGuids = function() { let guidlist = {}; if (!window.plugin.bookmarks) return guidlist; let visiblebounds = window.map.getBounds(); for (const ID in window.plugin.bookmarks.bkmrksObj.portals) { for (const bkmrkid in window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk) { let latlng = JSON.parse('[' + window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk[bkmrkid].latlng + ']'); guidlist[window.plugin.bookmarks.bkmrksObj.portals[ID].bkmrk[bkmrkid].guid] = visiblebounds.contains(latlng); } } return guidlist; }; self.count = function() { let bookmarkportalguids = self.getBookmarkPortalGuids(); let displayBounds = window.map.getBounds(); let count = { total: 0, history: 0, nevervisited: 0, nevervisitedCached: 0, visited: 0, visitedCached: 0, visitednevercapturedCached: 0, nevercaptured: 0, nevercapturedCached: 0, captured: 0, capturedCached: 0, neverscoutControlled: 0, neverscoutControlledCached: 0, scoutControlled: 0, scoutControlledCached: 0, nevercapturedenl: 0, capturedenl: 0, nevercapturedres: 0, capturedres: 0, nevercapturedneutral: 0, capturedneutral: 0, bookmarks: 0, bookmarksvisible: 0, bookmarksalreadyvisited: 0, bookmarksalreadycaptured: 0 }; count.bookmarks = Object.keys(bookmarkportalguids).length; for (const guid in bookmarkportalguids) { if (bookmarkportalguids[guid]) count.bookmarksvisible++; } for (const guid in window.portals) { let portal = window.portals[guid]; if (displayBounds.contains(portal.getLatLng())) { count.total++; if (portal.options.data.history && portal.options.data.history._raw != undefined) { count.history++; if (portal.options.data.history.visited) count.visited++; else count.nevervisited++; if (portal.options.data.history.captured) count.captured++; else count.nevercaptured++; if (portal.options.data.history.scoutControlled) count.scoutControlled++; else count.neverscoutControlled++; if (portal.options.team == window.TEAM_ENL) if (portal.options.data.history.captured) count.capturedenl++; else count.nevercapturedenl++; if (portal.options.team == window.TEAM_RES) if (portal.options.data.history.captured) count.capturedres++; else count.nevercapturedres++; if (portal.options.team == window.TEAM_NONE) if (portal.options.data.history.captured) count.capturedneutral++; else count.nevercapturedneutral++; if (bookmarkportalguids[guid]) { // bookmark for portal exists and is visible if (portal.options.data.history.visited) count.bookmarksalreadyvisited++; if (portal.options.data.history.captured) count.bookmarksalreadycaptured++; } } } } if (window.plugin.portalhistorysupport) { for (const guid in window.plugin.portalhistorysupport.cache[window.PLAYER.nickname]) { let bitarray = window.plugin.portalhistorysupport.cache[window.PLAYER.nickname][guid]; let history = window.plugin.portalhistorysupport.decodeHistory(bitarray); if (history.visited) count.visitedCached++; else count.nevervisitedCached++; if (history.captured) count.capturedCached++; else count.nevercapturedCached++; if (history.visited && !history.captured) count.visitednevercapturedCached++; if (history.scoutControlled) count.scoutControlledCached++; else count.neverscoutControlledCached++; } } return count; }; self.updatemenu = function() { if (!$('#dialog-' + self.pluginname + '-dialog').length) return; for (const layername in self.toggle_layers) { $('#' + self.id + '_' + layername).text((self.settings.invertresults?self.toggle_layernamesinverted[layername]:self.toggle_layernames[layername])); $('#' + self.id + 'show' + layername + 'toggle').prop("checked",self.layeractive(layername)); } var count = self.count(); for (const id in count) { $('#' + self.id + '_count_' + id).html(count[id]); } }; self.menu = function() { var html = '
' + 'Visible portals:
' + 'With history:
'; html += '' + '
'; html += '' + '
'; for (const layername in self.toggle_layers) { html += ' ' + '' + ' ()
'; } html += 'version ' + self.version + ' by ' + self.author + '' + '
'; if (window.useAndroidPanes()) window.show('map'); // hide sidepane window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-' + self.pluginname, title: self.title, width: 'auto' }).dialog('option', 'buttons', { 'Bookmarks': function() { self.bookmarks(); }, 'About': function() { self.about(); }, 'Ok': function() { $(this).dialog('close'); }, }); for (const layername in self.toggle_layers) { $('#' + self.id + '_color' + layername).spectrum($.extend({}, self.colorpickeroptions, { change: function(color) { self.settings.color[layername] = color.toHexString(); self.storesettings(); self.updateMarkers(layername); }, color: self.settings.color[layername] })); } self.updatemenu(); }; self.setupColorpickerSpectrum = function() { // source: https://github.com/bgrins/spectrum // minified with https://www.minifier.org/ // Spectrum Colorpicker v1.8.1 // https://github.com/bgrins/spectrum // Author: Brian Grinstead // License: MIT (function(factory){"use strict";if(typeof define==='function'&&define.amd){define(['jquery'],factory)}else if(typeof exports=="object"&&typeof module=="object"){module.exports=factory(require('jquery'))}else{factory(jQuery)}})(function($,undefined){"use strict";var defaultOpts={beforeShow:noop,move:noop,change:noop,show:noop,hide:noop,color:!1,flat:!1,showInput:!1,allowEmpty:!1,showButtons:!0,clickoutFiresChange:!0,showInitial:!1,showPalette:!1,showPaletteOnly:!1,hideAfterPaletteSelect:!1,togglePaletteOnly:!1,showSelectionPalette:!0,localStorageKey:!1,appendTo:"body",maxSelectionSize:7,cancelText:"cancel",chooseText:"choose",togglePaletteMoreText:"more",togglePaletteLessText:"less",clearText:"Clear Color Selection",noColorSelectedText:"No Color Selected",preferredFormat:!1,className:"",containerClassName:"",replacerClassName:"",showAlpha:!1,theme:"sp-light",palette:[["#ffffff","#000000","#ff0000","#ff8000","#ffff00","#008000","#0000ff","#4b0082","#9400d3"]],selectionPalette:[],disabled:!1,offset:null},spectrums=[],IE=!!/msie/i.exec(window.navigator.userAgent),rgbaSupport=(function(){function contains(str,substr){return!!~(''+str).indexOf(substr)} var elem=document.createElement('div');var style=elem.style;style.cssText='background-color:rgba(0,0,0,.5)';return contains(style.backgroundColor,'rgba')||contains(style.backgroundColor,'hsla')})(),replaceInput=["
","
","
","
"].join(''),markup=(function(){var gradientFix="";if(IE){for(var i=1;i<=6;i++){gradientFix+="
"}} return["
","
","
","
","","
","
","
","
","
","
","
","
","
","
","
","
","
","
","
","
","
",gradientFix,"
","
","
","
","
","","
","
","
","","","
","
","
"].join("")})();function paletteTemplate(p,color,className,opts){var html=[];for(var i=0;i
')}else{var cls='sp-clear-display';html.push($('
').append($('').attr('title',opts.noColorSelectedText)).html())}} return"
"+html.join('')+"
"} function hideAll(){for(var i=0;iMath.abs(dragY-oldDragY);shiftMovementDirection=furtherFromX?"x":"y"} var setSaturation=!shiftMovementDirection||shiftMovementDirection==="x";var setValue=!shiftMovementDirection||shiftMovementDirection==="y";if(setSaturation){currentSaturation=parseFloat(dragX/dragWidth)} if(setValue){currentValue=parseFloat((dragHeight-dragY)/dragHeight)} isEmpty=!1;if(!opts.showAlpha){currentAlpha=1} move()},dragStart,dragStop);if(!!initialColor){set(initialColor);updateUI();currentPreferredFormat=opts.preferredFormat||tinycolor(initialColor).format;addColorToSelectionPalette(initialColor)}else{updateUI()} if(flat){show()} function paletteElementClick(e){if(e.data&&e.data.ignore){set($(e.target).closest(".sp-thumb-el").data("color"));move()}else{set($(e.target).closest(".sp-thumb-el").data("color"));move();if(opts.hideAfterPaletteSelect){updateOriginalInput(!0);hide()}else{updateOriginalInput()}} return!1} var paletteEvent=IE?"mousedown.spectrum":"click.spectrum touchstart.spectrum";paletteContainer.on(paletteEvent,".sp-thumb-el",paletteElementClick);initialColorContainer.on(paletteEvent,".sp-thumb-el:nth-child(1)",{ignore:!0},paletteElementClick)} function updateSelectionPaletteFromStorage(){if(localStorageKey&&window.localStorage){try{var oldPalette=window.localStorage[localStorageKey].split(",#");if(oldPalette.length>1){delete window.localStorage[localStorageKey];$.each(oldPalette,function(i,c){addColorToSelectionPalette(c)})}}catch(e){} try{selectionPalette=window.localStorage[localStorageKey].split(";")}catch(e){}}} function addColorToSelectionPalette(color){if(showSelectionPalette){var rgb=tinycolor(color).toRgbString();if(!paletteLookup[rgb]&&$.inArray(rgb,selectionPalette)===-1){selectionPalette.push(rgb);while(selectionPalette.length>maxSelectionSize){selectionPalette.shift()}} if(localStorageKey&&window.localStorage){try{window.localStorage[localStorageKey]=selectionPalette.join(";")}catch(e){}}}} function getUniqueSelectionPalette(){var unique=[];if(opts.showPalette){for(var i=0;iviewWidth&&viewWidth>dpWidth)?Math.abs(offsetLeft+dpWidth-viewWidth):0);offsetTop-=Math.min(offsetTop,((offsetTop+dpHeight>viewHeight&&viewHeight>dpHeight)?Math.abs(dpHeight+inputHeight-extraY):extraY));return{top:offsetTop,bottom:offset.bottom,left:offsetLeft,right:offset.right,width:offset.width,height:offset.height}} function noop(){} function stopPropagation(e){e.stopPropagation()} function bind(func,obj){var slice=Array.prototype.slice;var args=slice.call(arguments,2);return function(){return func.apply(obj,args.concat(slice.call(arguments)))}} function draggable(element,onmove,onstart,onstop){onmove=onmove||function(){};onstart=onstart||function(){};onstop=onstop||function(){};var doc=document;var dragging=!1;var offset={};var maxHeight=0;var maxWidth=0;var hasTouch=('ontouchstart' in window);var duringDragEvents={};duringDragEvents.selectstart=prevent;duringDragEvents.dragstart=prevent;duringDragEvents["touchmove mousemove"]=move;duringDragEvents["touchend mouseup"]=stop;function prevent(e){if(e.stopPropagation){e.stopPropagation()} if(e.preventDefault){e.preventDefault()} e.returnValue=!1} function move(e){if(dragging){if(IE&&doc.documentMode<9&&!e.button){return stop()} var t0=e.originalEvent&&e.originalEvent.touches&&e.originalEvent.touches[0];var pageX=t0&&t0.pageX||e.pageX;var pageY=t0&&t0.pageY||e.pageY;var dragX=Math.max(0,Math.min(pageX-offset.left,maxWidth));var dragY=Math.max(0,Math.min(pageY-offset.top,maxHeight));if(hasTouch){prevent(e)} onmove.apply(element,[dragX,dragY,e])}} function start(e){var rightclick=(e.which)?(e.which==3):(e.button==2);if(!rightclick&&!dragging){if(onstart.apply(element,arguments)!==!1){dragging=!0;maxHeight=$(element).height();maxWidth=$(element).width();offset=$(element).offset();$(doc).on(duringDragEvents);$(doc.body).addClass("sp-dragging");move(e);prevent(e)}}} function stop(){if(dragging){$(doc).off(duringDragEvents);$(doc.body).removeClass("sp-dragging");setTimeout(function(){onstop.apply(element,arguments)},0)} dragging=!1} $(element).on("touchstart mousedown",start)} function throttle(func,wait,debounce){var timeout;return function(){var context=this,args=arguments;var throttler=function(){timeout=null;func.apply(context,args)};if(debounce)clearTimeout(timeout);if(debounce||!timeout)timeout=setTimeout(throttler,wait)}} function inputTypeColorSupport(){return $.fn.spectrum.inputTypeColorSupport()} var dataID="spectrum.id";$.fn.spectrum=function(opts,extra){if(typeof opts=="string"){var returnValue=this;var args=Array.prototype.slice.call(arguments,1);this.each(function(){var spect=spectrums[$(this).data(dataID)];if(spect){var method=spect[opts];if(!method){throw new Error("Spectrum: no such method: '"+opts+"'")} if(opts=="get"){returnValue=spect.get()}else if(opts=="container"){returnValue=spect.container}else if(opts=="option"){returnValue=spect.option.apply(spect,args)}else if(opts=="destroy"){spect.destroy();$(this).removeData(dataID)}else{method.apply(spect,args)}}});return returnValue} return this.spectrum("destroy").each(function(){var options=$.extend({},$(this).data(),opts);var spect=spectrum(this,options);$(this).data(dataID,spect.id)})};$.fn.spectrum.load=!0;$.fn.spectrum.loadOpts={};$.fn.spectrum.draggable=draggable;$.fn.spectrum.defaults=defaultOpts;$.fn.spectrum.inputTypeColorSupport=function inputTypeColorSupport(){if(typeof inputTypeColorSupport._cachedResult==="undefined"){var colorInput=$("")[0];inputTypeColorSupport._cachedResult=colorInput.type==="color"&&colorInput.value!==""} return inputTypeColorSupport._cachedResult};$.spectrum={};$.spectrum.localization={};$.spectrum.palettes={};$.fn.spectrum.processNativeColorInputs=function(){var colorInputs=$("input[type=color]");if(colorInputs.length&&!inputTypeColorSupport()){colorInputs.spectrum({preferredFormat:"hex6"})}};(function(){var trimLeft=/^[\s,#]+/,trimRight=/\s+$/,tinyCounter=0,math=Math,mathRound=math.round,mathMin=math.min,mathMax=math.max,mathRandom=math.random;var tinycolor=function(color,opts){color=(color)?color:'';opts=opts||{};if(color instanceof tinycolor){return color} if(!(this instanceof tinycolor)){return new tinycolor(color,opts)} var rgb=inputToRGB(color);this._originalInput=color;this._r=rgb.r;this._g=rgb.g;this._b=rgb.b;this._a=rgb.a;this._roundA=mathRound(1000*this._a)/1000;this._format=opts.format||rgb.format;this._gradientType=opts.gradientType;if(this._r<1){this._r=mathRound(this._r)} if(this._g<1){this._g=mathRound(this._g)} if(this._b<1){this._b=mathRound(this._b)} this._ok=rgb.ok;this._tc_id=tinyCounter++};tinycolor.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var rgb=this.toRgb();return(rgb.r*299+rgb.g*587+rgb.b*114)/1000},setAlpha:function(value){this._a=boundAlpha(value);this._roundA=mathRound(1000*this._a)/1000;return this},toHsv:function(){var hsv=rgbToHsv(this._r,this._g,this._b);return{h:hsv.h*360,s:hsv.s,v:hsv.v,a:this._a}},toHsvString:function(){var hsv=rgbToHsv(this._r,this._g,this._b);var h=mathRound(hsv.h*360),s=mathRound(hsv.s*100),v=mathRound(hsv.v*100);return(this._a==1)?"hsv("+h+", "+s+"%, "+v+"%)":"hsva("+h+", "+s+"%, "+v+"%, "+this._roundA+")"},toHsl:function(){var hsl=rgbToHsl(this._r,this._g,this._b);return{h:hsl.h*360,s:hsl.s,l:hsl.l,a:this._a}},toHslString:function(){var hsl=rgbToHsl(this._r,this._g,this._b);var h=mathRound(hsl.h*360),s=mathRound(hsl.s*100),l=mathRound(hsl.l*100);return(this._a==1)?"hsl("+h+", "+s+"%, "+l+"%)":"hsla("+h+", "+s+"%, "+l+"%, "+this._roundA+")"},toHex:function(allow3Char){return rgbToHex(this._r,this._g,this._b,allow3Char)},toHexString:function(allow3Char){return'#'+this.toHex(allow3Char)},toHex8:function(){return rgbaToHex(this._r,this._g,this._b,this._a)},toHex8String:function(){return'#'+this.toHex8()},toRgb:function(){return{r:mathRound(this._r),g:mathRound(this._g),b:mathRound(this._b),a:this._a}},toRgbString:function(){return(this._a==1)?"rgb("+mathRound(this._r)+", "+mathRound(this._g)+", "+mathRound(this._b)+")":"rgba("+mathRound(this._r)+", "+mathRound(this._g)+", "+mathRound(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:mathRound(bound01(this._r,255)*100)+"%",g:mathRound(bound01(this._g,255)*100)+"%",b:mathRound(bound01(this._b,255)*100)+"%",a:this._a}},toPercentageRgbString:function(){return(this._a==1)?"rgb("+mathRound(bound01(this._r,255)*100)+"%, "+mathRound(bound01(this._g,255)*100)+"%, "+mathRound(bound01(this._b,255)*100)+"%)":"rgba("+mathRound(bound01(this._r,255)*100)+"%, "+mathRound(bound01(this._g,255)*100)+"%, "+mathRound(bound01(this._b,255)*100)+"%, "+this._roundA+")"},toName:function(){if(this._a===0){return"transparent"} if(this._a<1){return!1} return hexNames[rgbToHex(this._r,this._g,this._b,!0)]||!1},toFilter:function(secondColor){var hex8String='#'+rgbaToHex(this._r,this._g,this._b,this._a);var secondHex8String=hex8String;var gradientType=this._gradientType?"GradientType = 1, ":"";if(secondColor){var s=tinycolor(secondColor);secondHex8String=s.toHex8String()} return"progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"},toString:function(format){var formatSet=!!format;format=format||this._format;var formattedString=!1;var hasAlpha=this._a<1&&this._a>=0;var needsAlphaFormat=!formatSet&&hasAlpha&&(format==="hex"||format==="hex6"||format==="hex3"||format==="name");if(needsAlphaFormat){if(format==="name"&&this._a===0){return this.toName()} return this.toRgbString()} if(format==="rgb"){formattedString=this.toRgbString()} if(format==="prgb"){formattedString=this.toPercentageRgbString()} if(format==="hex"||format==="hex6"){formattedString=this.toHexString()} if(format==="hex3"){formattedString=this.toHexString(!0)} if(format==="hex8"){formattedString=this.toHex8String()} if(format==="name"){formattedString=this.toName()} if(format==="hsl"){formattedString=this.toHslString()} if(format==="hsv"){formattedString=this.toHsvString()} return formattedString||this.toHexString()},_applyModification:function(fn,args){var color=fn.apply(null,[this].concat([].slice.call(args)));this._r=color._r;this._g=color._g;this._b=color._b;this.setAlpha(color._a);return this},lighten:function(){return this._applyModification(lighten,arguments)},brighten:function(){return this._applyModification(brighten,arguments)},darken:function(){return this._applyModification(darken,arguments)},desaturate:function(){return this._applyModification(desaturate,arguments)},saturate:function(){return this._applyModification(saturate,arguments)},greyscale:function(){return this._applyModification(greyscale,arguments)},spin:function(){return this._applyModification(spin,arguments)},_applyCombination:function(fn,args){return fn.apply(null,[this].concat([].slice.call(args)))},analogous:function(){return this._applyCombination(analogous,arguments)},complement:function(){return this._applyCombination(complement,arguments)},monochromatic:function(){return this._applyCombination(monochromatic,arguments)},splitcomplement:function(){return this._applyCombination(splitcomplement,arguments)},triad:function(){return this._applyCombination(triad,arguments)},tetrad:function(){return this._applyCombination(tetrad,arguments)}};tinycolor.fromRatio=function(color,opts){if(typeof color=="object"){var newColor={};for(var i in color){if(color.hasOwnProperty(i)){if(i==="a"){newColor[i]=color[i]}else{newColor[i]=convertToPercentage(color[i])}}} color=newColor} return tinycolor(color,opts)};function inputToRGB(color){var rgb={r:0,g:0,b:0};var a=1;var ok=!1;var format=!1;if(typeof color=="string"){color=stringInputToObject(color)} if(typeof color=="object"){if(color.hasOwnProperty("r")&&color.hasOwnProperty("g")&&color.hasOwnProperty("b")){rgb=rgbToRgb(color.r,color.g,color.b);ok=!0;format=String(color.r).substr(-1)==="%"?"prgb":"rgb"}else if(color.hasOwnProperty("h")&&color.hasOwnProperty("s")&&color.hasOwnProperty("v")){color.s=convertToPercentage(color.s);color.v=convertToPercentage(color.v);rgb=hsvToRgb(color.h,color.s,color.v);ok=!0;format="hsv"}else if(color.hasOwnProperty("h")&&color.hasOwnProperty("s")&&color.hasOwnProperty("l")){color.s=convertToPercentage(color.s);color.l=convertToPercentage(color.l);rgb=hslToRgb(color.h,color.s,color.l);ok=!0;format="hsl"} if(color.hasOwnProperty("a")){a=color.a}} a=boundAlpha(a);return{ok:ok,format:color.format||format,r:mathMin(255,mathMax(rgb.r,0)),g:mathMin(255,mathMax(rgb.g,0)),b:mathMin(255,mathMax(rgb.b,0)),a:a}} function rgbToRgb(r,g,b){return{r:bound01(r,255)*255,g:bound01(g,255)*255,b:bound01(b,255)*255}} function rgbToHsl(r,g,b){r=bound01(r,255);g=bound01(g,255);b=bound01(b,255);var max=mathMax(r,g,b),min=mathMin(r,g,b);var h,s,l=(max+min)/2;if(max==min){h=s=0}else{var d=max-min;s=l>0.5?d/(2-max-min):d/(max+min);switch(max){case r:h=(g-b)/d+(g1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p} if(s===0){r=g=b=l}else{var q=l<0.5?l*(1+s):l+s-l*s;var p=2*l-q;r=hue2rgb(p,q,h+1/3);g=hue2rgb(p,q,h);b=hue2rgb(p,q,h-1/3)} return{r:r*255,g:g*255,b:b*255}} function rgbToHsv(r,g,b){r=bound01(r,255);g=bound01(g,255);b=bound01(b,255);var max=mathMax(r,g,b),min=mathMin(r,g,b);var h,s,v=max;var d=max-min;s=max===0?0:d/max;if(max==min){h=0}else{switch(max){case r:h=(g-b)/d+(g>1))+720)%360;--results;){hsl.h=(hsl.h+part)%360;ret.push(tinycolor(hsl))} return ret} function monochromatic(color,results){results=results||6;var hsv=tinycolor(color).toHsv();var h=hsv.h,s=hsv.s,v=hsv.v;var ret=[];var modification=1/results;while(results--){ret.push(tinycolor({h:h,s:s,v:v}));v=(v+modification)%1} return ret} tinycolor.mix=function(color1,color2,amount){amount=(amount===0)?0:(amount||50);var rgb1=tinycolor(color1).toRgb();var rgb2=tinycolor(color2).toRgb();var p=amount/100;var w=p*2-1;var a=rgb2.a-rgb1.a;var w1;if(w*a==-1){w1=w}else{w1=(w+a)/(1+w*a)} w1=(w1+1)/2;var w2=1-w1;var rgba={r:rgb2.r*w1+rgb1.r*w2,g:rgb2.g*w1+rgb1.g*w2,b:rgb2.b*w1+rgb1.b*w2,a:rgb2.a*p+rgb1.a*(1-p)};return tinycolor(rgba)};tinycolor.readability=function(color1,color2){var c1=tinycolor(color1);var c2=tinycolor(color2);var rgb1=c1.toRgb();var rgb2=c2.toRgb();var brightnessA=c1.getBrightness();var brightnessB=c2.getBrightness();var colorDiff=(Math.max(rgb1.r,rgb2.r)-Math.min(rgb1.r,rgb2.r)+Math.max(rgb1.g,rgb2.g)-Math.min(rgb1.g,rgb2.g)+Math.max(rgb1.b,rgb2.b)-Math.min(rgb1.b,rgb2.b));return{brightness:Math.abs(brightnessA-brightnessB),color:colorDiff}};tinycolor.isReadable=function(color1,color2){var readability=tinycolor.readability(color1,color2);return readability.brightness>125&&readability.color>500};tinycolor.mostReadable=function(baseColor,colorList){var bestColor=null;var bestScore=0;var bestIsReadable=!1;for(var i=0;i125&&readability.color>500;var score=3*(readability.brightness/125)+(readability.color/500);if((readable&&!bestIsReadable)||(readable&&bestIsReadable&&score>bestScore)||((!readable)&&(!bestIsReadable)&&score>bestScore)){bestIsReadable=readable;bestScore=score;bestColor=tinycolor(colorList[i])}} return bestColor};var names=tinycolor.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"};var hexNames=tinycolor.hexNames=flip(names);function flip(o){var flipped={};for(var i in o){if(o.hasOwnProperty(i)){flipped[o[i]]=i}} return flipped} function boundAlpha(a){a=parseFloat(a);if(isNaN(a)||a<0||a>1){a=1} return a} function bound01(n,max){if(isOnePointZero(n)){n="100%"} var processPercent=isPercentage(n);n=mathMin(max,mathMax(0,parseFloat(n)));if(processPercent){n=parseInt(n*max,10)/100} if((math.abs(n-max)<0.000001)){return 1} return(n%max)/parseFloat(max)} function clamp01(val){return mathMin(1,mathMax(0,val))} function parseIntFromHex(val){return parseInt(val,16)} function isOnePointZero(n){return typeof n=="string"&&n.indexOf('.')!=-1&&parseFloat(n)===1} function isPercentage(n){return typeof n==="string"&&n.indexOf('%')!=-1} function pad2(c){return c.length==1?'0'+c:''+c} function convertToPercentage(n){if(n<=1){n=(n*100)+"%"} return n} function convertDecimalToHex(d){return Math.round(parseFloat(d)*255).toString(16)} function convertHexToDecimal(h){return(parseIntFromHex(h)/255)} var matchers=(function(){var CSS_INTEGER="[-\\+]?\\d+%?";var CSS_NUMBER="[-\\+]?\\d*\\.\\d+%?";var CSS_UNIT="(?:"+CSS_NUMBER+")|(?:"+CSS_INTEGER+")";var PERMISSIVE_MATCH3="[\\s|\\(]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")\\s*\\)?";var PERMISSIVE_MATCH4="[\\s|\\(]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")[,|\\s]+("+CSS_UNIT+")\\s*\\)?";return{rgb:new RegExp("rgb"+PERMISSIVE_MATCH3),rgba:new RegExp("rgba"+PERMISSIVE_MATCH4),hsl:new RegExp("hsl"+PERMISSIVE_MATCH3),hsla:new RegExp("hsla"+PERMISSIVE_MATCH4),hsv:new RegExp("hsv"+PERMISSIVE_MATCH3),hsva:new RegExp("hsva"+PERMISSIVE_MATCH4),hex3:/^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex8:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/}})();function stringInputToObject(color){color=color.replace(trimLeft,'').replace(trimRight,'').toLowerCase();var named=!1;if(names[color]){color=names[color];named=!0}else if(color=='transparent'){return{r:0,g:0,b:0,a:0,format:"name"}} var match;if((match=matchers.rgb.exec(color))){return{r:match[1],g:match[2],b:match[3]}} if((match=matchers.rgba.exec(color))){return{r:match[1],g:match[2],b:match[3],a:match[4]}} if((match=matchers.hsl.exec(color))){return{h:match[1],s:match[2],l:match[3]}} if((match=matchers.hsla.exec(color))){return{h:match[1],s:match[2],l:match[3],a:match[4]}} if((match=matchers.hsv.exec(color))){return{h:match[1],s:match[2],v:match[3]}} if((match=matchers.hsva.exec(color))){return{h:match[1],s:match[2],v:match[3],a:match[4]}} if((match=matchers.hex8.exec(color))){return{a:convertHexToDecimal(match[1]),r:parseIntFromHex(match[2]),g:parseIntFromHex(match[3]),b:parseIntFromHex(match[4]),format:named?"name":"hex8"}} if((match=matchers.hex6.exec(color))){return{r:parseIntFromHex(match[1]),g:parseIntFromHex(match[2]),b:parseIntFromHex(match[3]),format:named?"name":"hex"}} if((match=matchers.hex3.exec(color))){return{r:parseIntFromHex(match[1]+''+match[1]),g:parseIntFromHex(match[2]+''+match[2]),b:parseIntFromHex(match[3]+''+match[3]),format:named?"name":"hex"}} return!1} window.tinycolor=tinycolor})();$(function(){if($.fn.spectrum.load){$.fn.spectrum.processNativeColorInputs()}})}); $('head').append(''); }; // end setupColorpickerSpectrum self.checkforolderplugin = function() { let oldlocalstoragesettings = 'plugin-portalagentstatus-settings'; let disableplugin = false; if (!window.plugin.portalagentstatus && !localStorage[oldlocalstoragesettings]) return disableplugin; let html = '
Thank you for choosing this plugin.
' + '
' + 'The plugin was recently renamed from "Portal Agent Status" to "' + self.title + '".
' + '
'; if (window.plugin.portalagentstatus) { html += 'You now have both plugins activated and this can cause a conflict.
' + 'Before you can use this new plugin, you must manually disable and/or remove the old plugin "Portal Agent Status".
' + '
' + 'The new plugin will not run until this is changed.
'; disableplugin = true; } else if (localStorage[oldlocalstoragesettings]) { html += 'You need to check the new plugin settings because the old settings are not compatible.
' + 'Main menu' + 'About
' + 'There have been a lot of changes, so make sure you check out the About menu.
'; localStorage.removeItem(oldlocalstoragesettings); } html += 'version ' + self.version + ' by ' + self.author + '' + '
'; window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-' + self.pluginname, title: self.title, width: 'auto' }); return disableplugin; }; self.missingPortalHistorySupportplugin = function() { if (window.plugin.portalhistorysupport) return false; // plugin found let html = '
Thank you for choosing this plugin.
' + '
' + 'This plugin requires another plugin: Portal History Support
' + 'You can download it here: softspot.nl
' + '
' + 'This plugin will not run until it is installed.
' + 'version ' + self.version + ' by ' + self.author + '' + '
'; window.dialog({ html: html, id: self.pluginname + '-dialog', dialogClass: 'ui-dialog-' + self.pluginname, title: self.title, width: 'auto' }); return true; }; self.setup = function() { if ('pluginloaded' in self) { console.log('IITC plugin already loaded: ' + self.title + ' version ' + self.version); return; } else { self.pluginloaded = true; } if (self.checkforolderplugin()) return; // prevent conflicts with the previous plugin if (self.missingPortalHistorySupportplugin()) return; // check for required plugin self.moveHistoryToNewPlugin(); // convert old history data to new plugin self.setupPortalsList(); self.setupColorpickerSpectrum(); self.restoresettings(); self.lastportalscale = window.portalMarkerScale(); self.setupLayers(); window.addHook('mapDataRefreshEnd', function() { setTimeout(self.layersbringToFront,100); self.updatemenu(); }); window.addHook('portalSelected', function(data) { self.onportalSelected(data); self.layersbringToFront(); self.updatemenu(); }); window.addHook('portalDetailLoaded', function() { setTimeout(self.layersbringToFront,100); }); window.addHook('portalAdded', self.onportalAdded); window.addHook('portalRemoved', self.onportalRemoved); window.addHook('portalDetailLoaded', self.addbookmarkanywhererequestloaded); window.map.on('zoomend zoomlevelschange', self.onzoomlevelschange); window.map.on('zoomend', function() { self.clearbookmarktimerlist(); self.onzoomend(); }); window.map.on('moveend', function() { self.clearbookmarktimerlist(); self.updatemenu(); }); if (window.plugin.bookmarks) window.addHook('pluginBkmrksEdit', self.updatemenu); $('#toolbox').append('' + self.title + ''); $('head').append( ''); console.log('IITC plugin loaded: ' + self.title + ' version ' + self.version); }; var setup = function() { (window.iitcLoaded?self.setup():window.addHook('iitcLoaded',self.setup)); }; 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);