您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tag your street view by date, exactTime, address, generation, elevation
当前为
// ==UserScript== // @name Geoguessr Map-Making Auto-Tag // @namespace https://greasyfork.org/users/1179204 // @version 3.86.3 // @description Tag your street view by date, exactTime, address, generation, elevation // @author KaKa // @match *://map-making.app/maps/* // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @license MIT // @icon https://www.svgrepo.com/show/423677/tag-price-label.svg // ==/UserScript== (function() { 'use strict'; let accuracy=60 /* You could modifiy accuracy here, default setting is 60s */ let tagBox = ['Year', 'Month','Day', 'Time','Country', 'Subdivision', 'Generation', 'Elevation','Type','Update','Fix','Detect'] let months = ['January', 'February', 'March', 'April', 'May', 'June','July', 'August', 'September', 'October', 'November', 'December']; let mapData function getMap() { return new Promise(function(resolve, reject) { var requestURL = window.location.origin + "/api" + window.location.pathname + "/locations"; fetch(requestURL, { headers: { 'Accept': 'application/json', 'Content-Encoding':'gzip' } }) .then(function(response) { if (!response.ok) { throw new Error('HTTP error, status = ' + response.status); } return response.json(); }) .then(function(jsonData) { resolve(jsonData); }) .catch(function(error) { console.error('Fetch Error:', error); reject('Error fetching meta data of the map!'); }); }); } async function getSelection() { return new Promise((resolve, reject) => { var exportButtonText = 'Export'; var buttons = document.querySelectorAll('button.button'); for (var i = 0; i < buttons.length; i++) { if (buttons[i].textContent.trim() === exportButtonText) { buttons[i].click(); var modalDialog = document.querySelector('.modal__dialog.export-modal'); } } setTimeout(() => { const radioButton = document.querySelector('input[type="radio"][name="selection"][value="1"]'); const spanText = radioButton.nextElementSibling.textContent.trim(); if (spanText==="Export selection (0 locations)") { swal.fire('Selection not found!', 'Please select at least one location as selection!','warning') reject(new Error('Export selection is empty!')); } if (radioButton) radioButton.click() else{ reject(new Error('Radio button not found'));} }, 100); setTimeout(() => { const copyButton = document.querySelector('.export-modal__export-buttons button:first-of-type'); if (!copyButton) { reject(new Error('Copy button not found')); } copyButton.click(); }, 200); setTimeout(() => { const closeButton = document.querySelector('.modal__close'); if (closeButton) closeButton.click(); else reject(new Error('Close button not found')); }, 400); setTimeout(async () => { try { const data = await navigator.clipboard.readText() const selection = JSON.parse(data); resolve(selection); } catch (error) { console.error("Error getting selection:", error); reject(error); } }, 800); }); } function matchSelection(selection, locations) { const matchingLocations = []; const customCoordinates = selection.customCoordinates; const locationMap = {}; locations.forEach(loc => { const locString = JSON.stringify(loc.location); locationMap[locString] = loc; }); for (const coord of customCoordinates) { const coordString = JSON.stringify({ lat: coord.lat, lng: coord.lng }); if (locationMap.hasOwnProperty(coordString)) { const matchingLoc = locationMap[coordString]; if (coord.extra.hasOwnProperty('panoDate') && coord.extra.panoDate) { matchingLoc.panoDate = coord.panoDate; } matchingLocations.push(matchingLoc); } } return matchingLocations; } function findRange(elevation, ranges) { for (let i = 0; i < ranges.length; i++) { const range = ranges[i]; if (elevation >= range.min && elevation <= range.max) { return `${range.min}-${range.max}m`; } } if (!elevation) { return 'noElevation'; } return `${JSON.stringify(elevation)}m`; } function updateSelection(entries) { var requestURL = window.location.origin + "/api" + window.location.pathname + "/locations"; var payload = { edits: [] }; entries.forEach(function(entry) { var createEntry = { id: -1, author: entry.author, mapId: entry.mapId, location: entry.location, panoId: entry.panoId, panoDate: entry.panoDate, heading: entry.heading, pitch: entry.pitch, zoom: entry.zoom, tags: entry.tags, flags: entry.flags, createdAt: entry.createdAt, }; payload.edits.push({ action: { type: 3 }, create: [createEntry], remove: [entry.id] }); }); var xhr = new XMLHttpRequest(); xhr.open("POST", requestURL); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { console.log("Request succeeded"); } else { console.error("Request failed with status", xhr.status); } }; xhr.onerror = function() { swal.fire({ icon: 'error', title: 'Oops...', text: 'Failed to update the map! Please retrieve JSON data from your clipboard.' }); }; xhr.send(JSON.stringify(payload)); } async function runScript(tags,sR) { let taggedLocs=[]; let exportMode,selections,fixStrategy if (tags.length<1){ swal.fire('Feature not found!', 'Please select at least one feature!','warning') return} if (tags.includes('fix')){ const { value: fixOption,dismiss: fixDismiss } = await Swal.fire({ title:'Fix Strategy', icon:'question', text: 'Would you like to fix the location based on the map-making data. (more suitable for those locs with a specific date coverage) Else it will update the broken loc with recent coverage.', showCancelButton: true, showCloseButton:true, allowOutsideClick: false, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Yes', cancelButtonText: 'No', }) if(fixOption)fixStrategy='exactly' else if(!fixOption&&fixDismiss==='cancel'){ fixStrategy=null } else{ return } }; const { value: option,dismiss: inputDismiss } = await Swal.fire({ title: 'Export', text: 'Do you want to update and save your map? If you click "Cancel", the script will just paste JSON data to the clipboard after finish tagging.', icon: 'question', showCancelButton: true, showCloseButton:true, allowOutsideClick: false, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Yes', cancelButtonText: 'Cancel' }); if (option) { exportMode='save' } else if(!selections&&inputDismiss==='cancel'){ exportMode=null } else{ return } const loadingSwal = Swal.fire({ title: 'Preparing', text: 'Fetching selected locs from map-making. Please wait...', allowOutsideClick: false, allowEscapeKey: false, showConfirmButton: false, icon:"info", didOpen: () => { Swal.showLoading(); } }); const selectedLocs=await getSelection() mapData=await getMap() selections=await matchSelection(selectedLocs,mapData) loadingSwal.close() async function UE(t, e, s, d) { try { const r = `https://maps.googleapis.com/$rpc/google.internal.maps.mapsjs.v1.MapsJsInternalService/${t}`; let payload = createPayload(t, e,s,d); const response = await fetch(r, { method: "POST", headers: { "content-type": "application/json+protobuf", "x-user-agent": "grpc-web-javascript/0.1" }, body: payload, mode: "cors", credentials: "omit" }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } else { return await response.json(); } } catch (error) { console.error(`There was a problem with the UE function: ${error.message}`); } } function createPayload(mode,coorData,s,d,r) { let payload; if(!r)r=50 // default search radius if (mode === 'GetMetadata') { payload = [["apiv3",null,null,null,"US",null,null,null,null,null,[[0]]],["en","US"],[[[2,coorData]]],[[1,2,3,4,8,6]]]; } else if (mode === 'SingleImageSearch') { var lat = coorData.lat; var lng = coorData.lng; lat = lat % 1 !== 0 && lat.toString().split('.')[1].length >6 ? parseFloat(lat.toFixed(6)) : lat; lng = lng % 1 !== 0 && lng.toString().split('.')[1].length > 6 ? parseFloat(lng.toFixed(6)) : lng; if(s&&d){ payload=[["apiv3"],[[null,null,lat,lng],r],[[null,null,null,null,null,null,null,null,null,null,[s,d]],null,null,null,null,null,null,null,[2],null,[[[2,true,2]]]],[[2,6]]] }else{ payload =[["apiv3"], [[null,null,lat,lng],r], [null,["en","US"],null,null,null,null,null,null,[2],null,[[[2,1,2],[3,1,2],[10,1,2]]]], [[1,2,3,4,8,6]]];} } else { throw new Error("Invalid mode!"); } return JSON.stringify(payload); } function monthToTimestamp(m) { const [year, month] = m.split('-'); const startDate =Math.round( new Date(year, month-1,1).getTime()/1000); const endDate =Math.round( new Date(year, month, 1).getTime()/1000)-1; return { startDate, endDate }; } async function binarySearch(c, start,end) { let capture let response while (end - start >= accuracy) { let mid= Math.round((start + end) / 2); response = await UE("SingleImageSearch", c, start,end,10); if (response&&response[0][2]== "Search returned no images." ){ start=mid+start-end end=start-mid+end mid=Math.round((start+end)/2) } else { start=mid mid=Math.round((start+end)/2) } capture=mid } return capture } function getMetaData(svData) { let year = 'Year not found',month = 'Month not found' let panoType='unofficial' let subdivision='Subdivision not found' if (svData) { if (svData.imageDate) { const matchYear = svData.imageDate.match(/\d{4}/); if (matchYear) { year = matchYear[0]; } const matchMonth = svData.imageDate.match(/-(\d{2})/); if (matchMonth) { month = matchMonth[1]; } } if (svData.copyright.includes('Google')) { panoType = 'Official'; } if(svData.location.description){ let parts = svData.location.description.split(','); if(parts.length > 1){ subdivision = parts[parts.length-1].trim(); } else { subdivision = svData.location.description; } } return [year,month,panoType,subdivision] } else{ return null } } function extractDate(array) { var year,month array.forEach(element => { const yearRegex1 = /^(\d{2})-(\d{2})$/; const yearRegex2 = /^(\d{4})-(\d{2})$/; const yearRegex3=/^(\d{4})$/; const monthRegex1 = /^(\d{2})$/; const monthRegex2 = /^(January|February|March|April|May|June|July|August|September|October|November|December)$/i; if (!year &&!month&& yearRegex1.test(element)) { const match = yearRegex1.exec(element); year = parseInt(match[1]) + 2000; month = parseInt(match[2]); } if (!year && !month&& yearRegex2.test(element)) { const match = yearRegex2.exec(element); year = parseInt(match[1]); month = parseInt(match[2]); } if(!year&& yearRegex3.test(element)){ const match = yearRegex3.test(element); year=parseInt(element) } if (!month && monthRegex1.test(element)) { month= parseInt(element); } if (!month && monthRegex2.test(element)) { const months = { "January": 1, "February": 2, "March": 3, "April": 4, "May": 5, "June": 6, "July": 7, "August": 8, "September": 9, "October": 10, "November": 11, "December": 12 }; month = months[element]; } }); return {year,month} } function getGeneration(svData,country) { if (svData&&svData.tiles) { if (svData.tiles.worldSize.height === 1664) { // Gen 1 return 'Gen1'; } else if (svData.tiles.worldSize.height === 6656) { // Gen 2 or 3 let lat; for (let key in svData.Sv) { lat = svData.Sv[key].lat; break; } let date; if (svData.imageDate) { date = new Date(svData.imageDate); } else { date = 'nodata'; } if (date!=='nodata'&&((country === 'BD' && (date >= new Date('2021-04'))) || (country === 'EC' && (date >= new Date('2022-03'))) || (country === 'FI' && (date >= new Date('2020-09'))) || (country === 'IN' && (date >= new Date('2021-10'))) || (country === 'LK' && (date >= new Date('2021-02'))) || (country === 'KH' && (date >= new Date('2022-10'))) || (country === 'LB' && (date >= new Date('2021-05'))) || (country === 'NG' && (date >= new Date('2021-06'))) || (country === 'ST') || (country === 'US' && lat > 52 && (date >= new Date('2019-01'))))) { return 'Shitcam'; } let gen2Countries = ['AU', 'BR', 'CA', 'CL', 'JP', 'GB', 'IE', 'NZ', 'MX', 'RU', 'US', 'IT', 'DK', 'GR', 'RO', 'PL', 'CZ', 'CH', 'SE', 'FI', 'BE', 'LU', 'NL', 'ZA', 'SG', 'TW', 'HK', 'MO', 'MC', 'SM', 'AD', 'IM', 'JE', 'FR', 'DE', 'ES', 'PT']; if (gen2Countries.includes(country)) { return 'Gen2or3'; } else{ return 'Gen3';} } else if(svData.tiles.worldSize.height === 8192){ return 'Gen4'; } } return 'Unknown'; } async function getLocal(coord, timestamp) { const systemTimezoneOffset = -new Date().getTimezoneOffset() * 60; try { const [lat, lng] = coord; const url = `https://api.wheretheiss.at/v1/coordinates/${lat},${lng}`; const response = await fetch(url); if (!response.ok) { throw new Error("Request failed: " + response.statusText); } const data = await response.json(); const targetTimezoneOffset = data.offset * 3600; const offsetDiff = systemTimezoneOffset - targetTimezoneOffset; const convertedTimestamp = Math.round(timestamp - offsetDiff); return convertedTimestamp; } catch (error) { throw error; } } var CHUNK_SIZE = 1200; if (tags.includes('time')){ CHUNK_SIZE = 500 } var promises = []; async function processCoord(coord, tags, svData,ccData) { try{ if (svData||ccData){ var panoYear,panoMonth if (coord.panoDate){ panoYear=parseInt(coord.panoDate.substring(0,4)) panoMonth=parseInt(coord.panoDate.substring(5,7)) } else if(coord.panoId){ panoYear=parseInt(svData.imageDate.substring(0,4)) panoMonth=parseInt(svData.imageDate.substring(5,7)) } else{ panoYear=parseInt(extractDate(coord.tags).year) panoMonth=parseInt(extractDate(coord.tags).month) } let meta=getMetaData(svData) let yearTag=meta[0] let monthTag=parseInt(meta[1]) let typeTag=meta[2] let subdivisionTag=meta[3] let countryTag,elevationTag let genTag,trekkerTag,floorTag let dayTag,timeTag,exactTime,timeRange //if(monthTag){monthTag=months[monthTag-1]} monthTag=yearTag.slice(-2)+'-'+(monthTag.toString()) if (!monthTag){monthTag='Month not found'} var date=monthToTimestamp(svData.imageDate) if(tags.includes('day')||tags.includes('time')){ const initialSearch=await UE('SingleImageSearch',{'lat':coord.location.lat,'lng':coord.location.lng},date.startDate,date.endDate) if (initialSearch){ if (initialSearch.length!=3)exactTime=null; else{ exactTime=await binarySearch({'lat':coord.location.lat,'lng':coord.location.lng}, date.startDate,date.endDate) } } } if(!exactTime){dayTag='Day not found' timeTag='Time not found' } else{ const currentDate = new Date(); const currentOffset =-(currentDate.getTimezoneOffset())*60 const dayOffset = currentOffset-Math.round((coord.location.lng / 15) * 3600); const LocalDay=new Date(Math.round(exactTime-dayOffset)*1000) dayTag = LocalDay.toISOString().split('T')[0]; if(tags.includes('time')) { var localTime=await getLocal([coord.location.lat,coord.location.lng],exactTime) var timeObject=new Date(localTime*1000) timeTag =`${timeObject.getHours().toString().padStart(2, '0')}:${timeObject.getMinutes().toString().padStart(2, '0')}:${timeObject.getSeconds().toString().padStart(2, '0')}`; var hour = timeObject.getHours(); if (hour < 11) { timeRange = 'Morning'; } else if (hour >= 11 && hour < 13) { timeRange = 'Noon'; } else if (hour >= 13 && hour < 17) { timeRange = 'Afternoon'; } else if(hour >= 17 && hour < 19) { timeRange = 'Evening'; } else{ timeRange = 'Night'; } } } try {if (ccData.length!=3) ccData=ccData[1][0] else ccData=ccData[1] } catch (error) { ccData=null } if (ccData){ try{ countryTag = ccData[5][0][1][4]} catch(error){ countryTag=null } try{ elevationTag=ccData[5][0][1][1][0]} catch(error){ elevationTag=null } try{ trekkerTag=ccData[6][5]} catch(error){ trekkerTag=null } try{ floorTag=ccData[5][0][1][3][2][0] } catch(error){ floorTag=null } if (tags.includes('detect')){ const defaultDate=3 } } if (trekkerTag){ trekkerTag=trekkerTag.toString() if( trekkerTag.includes('scout')){ trekkerTag='trekker' } else{ trekkerTag=null }} if(elevationTag){ elevationTag=Math.round(elevationTag*100)/100 if(sR){ elevationTag=findRange(elevationTag,sR) } else{ elevationTag=elevationTag.toString()+'m' } } if (!countryTag)countryTag='Country not found' if (!elevationTag)elevationTag='Elevation not found' if (tags.includes('generation')&&typeTag=='Official'&&countryTag){ genTag = getGeneration(svData,countryTag) coord.tags.push(genTag)} if (tags.includes('year'))coord.tags.push(yearTag) if (tags.includes('month'))coord.tags.push(monthTag) if (tags.includes('day'))coord.tags.push(dayTag) if (tags.includes('time')) coord.tags.push(timeTag) if (tags.includes('time')&&timeRange) coord.tags.push(timeRange) if (tags.includes('type'))coord.tags.push(typeTag) if (tags.includes('type')&&trekkerTag&&typeTag=='Official')coord.tags.push('trekker') if (tags.includes('type')&&floorTag&&typeTag=='Official')coord.tags.push(floorTag) if (tags.includes('country'))coord.tags.push(countryTag) if (tags.includes('subdivision')&&typeTag=='Official')coord.tags.push(subdivisionTag) if (tags.includes('elevation'))coord.tags.push(elevationTag) if (tags.includes('update')){ try{ const resultPano=await UE('SingleImageSearch',{lat: coord.location.lat, lng: coord.location.lng},null,null,10) const updatedPnaoId=resultPano[1][1][1] const updatedYear=resultPano[1][6][7][0] const updatedMonth=resultPano[1][6][7][1] if (coord.panoId){ if (updatedPnaoId&&updatedPnaoId!=coord.panoId) { if(panoYear!=updatedYear||panoMonth!=updatedMonth){ coord.panoId=updatedPnaoId coord.tags.push('Updated')} else{ coord.panoId=updatedPnaoId coord.tags.push('Copyright changed') } }} else{ if (panoYear&&panoMonth&&updatedYear&&updatedMonth){ if(panoYear!=updatedYear||panoMonth!=updatedMonth){ coord.panoId=updatedPnaoId coord.tags.push('Updated') } } else{ coord.tags.push('Failed to update') } } } catch (error){ coord.tags.push('Failed to update') } } } } catch (error) { if(!tags.includes('fix'))coord.tags.push('Pano not found'); else{ var fixState try{ const resultPano=await UE('SingleImageSearch',{lat: coord.location.lat, lng: coord.location.lng},null,null,5) if(fixStrategy){ const panos=resultPano[1][5][0][8] for(const pano of panos){ if((pano[1][0]===panoYear&&pano[1][1]===panoMonth)){ const panoIndex=pano[0] const fixedPanoId=resultPano[1][5][0][3][0][panoIndex][0][1] coord.panoId=fixedPanoId coord.location.lat=resultPano[1][5][0][1][0][2] coord.location.lng=resultPano[1][5][0][1][0][3] fixState=true } } } else{ coord.panoId=resultPano[1][1][1] fixState=true } } catch (error){ fixState=null } if (!fixState)coord.tags.push('Failed to fix') else coord.tags.push('Fixed') } } if (coord.tags) {coord.tags=Array.from(new Set(coord.tags))} taggedLocs.push(coord); } async function processChunk(chunk, tags) { var service = new google.maps.StreetViewService(); var promises = chunk.map(async coord => { let panoId = coord.panoId; let latLng = {lat: coord.location.lat, lng: coord.location.lng}; let svData; let ccData; if ((panoId || latLng)) { if(tags!=['country']&&tags!=['elevation']&&tags!=['detect']){ svData = await getSVData(service, panoId ? {pano: panoId} : {location: latLng, radius: 50});} } if (!panoId && (tags.includes('generation')||('country')||('elevation')||('type'))) { ccData = await UE('SingleImageSearch', latLng); } else if (panoId && (tags.includes('generation')||('country')||('elevation')||('type'))) { ccData = await UE('GetMetadata', panoId); } if (latLng && (tags.includes('detect'))) { var detectYear,detectMonth if (coord.panoDate){ detectYear=parseInt(coord.panoDate.substring(0,4)) detectMonth=parseInt(coord.panoDate.substring(5,7)) } else{ if(coord.panoId){ const metaData=await getSVData(service,{pano: panoId}) if (metaData){ if(metaData.imageDate){ detectYear=parseInt(metaData.imageDate.substring(0,4)) detectMonth=parseInt(metaData.imageDate.substring(5,7)) } } } } if (detectYear&&detectMonth){ const metaData = await UE('SingleImageSearch', latLng,10); if (metaData){ if(metaData.length>1){ const defaultDate=metaData[1][6][7] if (defaultDate[0]===detectYear&&defaultDate[1]!=detectMonth){ coord.tags.push('Dangerous')} } } } } if (tags!=['detect']){ await processCoord(coord, tags, svData,ccData)} }); await Promise.all(promises); } function getSVData(service, options) { return new Promise(resolve => service.getPanorama({...options}, (data, status) => { resolve(data); })); } async function processData(tags) { let successText='The JSON data has been pasted to your clipboard!'; try { const totalChunks = Math.ceil(selections.length / CHUNK_SIZE); let processedChunks = 0; const swal = Swal.fire({ title: 'Tagging', text: 'If you try to tag a large number of locs by exact time or elevation, it could take quite some time. Please wait...', allowOutsideClick: false, allowEscapeKey: false, showConfirmButton: false, icon:"info", didOpen: () => { Swal.showLoading(); } }); for (let i = 0; i < selections.length; i += CHUNK_SIZE) { let chunk = selections.slice(i, i + CHUNK_SIZE); await processChunk(chunk, tags); processedChunks++; const progress = Math.min((processedChunks / totalChunks) * 100, 100); Swal.update({ html: `<div>${progress.toFixed(2)}% completed</div> <div class="swal2-progress"> <div class="swal2-progress-bar" role="progressbar" aria-valuenow="${progress}" aria-valuemin="0" aria-valuemax="100" style="width: ${progress}%;"> </div> </div>` }); if(exportMode){ updateSelection(chunk) successText='Tagging completed! Do you want to refresh the page?(The JSON data is also pasted to your clipboard)' } } swal.close(); var newJSON=[] taggedLocs.forEach((loc)=>{ newJSON.push({lat:loc.location.lat, lng:loc.location.lng, heading:loc.heading, pitch: loc.pitch !== undefined && loc.pitch !== null ? loc.pitch : 90, zoom: loc.zoom !== undefined && loc.zoom !== null ? loc.zoom : 0, panoId:loc.panoId, extra:{tags:loc.tags} }) }) GM_setClipboard(JSON.stringify(newJSON)) Swal.fire({ title: 'Success!', text: successText, icon: 'success', showCancelButton: true, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'OK' }).then((result) => { if (result.isConfirmed) { if(exportMode){ location.reload();} } }); } catch (error) { swal.close(); Swal.fire('Error Tagging!', '','error'); console.error('Error processing JSON data:', error); } } if(selections){ if(selections.length>=1){processData(tags);} else{ Swal.fire('Error Parsing JSON Data!', 'The input JSON data is empty! If you update the map after the page is loaded, please save it and refresh the page before tagging','error');} }else{Swal.fire('Error Parsing JSON Data!', 'The input JSON data is invaild or incorrectly formatted.','error');} } function createCheckbox(text, tags) { var label = document.createElement('label'); var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.value = text; checkbox.name = 'tags'; checkbox.id = tags; checkbox.style.marginLeft='2px' label.appendChild(checkbox); label.appendChild(document.createTextNode(text)); buttonContainer.appendChild(label); return checkbox; } const elementContainer=document.getElementsByClassName('tool-block')[0]; var mainButton = document.createElement('button'); mainButton.textContent = 'Auto-Tag'; mainButton.id='main-button'; mainButton.style.position = 'fixed'; mainButton.style.right = '20px'; mainButton.style.bottom = '15px'; mainButton.style.borderRadius = "18px"; mainButton.style.fontSize = "15px"; mainButton.style.padding = "10px 20px"; mainButton.style.border = "none"; mainButton.style.color = "white"; mainButton.style.cursor = "pointer"; mainButton.style.backgroundColor = "#4CAF50"; mainButton.addEventListener('click', function () { if (buttonContainer.style.display === 'none') { buttonContainer.style.display = 'block'; } else { buttonContainer.style.display = 'none'; } }); document.body.appendChild(mainButton); var buttonContainer = document.createElement('div'); buttonContainer.style.position = 'fixed'; buttonContainer.id='button-container'; buttonContainer.style.right = '20px'; buttonContainer.style.bottom = '50px'; buttonContainer.style.display = 'none'; buttonContainer.style.fontSize = '14px'; document.body.appendChild(buttonContainer); var selectAllCheckbox = document.createElement('input'); selectAllCheckbox.type = 'checkbox'; selectAllCheckbox.id = 'selectAll'; selectAllCheckbox.addEventListener('change', function () { var checkboxes = document.getElementsByName('tags'); for (var i = 0; i < checkboxes.length; i++) { if (!['Fix', 'Detect','Update'].includes(checkboxes[i].value)){ checkboxes[i].checked = this.checked; } } }); var selectAllLabel = document.createElement('label'); selectAllLabel.textContent = 'Select All'; selectAllLabel.htmlFor = 'selectAll'; var triggerButton = document.createElement('button'); triggerButton.textContent = 'Start Tagging'; triggerButton.addEventListener('click', function () { var checkboxes = document.getElementsByName('tags'); var checkedTags = []; for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedTags.push(checkboxes[i].id); } } if (checkedTags.includes('elevation')) { Swal.fire({ title: 'Set A Range For Elevation', text: 'If you select "Cancel", the script will return the exact elevation for each location.', icon: 'question', showCancelButton: true, showCloseButton: true, allowOutsideClick: false, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Yes', cancelButtonText: 'Cancel' }).then((result) => { if (result.isConfirmed) { Swal.fire({ title: 'Define Range for Each Segment', html: ` <label> <br>Enter range for each segment, separated by commas</br></label> <textarea id="segmentRanges" class="swal2-textarea" placeholder="such as:-1-10,11-35"></textarea> `, icon: 'question', showCancelButton: true, showCloseButton: true, allowOutsideClick: false, focusConfirm: false, preConfirm: () => { const segmentRangesInput = document.getElementById('segmentRanges').value.trim(); if (!segmentRangesInput) { Swal.showValidationMessage('Please enter range for each segment'); return false; } const segmentRanges = segmentRangesInput.split(','); const validatedRanges = segmentRanges.map(range => { const matches = range.trim().match(/^\s*(-?\d+)\s*-\s*(-?\d+)\s*$/); if (matches) { const min = Number(matches[1]); const max = Number(matches[2]); return { min, max }; } else { Swal.showValidationMessage('Invalid range format. Please use format: minValue-maxValue'); return false; } }); return validatedRanges.filter(Boolean); }, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Yes', cancelButtonText: 'Cancel', inputValidator: (value) => { if (!value.trim()) { return 'Please enter range for each segment'; } } }).then((result) => { if (result.isConfirmed) { runScript(checkedTags, result.value) } else { Swal.showValidationMessage('You canceled input'); } }); } else if (result.dismiss === Swal.DismissReason.cancel) { runScript(checkedTags) } }); } else { runScript(checkedTags) } }) buttonContainer.appendChild(triggerButton); buttonContainer.appendChild(selectAllCheckbox); buttonContainer.appendChild(selectAllLabel); tagBox.forEach(tag => { createCheckbox(tag, tag.toLowerCase()); }); })();