2025-06-26 05:20:12 +02:00
import { RenderingSpecification , SpecialVisualization } from "./SpecialVisualization"
2023-06-14 20:39:36 +02:00
import { UploadToOsmViz } from "./Popup/UploadToOsmViz"
import { MultiApplyViz } from "./Popup/MultiApplyViz"
2025-06-02 17:03:07 +02:00
import AutoApplyButtonVis from "./Popup/AutoApplyButtonVis"
2024-01-25 13:41:33 +01:00
import SpecialVisualisationUtils from "./SpecialVisualisationUtils"
2024-05-07 00:42:52 +02:00
import MarkdownUtils from "../Utils/MarkdownUtils"
2025-01-27 04:50:44 +01:00
import { ImageVisualisations } from "./SpecialVisualisations/ImageVisualisations"
import { NoteVisualisations } from "./SpecialVisualisations/NoteVisualisations"
import { FavouriteVisualisations } from "./SpecialVisualisations/FavouriteVisualisations"
import { UISpecialVisualisations } from "./SpecialVisualisations/UISpecialVisualisations"
import { SettingsVisualisations } from "./SpecialVisualisations/SettingsVisualisations"
import { ReviewSpecialVisualisations } from "./SpecialVisualisations/ReviewSpecialVisualisations"
2025-01-29 20:37:04 +01:00
import { DataImportSpecialVisualisations } from "./SpecialVisualisations/DataImportSpecialVisualisations"
2025-06-26 05:20:12 +02:00
import TagrenderingManipulationSpecialVisualisations
from "./SpecialVisualisations/TagrenderingManipulationSpecialVisualisations"
import {
WebAndCommunicationSpecialVisualisations ,
} from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations"
import { DataVisualisations } from "./Popup/DataVisualisations"
import { DataExportVisualisations } from "./Popup/DataExportVisualisations"
2023-10-06 03:34:26 +02:00
2020-10-09 20:10:21 +02:00
export default class SpecialVisualizations {
2022-11-02 13:47:34 +01:00
public static specialVisualizations : SpecialVisualization [ ] = SpecialVisualizations . initList ( )
2024-08-14 13:53:56 +02:00
public static specialVisualisationsDict : Map < string , SpecialVisualization > = new Map <
string ,
SpecialVisualization
> ( )
2024-08-13 17:56:33 +02:00
static {
for ( const specialVisualization of SpecialVisualizations . specialVisualizations ) {
2024-08-14 13:53:56 +02:00
SpecialVisualizations . specialVisualisationsDict . set (
specialVisualization . funcName ,
2025-01-18 00:30:06 +01:00
specialVisualization
2024-08-14 13:53:56 +02:00
)
2024-08-13 17:56:33 +02:00
}
}
2021-12-12 02:59:24 +01:00
2024-05-07 00:42:52 +02:00
public static DocumentationFor ( viz : string | SpecialVisualization ) : string {
2023-02-14 00:09:04 +01:00
if ( typeof viz === "string" ) {
viz = SpecialVisualizations . specialVisualizations . find ( ( sv ) = > sv . funcName === viz )
}
if ( viz === undefined ) {
2024-05-07 00:42:52 +02:00
return ""
2023-02-14 00:09:04 +01:00
}
2024-06-16 16:06:26 +02:00
const example =
viz . example ? ?
"`{" + viz . funcName + "(" + viz . args . map ( ( arg ) = > arg . defaultValue ) . join ( "," ) + ")}`"
2025-06-26 05:20:12 +02:00
let definitionPlace = ""
if ( viz . definedIn ) {
const path = viz . definedIn
definitionPlace = ` Defined in [ ${ path . markdownLocation } ]( ${ path . markdownLocation } ) `
}
2024-05-07 00:42:52 +02:00
return [
"### " + viz . funcName ,
2023-02-14 00:09:04 +01:00
viz . docs ,
viz . args . length > 0
2024-05-07 00:42:52 +02:00
? MarkdownUtils . table (
2025-02-10 02:04:58 +01:00
[ "name" , "default" , "description" ] ,
viz . args . map ( ( arg ) = > {
let defaultArg = arg . defaultValue ? ? "_undefined_"
if ( defaultArg == "" ) {
defaultArg = "_empty string_"
}
return [ arg . name , defaultArg , arg . doc ]
} )
)
2023-02-14 00:09:04 +01:00
: undefined ,
2025-06-26 05:20:12 +02:00
definitionPlace ,
2024-05-07 00:42:52 +02:00
"#### Example usage of " + viz . funcName ,
2025-02-10 02:04:58 +01:00
example ,
2024-05-07 00:42:52 +02:00
] . join ( "\n\n" )
2023-02-14 00:09:04 +01:00
}
2024-01-25 13:41:33 +01:00
public static constructSpecification (
template : string ,
2025-01-18 00:30:06 +01:00
extraMappings : SpecialVisualization [ ] = [ ]
2024-01-25 13:41:33 +01:00
) : RenderingSpecification [ ] {
2024-08-14 13:53:56 +02:00
return SpecialVisualisationUtils . constructSpecification (
template ,
SpecialVisualizations . specialVisualisationsDict ,
2025-01-18 00:30:06 +01:00
extraMappings
2024-08-14 13:53:56 +02:00
)
2024-01-25 13:41:33 +01:00
}
2024-02-14 12:20:29 +01:00
2024-05-07 00:42:52 +02:00
public static HelpMessage ( ) : string {
2025-01-27 23:20:21 +01:00
const vis = [ . . . SpecialVisualizations . specialVisualizations ]
vis . sort ( ( a , b ) = > {
return a . funcName < b . funcName ? - 1 : 1
} )
vis . sort ( ( a , b ) = > {
if ( a . group === b . group ) {
return 0
}
return ( a . group ? ? "xxx" ) < ( b . group ? ? "xxx" ) ? - 1 : 1
} )
const groupExplanations : Record < string , string > = {
2025-02-10 02:04:58 +01:00
default :
"These special visualisations are (mostly) interactive components that most elements get by default. You'll normally won't need them in custom layers. There are also a few miscellaneous elements supporting the map UI." ,
2025-06-26 05:20:12 +02:00
data : "Visualises data of a POI, sometimes with data updating capabilities" ,
2025-02-10 02:04:58 +01:00
favourites :
"Elements relating to marking an object as favourite (giving it a heart). Default element" ,
settings : "Elements part of the usersettings-ui" ,
images : "Elements related to adding or manipulating images. Normally also added by default, but in some cases a tweaked version is needed" ,
notes : "Elements relating to OpenStreetMap-notes, e.g. the component to close and/or add a comment" ,
reviews :
"Elements relating to seeing and adding ratings and reviews with Mangrove.reviews" ,
data_import :
"Elements to help with importing data to OSM. For example: buttons to import a feature, apply tags on an element, apply multiple tags on an element or to work with maproulette" ,
tagrendering_manipulation :
"Special visualisations which reuse other tagRenderings to show data, but with a twist." ,
web_and_communication :
"Tools to show data from external websites, which link to external websites or which link to external profiles" ,
2025-06-26 05:20:12 +02:00
ui : "Elements to support the user interface, e.g. 'title', 'translated'"
2025-01-27 23:20:21 +01:00
}
const helpTexts : string [ ] = [ ]
let lastGroup : string = null
for ( const viz of vis ) {
2025-06-26 05:20:12 +02:00
if ( viz . group ? . toLowerCase ( ) !== lastGroup ) {
lastGroup = viz . group ? . toLowerCase ( )
2025-01-27 23:20:21 +01:00
if ( viz . group === undefined ) {
helpTexts . push ( "## Unclassified elements\n\nVarious elements" )
} else {
helpTexts . push ( "## " + viz . group )
2025-06-26 05:20:12 +02:00
if ( ! groupExplanations [ viz . group . toLowerCase ( ) ] ) {
2025-02-10 02:04:58 +01:00
throw (
"\n\n >>>> ERROR <<<< Unknown visualisation group type: " +
viz . group +
2025-06-04 00:21:28 +02:00
" (used by " +
viz . funcName +
")\n\n\n"
2025-02-10 02:04:58 +01:00
)
2025-01-27 23:20:21 +01:00
}
helpTexts . push ( groupExplanations [ viz . group ] )
}
}
helpTexts . push ( SpecialVisualizations . DocumentationFor ( viz ) )
}
const example = JSON . stringify (
{
render : {
special : {
type : "some_special_visualisation" ,
argname : "some_arg" ,
message : {
en : "some other really long message" ,
2025-02-10 02:04:58 +01:00
nl : "een boodschap in een andere taal" ,
2025-01-27 23:20:21 +01:00
} ,
2025-02-10 02:04:58 +01:00
other_arg_name : "more args" ,
2025-01-27 23:20:21 +01:00
} ,
before : {
en : "Some text to prefix before the special element (e.g. a title)" ,
2025-02-10 02:04:58 +01:00
nl : "Een tekst om voor het element te zetten (bv. een titel)" ,
2025-01-27 23:20:21 +01:00
} ,
after : {
2025-02-10 02:04:58 +01:00
en : "Some text to put after the element, e.g. a footer" ,
} ,
} ,
2025-01-27 23:20:21 +01:00
} ,
null ,
" "
)
2023-02-14 00:09:04 +01:00
2025-01-27 23:20:21 +01:00
const firstPart = [
"# Special tag renderings" ,
2024-05-07 00:42:52 +02:00
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's." ,
2024-06-17 04:27:08 +02:00
"General usage is `{func_name()}`, `{func_name(arg, someotherarg)}` or `{func_name(args):cssClasses}`. Note that you _do not_ need to use quotes around your arguments, the comma is enough to separate them. This also implies you cannot use a comma in your args" ,
2025-01-29 20:37:04 +01:00
"# Using expanded syntax" ,
2024-05-07 00:42:52 +02:00
` Instead of using \` {"render": {"en": "{some_special_visualisation(some_arg, some other really long message, more args)} , "nl": "{some_special_visualisation(some_arg, een boodschap in een andere taal, more args)}} \` , one can also write ` ,
2025-01-27 23:20:21 +01:00
"```\n" + example + "\n```\n" ,
2025-02-10 02:04:58 +01:00
'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)' ,
"# Overview of all special components" ,
2025-01-27 23:20:21 +01:00
] . join ( "\n\n" )
2024-05-07 00:42:52 +02:00
return firstPart + "\n\n" + helpTexts . join ( "\n\n" )
2023-02-14 00:09:04 +01:00
}
2022-11-02 13:47:34 +01:00
private static initList ( ) : SpecialVisualization [ ] {
2022-01-08 04:22:50 +01:00
const specialVisualizations : SpecialVisualization [ ] = [
2025-01-27 04:50:44 +01:00
. . . ImageVisualisations . initList ( ) ,
. . . NoteVisualisations . initList ( ) ,
. . . FavouriteVisualisations . initList ( ) ,
. . . UISpecialVisualisations . initList ( ) ,
. . . SettingsVisualisations . initList ( ) ,
. . . ReviewSpecialVisualisations . initList ( ) ,
2025-01-29 20:37:04 +01:00
. . . DataImportSpecialVisualisations . initList ( ) ,
. . . TagrenderingManipulationSpecialVisualisations . initList ( ) ,
. . . WebAndCommunicationSpecialVisualisations . initList ( ) ,
2025-06-26 05:20:12 +02:00
. . . DataVisualisations . initList ( ) ,
. . . DataExportVisualisations . initList ( ) ,
2022-10-28 04:33:05 +02:00
new UploadToOsmViz ( ) ,
new MultiApplyViz ( ) ,
2020-10-17 02:37:53 +02:00
]
2022-01-08 04:22:50 +01:00
2025-06-02 17:03:07 +02:00
specialVisualizations . push ( new AutoApplyButtonVis ( specialVisualizations ) )
2022-04-22 01:45:54 +02:00
2024-08-14 13:53:56 +02:00
const regex = /[a-zA-Z_]+/
2022-11-02 13:47:34 +01:00
const invalid = specialVisualizations
2023-06-14 20:39:36 +02:00
. map ( ( sp , i ) = > ( { sp , i } ) )
2024-08-13 17:56:33 +02:00
. filter ( ( sp ) = > sp . sp . funcName === undefined || ! sp . sp . funcName . match ( regex ) )
2024-08-23 13:00:26 +02:00
2022-11-02 13:47:34 +01:00
if ( invalid . length > 0 ) {
throw (
2024-08-14 13:53:56 +02:00
"Invalid special visualisation found: funcName is undefined or doesn't match " +
regex +
2022-11-02 13:47:34 +01:00
invalid . map ( ( sp ) = > sp . i ) . join ( ", " ) +
2025-02-10 02:04:58 +01:00
'. Did you perhaps type \n funcName: "funcname" // type declaration uses COLON\ninstead of:\n funcName = "funcName" // value definition uses EQUAL'
2022-11-02 13:47:34 +01:00
)
2022-10-28 04:33:05 +02:00
}
2024-08-23 13:13:41 +02:00
const allNames = specialVisualizations . map ( ( f ) = > f . funcName )
2024-08-23 13:00:26 +02:00
const seen = new Set < string > ( )
for ( let name of allNames ) {
name = name . toLowerCase ( )
2024-08-23 13:13:41 +02:00
if ( seen . has ( name ) ) {
throw "Invalid special visualisations: detected a duplicate name: " + name
2024-08-23 13:00:26 +02:00
}
seen . add ( name )
}
2022-01-08 04:22:50 +01:00
return specialVisualizations
2020-10-17 02:37:53 +02:00
}
2020-10-09 20:10:21 +02:00
}