From c73cbc823d332927c92aa690f1d4f77d728d2313 Mon Sep 17 00:00:00 2001 From: nobkd <44443899+nobkd@users.noreply.github.com> Date: Fri, 28 Jun 2024 00:39:51 +0200 Subject: [PATCH] chore: move ts to js --- bun.lockb | Bin 321844 -> 321812 bytes package.json | 7 +- src/bg.html | 4 +- src/bg/{action.ts => action.js} | 6 +- src/bg/{bg.ts => bg.js} | 12 +- src/bg/utils/{actionIcon.ts => actionIcon.js} | 6 +- src/bg/utils/{domainEnds.ts => domainEnds.js} | 2 +- src/bg/utils/{storage.ts => storage.js} | 17 +-- src/map.html | 2 +- src/map/{map.ts => map.js} | 32 ++--- src/map/utils/parseDMS.js | 27 +++++ src/map/utils/parseDMS.ts | 26 ---- src/map/utils/{parsePB.ts => parsePB.js} | 32 +++-- src/map/utils/read.js | 112 ++++++++++++++++++ src/map/utils/read.ts | 104 ---------------- src/map/utils/{zoom.ts => zoom.js} | 10 +- src/options.html | 2 +- src/options/{options.ts => options.js} | 26 ++-- .../{parseDMS.test.ts => parseDMS.test.js} | 0 .../{parsePB.test.ts => parsePB.test.js} | 0 test/map/utils/{read.test.ts => read.test.js} | 8 +- test/map/utils/{zoom.test.ts => zoom.test.js} | 0 tsconfig.json | 25 ---- vite.config.ts => vite.config.js | 0 24 files changed, 228 insertions(+), 232 deletions(-) rename src/bg/{action.ts => action.js} (84%) rename src/bg/{bg.ts => bg.js} (77%) rename src/bg/utils/{actionIcon.ts => actionIcon.js} (83%) rename src/bg/utils/{domainEnds.ts => domainEnds.js} (98%) rename src/bg/utils/{storage.ts => storage.js} (72%) rename src/map/{map.ts => map.js} (80%) create mode 100644 src/map/utils/parseDMS.js delete mode 100644 src/map/utils/parseDMS.ts rename src/map/utils/{parsePB.ts => parsePB.js} (65%) create mode 100644 src/map/utils/read.js delete mode 100644 src/map/utils/read.ts rename src/map/utils/{zoom.ts => zoom.js} (69%) rename src/options/{options.ts => options.js} (73%) rename test/map/utils/{parseDMS.test.ts => parseDMS.test.js} (100%) rename test/map/utils/{parsePB.test.ts => parsePB.test.js} (100%) rename test/map/utils/{read.test.ts => read.test.js} (94%) rename test/map/utils/{zoom.test.ts => zoom.test.js} (100%) delete mode 100644 tsconfig.json rename vite.config.ts => vite.config.js (100%) diff --git a/bun.lockb b/bun.lockb index 29b270ae31a2d67c9ecac7c5f034377e28503221..2a4543e1b62dda4ef979fb92530519f338799159 100755 GIT binary patch delta 46543 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 - - + + diff --git a/src/bg/action.ts b/src/bg/action.js similarity index 84% rename from src/bg/action.ts rename to src/bg/action.js index 15251ce..f8295ef 100644 --- a/src/bg/action.ts +++ b/src/bg/action.js @@ -1,4 +1,4 @@ -import { browserAction, /*webNavigation,*/ type Tabs, tabs } from 'webextension-polyfill' +import { browserAction, /*webNavigation,*/ Tabs, tabs } from 'webextension-polyfill' import { getHostname, invertHostState } from './utils/storage' //import { matcher as mapsUrlMatcher, runtimeMapUrl } from './bg'; @@ -10,9 +10,9 @@ import { getHostname, invertHostState } from './utils/storage' * * Requests all frames from the current tab, filters them for extension Leaflet frames and Maps frames. * Reloads the full tab on extension Leaflet or Maps frame match. - * @param tab Currently active tab + * @param {Tabs.Tab} tab Currently active tab */ -async function actionClick(tab: Tabs.Tab): Promise { +async function actionClick(tab) { if (!tab.url || !tab.id) return let hostname = getHostname(tab.url) diff --git a/src/bg/bg.ts b/src/bg/bg.js similarity index 77% rename from src/bg/bg.ts rename to src/bg/bg.js index 7df1b44..1e14453 100644 --- a/src/bg/bg.ts +++ b/src/bg/bg.js @@ -1,10 +1,10 @@ -import { runtime, tabs, windows, webRequest, type WebRequest } from 'webextension-polyfill' +import { runtime, tabs, windows, webRequest, WebRequest } from 'webextension-polyfill' import { disabledHosts, getHostname } from './utils/storage' import { updateActiveTabIcon } from './utils/actionIcon' import { domainEnds } from './utils/domainEnds' -const gLocales: string = domainEnds.join('|') // TODO: collect more locales -export const matcher: RegExp = new RegExp( +const gLocales = domainEnds.join('|') // TODO: collect more locales +export const matcher = new RegExp( // TODO: fix regex to fit more patterns `^(https?:\/\/)?(maps\.google\.(${gLocales})\/maps.*\?.*output=embed|(www\.)?google\.(${gLocales})\/maps\/embed.*\?)` ) @@ -14,10 +14,10 @@ export const runtimeMapUrl = runtime.getURL('map.html') * Checks if `frames` send a request to Maps. * If they do and the extension isn't disabled for the current site, then the request is redirected to the extension leaflet map with all URL search params. * Else the request is'nt blocked. - * @param req Web Request from frame - * @returns Redirect to extension map or pass through if extension disabled for website + * @param {WebRequest.OnBeforeRequestDetailsType} req Web Request from frame + * @returns {WebRequest.BlockingResponse} Redirect to extension map or pass through if extension disabled for website */ -function redirect(req: WebRequest.OnBeforeRequestDetailsType): WebRequest.BlockingResponse { +function redirect(req) { // TODO: check if originUrl always matches current tab url -> e.g. in frames with subframes if (req.originUrl && req.url.match(matcher)) { if (!disabledHosts.includes(getHostname(req.originUrl))) { diff --git a/src/bg/utils/actionIcon.ts b/src/bg/utils/actionIcon.js similarity index 83% rename from src/bg/utils/actionIcon.ts rename to src/bg/utils/actionIcon.js index 542518e..e400188 100644 --- a/src/bg/utils/actionIcon.ts +++ b/src/bg/utils/actionIcon.js @@ -3,9 +3,9 @@ import { disabledHosts, getHostname } from './storage' /** * Updates the action icon - * @param hostname Hostname + * @param {string} hostname Hostname */ -export function updateIcon(hostname: string): void { +export function updateIcon(hostname) { let disabled = disabledHosts.includes(hostname) browserAction.setIcon({ @@ -24,7 +24,7 @@ export function updateIcon(hostname: string): void { /** * Async function to update the icon of the currently active tab. Uses `updateIcon` internally */ -export async function updateActiveTabIcon(): Promise { +export async function updateActiveTabIcon() { let browserTabs = await tabs.query({ active: true, currentWindow: true }) let tab = browserTabs[0] diff --git a/src/bg/utils/domainEnds.ts b/src/bg/utils/domainEnds.js similarity index 98% rename from src/bg/utils/domainEnds.ts rename to src/bg/utils/domainEnds.js index 6eba323..3903313 100644 --- a/src/bg/utils/domainEnds.ts +++ b/src/bg/utils/domainEnds.js @@ -1,5 +1,5 @@ // Domain ends of google -export const domainEnds: string[] = [ +export const domainEnds = [ 'com', 'ac', 'ad', diff --git a/src/bg/utils/storage.ts b/src/bg/utils/storage.js similarity index 72% rename from src/bg/utils/storage.ts rename to src/bg/utils/storage.js index c9eeaf9..2613880 100644 --- a/src/bg/utils/storage.ts +++ b/src/bg/utils/storage.js @@ -4,7 +4,8 @@ import { updateIcon } from './actionIcon' export const KEY_DISABLED_HOSTS = 'disabled_hosts' // Listens to changes on the storage. Updates disabled hosts list, if stored list changes -export let disabledHosts: string[] = await getDisabledHosts() +/** @type {string[]} */ +export let disabledHosts = await getDisabledHosts() storage.local.onChanged.addListener((changes) => { if (KEY_DISABLED_HOSTS in changes) { disabledHosts = changes[KEY_DISABLED_HOSTS].newValue ?? [] @@ -13,18 +14,18 @@ storage.local.onChanged.addListener((changes) => { /** * Async function to get the list of disabled hostnames - * @returns List of disabled hostnames + * @returns {Promise} List of disabled hostnames */ -async function getDisabledHosts(): Promise { +async function getDisabledHosts() { return (await storage.local.get(KEY_DISABLED_HOSTS))[KEY_DISABLED_HOSTS] ?? [] } /** * Async function to invert the state of a hostname. * Adds new entry if not disabled, removes entry, if already disabled - * @param hostname Hostname to invert the state of + * @param {string} hostname Hostname to invert the state of */ -export async function invertHostState(hostname: string): Promise { +export async function invertHostState(hostname) { if (disabledHosts.includes(hostname)) { disabledHosts.splice(disabledHosts.indexOf(hostname), 1) } else { @@ -40,10 +41,10 @@ export async function invertHostState(hostname: string): Promise { /** * Retrieves the hostname from a URL - * @param url Full URL string - * @returns Hostname string + * @param {string} url Full URL string + * @returns {string} Hostname string */ -export function getHostname(url: string): string { +export function getHostname(url) { url = url.replace(/^\w+:\/\//, '') url = url.split(/[\/#\?]/, 1)[0] return url diff --git a/src/map.html b/src/map.html index 1f49292..7ac0c16 100644 --- a/src/map.html +++ b/src/map.html @@ -12,6 +12,6 @@
- + diff --git a/src/map/map.ts b/src/map/map.js similarity index 80% rename from src/map/map.ts rename to src/map/map.js index 3c9fd81..5c90a8a 100644 --- a/src/map/map.ts +++ b/src/map/map.js @@ -1,18 +1,18 @@ import L from 'leaflet' import 'leaflet-fullscreen' -import { readPB, readQ, type MapData } from './utils/read' -import { type TileType, tileTypes } from './utils/parsePB' +import { readPB, readQ, MapData } from './utils/read' +import { tileTypes } from './utils/parsePB' -type Tiles = { - [K in TileType]: { - layer: string - attr: string - } -} +/** + * @typedef {object} Tile + * @property {string} layer + * @property {string} attr + */ // https://leaflet-extras.github.io/leaflet-providers/preview/ -const tileProviders: Tiles = { +/** @type {{TileTypes: Tile}} */ +const tileProviders = { roadmap: { layer: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', // OpenStreetMap.Mapnik attr: '© OpenStreetMap', @@ -24,12 +24,13 @@ const tileProviders: Tiles = { }, // TODO: add street layer etc to satellite } -const gPos: string = 'pb' -const gQuery: string = 'q' -const gZoom: string = 'z' -const params: URLSearchParams = new URLSearchParams(document.location.search) +const gPos = 'pb' +const gQuery = 'q' +const gZoom = 'z' +const params = new URLSearchParams(document.location.search) -let mapData: MapData = {} +/** @type {MapData} */ +const mapData = {} if (params.has(gPos)) { mapData = await readPB(params.get(gPos) as string) @@ -45,7 +46,8 @@ if (params.has(gZoom)) { mapData.zoom = parseInt(params.get(gZoom) as string) } -const map: L.Map = L.map('map', { +/** @type {L.Map} */ +const map = L.map('map', { fullscreenControl: true, scrollWheelZoom: true, // TODO: on pc allow ctrl + scroll zoom: mapData.zoom ?? 17, diff --git a/src/map/utils/parseDMS.js b/src/map/utils/parseDMS.js new file mode 100644 index 0000000..080f658 --- /dev/null +++ b/src/map/utils/parseDMS.js @@ -0,0 +1,27 @@ +/** + * Converts DMS coordinates to Lat and Lon + * @param {string} input String containing DMS coordinates + * @returns {[number, number]} Array containing Latitude and Longitude + */ +export function parseDMS(input) { + /** @type {string[]} */ + const parts = input.split(/[^\d\w\.]+/) + const lat = convertDMSToDD(parts.slice(0, 4)) + const lon = convertDMSToDD(parts.slice(4)) + + return [lat, lon] +} +/** + * Converts DMS part to Lat/Lon + * @param {string[]} dms Array of four strings representing: Degree Minutes Seconds Direction + * @returns {number} DMS part converted to Latitude / Longitude + */ +function convertDMSToDD(dms) { + const [degrees, minutes, seconds, direction] = dms + let dd = Number(degrees) + Number(minutes) / 60 + Number(seconds) / (60 * 60) + + if (direction === 'S' || direction === 'W') { + dd = dd * -1 + } // Don't do anything for N or E + return dd +} diff --git a/src/map/utils/parseDMS.ts b/src/map/utils/parseDMS.ts deleted file mode 100644 index 8f75b43..0000000 --- a/src/map/utils/parseDMS.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Converts DMS coordinates to Lat and Lon - * @param input String containing DMS coordinates - * @returns Array containing Latitude and Longitude - */ -export function parseDMS(input: string): [number, number] { - let parts: string[] = input.split(/[^\d\w\.]+/) - let lat = convertDMSToDD(parts.slice(0, 4)) - let lon = convertDMSToDD(parts.slice(4)) - - return [lat, lon] -} -/** - * Converts DMS part to Lat/Lon - * @param dms Array of four strings representing: Degree Minutes Seconds Direction - * @returns DMS part converted to Latitude / Longitude - */ -function convertDMSToDD(dms: string[]): number { - const [degrees, minutes, seconds, direction] = dms - let dd = Number(degrees) + Number(minutes) / 60 + Number(seconds) / (60 * 60) - - if (direction === 'S' || direction === 'W') { - dd = dd * -1 - } // Don't do anything for N or E - return dd -} diff --git a/src/map/utils/parsePB.ts b/src/map/utils/parsePB.js similarity index 65% rename from src/map/utils/parsePB.ts rename to src/map/utils/parsePB.js index 9ea13f6..89d6b49 100644 --- a/src/map/utils/parsePB.ts +++ b/src/map/utils/parsePB.js @@ -1,5 +1,9 @@ -export type TileType = 'roadmap' | 'satellite' -export const tileTypes: TileType[] = ['roadmap', 'satellite'] + +/** + * @typedef {'roadmap' | 'satellite'} TileType + * @type {TileType[]} + */ +export const tileTypes = ['roadmap', 'satellite'] /** * Takes one bang operator and decodes it. @@ -17,16 +21,18 @@ export const tileTypes: TileType[] = ['roadmap', 'satellite'] * * Unknown types are kept as string. * - * @param item One bang operator with the structure: position, one character (data type), encoded data - * @returns Array of two items. First is the decoded result. Second describes if the result is a new matrix. + * @param {string} item One bang operator with the structure: position, one character (data type), encoded data + * @returns {[string | TileType | number, boolean]} Array of two items. First is the decoded result. Second describes if the result is a new matrix. */ -function convertType(item: string): [string | TileType | number, boolean] { +function convertType(item) { item = item.replace(/^\d+/, '') - const type: string = item.charAt(0) + /** @type {string} */ + const type = item.charAt(0) item = item.substring(1) // s: string || v: timestamp || b: boolean?/byte? - let val: string | TileType | number = item + /** @type {string | TileType | number} */ + let val = item switch (type) { case 'f': @@ -66,11 +72,11 @@ function convertType(item: string): [string | TileType | number, boolean] { * - https://andrewwhitby.com/2014/09/09/google-maps-new-embed-format/ * - https://blog.themillhousegroup.com/2016/08/deep-diving-into-google-pb-embedded-map.html * - https://stackoverflow.com/a/47042514 - * @param items Bang operators (e.g. `!1m13`) split into pieces at `!` - * @param out Array for top and recursion levels to save results and return - * @returns Filled `out` array with bang operators converted into readable format + * @param {string[]} items Bang operators (e.g. `!1m13`) split into pieces at `!` + * @param {any[]} out Array for top and recursion levels to save results and return + * @returns {any[]} Filled `out` array with bang operators converted into readable format */ -export function parsePB(items: string[], out: any[] = []): any[] { +export function parsePB(items, out = []) { let i = 0 while (i < items.length) { let [val, isNew] = convertType(items[i]) @@ -78,9 +84,9 @@ export function parsePB(items: string[], out: any[] = []): any[] { if (!isNew) { out.push(val) } else { - let itemsPart = items.slice(i + 1, i + (val as number) + 1) + let itemsPart = items.slice(i + 1, i + val + 1) out.push(parsePB(itemsPart)) - i += val as number + i += val } i++ } diff --git a/src/map/utils/read.js b/src/map/utils/read.js new file mode 100644 index 0000000..8ad6d18 --- /dev/null +++ b/src/map/utils/read.js @@ -0,0 +1,112 @@ +import { TileType, parsePB, tileTypes } from './parsePB' +import { parseDMS } from './parseDMS' +import { getMapZoom } from './zoom' + +export const nominatimQ = 'https://nominatim.openstreetmap.org/search?limit=1&format=json&q=' +const cidMatch = /^0x[\da-f]+:0x[\da-f]+$/i +const dmsMatch = /^\d{1,2}°\d{1,2}'\d{1,2}\.\d"(N|S) \d{1,2}°\d{1,2}'\d{1,2}\.\d"(E|W)$/i + +/** + * @typedef {object} LatLon + * @property {number} lat + * @property {number} lon + */ + +/** + * @typedef {object} Marker + * @property {number} lat + * @property {number} lon + * @property {string} label + */ + +/** + * @typedef {object} MapData + * @property {LatLon?} area + * @property {number?} zoom + * @property {TileType?} tile + * @property {Marker[]?} markers + */ + +/** + * Decodes the `pb` parameter with the help of `parsePB` and `readQ` + * @param {string} param Content of the `pb` parameter as a string + * @returns {Promise} MapData with area, zoom, tile type and markers + */ +export async function readPB(param) { + /** @type {MapData} */ + const mapData = { + markers: [], + } + + let data = parsePB(param.split('!').slice(1))[0] + + /** @type {number[]} */ + let mapArea = data[0][0] + mapData.area = { + lat: mapArea[2], + lon: mapArea[1], + } + + mapData.zoom = getMapZoom(mapArea[0]) + + /** @type {any[] | string} */ + let currMarkers = data[1] + if (typeof currMarkers !== 'string') { + for (let markers of currMarkers[0]) { + if (markers.match(cidMatch)) { + // TODO: understand CID + console.log(markers) + } else if (markers.match(dmsMatch)) { + let [lat, lon] = parseDMS(markers) + if (lat && lon) { + mapData.markers?.push({ + lat: lat, + lon: lon, + label: `${lat} ${lon}`, + }) + } + } else { + let marker = await readQ(markers) + if (marker) { + mapData.markers?.push(marker) + } + } + } + } + + if (tileTypes.includes(data[data.length - 1])) { + mapData.tile = data[data.length - 1] + } + + return mapData +} + +/** + * Makes a request to the Nominatim API to get the coordinates of an address + * + * Reference: https://medium.com/@sowmyaaji/how-to-build-a-backend-with-jquery-and-add-a-leaflet-js-frontend-e77f2079c852 + * @param {string} addr An address or place + * @returns {Promise} The Latitude and Logitude of the address or place + */ +export async function readQ(addr) { + const uri = encodeURI(nominatimQ + addr) + const res = await fetch(uri) + + if (!res.ok) { + return null + } + + /** @type {LatLon[]} */ + const json = await res.json() + /** @type {LatLon} */ + const body = json[0] + + /** @type {Marker} */ + const marker = { + lat: parseFloat(body.lat), + lon: parseFloat(body.lon), + label: addr, + } + + return marker +} diff --git a/src/map/utils/read.ts b/src/map/utils/read.ts deleted file mode 100644 index 1b38f3a..0000000 --- a/src/map/utils/read.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { parsePB, tileTypes, type TileType } from './parsePB' -import { parseDMS } from './parseDMS' -import { getMapZoom } from './zoom' - -export const nominatimQ: string = - 'https://nominatim.openstreetmap.org/search?limit=1&format=json&q=' -const cidMatch: RegExp = /^0x[\da-f]+:0x[\da-f]+$/i -const dmsMatch: RegExp = /^\d{1,2}°\d{1,2}'\d{1,2}\.\d"(N|S) \d{1,2}°\d{1,2}'\d{1,2}\.\d"(E|W)$/i - -export type Marker = { - lat: number - lon: number - label: string -} - -export type MapData = { - area?: { - lat: number - lon: number - } - zoom?: number - tile?: TileType - - markers?: Marker[] -} - -/** - * Decodes the `pb` parameter with the help of `parsePB` and `readQ` - * @param param Content of the `pb` parameter as a string - * @returns MapData with area, zoom, tile type and markers - */ -export async function readPB(param: string): Promise { - let mapData: MapData = { - markers: [], - } - - let data = parsePB(param.split('!').slice(1))[0] - - let mapArea: number[] = data[0][0] - mapData.area = { - lat: mapArea[2], - lon: mapArea[1], - } - - mapData.zoom = getMapZoom(mapArea[0]) - - let currMarkers: any[] | string = data[1] - if (typeof currMarkers !== 'string') { - for (let markers of currMarkers[0] as string[]) { - if (markers.match(cidMatch)) { - // TODO: understand CID - console.log(markers) - } else if (markers.match(dmsMatch)) { - let [lat, lon] = parseDMS(markers) - if (lat && lon) { - mapData.markers?.push({ - lat: lat, - lon: lon, - label: `${lat} ${lon}`, - }) - } - } else { - let marker = await readQ(markers) - if (marker) { - mapData.markers?.push(marker) - } - } - } - } - - if (tileTypes.includes(data[data.length - 1])) { - mapData.tile = data[data.length - 1] - } - - return mapData -} - -/** - * Makes a request to the Nominatim API to get the coordinates of an address - * - * Reference: https://medium.com/@sowmyaaji/how-to-build-a-backend-with-jquery-and-add-a-leaflet-js-frontend-e77f2079c852 - * @param addr An address or place - * @returns The Latitude and Logitude of the address or place - */ -export async function readQ(addr: string): Promise { - let uri = encodeURI(nominatimQ + addr) - let res = await fetch(uri) - - if (!res.ok) { - return null - } - - let json: { lat: string; lon: string }[] = await res.json() - - let body: { lat: string; lon: string } = json[0] - - let marker: Marker = { - lat: parseFloat(body.lat), - lon: parseFloat(body.lon), - label: addr, - } - - return marker -} diff --git a/src/map/utils/zoom.ts b/src/map/utils/zoom.js similarity index 69% rename from src/map/utils/zoom.ts rename to src/map/utils/zoom.js index c226e3b..045bd3f 100644 --- a/src/map/utils/zoom.ts +++ b/src/map/utils/zoom.js @@ -1,15 +1,15 @@ -const factor: number = 35200000 -const precision: number = 10 +const factor = 35200000 +const precision = 10 /** * Converts *altitude over the map* to *zoom level of the map* * TODO: Should be rewritten!!! * * Reference: https://groups.google.com/g/google-earth-browser-plugin/c/eSL9GlAkWBk/m/T4mdToJz_FgJ - * @param alt Altitude as number - * @returns Zoom level between 0 and 19 + * @param {number} alt Altitude as number + * @returns {number} Zoom level between 0 and 19 */ -export function getMapZoom(alt: number): number { +export function getMapZoom(alt) { let zoom = Math.log2(factor / alt) * 1.225 zoom = Math.round((zoom + Number.EPSILON) * precision) / precision diff --git a/src/options.html b/src/options.html index cf89db0..e7ceb59 100644 --- a/src/options.html +++ b/src/options.html @@ -6,7 +6,7 @@ - + Replace Maps Options diff --git a/src/options/options.ts b/src/options/options.js similarity index 73% rename from src/options/options.ts rename to src/options/options.js index 5840048..77da5ab 100644 --- a/src/options/options.ts +++ b/src/options/options.js @@ -6,12 +6,12 @@ import { invertHostState, } from '../bg/utils/storage' -const table = document.querySelector('.table')! +const table = document.querySelector('.table') /** * (Re)Builds the list of diasabled hostnames */ -function buildEntries(): void { +function buildEntries() { table.innerHTML = '' disabledHosts.forEach(createEntry) } @@ -20,7 +20,7 @@ function buildEntries(): void { * Async function to add a hostname (from the form / URL Search Params) to the displayed list and the storage list of disabled hosts * If the entry is already present in the stored hosts, no entry is added to the display list */ -async function addEntry(): Promise { +async function addEntry() { const search = new URLSearchParams(document.location.search) let hostname = search.get('hostname') if (hostname === null) return @@ -34,9 +34,9 @@ async function addEntry(): Promise { /** * Creates a new entry for the displayed list of disabled hostnames and appends it to the view - * @param hostname Hostname to add to the list + * @param {string} hostname Hostname to add to the list */ -function createEntry(hostname: string): void { +function createEntry(hostname) { const div = document.createElement('div') let span = document.createElement('span') @@ -52,13 +52,13 @@ function createEntry(hostname: string): void { /** * Async funtion to remove an entry at click of its button. * Takes the index in the table to remove it from the list of stored hostnames - * @param click Button click + * @param {MouseEvent} click Button click */ -async function removeEntry(click: MouseEvent): Promise { - let target: EventTarget | null = click.target +async function removeEntry(click) { + const target = click.target if (target === null) return - let index = getIndex(target as HTMLButtonElement) + let index = getIndex(target) if (index === -1) return await invertHostState(disabledHosts[index]) @@ -66,11 +66,11 @@ async function removeEntry(click: MouseEvent): Promise { /** * Gets the index of a list entry using its clicked button - * @param button Button that was clicked to remove an entry - * @returns Index of the list entry + * @param {HTMLButtonElement} button Button that was clicked to remove an entry + * @returns {number} Index of the list entry */ -function getIndex(button: HTMLButtonElement): number { - let div: HTMLDivElement = button.parentElement as HTMLDivElement +function getIndex(button) { + let div = button.parentElement if (div === null) return -1 let index = Array.from(table.children).indexOf(div) diff --git a/test/map/utils/parseDMS.test.ts b/test/map/utils/parseDMS.test.js similarity index 100% rename from test/map/utils/parseDMS.test.ts rename to test/map/utils/parseDMS.test.js diff --git a/test/map/utils/parsePB.test.ts b/test/map/utils/parsePB.test.js similarity index 100% rename from test/map/utils/parsePB.test.ts rename to test/map/utils/parsePB.test.js diff --git a/test/map/utils/read.test.ts b/test/map/utils/read.test.js similarity index 94% rename from test/map/utils/read.test.ts rename to test/map/utils/read.test.js index 4470915..c549fc3 100644 --- a/test/map/utils/read.test.ts +++ b/test/map/utils/read.test.js @@ -6,7 +6,13 @@ globalThis.fetch = vitest.fn() const input = 'test position' const result = [{ lat: '1.1', lon: '1.1' }] -function mockNominatimResponse(data: { lat: string; lon: string }[], status: boolean) { +/** + * + * @param {{lat: string; lon: string}} data + * @param {boolean} status + * @returns {Response} + */ +function mockNominatimResponse(data, status) { return { ok: status, json: () => new Promise((resolve) => resolve(data)) } } diff --git a/test/map/utils/zoom.test.ts b/test/map/utils/zoom.test.js similarity index 100% rename from test/map/utils/zoom.test.ts rename to test/map/utils/zoom.test.js diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 74b53de..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "allowSyntheticDefaultImports": true, - "baseUrl": ".", - "paths": { - "~/*": ["src/*"] - }, - "target": "ESNext", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ESNext", "DOM"], - "moduleResolution": "Node", - "strict": true, - "sourceMap": true, - "resolveJsonModule": true, - "esModuleInterop": true, - "noEmit": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "declaration": false - }, - "include": ["src"], - "exclude": ["node_modules"] -} diff --git a/vite.config.ts b/vite.config.js similarity index 100% rename from vite.config.ts rename to vite.config.js