Add loading of live data
This commit is contained in:
parent
feab5a19b3
commit
0d6412f824
14 changed files with 291 additions and 35 deletions
|
@ -17,6 +17,7 @@ import * as bike_repair_station from "../../assets/layers/bike_repair_station/bi
|
|||
import * as birdhides from "../../assets/layers/bird_hide/birdhides.json"
|
||||
import * as nature_reserve from "../../assets/layers/nature_reserve/nature_reserve.json"
|
||||
import * as bike_cafes from "../../assets/layers/bike_cafe/bike_cafes.json"
|
||||
import * as bike_monitoring_station from "../../assets/layers/bike_monitoring_station/bike_monitoring_station.json"
|
||||
import * as cycling_themed_objects from "../../assets/layers/cycling_themed_object/cycling_themed_objects.json"
|
||||
import * as bike_shops from "../../assets/layers/bike_shop/bike_shop.json"
|
||||
import * as maps from "../../assets/layers/maps/maps.json"
|
||||
|
@ -43,6 +44,7 @@ export class FromJSON {
|
|||
FromJSON.Layer(viewpoint),
|
||||
FromJSON.Layer(bike_parking),
|
||||
FromJSON.Layer(bike_repair_station),
|
||||
FromJSON.Layer(bike_monitoring_station),
|
||||
FromJSON.Layer(birdhides),
|
||||
FromJSON.Layer(nature_reserve),
|
||||
FromJSON.Layer(bike_cafes),
|
||||
|
|
|
@ -117,7 +117,7 @@ export class FilteredLayer {
|
|||
feature.properties["_lon"] = "" + lat; // We expect a string here for lat/lon
|
||||
feature.properties["_lat"] = "" + lon;
|
||||
// But the codegrid SHOULD be a number!
|
||||
CodeGrid.grid.getCode(lat, lon, (error, code) => {
|
||||
CodeGrid.getCode(lat, lon, (error, code) => {
|
||||
if (error === null) {
|
||||
feature.properties["_country"] = code;
|
||||
} else {
|
||||
|
|
|
@ -91,6 +91,14 @@ export class LayerUpdater {
|
|||
|
||||
self.retries.setData(0);
|
||||
|
||||
let newIds = 1;
|
||||
for (const feature of geojson.features) {
|
||||
if(feature.properties.id === undefined){
|
||||
feature.properties.id = "ext/"+newIds;
|
||||
newIds++;
|
||||
}
|
||||
}
|
||||
|
||||
function renderLayers(layers: FilteredLayer[]) {
|
||||
if (layers.length === 0) {
|
||||
self.runningQuery.setData(false);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Bounds} from "../Bounds";
|
||||
import {TagsFilter} from "../Tags";
|
||||
import $ from "jquery"
|
||||
import * as $ from "jquery"
|
||||
import * as OsmToGeoJson from "osmtogeojson";
|
||||
|
||||
/**
|
||||
|
@ -28,13 +28,12 @@ export class Overpass {
|
|||
|
||||
queryGeoJson(bounds: Bounds, continuation: ((any) => void), onFail: ((reason) => void)): void {
|
||||
|
||||
let query = this.buildQuery( "[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]")
|
||||
let query = this.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]")
|
||||
|
||||
if(Overpass.testUrl !== null){
|
||||
if (Overpass.testUrl !== null) {
|
||||
console.log("Using testing URL")
|
||||
query = Overpass.testUrl;
|
||||
}
|
||||
|
||||
$.getJSON(query,
|
||||
function (json, status) {
|
||||
if (status !== "success") {
|
||||
|
@ -42,7 +41,7 @@ export class Overpass {
|
|||
onFail(status);
|
||||
}
|
||||
|
||||
if(json.elements === [] && json.remarks.indexOf("runtime error") > 0){
|
||||
if (json.elements === [] && json.remarks.indexOf("runtime error") > 0) {
|
||||
console.log("Timeout or other runtime error");
|
||||
onFail("Runtime error (timeout)")
|
||||
return;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import codegrid from "codegrid-js";
|
||||
|
||||
export default class CodeGrid {
|
||||
public static readonly grid = CodeGrid.InitGrid();
|
||||
private static readonly grid = CodeGrid.InitGrid();
|
||||
|
||||
|
||||
public static getCode(lat: any, lon: any, handle: (error, code) => void) {
|
||||
CodeGrid.grid.getCode(lat, lon, handle);
|
||||
}
|
||||
|
||||
private static InitGrid(): any {
|
||||
const grid = codegrid.CodeGrid("./tiles/");
|
||||
|
@ -15,4 +20,6 @@ export default class CodeGrid {
|
|||
});
|
||||
return grid;
|
||||
}
|
||||
|
||||
|
||||
}
|
53
Logic/Web/LiveQueryHandler.ts
Normal file
53
Logic/Web/LiveQueryHandler.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* Fetches data from random data sources
|
||||
*/
|
||||
import {UIEventSource} from "../UIEventSource";
|
||||
import * as $ from "jquery"
|
||||
|
||||
export default class LiveQueryHandler {
|
||||
|
||||
|
||||
private static cache = {} // url --> UIEventSource<actual data>
|
||||
private static neededShorthands = {} // url -> (shorthand:paths)[]
|
||||
|
||||
public static FetchLiveData(url: string, shorthands: string[]): UIEventSource<any /* string -> string */> {
|
||||
|
||||
const shorthandsSet: string[] = LiveQueryHandler.neededShorthands[url] ?? []
|
||||
|
||||
for (const shorthand of shorthands) {
|
||||
if (shorthandsSet.indexOf(shorthand) < 0) {
|
||||
shorthandsSet.push(shorthand);
|
||||
}
|
||||
}
|
||||
LiveQueryHandler.neededShorthands[url] = shorthandsSet;
|
||||
|
||||
|
||||
if (LiveQueryHandler[url] === undefined) {
|
||||
const source = new UIEventSource({});
|
||||
LiveQueryHandler[url] = source;
|
||||
|
||||
console.log("Fetching live data from a third-party (unknown) API:",url)
|
||||
$.getJSON(url, function (data) {
|
||||
for (const shorthandDescription of shorthandsSet) {
|
||||
|
||||
const descr = shorthandDescription.trim().split(":");
|
||||
const shorthand = descr[0];
|
||||
const path = descr[1];
|
||||
const parts = path.split(".");
|
||||
let trail = data;
|
||||
for (const part of parts) {
|
||||
if (trail !== undefined) {
|
||||
trail = trail[part];
|
||||
}
|
||||
}
|
||||
source.data[shorthand] = trail;
|
||||
}
|
||||
source.ping();
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
return LiveQueryHandler[url];
|
||||
}
|
||||
|
||||
}
|
|
@ -184,3 +184,6 @@ Park icon via http://www.onlinewebfonts.com/icon/425974, CC BY 3.0 (@sterankofra
|
|||
Forest icon via https://www.onlinewebfonts.com/icon/498112, CC BY
|
||||
|
||||
Statistics icon via https://www.onlinewebfonts.com/icon/197818
|
||||
|
||||
Chronometer (on monitoring_station.svg): ANTU chronometer
|
||||
https://commons.wikimedia.org/w/index.php?title=Antu_chronometer&action=edit&redlink=1
|
|
@ -1,6 +1,8 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import OpeningHoursVisualization from "./OhVisualization";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import LiveQueryHandler from "../Logic/Web/LiveQueryHandler";
|
||||
|
||||
export default class SpecialVisualizations {
|
||||
|
||||
|
@ -8,13 +10,13 @@ export default class SpecialVisualizations {
|
|||
funcName: string,
|
||||
constr: ((tagSource: UIEventSource<any>, argument: string[]) => UIElement),
|
||||
docs: string,
|
||||
args: {name: string, defaultValue: string, doc: string}[]
|
||||
args: { name: string, defaultValue?: string, doc: string }[]
|
||||
}[] =
|
||||
|
||||
[{
|
||||
funcName: "opening_hours_table",
|
||||
docs: "Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'.",
|
||||
args:[{name:"key", defaultValue: "opening_hours", doc: "The tag from which the table is constructed"}],
|
||||
args: [{name: "key", defaultValue: "opening_hours", doc: "The tag from which the table is constructed"}],
|
||||
constr: (tagSource: UIEventSource<any>, args) => {
|
||||
let keyname = args[0];
|
||||
if (keyname === undefined || keyname === "") {
|
||||
|
@ -24,6 +26,26 @@ export default class SpecialVisualizations {
|
|||
}
|
||||
},
|
||||
|
||||
{
|
||||
funcName: "live",
|
||||
docs: "Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)}",
|
||||
args: [{
|
||||
name: "Url", doc: "The URL to load"
|
||||
}, {
|
||||
name: "Shorthands",
|
||||
doc: "A list of shorthands, of the format 'shorthandname:path.path.path'. Seperated by ;"
|
||||
}, {
|
||||
name: "path", doc: "The path (or shorthand) that should be returned"
|
||||
}],
|
||||
constr: (tagSource: UIEventSource<any>, args) => {
|
||||
const url = args[0];
|
||||
const shorthands = args[1];
|
||||
const neededValue = args[2];
|
||||
const source = LiveQueryHandler.FetchLiveData(url, shorthands.split(";"));
|
||||
return new VariableUiElement(source.map(data => data[neededValue] ?? "Loading..."));
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
}
|
|
@ -47,10 +47,10 @@ export default class Translation extends UIElement {
|
|||
|
||||
for (const knownSpecial of knownSpecials) {
|
||||
|
||||
|
||||
do {
|
||||
const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*)\\)}(.*)`);
|
||||
if (matched === null) {
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
const partBefore = matched[1];
|
||||
const argument = matched[2];
|
||||
|
@ -64,6 +64,10 @@ export default class Translation extends UIElement {
|
|||
console.error(e);
|
||||
template = partBefore + partAfter;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
|
||||
}
|
||||
newTranslations[lang] = template;
|
||||
}
|
||||
|
|
|
@ -103,10 +103,10 @@ export default class Translations {
|
|||
}),
|
||||
|
||||
respectPrivacy: new T({
|
||||
en: "Please respect privacy. Do not photograph people nor license plates.<br/>Respect copyright. Only upload images you made yourself. Do not upload Google Streetview Images - these will be removed.",
|
||||
en: "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.",
|
||||
ca: "Respecta la privacitat. No fotografiïs gent o matrícules",
|
||||
es: "Respeta la privacidad. No fotografíes gente o matrículas",
|
||||
nl: "Respecteer privacy. Fotografeer geen mensen of nummerplaten.<br/>Repecteer auteursrechten. Voeg enkel foto's toe die je zelf maakte. Screenshots van andere services (zoals Google Streetview) worden verwijderd",
|
||||
nl: "Fotografeer geen mensen of nummerplaten. Voeg geen Google Maps, Google Streetview of foto's met auteursrechten toe.",
|
||||
fr: "Merci de respecter la vie privée. Ne publiez pas les plaques d\'immatriculation",
|
||||
gl: "Respecta a privacidade. Non fotografes xente ou matrículas",
|
||||
de: "Bitte respektieren Sie die Privatsphäre. Fotografieren Sie weder Personen noch Nummernschilder"
|
||||
|
@ -969,6 +969,9 @@ export default class Translations {
|
|||
}
|
||||
|
||||
public static WT(s: string | Translation): Translation {
|
||||
if(s === undefined){
|
||||
return undefined;
|
||||
}
|
||||
if (typeof (s) === "string") {
|
||||
return new Translation({en: s});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"id": "bike_monitoring_station",
|
||||
"name": {
|
||||
"en": "Monitoring stations"
|
||||
},
|
||||
"minzoom": 12,
|
||||
"overpassTags": {
|
||||
"and": [
|
||||
"man_made=monitoring_station",
|
||||
"monitoring:bicycle=yes"
|
||||
]
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"nl": "Fietstelstation",
|
||||
"en": "Bicycle counting station"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "name~*",
|
||||
"then": {
|
||||
"en": "Bicycle counting station {name}",
|
||||
"nl": "Fietstelstation {name}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "ref~*",
|
||||
"then": {
|
||||
"en": "Bicycle counting station {ref}",
|
||||
"nl": "Fietstelstation {ref}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": {},
|
||||
"tagRenderings": [
|
||||
{
|
||||
"render": "<b>{live({url},{url:format},hour)}</b> cyclists last hour<br/><b>{live({url},{url:format},day)}</b> cyclists today<br/><b>{live({url},{url:format},year)}</b> cyclists this year<br/>",
|
||||
"condition": {
|
||||
"and": ["url~*","url:format~*"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hideUnderlayingFeaturesMinPercentage": 0,
|
||||
"icon": {
|
||||
"render": "./assets/layers/bike_monitoring_station/monitoring_station.svg"
|
||||
},
|
||||
"width": {
|
||||
"render": "8"
|
||||
},
|
||||
"iconSize": {
|
||||
"render": "40,40,center"
|
||||
},
|
||||
"color": {
|
||||
"render": "#00f"
|
||||
},
|
||||
"presets": []
|
||||
}
|
96
assets/layers/bike_monitoring_station/monitoring_station.svg
Normal file
96
assets/layers/bike_monitoring_station/monitoring_station.svg
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?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="svg27"
|
||||
version="1.1"
|
||||
fill="none"
|
||||
viewBox="0 0 97 123"
|
||||
height="123"
|
||||
width="97">
|
||||
<metadata
|
||||
id="metadata31">
|
||||
<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>
|
||||
<path
|
||||
style="fill:#ffd42a"
|
||||
id="path2"
|
||||
fill="#5675DF"
|
||||
d="M52.1412 111.419C50.4633 115.605 44.5366 115.605 42.8588 111.419L24.7014 66.1099C23.385 62.8252 25.8039 59.25 29.3426 59.25L65.6574 59.25C69.1962 59.25 71.615 62.8252 70.2986 66.11L52.1412 111.419Z" />
|
||||
<ellipse
|
||||
style="fill:#ffd42a"
|
||||
id="ellipse4"
|
||||
fill="#5675DF"
|
||||
ry="47.5"
|
||||
rx="48.5"
|
||||
cy="47.5"
|
||||
cx="48.5" />
|
||||
<defs
|
||||
id="defs25">
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
filterUnits="userSpaceOnUse"
|
||||
height="53.5"
|
||||
width="40.7188"
|
||||
y="25.5"
|
||||
x="32.2812"
|
||||
id="filter0_d">
|
||||
<feFlood
|
||||
id="feFlood10"
|
||||
result="BackgroundImageFix"
|
||||
flood-opacity="0" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix12"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
type="matrix"
|
||||
in="SourceAlpha" />
|
||||
<feOffset
|
||||
id="feOffset14"
|
||||
dy="4" />
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur16"
|
||||
stdDeviation="2" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix18"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
|
||||
type="matrix" />
|
||||
<feBlend
|
||||
id="feBlend20"
|
||||
result="effect1_dropShadow"
|
||||
in2="BackgroundImageFix"
|
||||
mode="normal" />
|
||||
<feBlend
|
||||
id="feBlend22"
|
||||
result="shape"
|
||||
in2="effect1_dropShadow"
|
||||
in="SourceGraphic"
|
||||
mode="normal" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
style="fill:#ffffff"
|
||||
transform="matrix(0.1210062,0,0,0.1210062,13.583344,11.282657)"
|
||||
id="g844">
|
||||
<path
|
||||
d="m 464.99,141.27 c 0,0 8.996,9.05 15.346,0.017 l 12.1,-17.349 c 4.225,-6.103 -1.231,-10.327 -1.796,-10.744 l -73.41,-51.29 h -0.017 c -4.52,-3.145 -7.521,-1.236 -9.1,0.533 l -1.311,1.891 -11.181,16.01 c -0.053,0.061 -6.315,9.937 3.695,15.16 v 0.013 c 14.295,7.03 40.526,21.868 65.66,45.814 l 0.018,-0.051"
|
||||
id="path838" />
|
||||
<path
|
||||
d="M 341.45,0 H 225.76 c -8.978,0 -16.334,7.213 -16.334,16.338 v 13.484 c 0,9.112 7.356,16.468 16.334,16.468 h 5.725 v 9.521 c 0.009,13.688 8.648,14.825 12.985,14.534 0.737,-0.109 1.501,-0.226 2.308,-0.33 0.009,0 0.122,-0.026 0.122,-0.026 h -0.009 c 15.397,-2.108 40.9,-4.125 70.15,-0.403 l 0.017,-0.026 c 0,0 18.737,3.474 18.686,-13.758 v -9.52 h 5.725 c 8.987,0 16.325,-7.348 16.325,-16.473 V 16.338 C 357.7939,7.221 350.437,0 341.45,0"
|
||||
id="path840" />
|
||||
<path
|
||||
d="m 11,5.563 c -3.71,0 -6.719,3.01 -6.719,6.719 0,3.71 3.01,6.719 6.719,6.719 3.71,0 6.719,-3.01 6.719,-6.719 0,-3.711 -3.01,-6.719 -6.719,-6.719 m 0.1,2.938 c 0.542,0 1.06,0.107 1.553,0.318 0.493,0.212 0.918,0.496 1.275,0.854 0.358,0.358 0.644,0.782 0.855,1.275 0.212,0.493 0.316,1.011 0.316,1.553 0,0.542 -0.105,1.06 -0.316,1.553 -0.212,0.493 -0.498,0.918 -0.855,1.275 -0.358,0.358 -0.782,0.642 -1.275,0.854 -0.493,0.212 -1.011,0.318 -1.553,0.318 -0.597,0 -1.165,-0.125 -1.703,-0.377 C 8.859,15.872 8.401,15.516 8.022,15.058 7.998,15.023 7.987,14.984 7.989,14.941 7.991,14.898 8.006,14.863 8.034,14.836 l 0.713,-0.719 c 0.035,-0.031 0.079,-0.047 0.131,-0.047 0.056,0.007 0.095,0.028 0.119,0.063 0.253,0.33 0.564,0.585 0.932,0.766 0.368,0.181 0.759,0.27 1.172,0.27 0.361,0 0.707,-0.07 1.035,-0.211 0.328,-0.141 0.612,-0.331 0.852,-0.57 0.24,-0.24 0.43,-0.523 0.57,-0.852 0.141,-0.328 0.211,-0.672 0.211,-1.033 0,-0.361 -0.07,-0.705 -0.211,-1.033 -0.141,-0.328 -0.331,-0.612 -0.57,-0.852 -0.24,-0.24 -0.523,-0.43 -0.852,-0.57 -0.328,-0.141 -0.674,-0.211 -1.035,-0.211 -0.34,0 -0.666,0.06 -0.979,0.184 -0.312,0.123 -0.591,0.3 -0.834,0.529 l 0.715,0.719 c 0.108,0.104 0.131,0.224 0.072,0.359 -0.059,0.139 -0.161,0.209 -0.307,0.209 H 7.434 c -0.09,0 -0.168,-0.034 -0.234,-0.1 C 7.134,11.671 7.1,11.593 7.1,11.503 V 9.169 C 7.1,9.023 7.17,8.921 7.309,8.862 7.444,8.803 7.564,8.827 7.668,8.934 L 8.346,9.606 C 8.718,9.255 9.142,8.984 9.619,8.792 c 0.477,-0.193 0.97,-0.289 1.48,-0.289"
|
||||
transform="matrix(35.45144,0,0,35.45144,-106.35,-106.35)"
|
||||
id="path842" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
|
@ -20,11 +20,11 @@
|
|||
"icon": "./assets/themes/cyclofix/logo.svg",
|
||||
"version": "0",
|
||||
"startLat": 50.8465573,
|
||||
|
||||
"defaultBackgroundId": "CartoDB.Voyager",
|
||||
"startLon": 4.3516970,
|
||||
"startZoom": 16,
|
||||
"widenFactor": 0.05,
|
||||
"socialImage": "./assets/themes/cyclofix/logo.svg",
|
||||
"layers": ["bike_repair_station", "bike_cafes", "bike_shops", "drinking_water", "bike_parking","bike_themed_object"],
|
||||
"layers": ["bike_repair_station", "bike_cafes", "bike_shops", "drinking_water", "bike_parking","bike_themed_object","bike_monitoring_station"],
|
||||
"roamingRenderings": []
|
||||
}
|
15
test.ts
15
test.ts
|
@ -1,15 +1,16 @@
|
|||
//*
|
||||
|
||||
|
||||
import {UIEventSource} from "./Logic/UIEventSource";
|
||||
import OpeningHoursVisualization from "./UI/OhVisualization";
|
||||
import LiveQueryHandler from "./Logic/Web/LiveQueryHandler";
|
||||
import {VariableUiElement} from "./UI/Base/VariableUIElement";
|
||||
|
||||
const oh = "Tu-Fr 09:00-17:00 'as usual'; mo off 'yyy'; su off 'xxx'"
|
||||
const tags = new UIEventSource<any>({opening_hours:oh});
|
||||
new OpeningHoursVisualization(tags, 'opening_hours').AttachTo('maindiv')
|
||||
const source = LiveQueryHandler.FetchLiveData("https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CJM90",
|
||||
"hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt".split(";"))
|
||||
source.addCallback((data) => {console.log(data)})
|
||||
new VariableUiElement(source.map(data => {
|
||||
return ["Data is:", data.hour, "last hour;", data.day, "last day; ", data.year, "last year;"].join(" ")
|
||||
})).AttachTo('maindiv')
|
||||
|
||||
|
||||
window.setTimeout(() => {tags.data._country = "be"; }, 5000)
|
||||
/*/
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue