forked from MapComplete/MapComplete
		
	Add wrong default import from json files to code quality checks, fix those imports
This commit is contained in:
		
							parent
							
								
									71c815d37d
								
							
						
					
					
						commit
						ce44f34bf3
					
				
					 42 changed files with 167 additions and 148 deletions
				
			
		|  | @ -1,4 +1,4 @@ | |||
| import * as known_themes from "../assets/generated/known_layers_and_themes.json" | ||||
| import known_themes from "../assets/generated/known_layers_and_themes.json" | ||||
| import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" | ||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
| import BaseUIElement from "../UI/BaseUIElement" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as questions from "../assets/tagRenderings/questions.json" | ||||
| import * as icons from "../assets/tagRenderings/icons.json" | ||||
| import questions from "../assets/tagRenderings/questions.json" | ||||
| import icons from "../assets/tagRenderings/icons.json" | ||||
| import { Utils } from "../Utils" | ||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" | ||||
|  |  | |||
|  | @ -12,9 +12,9 @@ import LZString from "lz-string" | |||
| import { FixLegacyTheme } from "../Models/ThemeConfig/Conversion/LegacyJsonConvert" | ||||
| import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" | ||||
| import SharedTagRenderings from "../Customizations/SharedTagRenderings" | ||||
| import * as known_layers from "../assets/generated/known_layers.json" | ||||
| import known_layers from "../assets/generated/known_layers.json" | ||||
| import { PrepareTheme } from "../Models/ThemeConfig/Conversion/PrepareTheme" | ||||
| import * as licenses from "../assets/generated/license_info.json" | ||||
| import licenses from "../assets/generated/license_info.json" | ||||
| import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { FixImages } from "../Models/ThemeConfig/Conversion/FixImages" | ||||
| import Svg from "../Svg" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import { Utils } from "../../Utils" | ||||
| import * as polygon_features from "../../assets/polygon-features.json" | ||||
| import polygon_features from "../../assets/polygon-features.json" | ||||
| import { Store, UIEventSource } from "../UIEventSource" | ||||
| import { BBox } from "../BBox" | ||||
| import OsmToGeoJson from "osmtogeojson" | ||||
|  | @ -290,7 +290,7 @@ export abstract class OsmObject { | |||
|         { values: Set<string>; blacklist: boolean } | ||||
|     > { | ||||
|         const result = new Map<string, { values: Set<string>; blacklist: boolean }>() | ||||
|         for (const polygonFeature of polygon_features["default"] ?? polygon_features) { | ||||
|         for (const polygonFeature of polygon_features) { | ||||
|             const key = polygonFeature.key | ||||
| 
 | ||||
|             if (polygonFeature.polygon === "all") { | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ import SelectedElementTagsUpdater from "../Actors/SelectedElementTagsUpdater" | |||
| import { Changes } from "../Osm/Changes" | ||||
| import ChangeToElementsActor from "../Actors/ChangeToElementsActor" | ||||
| import PendingChangesUploader from "../Actors/PendingChangesUploader" | ||||
| import * as translators from "../../assets/translators.json" | ||||
| import translators from "../../assets/translators.json" | ||||
| import Maproulette from "../Maproulette" | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
|  | @ -7,13 +7,13 @@ import { RegexTag } from "./RegexTag" | |||
| import SubstitutingTag from "./SubstitutingTag" | ||||
| import { Or } from "./Or" | ||||
| import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson" | ||||
| import * as key_counts from "../../assets/key_totals.json" | ||||
| import key_counts from "../../assets/key_totals.json" | ||||
| 
 | ||||
| type Tags = Record<string, string> | ||||
| export type UploadableTag = Tag | SubstitutingTag | And | ||||
| 
 | ||||
| export class TagUtils { | ||||
|     private static keyCounts: { keys: any; tags: any } = key_counts["default"] ?? key_counts | ||||
|     private static keyCounts: { keys: any; tags: any } = key_counts | ||||
|     private static comparators: [string, (a: number, b: number) => boolean][] = [ | ||||
|         ["<=", (a, b) => a <= b], | ||||
|         [">=", (a, b) => a >= b], | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ export class Denomination { | |||
| 
 | ||||
|         this.alternativeDenominations = json.alternativeDenomination?.map((v) => v.trim()) ?? [] | ||||
| 
 | ||||
|         if (json["default"] !== undefined) { | ||||
|         if (json["default" /* @code-quality: ignore*/] !== undefined) { | ||||
|             throw `${context} uses the old 'default'-key. Use "useIfNoUnitGiven" or "useAsDefaultInput" instead` | ||||
|         } | ||||
|         this.useIfNoUnitGiven = json.useIfNoUnitGiven | ||||
|  |  | |||
|  | @ -1,21 +1,20 @@ | |||
| import { Conversion, DesugaringStep } from "./Conversion" | ||||
| import { LayoutConfigJson } from "../Json/LayoutConfigJson" | ||||
| import { Utils } from "../../../Utils" | ||||
| import * as metapaths from "../../../assets/layoutconfigmeta.json" | ||||
| import * as tagrenderingmetapaths from "../../../assets/questionabletagrenderingconfigmeta.json" | ||||
| import metapaths from "../../../assets/layoutconfigmeta.json" | ||||
| import tagrenderingmetapaths from "../../../assets/questionabletagrenderingconfigmeta.json" | ||||
| import Translations from "../../../UI/i18n/Translations" | ||||
| 
 | ||||
| export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | ||||
|     private _isOfficial: boolean | ||||
|     private _sharedTagRenderings: Map<string, any> | ||||
| 
 | ||||
|     private static readonly layoutMetaPaths = (metapaths["default"] ?? metapaths).filter( | ||||
|     private static readonly layoutMetaPaths = metapaths.filter( | ||||
|         (mp) => | ||||
|             ExtractImages.mightBeTagRendering(mp) || | ||||
|             ExtractImages.mightBeTagRendering(<any>mp) || | ||||
|             (mp.typeHint !== undefined && (mp.typeHint === "image" || mp.typeHint === "icon")) | ||||
|     ) | ||||
|     private static readonly tagRenderingMetaPaths = | ||||
|         tagrenderingmetapaths["default"] ?? tagrenderingmetapaths | ||||
|     private static readonly tagRenderingMetaPaths = tagrenderingmetapaths | ||||
| 
 | ||||
|     constructor(isOfficial: boolean, sharedTagRenderings: Map<string, any>) { | ||||
|         super("Extract all images from a layoutConfig using the meta paths.", [], "ExctractImages") | ||||
|  | @ -23,14 +22,16 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | |||
|         this._sharedTagRenderings = sharedTagRenderings | ||||
|     } | ||||
| 
 | ||||
|     public static mightBeTagRendering(metapath: { type: string | string[] }): boolean { | ||||
|     public static mightBeTagRendering(metapath: { type?: string | string[] }): boolean { | ||||
|         if (!Array.isArray(metapath.type)) { | ||||
|             return false | ||||
|         } | ||||
|         return metapath.type.some( | ||||
|         return ( | ||||
|             metapath.type?.some( | ||||
|                 (t) => | ||||
|                     t["$ref"] == "#/definitions/TagRenderingConfigJson" || | ||||
|                     t["$ref"] == "#/definitions/QuestionableTagRenderingConfigJson" | ||||
|             ) ?? false | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|  | @ -83,7 +84,7 @@ export class ExtractImages extends Conversion<LayoutConfigJson, string[]> { | |||
|         const errors = [] | ||||
|         const warnings = [] | ||||
|         for (const metapath of ExtractImages.layoutMetaPaths) { | ||||
|             const mightBeTr = ExtractImages.mightBeTagRendering(metapath) | ||||
|             const mightBeTr = ExtractImages.mightBeTagRendering(<any>metapath) | ||||
|             const allRenderedValuesAreImages = | ||||
|                 metapath.typeHint === "icon" || metapath.typeHint === "image" | ||||
|             const found = Utils.CollectPath(metapath.path, json) | ||||
|  | @ -271,14 +272,11 @@ export class FixImages extends DesugaringStep<LayoutConfigJson> { | |||
| 
 | ||||
|         json = Utils.Clone(json) | ||||
| 
 | ||||
|         let paths = metapaths["default"] ?? metapaths | ||||
|         let trpaths = tagrenderingmetapaths["default"] ?? tagrenderingmetapaths | ||||
| 
 | ||||
|         for (const metapath of paths) { | ||||
|         for (const metapath of metapaths) { | ||||
|             if (metapath.typeHint !== "image" && metapath.typeHint !== "icon") { | ||||
|                 continue | ||||
|             } | ||||
|             const mightBeTr = ExtractImages.mightBeTagRendering(metapath) | ||||
|             const mightBeTr = ExtractImages.mightBeTagRendering(<any>metapath) | ||||
|             Utils.WalkPath(metapath.path, json, (leaf, path) => { | ||||
|                 if (typeof leaf === "string") { | ||||
|                     return replaceString(leaf) | ||||
|  | @ -287,7 +285,7 @@ export class FixImages extends DesugaringStep<LayoutConfigJson> { | |||
|                 if (mightBeTr) { | ||||
|                     // We might have reached a tagRenderingConfig containing icons
 | ||||
|                     // lets walk every rendered value and fix the images in there
 | ||||
|                     for (const trpath of trpaths) { | ||||
|                     for (const trpath of tagrenderingmetapaths) { | ||||
|                         if (trpath.typeHint !== "rendered") { | ||||
|                             continue | ||||
|                         } | ||||
|  |  | |||
|  | @ -16,10 +16,10 @@ import RewritableConfigJson from "../Json/RewritableConfigJson" | |||
| import SpecialVisualizations from "../../../UI/SpecialVisualizations" | ||||
| import Translations from "../../../UI/i18n/Translations" | ||||
| import { Translation } from "../../../UI/i18n/Translation" | ||||
| import * as tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json" | ||||
| import tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json" | ||||
| import { AddContextToTranslations } from "./AddContextToTranslations" | ||||
| import FilterConfigJson from "../Json/FilterConfigJson" | ||||
| import * as predifined_filters from "../../../assets/layers/filters/filters.json" | ||||
| import predifined_filters from "../../../assets/layers/filters/filters.json" | ||||
| 
 | ||||
| class ExpandFilter extends DesugaringStep<LayerConfigJson> { | ||||
|     private static load_filters(): Map<string, FilterConfigJson> { | ||||
|  | @ -730,8 +730,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|     } { | ||||
|         const errors = [] | ||||
|         json = Utils.Clone(json) | ||||
|         const paths: { path: string[]; type?: any; typeHint?: string }[] = | ||||
|             tagrenderingconfigmeta["default"] ?? tagrenderingconfigmeta | ||||
|         const paths: { path: string[]; type?: any; typeHint?: string }[] = tagrenderingconfigmeta | ||||
|         for (const path of paths) { | ||||
|             if (path.typeHint !== "rendered") { | ||||
|                 continue | ||||
|  |  | |||
|  | @ -9,11 +9,9 @@ import LayoutConfig from "../LayoutConfig" | |||
| import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" | ||||
| import { TagUtils } from "../../../Logic/Tags/TagUtils" | ||||
| import { ExtractImages } from "./FixImages" | ||||
| import ScriptUtils from "../../../scripts/ScriptUtils" | ||||
| import { And } from "../../../Logic/Tags/And" | ||||
| import Translations from "../../../UI/i18n/Translations" | ||||
| import Svg from "../../../Svg" | ||||
| import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" | ||||
| import FilterConfigJson from "../Json/FilterConfigJson" | ||||
| import DeleteConfig from "../DeleteConfig" | ||||
| 
 | ||||
|  | @ -365,7 +363,7 @@ export class PrevalidateTheme extends Fuse<LayoutConfigJson> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRenderingConfigJson> { | ||||
| export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJson> { | ||||
|     private readonly _calculatedTagNames: string[] | ||||
| 
 | ||||
|     constructor(layerConfig?: LayerConfigJson) { | ||||
|  | @ -425,9 +423,9 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender | |||
|      * r.errors[0].indexOf("The mapping key=value&x=y is fully matched by a previous mapping (namely 0)") >= 0 // => true
 | ||||
|      */ | ||||
|     convert( | ||||
|         json: QuestionableTagRenderingConfigJson, | ||||
|         json: TagRenderingConfigJson, | ||||
|         context: string | ||||
|     ): { result: QuestionableTagRenderingConfigJson; errors?: string[]; warnings?: string[] } { | ||||
|     ): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[] } { | ||||
|         const errors = [] | ||||
|         const warnings = [] | ||||
|         if (json.mappings === undefined || json.mappings.length === 0) { | ||||
|  | @ -441,12 +439,9 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender | |||
|         const parsedConditions = json.mappings.map((m, i) => { | ||||
|             const ctx = `${context}.mappings[${i}]` | ||||
|             const ifTags = TagUtils.Tag(m.if, ctx) | ||||
|             if ( | ||||
|                 m.hideInAnswer !== undefined && | ||||
|                 m.hideInAnswer !== false && | ||||
|                 m.hideInAnswer !== true | ||||
|             ) { | ||||
|                 let conditionTags = TagUtils.Tag(m.hideInAnswer) | ||||
|             const hideInAnswer = m["hideInAnswer"] | ||||
|             if (hideInAnswer !== undefined && hideInAnswer !== false && hideInAnswer !== true) { | ||||
|                 let conditionTags = TagUtils.Tag(hideInAnswer) | ||||
|                 // Merge the condition too!
 | ||||
|                 return new And([conditionTags, ifTags]) | ||||
|             } | ||||
|  | @ -467,8 +462,8 @@ export class DetectShadowedMappings extends DesugaringStep<QuestionableTagRender | |||
|                 const doesMatch = parsedConditions[j].matchesProperties(properties) | ||||
|                 if ( | ||||
|                     doesMatch && | ||||
|                     json.mappings[j].hideInAnswer === true && | ||||
|                     json.mappings[i].hideInAnswer !== true | ||||
|                     json.mappings[j]["hideInAnswer"] === true && | ||||
|                     json.mappings[i]["hideInAnswer"] !== true | ||||
|                 ) { | ||||
|                     warnings.push( | ||||
|                         `At ${context}: Mapping ${i} is shadowed by mapping ${j}. However, mapping ${j} has 'hideInAnswer' set, which will result in a different rendering in question-mode.` | ||||
|  | @ -623,7 +618,8 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> { | |||
|         super( | ||||
|             "Various validation on tagRenderingConfigs", | ||||
|             new DetectShadowedMappings(layerConfig), | ||||
|             new DetectMappingsWithImages(doesImageExist) | ||||
|             new DetectMappingsWithImages(doesImageExist), | ||||
|             new MiscTagRenderingChecks() | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ export interface MappingConfigJson { | |||
|               /** | ||||
|                * Size of the image | ||||
|                */ | ||||
|               class: "small" | "medium" | "large" | string | ||||
|               class?: "small" | "medium" | "large" | string | ||||
|           } | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
|  | @ -125,7 +125,7 @@ export interface TagRenderingConfigJson { | |||
|                    * A hint to mapcomplete on how to render this icon within the mapping. | ||||
|                    * This is translated to 'mapping-icon-<classtype>', so defining your own in combination with a custom CSS is possible (but discouraged) | ||||
|                    */ | ||||
|                   class: "small" | "medium" | "large" | string | ||||
|                   class?: "small" | "medium" | "large" | string | ||||
|               } | ||||
|     }[] | ||||
| } | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ import TilesourceConfig from "./TilesourceConfig" | |||
| import { ExtractImages } from "./Conversion/FixImages" | ||||
| import ExtraLinkConfig from "./ExtraLinkConfig" | ||||
| import { Utils } from "../../Utils" | ||||
| import * as used_languages from "../../assets/generated/used_languages.json" | ||||
| import used_languages from "../../assets/generated/used_languages.json" | ||||
| export default class LayoutConfig { | ||||
|     public static readonly defaultSocialImage = "assets/SocialImage.png" | ||||
|     public readonly id: string | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ import { QueryParameters } from "../Logic/Web/QueryParameters" | |||
| import { SubstitutedTranslation } from "./SubstitutedTranslation" | ||||
| import { AutoAction } from "./Popup/AutoApplyButton" | ||||
| import DynamicGeoJsonTileSource from "../Logic/FeatureSource/TiledFeatureSource/DynamicGeoJsonTileSource" | ||||
| import * as themeOverview from "../assets/generated/theme_overview.json" | ||||
| import themeOverview from "../assets/generated/theme_overview.json" | ||||
| 
 | ||||
| class AutomationPanel extends Combine { | ||||
|     private static readonly openChangeset = new UIEventSource<number>(undefined) | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| import Combine from "../Base/Combine" | ||||
| import * as welcome_messages from "../../assets/welcome_message.json" | ||||
| import welcome_messages from "../../assets/welcome_message.json" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import MoreScreen from "./MoreScreen" | ||||
| import * as themeOverview from "../../assets/generated/theme_overview.json" | ||||
| import themeOverview from "../../assets/generated/theme_overview.json" | ||||
| import Translations from "../i18n/Translations" | ||||
| import Title from "../Base/Title" | ||||
| 
 | ||||
|  | @ -43,7 +43,7 @@ export default class FeaturedMessage extends Combine { | |||
|         }[] = [] | ||||
| 
 | ||||
|         const themesById = new Map<string, { id: string; title: any; shortDescription: any }>() | ||||
|         for (const theme of themeOverview["default"]) { | ||||
|         for (const theme of themeOverview) { | ||||
|             themesById.set(theme.id, theme) | ||||
|         } | ||||
| 
 | ||||
|  | @ -88,9 +88,7 @@ export default class FeaturedMessage extends Combine { | |||
|         const msg = new FixedUiElement(welcome_message.message).SetClass("link-underline font-lg") | ||||
|         els.push(new Combine([title, msg]).SetClass("m-4")) | ||||
|         if (welcome_message.featured_theme !== undefined) { | ||||
|             const theme = themeOverview["default"].filter( | ||||
|                 (th) => th.id === welcome_message.featured_theme | ||||
|             )[0] | ||||
|             const theme = themeOverview.filter((th) => th.id === welcome_message.featured_theme)[0] | ||||
| 
 | ||||
|             els.push( | ||||
|                 MoreScreen.createLinkButton({}, theme) | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import Svg from "../../Svg" | |||
| import Combine from "../Base/Combine" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import Translations from "../i18n/Translations" | ||||
| import * as personal from "../../assets/themes/personal/personal.json" | ||||
| import personal from "../../assets/themes/personal/personal.json" | ||||
| import Constants from "../../Models/Constants" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
|  | @ -14,7 +14,7 @@ import UserRelatedState from "../../Logic/State/UserRelatedState" | |||
| import Toggle from "../Input/Toggle" | ||||
| import { Utils } from "../../Utils" | ||||
| import Title from "../Base/Title" | ||||
| import * as themeOverview from "../../assets/generated/theme_overview.json" | ||||
| import themeOverview from "../../assets/generated/theme_overview.json" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| import { TextField } from "../Input/TextField" | ||||
| import FilteredCombine from "../Base/FilteredCombine" | ||||
|  | @ -30,7 +30,7 @@ export default class MoreScreen extends Combine { | |||
|         mustHaveLanguage?: boolean | ||||
|         hideFromOverview: boolean | ||||
|         keywors?: any[] | ||||
|     }[] = themeOverview["default"] | ||||
|     }[] = themeOverview | ||||
| 
 | ||||
|     constructor( | ||||
|         state: UserRelatedState & { | ||||
|  | @ -287,7 +287,7 @@ export default class MoreScreen extends Combine { | |||
|     ): BaseUIElement { | ||||
|         const t = Translations.t.general.morescreen | ||||
|         const prefix = "mapcomplete-hidden-theme-" | ||||
|         const hiddenThemes = themeOverview["default"].filter((layout) => layout.hideFromOverview) | ||||
|         const hiddenThemes = themeOverview.filter((layout) => layout.hideFromOverview) | ||||
|         const hiddenTotal = hiddenThemes.length | ||||
| 
 | ||||
|         return new Toggle( | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ import Title from "../Base/Title" | |||
| import { Store } from "../../Logic/UIEventSource" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import Svg from "../../Svg" | ||||
| import * as native_languages from "../../assets/language_native.json" | ||||
| import native_languages from "../../assets/language_native.json" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| 
 | ||||
| class TranslatorsPanelContent extends Combine { | ||||
|  |  | |||
|  | @ -19,11 +19,11 @@ import EditableTagRendering from "../Popup/EditableTagRendering" | |||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { SaveButton } from "../Popup/SaveButton" | ||||
| import { TagUtils } from "../../Logic/Tags/TagUtils" | ||||
| import * as usersettings from "../../assets/generated/layers/usersettings.json" | ||||
| import usersettings from "../../assets/generated/layers/usersettings.json" | ||||
| import { LoginToggle } from "../Popup/LoginButton" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import * as translators from "../../assets/translators.json" | ||||
| import * as codeContributors from "../../assets/contributors.json" | ||||
| import translators from "../../assets/translators.json" | ||||
| import codeContributors from "../../assets/contributors.json" | ||||
| 
 | ||||
| export class ImportViewerLinks extends VariableUiElement { | ||||
|     constructor(osmConnection: OsmConnection) { | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import { Utils } from "../Utils" | |||
| import Combine from "./Base/Combine" | ||||
| import ShowDataLayer from "./ShowDataLayer/ShowDataLayer" | ||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
| import * as home_location_json from "../assets/layers/home_location/home_location.json" | ||||
| import home_location_json from "../assets/layers/home_location/home_location.json" | ||||
| import State from "../State" | ||||
| import Title from "./Base/Title" | ||||
| import { MinimapObj } from "./Base/Minimap" | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ import SimpleAddUI from "./BigComponents/SimpleAddUI" | |||
| import StrayClickHandler from "../Logic/Actors/StrayClickHandler" | ||||
| import { DefaultGuiState } from "./DefaultGuiState" | ||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
| import * as home_location_json from "../assets/layers/home_location/home_location.json" | ||||
| import home_location_json from "../assets/layers/home_location/home_location.json" | ||||
| import NewNoteUi from "./Popup/NewNoteUi" | ||||
| import Combine from "./Base/Combine" | ||||
| import AddNewMarker from "./BigComponents/AddNewMarker" | ||||
|  | @ -33,7 +33,6 @@ import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler" | |||
| import { GeoLocationState } from "../Logic/State/GeoLocationState" | ||||
| import Hotkeys from "./Base/Hotkeys" | ||||
| import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers" | ||||
| import Lazy from "./Base/Lazy" | ||||
| import CopyrightPanel from "./BigComponents/CopyrightPanel" | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
|  | @ -13,12 +13,12 @@ import Minimap from "../Base/Minimap" | |||
| import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | ||||
| import FeatureInfoBox from "../Popup/FeatureInfoBox" | ||||
| import { ImportUtils } from "./ImportUtils" | ||||
| import * as import_candidate from "../../assets/layers/import_candidate/import_candidate.json" | ||||
| import import_candidate from "../../assets/layers/import_candidate/import_candidate.json" | ||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||
| import Title from "../Base/Title" | ||||
| import Loading from "../Base/Loading" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import * as known_layers from "../../assets/generated/known_layers.json" | ||||
| import known_layers from "../../assets/generated/known_layers.json" | ||||
| import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" | ||||
| import Translations from "../i18n/Translations" | ||||
| import { Feature } from "geojson" | ||||
|  |  | |||
|  | @ -22,12 +22,12 @@ import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | |||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||
| import ValidatedTextField from "../Input/ValidatedTextField" | ||||
| import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource" | ||||
| import * as import_candidate from "../../assets/layers/import_candidate/import_candidate.json" | ||||
| import import_candidate from "../../assets/layers/import_candidate/import_candidate.json" | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import FeatureInfoBox from "../Popup/FeatureInfoBox" | ||||
| import { ImportUtils } from "./ImportUtils" | ||||
| import Translations from "../i18n/Translations" | ||||
| import * as currentview from "../../assets/layers/current_view/current_view.json" | ||||
| import currentview from "../../assets/layers/current_view/current_view.json" | ||||
| import { CheckBox } from "../Input/Checkboxes" | ||||
| import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch" | ||||
| import { Feature, FeatureCollection, Point } from "geojson" | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ import { FixedUiElement } from "../Base/FixedUiElement" | |||
| import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Toggle from "./Toggle" | ||||
| import * as matchpoint from "../../assets/layers/matchpoint/matchpoint.json" | ||||
| import matchpoint from "../../assets/layers/matchpoint/matchpoint.json" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| import FilteredLayer from "../../Models/FilteredLayer" | ||||
| import { ElementStorage } from "../../Logic/ElementStorage" | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import { DropDown } from "./Input/DropDown" | ||||
| import Locale from "./i18n/Locale" | ||||
| import BaseUIElement from "./BaseUIElement" | ||||
| import * as native from "../assets/language_native.json" | ||||
| import * as language_translations from "../assets/language_translations.json" | ||||
| import native from "../assets/language_native.json" | ||||
| import language_translations from "../assets/language_translations.json" | ||||
| import { Translation } from "./i18n/Translation" | ||||
| import * as used_languages from "../assets/generated/used_languages.json" | ||||
| import used_languages from "../assets/generated/used_languages.json" | ||||
| import Lazy from "./Base/Lazy" | ||||
| import Toggle from "./Input/Toggle" | ||||
| 
 | ||||
|  | @ -35,9 +35,8 @@ export default class LanguagePicker extends Toggle { | |||
| 
 | ||||
|     private static hybrid(lang: string): Translation { | ||||
|         const nativeText = native[lang] ?? lang | ||||
|         const allTranslations = language_translations["default"] ?? language_translations | ||||
|         const translation = {} | ||||
|         const trans = allTranslations[lang] | ||||
|         const trans = language_translations[lang] | ||||
|         if (trans === undefined) { | ||||
|             return new Translation({ "*": nativeText }) | ||||
|         } | ||||
|  | @ -45,7 +44,7 @@ export default class LanguagePicker extends Toggle { | |||
|             if (key.startsWith("_")) { | ||||
|                 continue | ||||
|             } | ||||
|             const translationInKey = allTranslations[lang][key] | ||||
|             const translationInKey = language_translations[lang][key] | ||||
|             if (nativeText.toLowerCase() === translationInKey.toLowerCase()) { | ||||
|                 translation[key] = nativeText | ||||
|             } else { | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { SearchablePillsSelector } from "../Input/SearchableMappingsSelector" | ||||
| import { Store } from "../../Logic/UIEventSource" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import * as all_languages from "../../assets/language_translations.json" | ||||
| import all_languages from "../../assets/language_translations.json" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| 
 | ||||
| export class AllLanguagesSelector extends SearchablePillsSelector<string> { | ||||
|  | @ -18,7 +18,7 @@ export class AllLanguagesSelector extends SearchablePillsSelector<string> { | |||
|             hasPriority?: Store<boolean> | ||||
|         }[] = [] | ||||
| 
 | ||||
|         const langs = options?.supportedLanguages ?? all_languages["default"] ?? all_languages | ||||
|         const langs = options?.supportedLanguages ?? all_languages | ||||
|         for (const ln in langs) { | ||||
|             let languageInfo: Record<string, string> & { _meta?: { countries: string[] } } = | ||||
|                 all_languages[ln] | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ import CreateMultiPolygonWithPointReuseAction from "../../Logic/Osm/Actions/Crea | |||
| import { Tag } from "../../Logic/Tags/Tag" | ||||
| import TagApplyButton from "./TagApplyButton" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import * as conflation_json from "../../assets/layers/conflation/conflation.json" | ||||
| import conflation_json from "../../assets/layers/conflation/conflation.json" | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import { LoginToggle } from "./LoginButton" | ||||
| import { AutoAction } from "./AutoApplyButton" | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ import { UIEventSource } from "../../Logic/UIEventSource" | |||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import { OsmTags } from "../../Models/OsmFeature" | ||||
| import * as all_languages from "../../assets/language_translations.json" | ||||
| import all_languages from "../../assets/language_translations.json" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| import Combine from "../Base/Combine" | ||||
| import Title from "../Base/Title" | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import Loc from "../../Models/Loc" | |||
| import Minimap from "../Base/Minimap" | ||||
| import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import * as left_right_style_json from "../../assets/layers/left_right_style/left_right_style.json" | ||||
| import left_right_style_json from "../../assets/layers/left_right_style/left_right_style.json" | ||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeature | |||
| import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import { BBox } from "../../Logic/BBox" | ||||
| import * as split_point from "../../assets/layers/split_point/split_point.json" | ||||
| import split_point from "../../assets/layers/split_point/split_point.json" | ||||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import { Changes } from "../../Logic/Osm/Changes" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import ShowDataLayer from "./ShowDataLayer" | |||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import { Tiles } from "../../Models/TileRange" | ||||
| import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.json" | ||||
| import clusterstyle from "../../assets/layers/cluster_style/cluster_style.json" | ||||
| 
 | ||||
| export default class ShowTileInfo { | ||||
|     public static readonly styling = new LayerConfig(clusterstyle, "ShowTileInfo", true) | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import { Translation, TypedTranslation } from "./Translation" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import * as known_languages from "../../assets/generated/used_languages.json" | ||||
| import known_languages from "../../assets/generated/used_languages.json" | ||||
| import CompiledTranslations from "../../assets/generated/CompiledTranslations" | ||||
| 
 | ||||
| export default class Translations { | ||||
|  |  | |||
							
								
								
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							|  | @ -1,4 +1,4 @@ | |||
| import * as colors from "./assets/colors.json" | ||||
| import colors from "./assets/colors.json" | ||||
| 
 | ||||
| export class Utils { | ||||
|     /** | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as used_languages from "../assets/generated/used_languages.json" | ||||
| import used_languages from "../assets/generated/used_languages.json" | ||||
| 
 | ||||
| export default class LanguageUtils { | ||||
|     /** | ||||
|  |  | |||
							
								
								
									
										2
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								index.ts
									
										
									
									
									
								
							|  | @ -60,7 +60,7 @@ new Combine([ | |||
|         .SetClass("link-underline small") | ||||
|         .onClick(() => { | ||||
|             localStorage.clear() | ||||
|             window.location.reload(true) | ||||
|             window.location.reload() | ||||
|         }), | ||||
| ]).AttachTo("centermessage") // Add an initialization and reset button if something goes wrong
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,9 +33,9 @@ ShowOverlayLayerImplementation.Implement(); | |||
| // Miscelleanous | ||||
| Utils.DisableLongPresses() | ||||
| if(new URLSearchParams(window.location.search).get("test") === "true"){ | ||||
|     console.log(themeConfig["default"]) | ||||
|     console.log(themeConfig) | ||||
| } | ||||
| const layoutToUse = new LayoutConfig(themeConfig["default"]) | ||||
| const layoutToUse = new LayoutConfig(themeConfig) | ||||
| 
 | ||||
| 
 | ||||
| // Workaround/legacy to keep the old paramters working as I renamed some of them | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import * as languages from "../assets/generated/used_languages.json" | ||||
| import { readFileSync, writeFileSync } from "fs" | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
|  | @ -15,14 +15,15 @@ import List from "../UI/Base/List" | |||
| import SharedTagRenderings from "../Customizations/SharedTagRenderings" | ||||
| import { writeFile } from "fs" | ||||
| import Translations from "../UI/i18n/Translations" | ||||
| import * as themeOverview from "../assets/generated/theme_overview.json" | ||||
| import themeOverview from "../assets/generated/theme_overview.json" | ||||
| import DefaultGUI from "../UI/DefaultGUI" | ||||
| import FeaturePipelineState from "../Logic/State/FeaturePipelineState" | ||||
| import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" | ||||
| import * as bookcases from "../assets/generated/themes/bookcases.json" | ||||
| import bookcases from "../assets/generated/themes/bookcases.json" | ||||
| import { DefaultGuiState } from "../UI/DefaultGuiState" | ||||
| import * as fakedom from "fake-dom" | ||||
| import fakedom from "fake-dom" | ||||
| import Hotkeys from "../UI/Base/Hotkeys" | ||||
| import { QueryParameters } from "../Logic/Web/QueryParameters" | ||||
| function WriteFile( | ||||
|     filename, | ||||
|     html: BaseUIElement, | ||||
|  | @ -103,7 +104,7 @@ function generateWikipage() { | |||
|         "! Name, link !! Genre !! Covered region !! Language !! Description !! Free materials !! Image\n" + | ||||
|         "|-" | ||||
| 
 | ||||
|     for (const layout of themeOverview["default"] ?? themeOverview) { | ||||
|     for (const layout of themeOverview) { | ||||
|         if (layout.hideFromOverview) { | ||||
|             continue | ||||
|         } | ||||
|  | @ -225,6 +226,12 @@ WriteFile("./Docs/URL_Parameters.md", QueryParameterDocumentation.GenerateQueryP | |||
| if (fakedom === undefined || window === undefined) { | ||||
|     throw "FakeDom not initialized" | ||||
| } | ||||
| QueryParameters.GetQueryParameter( | ||||
|     "mode", | ||||
|     "map", | ||||
|     "The mode the application starts in, e.g. 'map', 'dashboard' or 'statistics'" | ||||
| ) | ||||
| 
 | ||||
| new DefaultGUI( | ||||
|     new FeaturePipelineState(new LayoutConfig(<any>bookcases)), | ||||
|     new DefaultGuiState() | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import ScriptUtils from "./ScriptUtils" | ||||
| import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs" | ||||
| import * as licenses from "../assets/generated/license_info.json" | ||||
| import licenses from "../assets/generated/license_info.json" | ||||
| import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson" | ||||
| import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" | ||||
| import Constants from "../Models/Constants" | ||||
|  | @ -14,8 +14,8 @@ import { | |||
| } from "../Models/ThemeConfig/Conversion/Validation" | ||||
| import { Translation } from "../UI/i18n/Translation" | ||||
| import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson" | ||||
| import * as questions from "../assets/tagRenderings/questions.json" | ||||
| import * as icons from "../assets/tagRenderings/icons.json" | ||||
| import questions from "../assets/tagRenderings/questions.json" | ||||
| import icons from "../assets/tagRenderings/icons.json" | ||||
| import PointRenderingConfigJson from "../Models/ThemeConfig/Json/PointRenderingConfigJson" | ||||
| import { PrepareLayer } from "../Models/ThemeConfig/Conversion/PrepareLayer" | ||||
| import { PrepareTheme } from "../Models/ThemeConfig/Conversion/PrepareTheme" | ||||
|  | @ -155,7 +155,7 @@ class LayerOverviewUtils { | |||
|         const dict = new Map<string, TagRenderingConfigJson>() | ||||
| 
 | ||||
|         const validator = new ValidateTagRenderings(undefined, doesImageExist) | ||||
|         for (const key in questions["default"]) { | ||||
|         for (const key in questions) { | ||||
|             if (key === "id") { | ||||
|                 continue | ||||
|             } | ||||
|  | @ -168,7 +168,7 @@ class LayerOverviewUtils { | |||
|             ) | ||||
|             dict.set(key, config) | ||||
|         } | ||||
|         for (const key in icons["default"]) { | ||||
|         for (const key in icons) { | ||||
|             if (key === "id") { | ||||
|                 continue | ||||
|             } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFile, writeFi | |||
| import Locale from "../UI/i18n/Locale" | ||||
| import Translations from "../UI/i18n/Translations" | ||||
| import { Translation } from "../UI/i18n/Translation" | ||||
| import * as all_known_layouts from "../assets/generated/known_layers_and_themes.json" | ||||
| import all_known_layouts from "../assets/generated/known_layers_and_themes.json" | ||||
| import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson" | ||||
| import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" | ||||
| import xml2js from "xml2js" | ||||
|  | @ -295,7 +295,7 @@ async function createIndexFor(theme: LayoutConfig) { | |||
|     const filename = "index_" + theme.id + ".ts" | ||||
|     writeFileSync( | ||||
|         filename, | ||||
|         `import * as themeConfig from "./assets/generated/themes/${theme.id}.json"\n` | ||||
|         `import themeConfig from "./assets/generated/themes/${theme.id}.json"\n` | ||||
|     ) | ||||
|     appendFileSync(filename, codeTemplate) | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import * as known_layers from "../assets/generated/known_layers.json" | ||||
| import known_layers from "../assets/generated/known_layers.json" | ||||
| import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" | ||||
| import { TagUtils } from "../Logic/Tags/TagUtils" | ||||
| import { Utils } from "../Utils" | ||||
|  | @ -10,7 +10,7 @@ import Constants from "../Models/Constants" | |||
| 
 | ||||
| async function main(includeTags = true) { | ||||
|     ScriptUtils.fixUtils() | ||||
|     const layers: LayerConfigJson[] = (known_layers["default"] ?? known_layers).layers | ||||
|     const layers = <LayerConfigJson[]>known_layers.layers | ||||
| 
 | ||||
|     const keysAndTags = new Map<string, Set<string>>() | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
|  */ | ||||
| import ScriptUtils from "../ScriptUtils" | ||||
| import { existsSync, readFileSync, writeFileSync } from "fs" | ||||
| import * as known_languages from "../../assets/language_native.json" | ||||
| import known_languages from "../../assets/language_native.json" | ||||
| import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" | ||||
| import { MappingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" | ||||
| import SmallLicense from "../../Models/smallLicense" | ||||
|  |  | |||
|  | @ -7,7 +7,8 @@ import { exec } from "child_process" | |||
|  * @param reason | ||||
|  * @private | ||||
|  */ | ||||
| function detectInCode(forbidden: string, reason: string) { | ||||
| function detectInCode(forbidden: string, reason: string): (done: () => void) => void { | ||||
|     return (done: () => void) => { | ||||
|         const excludedDirs = [ | ||||
|             ".git", | ||||
|             "node_modules", | ||||
|  | @ -26,7 +27,7 @@ function detectInCode(forbidden: string, reason: string) { | |||
|                 excludedDirs.map((d) => "--exclude-dir=" + d).join(" "), | ||||
|             (error, stdout, stderr) => { | ||||
|                 if (error?.message?.startsWith("Command failed: grep")) { | ||||
|                 console.warn("Command failed!") | ||||
|                     console.warn("Command failed!", error) | ||||
|                     return | ||||
|                 } | ||||
|                 if (error !== null) { | ||||
|  | @ -41,28 +42,51 @@ function detectInCode(forbidden: string, reason: string) { | |||
|                     .filter((s) => s !== "") | ||||
|                     .filter((s) => !s.startsWith("./test/")) | ||||
|                 if (found.length > 0) { | ||||
|                 throw `Found a '${forbidden}' at \n    ${found.join("\n     ")}.\n ${reason}` | ||||
|                     const msg = `Found a '${forbidden}' at \n    ${found.join( | ||||
|                         "\n     " | ||||
|                     )}.\n ${reason}` | ||||
|                     console.error(msg) | ||||
|                     console.error(found.length, "issues found") | ||||
|                     throw msg | ||||
|                 } | ||||
|                 done() | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| describe("Code quality", () => { | ||||
|     it("should not contain reverse", () => { | ||||
|     it( | ||||
|         "should not contain reverse", | ||||
|         detectInCode( | ||||
|             "reverse()", | ||||
|             "Reverse is stateful and changes the source list. This often causes subtle bugs" | ||||
|         ) | ||||
|     }) | ||||
|     ) | ||||
| 
 | ||||
|     it("should not contain 'constructor.name'", () => { | ||||
|     it( | ||||
|         "should not contain 'constructor.name'", | ||||
|         detectInCode("constructor\\.name", "This is not allowed, as minification does erase names.") | ||||
|     }) | ||||
|     ) | ||||
| 
 | ||||
|     it("should not contain 'innerText'", () => { | ||||
|     it( | ||||
|         "should not contain 'innerText'", | ||||
|         detectInCode( | ||||
|             "innerText", | ||||
|             "innerText is not allowed as it is not testable with fakeDom. Use 'textContent' instead." | ||||
|         ) | ||||
|     }) | ||||
|     ) | ||||
| 
 | ||||
|     it( | ||||
|         "should not contain 'import * as name from \"xyz.json\"'", | ||||
|         detectInCode( | ||||
|             'import \\* as [a-zA-Z0-9_]\\+ from \\"[.-_/a-zA-Z0-9]\\+\\.json\\"', | ||||
|             "With vite, json files have a default export. Use import name from file.json instead" | ||||
|         ) | ||||
|     ) | ||||
| 
 | ||||
|     it( | ||||
|         "should not contain '[\"default\"]'", | ||||
|         detectInCode('\\[\\"default\\"\\]', "Possible leftover of faulty default import") | ||||
|     ) | ||||
| }) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue