Housekeeping

This commit is contained in:
Pieter Vander Vennet 2024-07-09 13:42:08 +02:00
parent 4ad1e67f6e
commit da615acfb1
55 changed files with 1772 additions and 1455 deletions

View file

@ -84,7 +84,11 @@ export class SummaryTileSource extends DynamicTileSource {
zoomRounded,
0, // minzoom
(tileIndex) => {
const features = SummaryTileSource.downloadTile(tileIndex, cacheserver, layersSummed)
const features = SummaryTileSource.downloadTile(
tileIndex,
cacheserver,
layersSummed
)
const [z] = Tiles.tile_from_index(tileIndex)
return new StaticFeatureSource(
features.map(
@ -103,7 +107,11 @@ export class SummaryTileSource extends DynamicTileSource {
)
}
public static downloadTile(tileIndex: number, cacheserver: string, layersSummed: string): Store<Feature<Point>[]>{
public static downloadTile(
tileIndex: number,
cacheserver: string,
layersSummed: string
): Store<Feature<Point>[]> {
const [z, x, y] = Tiles.tile_from_index(tileIndex)
let coordinates = Tiles.centerPointOf(z, x, y)
const url = `${cacheserver}/${layersSummed}/${z}/${x}/${y}.json`

View file

@ -18,14 +18,14 @@ class FeatureSwitchUtils {
key,
"" + defaultValue,
documentation,
{ stackOffset: -1 },
{ stackOffset: -1 }
)
// It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened
return queryParam.sync(
(str) => (str === undefined ? defaultValue : str !== "false"),
[],
(b) => (b == defaultValue ? undefined : "" + b),
(b) => (b == defaultValue ? undefined : "" + b)
)
}
}
@ -37,7 +37,7 @@ export class OsmConnectionFeatureSwitches {
this.featureSwitchFakeUser = QueryParameters.GetBooleanQueryParameter(
"fake-user",
false,
"If true, 'dryrun' mode is activated and a fake user account is loaded",
"If true, 'dryrun' mode is activated and a fake user account is loaded"
)
}
}
@ -99,14 +99,14 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch(
"fs-enable-login",
layoutToUse?.enableUserBadge ?? true,
"Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode.",
"Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode."
)
{
if (QueryParameters.wasInitialized("fs-userbadge")) {
// userbadge is the legacy name for 'enable-login'
this.featureSwitchEnableLogin.setData(
QueryParameters.GetBooleanQueryParameter("fs-userbadge", undefined, "Legacy")
.data,
.data
)
}
}
@ -114,64 +114,66 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchSearch = FeatureSwitchUtils.initSwitch(
"fs-search",
layoutToUse?.enableSearch ?? true,
"Disables/Enables the search bar",
"Disables/Enables the search bar"
)
this.featureSwitchBackgroundSelection = FeatureSwitchUtils.initSwitch(
"fs-background",
layoutToUse?.enableBackgroundLayerSelection ?? true,
"Disables/Enables the background layer control where a user can enable e.g. aerial imagery",
"Disables/Enables the background layer control where a user can enable e.g. aerial imagery"
)
this.featureSwitchFilter = FeatureSwitchUtils.initSwitch(
"fs-filter",
layoutToUse?.enableLayers ?? true,
"Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties",
"Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties"
)
this.featureSwitchWelcomeMessage = FeatureSwitchUtils.initSwitch(
"fs-welcome-message",
true,
"Disables/enables the help menu or welcome message",
"Disables/enables the help menu or welcome message"
)
this.featureSwitchCommunityIndex = FeatureSwitchUtils.initSwitch(
"fs-community-index",
this.featureSwitchEnableLogin.data,
"Disables/enables the button to get in touch with the community",
"Disables/enables the button to get in touch with the community"
)
this.featureSwitchExtraLinkEnabled = FeatureSwitchUtils.initSwitch(
"fs-iframe-popout",
true,
"Disables/Enables the extraLink button. By default, if in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch or another extraLink button is enabled)",
"Disables/Enables the extraLink button. By default, if in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch or another extraLink button is enabled)"
)
this.featureSwitchBackToThemeOverview = FeatureSwitchUtils.initSwitch(
"fs-homepage-link",
layoutToUse?.enableMoreQuests ?? true,
"Disables/Enables the various links which go back to the index page with the theme overview",
"Disables/Enables the various links which go back to the index page with the theme overview"
)
this.featureSwitchShareScreen = FeatureSwitchUtils.initSwitch(
"fs-share-screen",
layoutToUse?.enableShareScreen ?? true,
"Disables/Enables the 'Share-screen'-tab in the welcome message",
"Disables/Enables the 'Share-screen'-tab in the welcome message"
)
this.featureSwitchGeolocation = FeatureSwitchUtils.initSwitch(
"fs-geolocation",
layoutToUse?.enableGeolocation ?? true,
"Disables/Enables the geolocation button",
"Disables/Enables the geolocation button"
)
this.featureSwitchLayerDefault = QueryParameters.GetBooleanQueryParameter("fs-layers-enabled", true,
"If set to false, all layers will be disabled - except the explicitly enabled layers",
this.featureSwitchLayerDefault = QueryParameters.GetBooleanQueryParameter(
"fs-layers-enabled",
true,
"If set to false, all layers will be disabled - except the explicitly enabled layers"
)
this.featureSwitchShowAllQuestions = FeatureSwitchUtils.initSwitch(
"fs-all-questions",
layoutToUse?.enableShowAllQuestions ?? false,
"Always show all questions",
"Always show all questions"
)
this.featureSwitchEnableExport = FeatureSwitchUtils.initSwitch(
"fs-export",
layoutToUse?.enableExportButton ?? true,
"Enable the export as GeoJSON and CSV button",
"Enable the export as GeoJSON and CSV button"
)
let testingDefaultValue = false
@ -185,60 +187,59 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchIsTesting = QueryParameters.GetBooleanQueryParameter(
"test",
testingDefaultValue,
"If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org",
"If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org"
)
this.featureSwitchIsDebugging = QueryParameters.GetBooleanQueryParameter(
"debug",
false,
"If true, shows some extra debugging help such as all the available tags on every object",
"If true, shows some extra debugging help such as all the available tags on every object"
)
this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter(
"moreprivacy",
layoutToUse.enableMorePrivacy,
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken.",
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken."
)
this.overpassUrl = QueryParameters.GetQueryParameter(
"overpassUrl",
(layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","),
"Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter",
"Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter"
).sync(
(param) => param?.split(","),
[],
(urls) => urls?.join(","),
(urls) => urls?.join(",")
)
this.overpassTimeout = UIEventSource.asInt(
QueryParameters.GetQueryParameter(
"overpassTimeout",
"" + layoutToUse?.overpassTimeout,
"Set a different timeout (in seconds) for queries in overpass",
),
"Set a different timeout (in seconds) for queries in overpass"
)
)
this.overpassMaxZoom = UIEventSource.asFloat(
QueryParameters.GetQueryParameter(
"overpassMaxZoom",
"" + layoutToUse?.overpassMaxZoom,
" point to switch between OSM-api and overpass",
),
" point to switch between OSM-api and overpass"
)
)
this.osmApiTileSize = UIEventSource.asInt(
QueryParameters.GetQueryParameter(
"osmApiTileSize",
"" + layoutToUse?.osmApiTileSize,
"Tilesize when the OSM-API is used to fetch data within a BBOX",
),
"Tilesize when the OSM-API is used to fetch data within a BBOX"
)
)
this.backgroundLayerId = QueryParameters.GetQueryParameter(
"background",
layoutToUse?.defaultBackgroundId,
"The id of the background layer to start with",
"The id of the background layer to start with"
)
}
}

View file

@ -35,13 +35,23 @@ export default class LayerState {
* @param context
* @param layersEnabledByDefault
*/
constructor(osmConnection: OsmConnection, layers: LayerConfig[], context: string, layersEnabledByDefault: Store<boolean>) {
constructor(
osmConnection: OsmConnection,
layers: LayerConfig[],
context: string,
layersEnabledByDefault: Store<boolean>
) {
this.osmConnection = osmConnection
const filteredLayers = new Map()
for (const layer of layers) {
filteredLayers.set(
layer.id,
FilteredLayer.initLinkedState(layer, context, this.osmConnection, layersEnabledByDefault)
FilteredLayer.initLinkedState(
layer,
context,
this.osmConnection,
layersEnabledByDefault
)
)
}
this.filteredLayers = filteredLayers

View file

@ -1,14 +1,42 @@
import { Utils } from "../../Utils"
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
export class ThemeMetaTagging {
public static readonly themeName = "usersettings"
public static readonly themeName = "usersettings"
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/&lt;/g,'<')?.replace(/&gt;/g,'>') ?? '' )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
feat.properties['__current_backgroun'] = 'initial_value'
}
}
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
feat.properties._description
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
?.at(1)
)
Utils.AddLazyProperty(
feat.properties,
"_d",
() => feat.properties._description?.replace(/&lt;/g, "<")?.replace(/&gt;/g, ">") ?? ""
)
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.href.match(/mastodon|en.osm.town/) !== null
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
((feat) => {
const e = document.createElement("div")
e.innerHTML = feat.properties._d
return Array.from(e.getElementsByTagName("a")).filter(
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
)[0]?.href
})(feat)
)
Utils.AddLazyProperty(
feat.properties,
"_mastodon_candidate",
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
)
feat.properties["__current_backgroun"] = "initial_value"
}
}

View file

@ -169,7 +169,17 @@ export default class Constants {
public static readonly maptilerApiKey = "GvoVAJgu46I5rZapJuAy"
public static readonly SummaryServer: string = Constants.config.summary_server
public static allServers: string[] = [Constants.SummaryServer, Constants.VectorTileServer, Constants.GeoIpServer, Constants.ErrorReportServer, Constants.countryCoderEndpoint, Constants.osmAuthConfig.url, Constants.nominatimEndpoint, Constants.linkedDataProxy, ...Constants.defaultOverpassUrls]
public static allServers: string[] = [
Constants.SummaryServer,
Constants.VectorTileServer,
Constants.GeoIpServer,
Constants.ErrorReportServer,
Constants.countryCoderEndpoint,
Constants.osmAuthConfig.url,
Constants.nominatimEndpoint,
Constants.linkedDataProxy,
...Constants.defaultOverpassUrls,
]
private static isRetina(): boolean {
if (Utils.runningFromConsole) {

View file

@ -104,12 +104,12 @@ export default class FilteredLayer {
)
} else {
let isShown = layer.shownByDefault
if(enabledByDefault !== undefined && enabledByDefault.data === false){
if (enabledByDefault !== undefined && enabledByDefault.data === false) {
isShown = false
}
isDisplayed = QueryParameters.GetBooleanQueryParameter(
FilteredLayer.queryParameterKey(layer),
isShown ,
isShown,
"Whether or not layer " + layer.id + " is shown"
)
}

View file

@ -39,7 +39,9 @@ export class MenuState {
/**
* Standalone copyright panel
*/
public readonly copyrightPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
public readonly copyrightPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(
false
)
public readonly communityIndexPanelIsOpened: UIEventSource<boolean> = new UIEventSource(false)
public readonly allToggles: {
@ -140,7 +142,6 @@ export class MenuState {
name: "background",
showOverOthers: true,
},
]
for (const toggle of this.allToggles) {
toggle.toggle.addCallback((isOpen) => {

View file

@ -336,13 +336,12 @@ export default class LayoutConfig implements LayoutInformation {
...json,
layers: json.layers.filter((l) => l["id"] !== "favourite"),
}
const usedImages =
new ExtractImages(this.official, undefined)
.convertStrict(
jsonNoFavourites,
ConversionContext.construct([json.id], ["ExtractImages"])
)
.flatMap((i) => i.path)
const usedImages = new ExtractImages(this.official, undefined)
.convertStrict(
jsonNoFavourites,
ConversionContext.construct([json.id], ["ExtractImages"])
)
.flatMap((i) => i.path)
usedImages.sort()
this.usedImages = Utils.Dedup(usedImages)

View file

@ -158,7 +158,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.featureSwitches = new FeatureSwitchState(layout)
this.guistate = new MenuState(
this.featureSwitches.featureSwitchWelcomeMessage.data,
layout.id,
layout.id
)
this.map = new UIEventSource<MlMap>(undefined)
const geolocationState = new GeoLocationState()
@ -174,14 +174,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
oauth_token: QueryParameters.GetQueryParameter(
"oauth_token",
undefined,
"Used to complete the login",
"Used to complete the login"
),
})
this.userRelatedState = new UserRelatedState(
this.osmConnection,
layout,
this.featureSwitches,
this.mapProperties,
this.mapProperties
)
this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => {
this.mapProperties.allowRotating.setData(fixated !== "yes")
@ -192,17 +192,22 @@ export default class ThemeViewState implements SpecialVisualizationState {
geolocationState,
this.selectedElement,
this.mapProperties,
this.userRelatedState.gpsLocationHistoryRetentionTime,
this.userRelatedState.gpsLocationHistoryRetentionTime
)
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
this.availableLayers = AvailableRasterLayers.layersAvailableAt(
this.mapProperties.location,
this.osmConnection.isLoggedIn,
this.osmConnection.isLoggedIn
)
const self = this
this.layerState = new LayerState(this.osmConnection, layout.layers, layout.id, this.featureSwitches.featureSwitchLayerDefault)
this.layerState = new LayerState(
this.osmConnection,
layout.layers,
layout.id,
this.featureSwitches.featureSwitchLayerDefault
)
{
const overlayLayerStates = new Map<string, { isDisplayed: UIEventSource<boolean> }>()
@ -210,7 +215,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const isDisplayed = QueryParameters.GetBooleanQueryParameter(
"overlay-" + rasterInfo.id,
rasterInfo.defaultState ?? true,
"Whether or not overlay layer " + rasterInfo.id + " is shown",
"Whether or not overlay layer " + rasterInfo.id + " is shown"
)
const state = { isDisplayed }
overlayLayerStates.set(rasterInfo.id, state)
@ -235,7 +240,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.osmConnection.Backend(),
(id) => self.layerState.filteredLayers.get(id).isDisplayed,
mvtAvailableLayers,
this.fullNodeDatabase,
this.fullNodeDatabase
)
let currentViewIndex = 0
@ -253,7 +258,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
id: "current_view_" + currentViewIndex,
}),
]
}),
})
)
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
@ -271,19 +276,19 @@ export default class ThemeViewState implements SpecialVisualizationState {
featureSwitches: this.featureSwitches,
},
layout?.isLeftRightSensitive() ?? false,
(e) => this.reportError(e),
(e) => this.reportError(e)
)
this.historicalUserLocations = this.geolocation.historicalUserLocations
this.newFeatures = new NewGeometryFromChangesFeatureSource(
this.changes,
layoutSource,
this.featureProperties,
this.featureProperties
)
layoutSource.addSource(this.newFeatures)
const perLayer = new PerLayerFeatureSourceSplitter(
Array.from(this.layerState.filteredLayers.values()).filter(
(l) => l.layerDef?.source !== null,
(l) => l.layerDef?.source !== null
),
new ChangeGeometryApplicator(this.indexedFeatures, this.changes),
{
@ -294,10 +299,10 @@ export default class ThemeViewState implements SpecialVisualizationState {
"Got ",
features.length,
"leftover features, such as",
features[0].properties,
features[0].properties
)
},
},
}
)
this.perLayer = perLayer.perLayer
}
@ -337,12 +342,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.lastClickObject = new LastClickFeatureSource(
this.layout,
this.mapProperties.lastClickLocation,
this.userRelatedState.addNewFeatureMode,
this.userRelatedState.addNewFeatureMode
)
this.osmObjectDownloader = new OsmObjectDownloader(
this.osmConnection.Backend(),
this.changes,
this.changes
)
this.perLayerFiltered = this.showNormalDataOn(this.map)
@ -353,7 +358,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
currentZoom: this.mapProperties.zoom,
layerState: this.layerState,
bounds: this.visualFeedbackViewportBounds,
},
}
)
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
this.imageUploadManager = new ImageUploadManager(
@ -361,11 +366,13 @@ export default class ThemeViewState implements SpecialVisualizationState {
Imgur.singleton,
this.featureProperties,
this.osmConnection,
this.changes,
this.changes
)
this.favourites = new FavouritesFeatureSource(this)
this.featureSummary = this.setupSummaryLayer(new LayerConfig(<LayerConfigJson>summaryLayer, "summaryLayer", true))
this.featureSummary = this.setupSummaryLayer(
new LayerConfig(<LayerConfigJson>summaryLayer, "summaryLayer", true)
)
this.toCacheSavers = this.initSaveToLocalStorage()
this.initActors()
this.drawSpecialLayers()
@ -404,7 +411,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
LayoutSource.fromCacheZoomLevel,
fs,
this.featureProperties,
fs.layer.layerDef.maxAgeOfCache,
fs.layer.layerDef.maxAgeOfCache
)
toLocalStorage.set(layerId, storage)
})
@ -417,7 +424,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const doShowLayer = this.mapProperties.zoom.map(
(z) =>
(fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0),
[fs.layer.isDisplayed],
[fs.layer.isDisplayed]
)
if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) {
@ -434,7 +441,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
fs.layer,
fs,
(id) => this.featureProperties.getStore(id),
this.layerState.globalFilters,
this.layerState.globalFilters
)
filteringFeatureSource.set(layerName, filtered)
@ -575,7 +582,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
return
}
this.selectClosestAtCenter(0)
},
}
)
for (let i = 1; i < 9; i++) {
@ -593,7 +600,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
onUp: true,
},
doc,
() => this.selectClosestAtCenter(i - 1),
() => this.selectClosestAtCenter(i - 1)
)
}
@ -610,7 +617,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
this.guistate.backgroundLayerSelectionIsOpened.setData(true)
}
},
}
)
Hotkeys.RegisterHotkey(
{
@ -622,14 +629,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.featureSwitches.featureSwitchFilter.data) {
this.guistate.openFilterView()
}
},
}
)
Hotkeys.RegisterHotkey(
{ shift: "O" },
Translations.t.hotkeyDocumentation.selectMapnik,
() => {
this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto)
},
}
)
const setLayerCategory = (category: EliCategory) => {
const available = this.availableLayers.data
@ -637,7 +644,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const best = RasterLayerUtils.SelectBestLayerAccordingTo(
available,
category,
current.data,
current.data
)
console.log("Best layer for category", category, "is", best.properties.id)
current.setData(best)
@ -646,26 +653,26 @@ export default class ThemeViewState implements SpecialVisualizationState {
Hotkeys.RegisterHotkey(
{ nomod: "O" },
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
() => setLayerCategory("osmbasedmap"),
() => setLayerCategory("osmbasedmap")
)
Hotkeys.RegisterHotkey(
{ nomod: "M" },
Translations.t.hotkeyDocumentation.selectMap,
() => setLayerCategory("map"),
() => setLayerCategory("map")
)
Hotkeys.RegisterHotkey(
{ nomod: "P" },
Translations.t.hotkeyDocumentation.selectAerial,
() => setLayerCategory("photo"),
() => setLayerCategory("photo")
)
Hotkeys.RegisterHotkey(
{ nomod: "L" },
Translations.t.hotkeyDocumentation.geolocate,
() => {
this.geolocationControl.handleClick()
},
}
)
return true
})
@ -677,7 +684,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
Translations.t.hotkeyDocumentation.translationMode,
() => {
Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data)
},
}
)
}
@ -688,7 +695,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
const normalLayers = this.layout.layers.filter(
(l) =>
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
!l.id.startsWith("note_import"),
!l.id.startsWith("note_import")
)
const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom))
@ -696,7 +703,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
(l) =>
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
l.source.geojsonSource === undefined &&
l.doCount,
l.doCount
)
const summaryTileSource = new SummaryTileSource(
Constants.SummaryServer,
@ -705,7 +712,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.mapProperties,
{
isActive: this.mapProperties.zoom.map((z) => z < maxzoom),
},
}
)
const src = new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers)
@ -727,12 +734,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
gps_location_history: this.geolocation.historicalUserLocations,
gps_track: this.geolocation.historicalUserLocationsTrack,
selected_element: new StaticFeatureSource(
this.selectedElement.map((f) => (f === undefined ? empty : [f])),
this.selectedElement.map((f) => (f === undefined ? empty : [f]))
),
range: new StaticFeatureSource(
this.mapProperties.maxbounds.map((bbox) =>
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })],
),
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })]
)
),
current_view: this.currentView,
favourite: this.favourites,
@ -747,7 +754,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
ShowDataLayer.showRange(
this.map,
new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]),
this.featureSwitches.featureSwitchIsTesting,
this.featureSwitches.featureSwitchIsTesting
)
}
const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view")
@ -761,7 +768,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
currentViewLayer,
this.layout,
this.osmObjectDownloader,
this.featureProperties,
this.featureProperties
)
})
}
@ -805,20 +812,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
const lastClickLayerConfig = new LayerConfig(
<LayerConfigJson>last_click_layerconfig,
"last_click",
"last_click"
)
const lastClickFiltered =
lastClickLayerConfig.isShown === undefined
? specialLayers.last_click
: specialLayers.last_click.features.mapD((fs) =>
fs.filter((f) => {
const matches = lastClickLayerConfig.isShown.matchesProperties(
f.properties,
)
console.debug("LastClick ", f, "matches", matches)
return matches
}),
)
fs.filter((f) => {
const matches = lastClickLayerConfig.isShown.matchesProperties(
f.properties
)
console.debug("LastClick ", f, "matches", matches)
return matches
})
)
new ShowDataLayer(this.map, {
features: new StaticFeatureSource(lastClickFiltered),
layer: lastClickLayerConfig,
@ -863,7 +870,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.mapProperties.rasterLayer,
this.availableLayers,
this.featureSwitches.backgroundLayerId,
this.userRelatedState.preferredBackgroundLayer,
this.userRelatedState.preferredBackgroundLayer
)
}

View file

@ -1,5 +1,4 @@
<script lang="ts">
import { Utils } from "../../Utils"
import Share from "@babeard/svelte-heroicons/solid/Share"
import { DocumentDuplicateIcon } from "@rgossiaux/svelte-heroicons/outline"
@ -30,8 +29,7 @@
}
</script>
<div class="flex flex-col w-full">
<div class="flex w-full flex-col">
<div class="flex w-full">
<div class="literal-code w-full" on:click={(e) => Utils.selectTextIn(e.target)}>
{text}
@ -48,11 +46,8 @@
</button>
{/if}
</div>
</div>
<div class="flex justify-center">
{#if isCopied}
<Tr t={tr.copiedToClipboard} cls="thanks m-2" />

View file

@ -1,5 +1,4 @@
<script lang="ts">
import Translations from "../i18n/Translations"
import { Utils } from "../../Utils"
import Hotkeys from "../Base/Hotkeys"
@ -24,7 +23,6 @@
let layout = state.layout
let featureSwitches = state.featureSwitches
</script>
<div class="link-underline links-w-full m-2 flex flex-col gap-y-1">
@ -51,16 +49,14 @@
<a
class="flex"
href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" +
layout.id +
".md"}
href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" + layout.id + ".md"}
target="_blank"
>
<DocumentChartBar class="h-6 w-6" />
<Tr
t={Translations.t.general.attribution.openThemeDocumentation.Subs({
name: layout.title,
})}
name: layout.title,
})}
/>
</a>
@ -74,10 +70,7 @@
<Tr t={Translations.t.general.attribution.donate} />
</a>
<button
class="as-link"
on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}
>
<button class="as-link" on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}>
<Community class="h-6 w-6" />
<Tr t={Translations.t.communityIndex.title} />
</button>
@ -88,10 +81,7 @@
<MapillaryLink large={false} mapProperties={state.mapProperties} />
</If>
<button
class="as-link"
on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}
>
<button class="as-link" on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}>
<EyeIcon class="h-6 w-6 pr-1" />
<Tr t={Translations.t.privacy.title} />
</button>

View file

@ -40,9 +40,9 @@ export default class CopyrightPanel extends Combine {
const t = Translations.t.general.attribution
const layoutToUse = state.layout
const iconAttributions: BaseUIElement[] =layoutToUse.getUsedImages().map(
CopyrightPanel.IconAttribution
)
const iconAttributions: BaseUIElement[] = layoutToUse
.getUsedImages()
.map(CopyrightPanel.IconAttribution)
let maintainer: BaseUIElement = undefined
if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") {

View file

@ -31,8 +31,7 @@
*/
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.layout.official
let layoutId = state.layout.id
let baseLink =
`${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?`
let baseLink = `${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?`
let showWelcomeMessage = true
let enableLogin = true
@ -45,7 +44,8 @@
enableLogin: boolean,
enableFilters: boolean,
enableBackground: boolean,
enableGeolocation: boolean) {
enableGeolocation: boolean
) {
const layout = state.layout
let excluded = Utils.NoNull([
showWelcomeMessage ? undefined : "fs-welcome-message",
@ -53,7 +53,6 @@
enableFilters ? undefined : "fs-filter",
enableBackground ? undefined : "fs-background",
enableGeolocation ? undefined : "fs-geolocation",
])
const layerParamsWhitelist: string[] = ["fs-layers-enabled=false"]
const layerParamsBlacklist: string[] = []
@ -79,9 +78,10 @@
const layersBlack = layerParamsBlacklist.join("&")
const layersWhite = layerParamsWhitelist.join("&")
const layers = layersBlack.length < layersWhite.length ? layerParamsBlacklist : layerParamsWhitelist
const layers =
layersBlack.length < layersWhite.length ? layerParamsBlacklist : layerParamsWhitelist
const params = QueryParameters.GetParts(new Set(excluded))
.filter(part => !part.startsWith("layer-"))
.filter((part) => !part.startsWith("layer-"))
.concat(...layers)
.concat(excluded.map((k) => k + "=" + false))
linkToShare = baseLink + Utils.Dedup(params).join("&")
@ -91,30 +91,42 @@
}
}
$: calculateLinkToShare(showWelcomeMessage, enableLogin, enableFilters, enableBackground, enableGeolocation)
$: calculateLinkToShare(
showWelcomeMessage,
enableLogin,
enableFilters,
enableBackground,
enableGeolocation
)
let iframeCode: string
$: iframeCode = `<iframe src="${linkToShare}"
${enableGeolocation ? 'allow="geolocation"' : ""} width="100%" height="100%" style="min-width: 250px; min-height: 250px"
${
enableGeolocation ? 'allow="geolocation"' : ""
} width="100%" height="100%" style="min-width: 250px; min-height: 250px"
title="${state.layout.title?.txt ?? "MapComplete"} with MapComplete">
</iframe>`
Array.from(state.layerState.filteredLayers.values()).forEach(flayer => flayer.isDisplayed.addCallbackAndRunD(_ => {
calculateLinkToShare(showWelcomeMessage, enableLogin, enableFilters, enableBackground, enableGeolocation)
}))
Array.from(state.layerState.filteredLayers.values()).forEach((flayer) =>
flayer.isDisplayed.addCallbackAndRunD((_) => {
calculateLinkToShare(
showWelcomeMessage,
enableLogin,
enableFilters,
enableBackground,
enableGeolocation
)
})
)
</script>
<div class="flex flex-col">
<div class="flex flex-col">
<Tr t={tr.intro} />
<Copyable {state} text={linkToShare}/>
<Copyable {state} text={linkToShare} />
</div>
<div class="flex justify-center">
<ToSvelte
construct={() => new Img(new Qr(linkToShare).toImageElement(125)).SetStyle("width: 125px")}
/>
@ -122,11 +134,11 @@
<Tr t={tr.embedIntro} />
<Copyable text={iframeCode}/>
<Copyable text={iframeCode} />
<AccordionSingle>
<div slot="header">
<Tr t={tr.options}/>
<Tr t={tr.options} />
</div>
<div class="link-underline my-1 flex flex-col">
<label>
@ -139,13 +151,11 @@
<Tr t={tr.fsUserbadge} />
</label>
<label>
<input bind:checked={enableFilters} type="checkbox" id="share_enable_filter" />
<Tr t={tr.fsFilter} />
</label>
<label>
<input bind:checked={enableBackground} type="checkbox" id="share_enable_background" />
<Tr t={tr.fsBackground} />
@ -157,15 +167,16 @@
</label>
<span>
<Tr t={tr.stateIsIncluded}/>
<a class="inline-block w-fit cursor-pointer" on:click={() => state.guistate.filtersPanelIsOpened.set(true)}>
<Tr t={tr.openLayers}/>
</a>
<Tr t={tr.stateIsIncluded} />
<a
class="inline-block w-fit cursor-pointer"
on:click={() => state.guistate.filtersPanelIsOpened.set(true)}
>
<Tr t={tr.openLayers} />
</a>
</span>
<Tr cls="link-underline" t={tr.documentation} />
</div>
</AccordionSingle>
</div>

View file

@ -86,7 +86,7 @@
</script>
{#if theme.id !== personal.id || $unlockedPersonal}
<a class="my-1 flex w-full items-center text-ellipsis rounded low-interaction p-1" href={$href}>
<a class="low-interaction my-1 flex w-full items-center text-ellipsis rounded p-1" href={$href}>
<Marker icons={theme.icon} size="block h-8 w-8 sm:h-11 sm:w-11 m-1 sm:mx-2 md:mx-4 shrink-0" />
<span class="flex flex-col overflow-hidden text-ellipsis text-xl font-bold">

View file

@ -63,7 +63,7 @@
<!-- Buttons: open map, go to location, search -->
<NextButton clss="primary w-full" on:click={() => state.guistate.themeIsOpened.setData(false)}>
<div class="flex flex-col w-full items-center">
<div class="flex w-full flex-col items-center">
<div class="flex w-full justify-center text-2xl">
<Tr t={Translations.t.general.openTheMap} />
</div>

View file

@ -57,7 +57,6 @@
tags.data["closed_at"] = new Date().toISOString()
NoteCommentElement.addCommentTo(txt.data, tags, state)
tags.ping()
}
async function reopenNote() {
@ -67,7 +66,6 @@
NoteCommentElement.addCommentTo(txt.data, tags, state)
tags.ping()
isProcessing.set(false)
}
</script>
@ -89,43 +87,43 @@
</label>
{#if $isProcessing}
<Loading/>
{:else}
<div class="flex flex-col">
{#if $txt?.length > 0}
<button class="primary flex" on:click|preventDefault={() => addComment()}>
<!-- Add a comment -->
<Speech_bubble class="h-7 w-7 pr-2" />
<Tr t={t.addCommentPlaceholder} />
</button>
{:else}
<div class="alert w-full">
<Tr t={t.typeText} />
</div>
{/if}
<Loading />
{:else}
<div class="flex flex-col">
{#if $txt?.length > 0}
<button class="primary flex" on:click|preventDefault={() => addComment()}>
<!-- Add a comment -->
<Speech_bubble class="h-7 w-7 pr-2" />
<Tr t={t.addCommentPlaceholder} />
</button>
{:else}
<div class="alert w-full">
<Tr t={t.typeText} />
</div>
{/if}
{#if !$isClosed}
<button class="flex items-center" on:click|preventDefault={() => closeNote()}>
<Resolved class="h-8 w-8 pr-2" />
<!-- Close note -->
{#if $txt === undefined || $txt === ""}
<Tr t={t.closeNote} />
{:else}
<Tr t={t.addCommentAndClose} />
{/if}
</button>
{:else}
<button class="flex items-center" on:click|preventDefault={() => reopenNote()}>
<!-- Reopen -->
<Note class="h-7 w-7 pr-2" />
{#if $txt === undefined || $txt === ""}
<Tr t={t.reopenNote} />
{:else}
<Tr t={t.reopenNoteAndComment} />
{/if}
</button>
{/if}
</div>
{/if}
{#if !$isClosed}
<button class="flex items-center" on:click|preventDefault={() => closeNote()}>
<Resolved class="h-8 w-8 pr-2" />
<!-- Close note -->
{#if $txt === undefined || $txt === ""}
<Tr t={t.closeNote} />
{:else}
<Tr t={t.addCommentAndClose} />
{/if}
</button>
{:else}
<button class="flex items-center" on:click|preventDefault={() => reopenNote()}>
<!-- Reopen -->
<Note class="h-7 w-7 pr-2" />
{#if $txt === undefined || $txt === ""}
<Tr t={t.reopenNote} />
{:else}
<Tr t={t.reopenNoteAndComment} />
{/if}
</button>
{/if}
</div>
{/if}
</form>
</LoginToggle>

View file

@ -66,7 +66,6 @@
<div class="flex">
<div class="m-4 flex w-full flex-col">
<NextButton clss="primary" on:click={() => state.highlightedItem.setData({ path, schema })}>
{#if schema.hints.question}
{schema.hints.question}

View file

@ -104,7 +104,9 @@
return `${singular} ${i}`
}
let genIconF: (x: any) => ({ icon: string, color: string }) = <any>Function("value", "return " + schema.hints.icon)
let genIconF: (x: any) => { icon: string; color: string } = <any>(
Function("value", "return " + schema.hints.icon)
)
console.log("Icon lambda is", schema.hints.icon, path, genIconF("test"))
function genIcon(value: any): string {
@ -117,7 +119,6 @@
}
return genIconF(value)?.color
}
</script>
<div class="pl-2">
@ -160,13 +161,14 @@
{#if schema.hints.icon}
<Icon clss="w-6 h-6" icon={genIcon(value)} color={genColor(value)} />
{/if}
{singular} {i}
{singular}
{i}
{#if schema.hints.title}
<div class="subtle ml-2">
<Tr t={Translations.T(genTitle(value, singular, i))}/>
<Tr t={Translations.T(genTitle(value, singular, i))} />
</div>
{/if}
{/if}
</h3>
<button
class="h-fit w-fit rounded-full border border-black p-1"
@ -177,10 +179,10 @@
<TrashIcon class="h-4 w-4" />
</button>
</div>
{:else if typeof value === "string"}
{:else if typeof value === "string"}
Builtin: <b>{value}</b>
{:else}
<Tr cls="font-bold" t={Translations.T(value.question ?? value.render)}/>
{:else}
<Tr cls="font-bold" t={Translations.T(value.question ?? value.render)} />
{/if}
</span>
<div class="normal-background p-2">

View file

@ -128,8 +128,11 @@
on:submit
/>
</div>
<a target="_blank" href="https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md">
<QuestionMarkCircle class="w-6 h-6"/>
<a
target="_blank"
href="https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md"
>
<QuestionMarkCircle class="h-6 w-6" />
</a>
{#if $feedbackKey}

View file

@ -23,7 +23,7 @@ export interface ConfigMeta {
typesdefault?: string
suggestions?: []
title?: string
multianswer?: "true" | string,
multianswer?: "true" | string
icon?: string
}
required: boolean

View file

@ -203,9 +203,7 @@
state.selectedTab.setData(Number(tab))
}
uid.AsPromise().then(
uid => selectStateBasedOnHash(uid)
)
uid.AsPromise().then((uid) => selectStateBasedOnHash(uid))
function backToStudio() {
console.log("Back to studio")

View file

@ -104,7 +104,7 @@
if (id.startsWith("current_view")) {
return currentViewLayer
}
if(id.startsWith("summary_")){
if (id.startsWith("summary_")) {
console.log("Not selecting a summary object. The summary object is", element)
return undefined
}
@ -125,11 +125,11 @@
state.mapProperties.installCustomKeyboardHandler(viewport)
let canZoomIn = mapproperties.maxzoom.map(
(mz) => mapproperties.zoom.data < mz,
[mapproperties.zoom],
[mapproperties.zoom]
)
let canZoomOut = mapproperties.minzoom.map(
(mz) => mapproperties.zoom.data > mz,
[mapproperties.zoom],
[mapproperties.zoom]
)
function updateViewport() {
@ -166,7 +166,7 @@
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name
}),
})
)
let previewedImage = state.previewedImage
@ -197,7 +197,7 @@
let openMapButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openMenuButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openCurrentViewLayerButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(
undefined,
undefined
)
let _openNewElementButton: HTMLButtonElement
let openNewElementButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
@ -257,7 +257,9 @@
on:keydown={forwardEventToMap}
htmlElem={openMapButton}
>
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
<div
class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2"
>
<Marker icons={layout.icon} size="h-4 w-4 md:h-8 md:w-8 mr-0.5 sm:mr-1 md:mr-2" />
<b class="mr-1">
<Tr t={layout.title} />
@ -354,10 +356,10 @@
<a
class="bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer self-end self-center overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100"
on:click={() => {
if(featureSwitches.featureSwitchWelcomeMessage.data){
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
}else{
if (featureSwitches.featureSwitchWelcomeMessage.data) {
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
} else {
state.guistate.copyrightPanelIsOpened.setData(true)
}
}}
@ -520,11 +522,10 @@
<Tr t={Translations.t.general.attribution.title} />
</div>
<div slot="content2" class="flex flex-col m-2">
<div slot="content2" class="m-2 flex flex-col">
<ToSvelte construct={() => new CopyrightPanel(state)} />
</div>
<div class="flex" slot="title3">
<Share class="h-4 w-4" />
<Tr t={Translations.t.general.sharescreen.title} />
@ -650,15 +651,14 @@
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
<div class="flex h-full flex-col overflow-hidden">
<h1 class="low-interaction m-0 flex items-center p-4 drop-shadow-md">
<Tr t= {Translations.t.general.attribution.title}/>
<Tr t={Translations.t.general.attribution.title} />
</h1>
<div class="overflow-auto p-4">
<h2>
<Tr t={Translations.t.general.menu.aboutMapComplete} />
<Tr t={Translations.t.general.menu.aboutMapComplete} />
</h2>
<AboutMapComplete {state} />
<ToSvelte construct={() => new CopyrightPanel(state)} />
<AboutMapComplete {state} />
<ToSvelte construct={() => new CopyrightPanel(state)} />
</div>
</div>
</FloatOver>

View file

@ -976,7 +976,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
}
}
xhr.send(content)
xhr.onerror = (ev: ProgressEvent<EventTarget>) => reject("Could not get "+url+", xhr status code is "+xhr.status+" ("+xhr.statusText+")")
xhr.onerror = (ev: ProgressEvent<EventTarget>) =>
reject(
"Could not get " +
url +
", xhr status code is " +
xhr.status +
" (" +
xhr.statusText +
")"
)
})
}
@ -1070,7 +1079,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
const injected = Utils.injectedDownloads[url]
if (injected !== undefined) {
console.debug("Using injected resource for test for URL", url)
return {content: injected}
return { content: injected }
}
const result = await Utils.downloadAdvanced(
url,

View file

@ -1,7 +1,7 @@
{
"contributors": [
{
"commits": 7612,
"commits": 7646,
"contributor": "Pieter Vander Vennet"
},
{

View file

@ -469,9 +469,9 @@
"na"
],
"NZ": [
"en",
"en",
"mi",
"en",
"mi"
],
"OM": [

View file

@ -1,6 +1,5 @@
{
"ca": "català",
"cs": "čeština",
"da": "dansk",
"de": "Deutsch",
"en": "English",
@ -12,7 +11,7 @@
"gl": "lingua galega",
"he": "עברית",
"hu": "magyar",
"id": "bahasa Indonesia",
"id": "Indonesia",
"it": "italiano",
"ja": "日本語",
"nb_NO": "bokmål",
@ -23,6 +22,7 @@
"ru": "русский язык",
"sl": "slovenščina",
"sv": "svenska",
"zgh": "ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ ⵜⴰⵎⵖⵔⵉⴱⵉⵜ",
"zh_Hans": "简体中文",
"zh_Hant": "繁體中文"
}

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
{
"contributors": [
{
"commits": 378,
"commits": 382,
"contributor": "Pieter Vander Vennet"
},
{
"commits": 351,
"commits": 352,
"contributor": "kjon"
},
{
@ -21,7 +21,7 @@
"contributor": "Robin van der Linde"
},
{
"commits": 69,
"commits": 70,
"contributor": "mcliquid"
},
{
@ -84,6 +84,10 @@
"commits": 18,
"contributor": "el_libre como el chaval"
},
{
"commits": 15,
"contributor": "Patchanka64"
},
{
"commits": 15,
"contributor": "macpac"
@ -112,10 +116,6 @@
"commits": 13,
"contributor": "Joost"
},
{
"commits": 12,
"contributor": "Patchanka64"
},
{
"commits": 12,
"contributor": "Ettore Atalan"
@ -132,6 +132,10 @@
"commits": 11,
"contributor": "Túllio Franca"
},
{
"commits": 10,
"contributor": "Manuel Tassi"
},
{
"commits": 10,
"contributor": "brunnerpaul"
@ -158,7 +162,7 @@
},
{
"commits": 9,
"contributor": "Manuel Tassi"
"contributor": "hugoalh"
},
{
"commits": 9,
@ -212,10 +216,6 @@
"commits": 7,
"contributor": "Niels Elgaard Larsen"
},
{
"commits": 6,
"contributor": "hugoalh"
},
{
"commits": 6,
"contributor": "Juele juele"
@ -256,6 +256,10 @@
"commits": 6,
"contributor": "lvgx"
},
{
"commits": 5,
"contributor": "Franco"
},
{
"commits": 5,
"contributor": "Christian Schmidt"
@ -328,10 +332,6 @@
"commits": 3,
"contributor": "Paolo Mauri"
},
{
"commits": 3,
"contributor": "Franco"
},
{
"commits": 3,
"contributor": "Peter Brodersen"