Merge branches

This commit is contained in:
Pieter Vander Vennet 2024-05-13 18:55:54 +02:00
commit bae90d50bc
304 changed files with 49983 additions and 31589 deletions

View file

@ -167,7 +167,7 @@ export class Denomination {
* Returns null if it doesn't match this unit
*/
public StrippedValue(value: string, actAsDefault: boolean, inverted: boolean): string {
if (value === undefined) {
if (value === undefined || value === "") {
return undefined
}
@ -186,7 +186,7 @@ export class Denomination {
if (self.prefix) {
return value.substring(key.length).trim()
}
let trimmed = value.substring(0, value.length - key.length).trim()
let trimmed = value.substring(0, value.length - key.length).trim()
if(!inverted){
return trimmed
}

View file

@ -100,6 +100,11 @@ export class MenuState {
})
this.allToggles = [
{
toggle: this.privacyPanelIsOpened,
name: "privacy",
showOverOthers: true,
},
{
toggle: this.menuIsOpened,
name: "menu",
@ -120,11 +125,6 @@ export class MenuState {
name: "community",
showOverOthers: true,
},
{
toggle: this.privacyPanelIsOpened,
name: "privacy",
showOverOthers: true,
},
{
toggle: this.filtersPanelIsOpened,
name: "filters",

View file

@ -258,11 +258,11 @@ export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[
}
}
let renderings = Array.isArray(rewrite.renderings)
const renderings = Array.isArray(rewrite.renderings)
? rewrite.renderings
: [rewrite.renderings]
for (let i = 0; i < keysToRewrite.into.length; i++) {
let ts: T[] = <T[]>Utils.Clone(renderings)
const ts: T[] = <T[]>Utils.Clone(renderings)
for (const tx of ts) {
let t = <T>tx
const sourceKeysToIgnore: string[] = []

View file

@ -161,7 +161,9 @@ class ExpandTagRendering extends Conversion<
private readonly _options: {
/* If true, will copy the 'osmSource'-tags into the condition */
applyCondition?: true | boolean
noHardcodedStrings?: false | boolean
noHardcodedStrings?: false | boolean,
addToContext?: false | boolean
}
constructor(
@ -169,11 +171,13 @@ class ExpandTagRendering extends Conversion<
self: LayerConfigJson,
options?: {
applyCondition?: true | boolean
noHardcodedStrings?: false | boolean
noHardcodedStrings?: false | boolean,
// If set, a question will be added to the 'sharedTagRenderings'. Should only be used for 'questions.json'
addToContext?: false | boolean
}
) {
super(
"Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question",
"Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question and reusing the builtins",
[],
"ExpandTagRendering"
)
@ -204,8 +208,17 @@ class ExpandTagRendering extends Conversion<
if (typeof tr === "string" || tr["builtin"] !== undefined) {
const stable = this.convert(tr, ctx.inOperation("recursive_resolve"))
result.push(...stable)
if(this._options?.addToContext){
for (const tr of stable) {
this._state.tagRenderings?.set(tr.id, tr)
}
}
} else {
result.push(tr)
if(this._options?.addToContext){
this._state.tagRenderings?.set(tr["id"], <QuestionableTagRenderingConfigJson> tr)
}
}
}
@ -220,7 +233,7 @@ class ExpandTagRendering extends Conversion<
}
const result: TagRenderingConfigJson[] = []
for (const tagRenderingConfigJson of direct) {
let nm: string | string[] | undefined = tagRenderingConfigJson["builtin"]
const nm: string | string[] | undefined = tagRenderingConfigJson["builtin"]
if (nm !== undefined) {
let indirect: TagRenderingConfigJson[]
if (typeof nm === "string") {
@ -1261,12 +1274,14 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
}
export class PrepareLayer extends Fuse<LayerConfigJson> {
constructor(state: DesugaringContext) {
constructor(state: DesugaringContext, options?: {addTagRenderingsToContext?: false | boolean}) {
super(
"Fully prepares and expands a layer for the LayerConfig.",
new On("tagRenderings", new Each(new RewriteSpecial())),
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))),
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer, {
addToContext: options?.addTagRenderingsToContext ?? false
}))),
new On("tagRenderings", new Each(new DetectInline())),
new AddQuestionBox(),
new AddEditingElements(state),

View file

@ -562,6 +562,65 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
}
}
export class DetectMappingsShadowedByCondition extends DesugaringStep<TagRenderingConfigJson> {
private readonly _forceError: boolean
constructor(forceError: boolean = false) {
super("Checks that, if the tagrendering has a condition, that a mapping is not contradictory to it, i.e. that there are no dead mappings", [], "DetectMappingsShadowedByCondition")
this._forceError = forceError
}
/**
*
* const validator = new DetectMappingsShadowedByCondition(true)
* const ctx = ConversionContext.construct([],["test"])
* validator.convert({
* condition: "count>0",
* mappings:[
* {
* if: "count=0",
* then:{
* en: "No count"
* }
* }
* ]
* }, ctx)
* ctx.hasErrors() // => true
*/
convert(json: TagRenderingConfigJson, context: ConversionContext): TagRenderingConfigJson {
if(!json.condition && !json.metacondition){
return json
}
if(!json.mappings || json.mappings?.length ==0){
return json
}
let conditionJson = json.condition ?? json.metacondition
if(json.condition !== undefined && json.metacondition !== undefined){
conditionJson = {and: [json.condition, json.metacondition]}
}
const condition = TagUtils.Tag(conditionJson, context.path.join("."))
for (let i = 0; i < json.mappings.length; i++){
const mapping = json.mappings[i]
const tagIf = TagUtils.Tag(mapping.if, context.path.join("."))
const optimized = new And([tagIf, condition]).optimize()
if(optimized === false){
const msg = ("Detected a conflicting mapping and condition. The mapping requires tags " + tagIf.asHumanString() + ", yet this can never happen because the set condition requires " + condition.asHumanString())
const ctx = context.enters("mappings", i)
if (this._forceError) {
ctx.err(msg)
} else {
ctx.warn(msg)
}
}
}
return undefined
}
}
export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJson> {
private readonly _calculatedTagNames: string[]
@ -1088,6 +1147,8 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
"Various validation on tagRenderingConfigs",
new MiscTagRenderingChecks(),
new DetectShadowedMappings(layerConfig),
new DetectMappingsShadowedByCondition(),
new DetectConflictingAddExtraTags(),
// TODO enable new DetectNonErasedKeysInMappings(),
new DetectMappingsWithImages(doesImageExist),

View file

@ -554,4 +554,15 @@ export interface LayerConfigJson {
* group: hidden
*/
fullNodeDatabase?: boolean
/**
* question: Should a theme using this layer leak some location info when making changes?
*
* When a changeset is made, a 'distance to object'-class is written to the changeset.
* For some particular themes and layers, this might leak too much information, and we want to obfuscate this
*
* ifunset: Write 'change_within_x_m' as usual and if GPS is enabled
* iftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey
*/
enableMorePrivacy?: boolean
}

View file

@ -439,4 +439,16 @@ export interface LayoutConfigJson {
* group: hidden
*/
enableNodeDatabase?: boolean
/**
* question: Should this theme leak some location info when making changes?
*
* When a changeset is made, a 'distance to object'-class is written to the changeset.
* For some particular themes and layers, this might leak too much information, and we want to obfuscate this
*
* ifunset: Write 'change_within_x_m' as usual and if GPS is enabled
* iftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey
*/
enableMorePrivacy: boolean
}

View file

@ -229,6 +229,7 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs
* A (translated) text that is shown (as gray text) within the textfield
* type: translation
* group: expert
* ifunset: No specific placeholder is set, show the type of the textfield
*/
placeholder?: Translatable

View file

@ -88,7 +88,7 @@ export interface TagRenderingConfigJson {
*
* question: When should this item be shown?
* type: tag
* ifunset: No specific condition set; always show this tagRendering or ask the question if unkown
* ifunset: No specific condition set; always show this tagRendering or show this question if unknown
*
* Only show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.
*
@ -132,9 +132,10 @@ export interface TagRenderingConfigJson {
/** question: When should this item be shown (including special conditions)?
* type: tag
* ifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown
*
* If set, this tag will be evaluated agains the _usersettings/application state_ table.
* Enable 'show debug info' in user settings to see available options.
* If set, this tag will be evaluated against the _usersettings/application state_ table.
* Enable 'show debug info' in user settings to see available options (at the settings-tab).
* Note that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_
*/
metacondition?: TagConfigJson

View file

@ -67,6 +67,7 @@ export default class LayerConfig extends WithContextLoader {
public readonly _needsFullNodeDatabase: boolean
public readonly popupInFloatover: boolean | string
public readonly enableMorePrivacy: boolean
constructor(json: LayerConfigJson, context?: string, official: boolean = true) {
context = context + "." + json.id
@ -149,6 +150,7 @@ export default class LayerConfig extends WithContextLoader {
this.shownByDefault = json.shownByDefault ?? true
this.doCount = json.isCounted ?? this.shownByDefault ?? true
this.forceLoad = json.forceLoad ?? false
this.enableMorePrivacy = json.enableMorePrivacy ?? false
if (json.presets === null) json.presets = undefined
if (json.presets !== undefined && json.presets?.map === undefined) {
throw "Presets should be a list of items (at " + context + ")"

View file

@ -63,6 +63,8 @@ export default class LayoutConfig implements LayoutInformation {
public readonly enableExportButton: boolean
public readonly enablePdfDownload: boolean
public readonly enableTerrain: boolean
public readonly enableMorePrivacy: boolean
public readonly customCss?: string
@ -204,6 +206,7 @@ export default class LayoutConfig implements LayoutInformation {
this.overpassTimeout = json.overpassTimeout ?? 30
this.overpassMaxZoom = json.overpassMaxZoom ?? 16
this.osmApiTileSize = json.osmApiTileSize ?? this.overpassMaxZoom + 1
this.enableMorePrivacy = json.enableMorePrivacy || json.layers.some(l => (<LayerConfigJson> l).enableMorePrivacy)
this.layersDict = new Map<string, LayerConfig>()
for (const layer of this.layers) {

View file

@ -69,6 +69,7 @@ import {
import summaryLayer from "../assets/generated/layers/summary.json"
import { LayerConfigJson } from "./ThemeConfig/Json/LayerConfigJson"
import Locale from "../UI/i18n/Locale"
import Hash from "../Logic/Web/Hash"
/**
*
@ -265,6 +266,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
featurePropertiesStore: this.featureProperties,
osmConnection: this.osmConnection,
historicalUserLocations: this.geolocation.historicalUserLocations,
featureSwitches: this.featureSwitches
},
layout?.isLeftRightSensitive() ?? false
)
@ -495,6 +497,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.layout.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) {
Utils.LoadCustomCss(this.layout.customCss)
}
Hash.hash.addCallbackAndRunD(hash => {
if(hash === "current_view" || hash.match(/current_view_[0-9]+/)){
this.selectCurrentView()
}
})
}
/**
@ -820,4 +828,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.userRelatedState.preferredBackgroundLayer
)
}
public selectCurrentView(){
this.guistate.closeAll()
this.selectedElement.setData(this.currentView.features?.data?.[0])
}
}