Performance improvements, add clock

This commit is contained in:
Pieter Vander Vennet 2020-12-05 03:22:17 +01:00
parent c2b1f6643b
commit efd7631837
21 changed files with 2947 additions and 105 deletions

View file

@ -181,7 +181,7 @@ export class InitUiElements {
// This layer is the layer that gives the questions // This layer is the layer that gives the questions
const featureBox = new FeatureInfoBox( const featureBox = new FeatureInfoBox(
State.state.allElements.getElement(data.id), State.state.allElements.getEventSourceById(data.id),
layer layer
); );

View file

@ -42,10 +42,14 @@ export class ElementStorage {
} }
} }
getElement(elementId): UIEventSource<any> { getEventSourceById(elementId): UIEventSource<any> {
if (elementId in this._elements) { if (elementId in this._elements) {
return this._elements[elementId]; return this._elements[elementId];
} }
console.log("Can not find eventsource with id ", elementId); console.error("Can not find eventsource with id ", elementId);
}
getEventSourceFor(feature): UIEventSource<any> {
return this.getEventSourceById(feature.properties.id);
} }
} }

View file

@ -7,6 +7,7 @@ import {UIElement} from "../UI/UIElement";
import State from "../State"; import State from "../State";
import LayerConfig from "../Customizations/JSON/LayerConfig"; import LayerConfig from "../Customizations/JSON/LayerConfig";
import Hash from "./Web/Hash"; import Hash from "./Web/Hash";
import LazyElement from "../UI/Base/LazyElement";
/*** /***
* A filtered layer is a layer which offers a 'set-data' function * A filtered layer is a layer which offers a 'set-data' function
@ -130,14 +131,13 @@ export class FilteredLayer {
let self = this; let self = this;
this._geolayer = L.geoJSON(data, { this._geolayer = L.geoJSON(data, {
style: feature => { style: feature => {
const tagsSource = State.state.allElements.getElement(feature.properties.id); const tagsSource = State.state.allElements.getEventSourceFor(feature);
return self.layerDef.GenerateLeafletStyle(tagsSource, self._showOnPopup !== undefined); return self.layerDef.GenerateLeafletStyle(tagsSource, self._showOnPopup !== undefined);
}, },
pointToLayer: function (feature, latLng) { pointToLayer: function (feature, latLng) {
// Point to layer converts the 'point' to a layer object - as the geojson layer natively cannot handle points // Point to layer converts the 'point' to a layer object - as the geojson layer natively cannot handle points
// Click handling is done in the next step // Click handling is done in the next step
const tagSource = State.state.allElements.getEventSourceFor(feature);
const tagSource = State.state.allElements.getElement(feature.properties.id);
const style = self.layerDef.GenerateLeafletStyle(tagSource, self._showOnPopup !== undefined); const style = self.layerDef.GenerateLeafletStyle(tagSource, self._showOnPopup !== undefined);
let marker; let marker;
@ -169,10 +169,9 @@ export class FilteredLayer {
closeOnEscapeKey: true, closeOnEscapeKey: true,
}, layer); }, layer);
let uiElement: UIElement;
const eventSource = State.state.allElements.addOrGetElement(feature); const eventSource = State.state.allElements.getEventSourceFor(feature);
uiElement = self._showOnPopup(eventSource, feature); let uiElement: LazyElement = new LazyElement(() => self._showOnPopup(eventSource, feature));
popup.setContent(uiElement.Render()); popup.setContent(uiElement.Render());
layer.bindPopup(popup); layer.bindPopup(popup);
// We first render the UIelement (which'll still need an update later on...) // We first render the UIelement (which'll still need an update later on...)
@ -181,11 +180,16 @@ export class FilteredLayer {
layer.on("click", (e) => { layer.on("click", (e) => {
// We set the element as selected... // We set the element as selected...
uiElement.Activate();
State.state.selectedElement.setData(feature); State.state.selectedElement.setData(feature);
uiElement.Update();
}); });
if (feature.properties.id.replace(/\//g, "_") === Hash.Get().data) { if (feature.properties.id.replace(/\//g, "_") === Hash.Get().data) {
// This element is in the URL, so this is a share link
// We already open it
uiElement.Activate();
popup.setContent(uiElement.Render());
const center = GeoOperations.centerpoint(feature).geometry.coordinates; const center = GeoOperations.centerpoint(feature).geometry.coordinates;
popup.setLatLng({lat: center[1], lng: center[0]}); popup.setLatLng({lat: center[1], lng: center[0]});
popup.openOn(State.state.bm.map); popup.openOn(State.state.bm.map);

View file

@ -1,11 +1,9 @@
import {GeoOperations} from "./GeoOperations"; import {GeoOperations} from "./GeoOperations";
import CodeGrid from "./Web/CodeGrid";
import State from "../State"; import State from "../State";
import opening_hours from "opening_hours"; import opening_hours from "opening_hours";
import {And, Or, Tag} from "./Tags"; import {And, Or, Tag} from "./Tags";
import {Utils} from "../Utils"; import {Utils} from "../Utils";
import CountryCoder from "latlon2country/lib/countryCoder"; import CountryCoder from "latlon2country"
class SimpleMetaTagger { class SimpleMetaTagger {
private _f: (feature: any, index: number) => void; private _f: (feature: any, index: number) => void;
@ -62,33 +60,31 @@ export default class MetaTagging {
}) })
); );
private static country = new SimpleMetaTagger( private static country = new SimpleMetaTagger(
["_country"], "", ["_country"], "The country code of the property (with latlon2country)",
((feature, index) => { (feature, index) => {
const coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/"); const coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/");
const centerPoint = GeoOperations.centerpoint(feature);
let centerPoint: any = GeoOperations.centerpoint(feature);
const lat = centerPoint.geometry.coordinates[1]; const lat = centerPoint.geometry.coordinates[1];
const lon = centerPoint.geometry.coordinates[0] const lon = centerPoint.geometry.coordinates[0]
// But the codegrid SHOULD be a number! coder.GetCountryCodeFor(lon, lat, (countries) => {
coder.CountryCodeFor(lon, lat, (countries) => { feature.properties["_country"] = countries[0].trim().toLowerCase();
feature.properties["_country"] = countries[0]; const tagsSource = State.state.allElements.getEventSourceFor(feature);
console.log("Country is ",countries.join(";")) tagsSource.ping();
}); });
}) }
) )
private static isOpen = new SimpleMetaTagger( private static isOpen = new SimpleMetaTagger(
["_isOpen", "_isOpen:description"], ["_isOpen", "_isOpen:description"],
"If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')", "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')",
(feature => { (feature => {
const tagsSource = State.state.allElements.addOrGetElement(feature);
tagsSource.addCallback(tags => {
if (tags["opening_hours"] !== undefined && tags["_country"] !== undefined) { const tagsSource = State.state.allElements.getEventSourceFor(feature);
tagsSource.addCallbackAndRun(tags => {
if (tags._isOpen !== undefined) { if (tags.opening_hours === undefined || tags._country === undefined) {
// Already defined
return; return;
} }
const oh = new opening_hours(tags["opening_hours"], { const oh = new opening_hours(tags["opening_hours"], {
lat: tags._lat, lat: tags._lat,
lon: tags._lon, lon: tags._lon,
@ -96,13 +92,19 @@ export default class MetaTagging {
country_code: tags._country.toLowerCase() country_code: tags._country.toLowerCase()
} }
}, {tag_key: "opening_hours"}); }, {tag_key: "opening_hours"});
// AUtomatically triggered on the next change
const updateTags = () => { const updateTags = () => {
const oldValueIsOpen = tags["_isOpen"];
tags["_isOpen"] = oh.getState() ? "yes" : "no"; tags["_isOpen"] = oh.getState() ? "yes" : "no";
const comment = oh.getComment(); const comment = oh.getComment();
if (comment) { if (comment) {
tags["_isOpen:description"] = comment; tags["_isOpen:description"] = comment;
} }
if (oldValueIsOpen !== tags._isOpen) {
tagsSource.ping();
}
const nextChange = oh.getNextChange() as Date; const nextChange = oh.getNextChange() as Date;
if (nextChange !== undefined) { if (nextChange !== undefined) {
window.setTimeout( window.setTimeout(
@ -112,7 +114,6 @@ export default class MetaTagging {
} }
} }
updateTags(); updateTags();
}
}) })
}) })

View file

@ -18,7 +18,7 @@ export class Changes {
if (changes.length == 0) { if (changes.length == 0) {
return; return;
} }
const eventSource = tags ?? State.state?.allElements.getElement(elementId); const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId);
const elementTags = eventSource.data; const elementTags = eventSource.data;
const pending : {elementId:string, key: string, value: string}[] = []; const pending : {elementId:string, key: string, value: string}[] = [];
for (const change of changes) { for (const change of changes) {

View file

@ -181,7 +181,7 @@ export class ChangesetHandler {
continue; continue;
} }
console.log("Rewriting id: ", oldId, "-->", newId); console.log("Rewriting id: ", oldId, "-->", newId);
const element = allElements.getElement("node/" + oldId); const element = allElements.getEventSourceById("node/" + oldId);
element.data.id = "node/" + newId; element.data.id = "node/" + newId;
allElements.addElementById("node/" + newId, element); allElements.addElementById("node/" + newId, element);
element.ping(); element.ping();

View file

@ -99,12 +99,15 @@ export class UpdateFromOverpass {
let newIds = 1; let newIds = 1;
for (const feature of geojson.features) { for (const feature of geojson.features) {
if(feature.properties.id === undefined){ if (feature.properties.id === undefined) {
feature.properties.id = "ext/"+newIds; feature.properties.id = "ext/" + newIds;
newIds++; newIds++;
} }
} }
geojson.features.forEach(feature => {
State.state.allElements.addElement(feature);
})
MetaTagging.addMetatags(geojson.features); MetaTagging.addMetatags(geojson.features);
function renderLayers(layers: FilteredLayer[]) { function renderLayers(layers: FilteredLayer[]) {
@ -125,6 +128,7 @@ export class UpdateFromOverpass {
}, 50) }, 50)
} }
renderLayers(State.state.filteredLayers.data); renderLayers(State.state.filteredLayers.data);
} }

View file

@ -263,3 +263,5 @@ Bench icons from StreetComplete: https://github.com/westnordost/StreetComplete/t
Urinal icon: https://thenounproject.com/term/urinal/1307984/ Urinal icon: https://thenounproject.com/term/urinal/1307984/
24/7 icon: https://www.vecteezy.com/vector-art/1394992-24-7-service-and-support-icon-set

12
Svg.ts

File diff suppressed because one or more lines are too long

30
UI/Base/LazyElement.ts Normal file
View file

@ -0,0 +1,30 @@
import {UIElement} from "../UIElement";
export default class LazyElement extends UIElement {
private _content: UIElement = undefined;
public Activate: () => void;
constructor(content: (() => UIElement)) {
super();
this.dumbMode = false;
const self = this;
this.Activate = () => {
if (this._content === undefined) {
self._content = content();
}
self.Update();
}
}
InnerRender(): string {
if (this._content === undefined) {
return "Rendering...";
}
return this._content.InnerRender();
}
}

View file

@ -12,7 +12,7 @@ export class TextField extends InputElement<string> {
private readonly _htmlType: string; private readonly _htmlType: string;
private readonly _textAreaRows: number; private readonly _textAreaRows: number;
private readonly _isValid: (string, country) => boolean; private readonly _isValid: (string,country) => boolean;
private _label: UIElement; private _label: UIElement;
constructor(options?: { constructor(options?: {
@ -22,7 +22,7 @@ export class TextField extends InputElement<string> {
htmlType?: string, htmlType?: string,
label?: UIElement, label?: UIElement,
textAreaRows?: number, textAreaRows?: number,
isValid?: ((s: string, country?: string) => boolean) isValid?: ((s: string, country?: () => string) => boolean)
}) { }) {
super(undefined); super(undefined);
const self = this; const self = this;

View file

@ -14,8 +14,8 @@ import DirectionInput from "./DirectionInput";
interface TextFieldDef { interface TextFieldDef {
name: string, name: string,
explanation: string, explanation: string,
isValid: ((s: string, country?: string) => boolean), isValid: ((s: string, country?:() => string) => boolean),
reformat?: ((s: string, country?: string) => string), reformat?: ((s: string, country?: () => string) => string),
inputHelper?: (value: UIEventSource<string>, options?: { inputHelper?: (value: UIEventSource<string>, options?: {
location: [number, number] location: [number, number]
}) => InputElement<string>, }) => InputElement<string>,
@ -26,8 +26,8 @@ export default class ValidatedTextField {
private static tp(name: string, private static tp(name: string,
explanation: string, explanation: string,
isValid?: ((s: string, country?: string) => boolean), isValid?: ((s: string, country?: () => string) => boolean),
reformat?: ((s: string, country?: string) => string), reformat?: ((s: string, country?: () => string) => string),
inputHelper?: (value: UIEventSource<string>, options?:{ inputHelper?: (value: UIEventSource<string>, options?:{
location: [number, number] location: [number, number]
}) => InputElement<string>): TextFieldDef { }) => InputElement<string>): TextFieldDef {
@ -154,13 +154,14 @@ export default class ValidatedTextField {
ValidatedTextField.tp( ValidatedTextField.tp(
"phone", "phone",
"A phone number", "A phone number",
(str, country: any) => { (str, country: () => string) => {
if (str === undefined) { if (str === undefined) {
return false; return false;
} }
return parsePhoneNumberFromString(str, country?.toUpperCase())?.isValid() ?? false console.log("Validating phone number",str,"in country",country())
return parsePhoneNumberFromString(str, (country())?.toUpperCase() as any)?.isValid() ?? false
}, },
(str, country: any) => parsePhoneNumberFromString(str, country?.toUpperCase()).formatInternational() (str, country: () => string) => parsePhoneNumberFromString(str, (country())?.toUpperCase() as any).formatInternational()
), ),
ValidatedTextField.tp( ValidatedTextField.tp(
"opening_hours", "opening_hours",
@ -200,8 +201,8 @@ export default class ValidatedTextField {
value?: UIEventSource<string>, value?: UIEventSource<string>,
textArea?: boolean, textArea?: boolean,
textAreaRows?: number, textAreaRows?: number,
isValid?: ((s: string, country: string) => boolean), isValid?: ((s: string, country: () => string) => boolean),
country?: string, country?: () => string,
location?: [number /*lat*/, number /*lon*/] location?: [number /*lat*/, number /*lon*/]
}): InputElement<string> { }): InputElement<string> {
options = options ?? {}; options = options ?? {};
@ -304,7 +305,7 @@ export default class ValidatedTextField {
textArea?: boolean, textArea?: boolean,
textAreaRows?: number, textAreaRows?: number,
isValid?: ((string: string) => boolean), isValid?: ((string: string) => boolean),
country?: string country?: () => string
}): InputElement<T> { }): InputElement<T> {
let textField: InputElement<string>; let textField: InputElement<string>;
if (options?.type) { if (options?.type) {

View file

@ -151,7 +151,7 @@ export default class OpeningHoursVisualization extends UIElement {
const tags = this._source.data; const tags = this._source.data;
if (tags._country === undefined) { if (tags._country === undefined) {
return "Loading..."; return "Loading country information...";
} }
let oh = null; let oh = null;
@ -165,7 +165,7 @@ export default class OpeningHoursVisualization extends UIElement {
}, {tag_key: this._key}); }, {tag_key: this._key});
} catch (e) { } catch (e) {
console.log(e); console.log(e);
return "Error: could not visualize these opening hours" return `Error: could not visualize these opening hours<br/><spann class='subtle'>${e}</spann>`
} }
if (!oh.getState() && !oh.getUnknown()) { if (!oh.getState() && !oh.getUnknown()) {

View file

@ -251,7 +251,7 @@ export default class TagRenderingQuestion extends UIElement {
const textField = ValidatedTextField.InputForType(this._configuration.freeform.type, { const textField = ValidatedTextField.InputForType(this._configuration.freeform.type, {
isValid: (str) => (str.length <= 255), isValid: (str) => (str.length <= 255),
country: this._tags.data._country, country: () => this._tags.data._country,
location: [this._tags.data._lat, this._tags.data._lon] location: [this._tags.data._lat, this._tags.data._lon]
}); });

View file

@ -169,5 +169,14 @@ export class Utils {
} }
static MatchKeys(object: any, prototype: any, context?: string){
for (const objectKey in object) {
if(prototype[objectKey] === undefined){
console.error("Key ", objectKey, "might be not supported (in context",context,")")
}
}
}
} }

22
assets/svg/clock.svg Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="100"
height="100"
viewBox="0 0 100 100"
version="1.1"
style="fill:none">
<circle
cx="50"
cy="50"
r="45.0"
id="circle4"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path823"
d="M 46,55.932203 H 66.550847"
style="fill:none;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path825"
d="m 46,55.533898 v -27.805085 0"
style="fill:none;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 805 B

42
assets/svg/closed.svg Normal file
View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg3"
width="100"
height="100"
viewBox="0 0 100 100"
version="1.1"
style="fill:none">
<metadata
id="metadata9">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs7" />
<circle
cx="50"
cy="50"
r="45.408772"
id="circle4"
style="fill:none;fill-opacity:1;stroke:#180000;stroke-width:7.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path823"
d="M 46,55.932203 H 66.550847"
style="fill:none;stroke:#180000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path825"
d="m 46,55.533898 v -27.805085 0"
style="fill:none;stroke:#180000;stroke-width:7.38227367;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -73,6 +73,16 @@
}, },
"tagRenderings": [ "tagRenderings": [
"images", "images",
{
"question": {
"en": "What is the name of this shop?",
"fr": "Qu'est-ce que le nom de ce magasin?"
},
"render": "This shop is called <i>{name}</i>",
"freeform": {
"key": "name"
}
},
{ {
"render": { "render": {
"en": "This shop sells {shop}", "en": "This shop sells {shop}",
@ -226,6 +236,28 @@
"icon": { "icon": {
"render": "./assets/themes/shops/shop.svg" "render": "./assets/themes/shops/shop.svg"
}, },
"iconOverlays": [
{
"if": "_isOpen=yes",
"then": "clock:#0f0",
"badge": true
},
{
"if": "_isOpen=no",
"then": "circle:#f00;clock:#fff",
"badge": true
},
{
"if": {
"and": [
"_isOpen=",
"opening_hours~*"
]
},
"then": "clock:#ff0",
"badge": true
}
],
"width": { "width": {
"render": "8" "render": "8"
}, },

2714
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -20,9 +20,9 @@
"generate:translations": "ts-node scripts/generateTranslations.ts", "generate:translations": "ts-node scripts/generateTranslations.ts",
"generate:layouts": "ts-node scripts/createLayouts.ts", "generate:layouts": "ts-node scripts/createLayouts.ts",
"optimize-images": "cd assets/generated/ && find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'", "optimize-images": "cd assets/generated/ && find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'",
"generate": "npm run generate:images && npm run generate:translations && npm run generate:layouts && npm run generate:editor-layer-index", "generate": "npm run generate:images && npm run generate:translations",
"build": "rm -rf dist/ && npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", "build": "rm -rf dist/ && npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*",
"prepare-deploy": "npm run generate && npm run build && rm -rf .cache", "prepare-deploy": "npm run generate:editor-layer-index && npm run generate:layouts && npm run generate && npm run build && rm -rf .cache",
"deploy:staging": "npm run prepare-deploy && rm -rf /home/pietervdvn/git/pietervdvn.github.io/Staging/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/Staging/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean", "deploy:staging": "npm run prepare-deploy && rm -rf /home/pietervdvn/git/pietervdvn.github.io/Staging/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/Staging/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
"deploy:production": "rm -rf ./assets/generated && npm run prepare-deploy && npm run optimize-images && rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean", "deploy:production": "rm -rf ./assets/generated && npm run prepare-deploy && npm run optimize-images && rm -rf /home/pietervdvn/git/pietervdvn.github.io/MapComplete/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/MapComplete/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
"clean": "rm -rf .cache/ && (find *.html | grep -v \"\\(index\\|land\\|test\\|preferences\\|customGenerator\\).html\" | xargs rm) && (find *.webmanifest | xargs rm)" "clean": "rm -rf .cache/ && (find *.html | grep -v \"\\(index\\|land\\|test\\|preferences\\|customGenerator\\).html\" | xargs rm) && (find *.webmanifest | xargs rm)"
@ -35,13 +35,12 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/leaflet-providers": "^1.2.0", "@types/leaflet-providers": "^1.2.0",
"codegrid-js": "git://github.com/hlaw/codegrid-js.git",
"country-language": "^0.1.7", "country-language": "^0.1.7",
"email-validator": "^2.0.4", "email-validator": "^2.0.4",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"i18next-client": "^1.11.4", "i18next-client": "^1.11.4",
"jquery": "latest", "jquery": "latest",
"latlon2country": "^1.0.3", "latlon2country": "^1.0.7",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"leaflet-providers": "^1.10.2", "leaflet-providers": "^1.10.2",
"libphonenumber": "0.0.10", "libphonenumber": "0.0.10",

14
test.ts
View file

@ -1,17 +1,5 @@
//* /*
import CountryCoder from "latlon2country/lib/countryCoder";
f
unction pr(countries) {
console.log(">>>>>", countries.join(";"))
}
coder.CountryCodeFor(3.2, 51.2, pr)
coder.CountryCodeFor(4.2, 51.2, pr);
coder.CountryCodeFor(4.92119, 51.43995, pr)
coder.CountryCodeFor(4.93189, 51.43552, pr)
coder.CountryCodeFor(34.2581, 44.7536, pr)
/*/ /*/