diff --git a/src/UI/Popup/SendEmail.svelte b/src/UI/Popup/SendEmail.svelte
new file mode 100644
index 000000000..ca91bb06b
--- /dev/null
+++ b/src/UI/Popup/SendEmail.svelte
@@ -0,0 +1,31 @@
+
+
+
+ {button_text}
+
+SEND EMAIL to {to}
+
+subject: {subject}
+
+body: {body}
+
+{button_text}
diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts
index bf3dce5ab..035841065 100644
--- a/src/UI/SpecialVisualizations.ts
+++ b/src/UI/SpecialVisualizations.ts
@@ -1,52 +1,56 @@
import Combine from "./Base/Combine"
-import {FixedUiElement} from "./Base/FixedUiElement"
+import { FixedUiElement } from "./Base/FixedUiElement"
import BaseUIElement from "./BaseUIElement"
import Title from "./Base/Title"
import Table from "./Base/Table"
-import {RenderingSpecification, SpecialVisualization, SpecialVisualizationState,} from "./SpecialVisualization"
-import {HistogramViz} from "./Popup/HistogramViz"
-import {MinimapViz} from "./Popup/MinimapViz"
-import {ShareLinkViz} from "./Popup/ShareLinkViz"
-import {UploadToOsmViz} from "./Popup/UploadToOsmViz"
-import {MultiApplyViz} from "./Popup/MultiApplyViz"
-import {AddNoteCommentViz} from "./Popup/AddNoteCommentViz"
-import {PlantNetDetectionViz} from "./Popup/PlantNetDetectionViz"
+import {
+ RenderingSpecification,
+ SpecialVisualization,
+ SpecialVisualizationState,
+} from "./SpecialVisualization"
+import { HistogramViz } from "./Popup/HistogramViz"
+import { MinimapViz } from "./Popup/MinimapViz"
+import { ShareLinkViz } from "./Popup/ShareLinkViz"
+import { UploadToOsmViz } from "./Popup/UploadToOsmViz"
+import { MultiApplyViz } from "./Popup/MultiApplyViz"
+import { AddNoteCommentViz } from "./Popup/AddNoteCommentViz"
+import { PlantNetDetectionViz } from "./Popup/PlantNetDetectionViz"
import TagApplyButton from "./Popup/TagApplyButton"
-import {CloseNoteButton} from "./Popup/CloseNoteButton"
-import {MapillaryLinkVis} from "./Popup/MapillaryLinkVis"
-import {Store, Stores, UIEventSource} from "../Logic/UIEventSource"
+import { CloseNoteButton } from "./Popup/CloseNoteButton"
+import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis"
+import { Store, Stores, UIEventSource } from "../Logic/UIEventSource"
import AllTagsPanel from "./Popup/AllTagsPanel.svelte"
import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"
-import {ImageCarousel} from "./Image/ImageCarousel"
-import {ImageUploadFlow} from "./Image/ImageUploadFlow"
-import {VariableUiElement} from "./Base/VariableUIElement"
-import {Utils} from "../Utils"
-import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata"
-import {Translation} from "./i18n/Translation"
+import { ImageCarousel } from "./Image/ImageCarousel"
+import { ImageUploadFlow } from "./Image/ImageUploadFlow"
+import { VariableUiElement } from "./Base/VariableUIElement"
+import { Utils } from "../Utils"
+import Wikidata, { WikidataResponse } from "../Logic/Web/Wikidata"
+import { Translation } from "./i18n/Translation"
import Translations from "./i18n/Translations"
import ReviewForm from "./Reviews/ReviewForm"
import ReviewElement from "./Reviews/ReviewElement"
import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"
import LiveQueryHandler from "../Logic/Web/LiveQueryHandler"
-import {SubtleButton} from "./Base/SubtleButton"
+import { SubtleButton } from "./Base/SubtleButton"
import Svg from "../Svg"
import NoteCommentElement from "./Popup/NoteCommentElement"
import ImgurUploader from "../Logic/ImageProviders/ImgurUploader"
import FileSelectorButton from "./Input/FileSelectorButton"
-import {LoginToggle} from "./Popup/LoginButton"
+import { LoginToggle } from "./Popup/LoginButton"
import Toggle from "./Input/Toggle"
-import {SubstitutedTranslation} from "./SubstitutedTranslation"
+import { SubstitutedTranslation } from "./SubstitutedTranslation"
import List from "./Base/List"
import StatisticsPanel from "./BigComponents/StatisticsPanel"
import AutoApplyButton from "./Popup/AutoApplyButton"
-import {LanguageElement} from "./Popup/LanguageElement"
+import { LanguageElement } from "./Popup/LanguageElement"
import FeatureReviews from "../Logic/Web/MangroveReviews"
import Maproulette from "../Logic/Maproulette"
import SvelteUIElement from "./Base/SvelteUIElement"
-import {BBoxFeatureSourceForLayer} from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
+import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
import QuestionViz from "./Popup/QuestionViz"
-import {Feature, Point} from "geojson"
-import {GeoOperations} from "../Logic/GeoOperations"
+import { Feature, Point } from "geojson"
+import { GeoOperations } from "../Logic/GeoOperations"
import CreateNewNote from "./Popup/CreateNewNote.svelte"
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
import UserProfile from "./BigComponents/UserProfile.svelte"
@@ -54,27 +58,32 @@ import LanguagePicker from "./LanguagePicker"
import Link from "./Base/Link"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
-import NearbyImages, {NearbyImageOptions, P4CPicture, SelectOneNearbyImage,} from "./Popup/NearbyImages"
-import {Tag} from "../Logic/Tags/Tag"
+import NearbyImages, {
+ NearbyImageOptions,
+ P4CPicture,
+ SelectOneNearbyImage,
+} from "./Popup/NearbyImages"
+import { Tag } from "../Logic/Tags/Tag"
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction"
-import {And} from "../Logic/Tags/And"
-import {SaveButton} from "./Popup/SaveButton"
+import { And } from "../Logic/Tags/And"
+import { SaveButton } from "./Popup/SaveButton"
import Lazy from "./Base/Lazy"
-import {CheckBox} from "./Input/Checkboxes"
+import { CheckBox } from "./Input/Checkboxes"
import Slider from "./Input/Slider"
-import {OsmTags, WayId} from "../Models/OsmFeature"
+import { OsmTags, WayId } from "../Models/OsmFeature"
import MoveWizard from "./Popup/MoveWizard"
import SplitRoadWizard from "./Popup/SplitRoadWizard"
-import {ExportAsGpxViz} from "./Popup/ExportAsGpxViz"
+import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz"
import WikipediaPanel from "./Wikipedia/WikipediaPanel.svelte"
import TagRenderingEditable from "./Popup/TagRendering/TagRenderingEditable.svelte"
-import {PointImportButtonViz} from "./Popup/ImportButtons/PointImportButtonViz"
+import { PointImportButtonViz } from "./Popup/ImportButtons/PointImportButtonViz"
import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz"
import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz"
import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte"
-import {OpenJosm} from "./BigComponents/OpenJosm"
+import { OpenJosm } from "./BigComponents/OpenJosm"
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
-import FediverseValidator from "./InputElement/Validators/FediverseValidator";
+import FediverseValidator from "./InputElement/Validators/FediverseValidator"
+import SendEmail from "./Popup/SendEmail.svelte"
class NearbyImageVis implements SpecialVisualization {
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
@@ -173,7 +182,7 @@ class NearbyImageVis implements SpecialVisualization {
towardsCenter,
new Combine([
new VariableUiElement(
- radius.GetValue().map((radius) => t.withinRadius.Subs({radius}))
+ radius.GetValue().map((radius) => t.withinRadius.Subs({ radius }))
),
radius,
]).SetClass("flex justify-between"),
@@ -386,24 +395,24 @@ export default class SpecialVisualizations {
viz.docs,
viz.args.length > 0
? new Table(
- ["name", "default", "description"],
- viz.args.map((arg) => {
- let defaultArg = arg.defaultValue ?? "_undefined_"
- if (defaultArg == "") {
- defaultArg = "_empty string_"
- }
- return [arg.name, defaultArg, arg.doc]
- })
- )
+ ["name", "default", "description"],
+ viz.args.map((arg) => {
+ let defaultArg = arg.defaultValue ?? "_undefined_"
+ if (defaultArg == "") {
+ defaultArg = "_empty string_"
+ }
+ return [arg.name, defaultArg, arg.doc]
+ })
+ )
: undefined,
new Title("Example usage of " + viz.funcName, 4),
new FixedUiElement(
viz.example ??
- "`{" +
- viz.funcName +
- "(" +
- viz.args.map((arg) => arg.defaultValue).join(",") +
- ")}`"
+ "`{" +
+ viz.funcName +
+ "(" +
+ viz.args.map((arg) => arg.defaultValue).join(",") +
+ ")}`"
).SetClass("literal-code"),
])
}
@@ -462,14 +471,14 @@ export default class SpecialVisualizations {
s.structuredExamples === undefined
? []
: s.structuredExamples().map((e) => {
- return s.constr(
- state,
- new UIEventSource>(e.feature.properties),
- e.args,
- e.feature,
- undefined
- )
- })
+ return s.constr(
+ state,
+ new UIEventSource>(e.feature.properties),
+ e.args,
+ e.feature,
+ undefined
+ )
+ })
return new Combine([new Title(s.funcName), s.docs, ...examples])
}
@@ -484,7 +493,7 @@ export default class SpecialVisualizations {
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
return new SvelteUIElement(AddNewPoint, {
state,
- coordinate: {lon, lat},
+ coordinate: { lon, lat },
})
},
},
@@ -603,7 +612,7 @@ export default class SpecialVisualizations {
feature: Feature
): BaseUIElement {
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
- return new SvelteUIElement(CreateNewNote, {state, coordinate: {lon, lat}})
+ return new SvelteUIElement(CreateNewNote, { state, coordinate: { lon, lat } })
},
},
new CloseNoteButton(),
@@ -680,7 +689,7 @@ export default class SpecialVisualizations {
docs: "Prints all key-value pairs of the object - used for debugging",
args: [],
constr: (state, tags: UIEventSource) =>
- new SvelteUIElement(AllTagsPanel, {tags, state}),
+ new SvelteUIElement(AllTagsPanel, { tags, state }),
},
{
funcName: "image_carousel",
@@ -1229,23 +1238,7 @@ export default class SpecialVisualizations {
},
],
constr(__, tags, args) {
- return new VariableUiElement(
- tags.map((tags) => {
- const [to, subject, body, button_text] = args.map((str) =>
- Utils.SubstituteKeys(str, tags)
- )
- const url =
- "mailto:" +
- to +
- "?subject=" +
- encodeURIComponent(subject) +
- "&body=" +
- encodeURIComponent(body)
- return new SubtleButton(Svg.envelope_svg(), button_text, {
- url,
- })
- })
- )
+ return new SvelteUIElement(SendEmail, { args, tags })
},
},
{
@@ -1319,7 +1312,7 @@ export default class SpecialVisualizations {
],
constr(state, featureTags, args) {
const [key, tr] = args
- const translation = new Translation({"*": tr})
+ const translation = new Translation({ "*": tr })
return new VariableUiElement(
featureTags.map((tags) => {
const properties: object[] = JSON.parse(tags[key])
@@ -1340,29 +1333,46 @@ export default class SpecialVisualizations {
{
funcName: "fediverse_link",
docs: "Converts a fediverse username or link into a clickable link",
- args: [{
- name: "key",
- doc: "The attribute-name containing the link",
- required: true
- }],
- constr(state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement {
+ args: [
+ {
+ name: "key",
+ doc: "The attribute-name containing the link",
+ required: true,
+ },
+ ],
+ constr(
+ state: SpecialVisualizationState,
+ tagSource: UIEventSource>,
+ argument: string[],
+ feature: Feature,
+ layer: LayerConfig
+ ): BaseUIElement {
const key = argument[0]
const validator = new FediverseValidator()
- return new VariableUiElement(tagSource.map(tags => tags[key]).map(fediAccount => {
- fediAccount = validator.reformat(fediAccount)
- const [_, username, host] = fediAccount.match(FediverseValidator.usernameAtServer)
+ return new VariableUiElement(
+ tagSource
+ .map((tags) => tags[key])
+ .map((fediAccount) => {
+ fediAccount = validator.reformat(fediAccount)
+ const [_, username, host] = fediAccount.match(
+ FediverseValidator.usernameAtServer
+ )
- return new Link(fediAccount, "https://" + host + "/@" + username, true)
- }
- ))
- }
- }
+ return new Link(
+ fediAccount,
+ "https://" + host + "/@" + username,
+ true
+ )
+ })
+ )
+ },
+ },
]
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
const invalid = specialVisualizations
- .map((sp, i) => ({sp, i}))
+ .map((sp, i) => ({ sp, i }))
.filter((sp) => sp.sp.funcName === undefined)
if (invalid.length > 0) {
throw (
diff --git a/src/Utils.ts b/src/Utils.ts
index 57ff3658a..9767027f4 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -442,6 +442,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
* Utils.SubstituteKeys("abc{def}ghi", {def: 'XYZ'}) // => "abcXYZghi"
* Utils.SubstituteKeys("abc{def}{def}ghi", {def: 'XYZ'}) // => "abcXYZXYZghi"
* Utils.SubstituteKeys("abc{def}ghi", {def: '{XYZ}'}) // => "abc{XYZ}ghi"
+ * Utils.SubstituteKeys("abc\n\n{def}ghi", {def: '{XYZ}'}) // => "abc\n\n{XYZ}ghi"
*
* @param txt
* @param tags
@@ -456,7 +457,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
if (txt === undefined) {
return undefined
}
- const regex = /(.*?){([^}]*)}(.*)/
+ const regex = /(.*?){([^}]*)}(.*)/s
let match = txt.match(regex)