import { App } from '../../gogocarto' import { TileLayer } from '../map/tile-layer.class' import { GoGoFeature } from './gogo-feature.class' import { MenuFilter } from '../classes' import { DEFAULT_FEATURES } from './gogo-default-feature' import * as FR from '../../../locales/fr.json' import * as EN from '../../../locales/en.json' import * as TR from '../../../locales/tr.json' import * as DE from '../../../locales/de.json' import * as NB_NO from '../../../locales/nb_NO.json' import * as PT from '../../../locales/pt.json' import * as BR from '../../../locales/br.json' import * as ES from '../../../locales/es.json' import * as EU from '../../../locales/eu.json' declare let L: any, $, moment import * as tinycolor2 from 'tinycolor2' import { MarkerShape } from '../map/marker-shape.class' const tinycolor = (tinycolor2).default export class GoGoConfig { readonly mode = { autocompleteOnly: false, // we use gogocarto lib only to create the autoComplete search input } readonly data = { taxonomy: undefined, elements: undefined, elementsApiUrl: undefined, elementsCompactApiUrl: undefined, elementByIdUrl: undefined, requestByBounds: false, transformElements: (data) => { return data }, transformTaxonomy: (data) => { return data }, } readonly menu = { width: undefined, smallWidthStyle: false, showOnePanePerMainOption: false, showCheckboxForMainFilterPane: true, showCheckboxForSubFilterPane: true, displayNumberOfElementForEachCategory: false, displayNumberOfElementRoundResults: false, filters: [new MenuFilter({ type: 'taxonomy' })], } readonly infobar = { width: 500, activate: true, bodyTemplate: { content: undefined, type: 'string', // string | url isMarkdown: true, }, headerTemplate: { content: undefined, type: 'string', // string | url isMarkdown: true, }, displayDateField: undefined, // name of the field who will provide the main element date elementRefreshNeeded: { needed: { active: false, shownOnInfoBar: false, maxDaysBeforeSendingMail: 0, }, muchNeeded: { active: false, shownOnInfoBar: false, maxDaysBeforeSendingMail: 0, }, }, } readonly marker = { displayPopup: true, popupAlwaysVisible: false, popupTemplate: { content: undefined, type: 'string', // string | url isMarkdown: true, }, defaultColor: undefined, defaultIcon: 'gogo-icon-marker-simple-1', defaultShape: undefined, defaultSize: 1, // override the value calculated by gogocarto customColorOptionId: (app, element, gogoCalculatedColor) => { return gogoCalculatedColor }, customMainIcon: (app, element, gogoCalculatedIcon) => { return gogoCalculatedIcon }, customShape: (app, element, gogoCalculatedShape) => { return gogoCalculatedShape }, customSize: (app, element, gogoCalculatedSize) => { return gogoCalculatedSize }, } readonly general = { activateHistoryStateAndRouting: true, } readonly map = { // france defaultBounds: L.latLngBounds(L.latLng(52, 10), L.latLng(40, -5)), defaultBoundsProvided: false, defaultCenter: L.latLng(46, 0), maxBounds: L.latLngBounds(L.latLng(-90, -180), L.latLng(90, 180)), useClusters: true, saveViewportInCookies: false, saveTileLayerInCookies: false, scale: 'none', defaultTileLayer: 'cartodb', tileLayers: [ new TileLayer( 'cartodb', 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', '© OpenStreetMap © CartoDB', 20, ), new TileLayer( 'osmfr', 'https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', '© Openstreetmap France | © OpenStreetMap', 20, ), new TileLayer( 'lyrk ', 'https://tiles.lyrk.org/ls/{z}/{x}/{y}?apikey =982c82cc765f42cf950a57de0d891076', '© Lyrk | Map data © OpenStreetMap', 13, ), new TileLayer( 'stamen', 'https://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png', 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap', 20, ), new TileLayer( 'stamenWaterColor', 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png', 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap', 13, ), ], geojsonLayers: [], optionnalGeojsonLayers: [], geocoding: { geocodingBoundsType: 'none', geocodingBoundsByCountryCodes: '', geocodingBounds: null, }, } readonly features = { // element menu favorite: new GoGoFeature(), share: new GoGoFeature(), // element content sendMail: new GoGoFeature(), elementHistory: new GoGoFeature(), // gogo controls listMode: new GoGoFeature(), // map controls export: new GoGoFeature(), layers: new GoGoFeature(), mapdefaultview: new GoGoFeature(), pending: new GoGoFeature(), searchPlace: new GoGoFeature(), searchElements: new GoGoFeature(), searchCategories: new GoGoFeature(), searchGeolocate: new GoGoFeature(), // element interaction / moderation edit: new GoGoFeature(), delete: new GoGoFeature(), report: new GoGoFeature(), vote: new GoGoFeature(), moderation: new GoGoFeature(), stamp: new GoGoFeature(), subscribe: new GoGoFeature(), directModeration: new GoGoFeature(), // others customPopup: new GoGoFeature(), } readonly security = { userRoles: ['anonymous'], userEmail: '', userGroups: [], loginAction: function () { console.warn('[GoGoCarto] You need login to access this feature') }, } public static readonly GEOCODING_PROVIDER_NOMINATIM = 'nominatim' public static readonly GEOCODING_PROVIDER_MAPBOX = 'mapbox' readonly search: { geocodingProvider: 'nominatim' | 'mapbox' canAutocomplete: boolean meta: { apiKey?: string } } = { geocodingProvider: GoGoConfig.GEOCODING_PROVIDER_NOMINATIM, canAutocomplete: undefined, meta: { apiKey: '' }, } theme = 'default' // see gogo-styles for defaut values readonly colors = { contentBackground: tinycolor('white'), // background color of text zone background: tinycolor('#f4f4f4'), // background color of non text zone // simple text and textSoft properties can be used, see this.fillColors textDark: tinycolor('#222120'), // text color on light background textDarkSoft: undefined, // soft text color on light background textLight: tinycolor('white'), // text color on dark background textLightSoft: undefined, // soft text color on dark background primary: tinycolor('#de5a5f'), // Used for buttons, and for search bar by default secondary: undefined, // Used by some themes (not default one). Equal primary by default infoBarHeader: undefined, // by default auto colored with main option color infoBarMenu: undefined, // by default auto colored with main option color contentBackgroundElementBody: undefined, // by default calculated from contentBackground menuOptionHover: undefined, // by default calculated from contentBackground lineBorder: undefined, // by default calculated from contentBackground disabled: undefined, // by default calculated from contentBackground searchBar: undefined, // by default primary interactiveSection: undefined, // by default primary mapControlsBgd: undefined, mapControls: undefined, mapListBtn: undefined, pending: tinycolor('#565656'), } readonly fonts = { mainFont: 'Roboto', titleFont: undefined, // by default titleFont } readonly images = { buttonOpenMenu: undefined, menuTopImage: undefined, } language = 'en' // use 'test' to display the entry names readonly i18n = { en: EN, fr: FR, nb_NO: NB_NO, tr: TR, de: DE, pt: PT, br: BR, es: ES, eu: EU, } readonly url: URL constructor(config: any) { this.url = config.url if (!config.features) { config.features = DEFAULT_FEATURES } // locale JSON are imported as modules, which are not editable, so convert them to standard js object for (const locale in this.i18n) { this.i18n[locale] = JSON.parse(JSON.stringify(this.i18n[locale] || {})) } // Copy all the defined options // All the options non specified will be initialized with default values this.recursiveFillProperty(this, config) if (typeof this.data.elements == 'string') { this.data.elementsApiUrl = this.data.elements this.data.elements = undefined } if (config.map && config.map.defaultBounds) { this.map.defaultBoundsProvided = true } if (!this.features['sendMail'].url) { this.features['sendMail'].active = false } this.search.canAutocomplete = true if (GoGoConfig.GEOCODING_PROVIDER_NOMINATIM === this.search.geocodingProvider) { this.search.canAutocomplete = false } // COLORS if (!this.colors.menuOptionHover) { const menuOptionHover = tinycolor(this.colors.contentBackground.toString()) this.colors.menuOptionHover = menuOptionHover.isDark() ? menuOptionHover.lighten(5) : menuOptionHover.darken(5) } if (!this.colors.contentBackgroundElementBody) { this.colors.contentBackgroundElementBody = this.colors.background // tinycolor(this.colors.contentBackground.toString()).darken(3); } if (!this.colors.lineBorder) { const lineBorder = tinycolor(this.colors.contentBackground.toString()).greyscale() this.colors.lineBorder = lineBorder.isDark() ? lineBorder.lighten(15) : lineBorder.darken(15) } if (!this.colors.disabled) { const disabled = tinycolor(this.colors.contentBackground.toString()).greyscale() this.colors.disabled = disabled.isDark() ? disabled.lighten(35) : disabled.darken(35) } if (!this.colors.mapControlsBgd) { this.colors.mapControlsBgd = this.colors.contentBackground } if (config.colors && !config.colors.text && !config.colors.textDark && !config.colors.textLight) { if (this.colors.contentBackground.isDark()) this.colors.textDark = this.colors.contentBackground else this.colors.textLight = this.colors.contentBackground } if (!this.colors.textLightSoft) { this.colors.textLightSoft = tinycolor(this.colors.textLight.toString()).darken(15) } if (!this.colors.textDarkSoft) { this.colors.textDarkSoft = tinycolor(this.colors.textDark.toString()).lighten(15) } if (!this.colors.mapListBtn) { this.colors.mapListBtn = tinycolor.readability(this.colors.primary, this.colors.mapControlsBgd) ? this.colors.primary : this.colors.mapControls } if (this.theme == 'transiscope') { this.colors.infoBarHeader = this.colors.textDark this.colors.infoBarMenu = this.colors.primary this.colors.menuOptionHover = this.colors.contentBackground if (!this.colors.interactiveSection) this.colors.interactiveSection = this.colors.secondary if (!this.colors.searchBar) this.colors.searchBar = this.colors.textDark } // MARKERS if (this.marker.defaultColor) this.marker.defaultColor = tinycolor(this.marker.defaultColor) else this.marker.defaultColor = this.colors.primary this.marker.defaultShape = new MarkerShape(this.marker.defaultShape || 'waterdrop') // TRANSLATIONS this.modifyTranslations(this, config.translations) // FEATURES if (!config.features) config.features = DEFAULT_FEATURES // FILTERS for (let i = 0; i < this.menu.filters.length; i++) { const menu = this.menu.filters[i] menu.id = i // give an id to each filter // if there is a date filter, display the date-header in info bar & list if (menu.type == 'date' && this.infobar.displayDateField == undefined) this.infobar.displayDateField = menu.field } // set Moment locale using the datepicker locales (which use same options name than moment...) let datepicker = $.fn.datepicker.dates[this.language] if (!datepicker) datepicker = $.fn.datepicker.dates.en // fallback to english moment.locale(this.language, { months: datepicker.months, monthsShort: datepicker.monthsShort, weekdays: datepicker.days, weekdaysShort: datepicker.daysShort, weekdaysMin: datepicker.daysMin, }) // fix some values if (typeof this.infobar.width === 'string') this.infobar.width = parseInt((this.infobar.width).split('px')[0]) if (typeof this.menu.width === 'string') this.menu.width = parseInt((this.menu.width).split('px')[0]) console.log(this) } // Function for i18n, mapping between the given entry and the string according to the language chosen translate(entry: string): string { if (!entry) return if (this.language == 'test') return `i18n(${entry})` let value = this.i18n[this.language] ? this.i18n[this.language][entry] : null if (!value) { console.warn(`[GoGoCartoJS] Entry '${entry}' not found for language ${this.language}. Using english`) value = this.i18n['en'][entry] } if (value) value = value.replace(/\$\{([\w\.]+)\}/g, (match, otherEntry) => this.translate(otherEntry)) else { value = `MISSING TRANSLATION KEY : ${entry}` console.warn(`[GoGoCartoJS] Entry '${entry}' does not exist in english`) } return value } isFeatureActivated(featureName): boolean { if (!this.features.hasOwnProperty(featureName)) { console.warn(`[GoGoCartoJs] feature ${featureName} doesn't exist`) return false } return this.features[featureName].active && (!App.isIframe || this.features[featureName].inIframe) } // is feature is activated and the actual user is granted to use it isFeatureAvailable(featureName: string): boolean { if (!this.features.hasOwnProperty(featureName)) { console.warn(`[GoGoCartoJs] feature ${featureName} doesn't exist`) return false } const feature = this.features[featureName] let roleProvided = true if (feature.hasOwnProperty('roles')) { roleProvided = feature.hasRole(App.loginModule.getRoles()) } return this.isFeatureActivated(featureName) && roleProvided } private recursiveFillProperty(gogoConfig, userConfig) { // we don't want to apply recursively inside objects properties const objectsProperties = [ 'roles', 'userGroups', 'defaultCenter', 'defaultBounds', 'geocodingBounds', 'tileLayers', 'geojsonLayers', 'optionnalGeojsonLayers', 'options', 'filters', ] // if we provide feature config, we enable it automatically if (gogoConfig instanceof GoGoFeature) gogoConfig.active = true for (const prop in userConfig) { if (gogoConfig.hasOwnProperty(prop)) { if (typeof gogoConfig[prop] != 'object' || objectsProperties.indexOf(prop) > -1) { let new_prop switch (prop) { case 'defaultBounds': new_prop = L.latLngBounds(userConfig[prop]) break case 'geocodingBounds': new_prop = L.latLngBounds(userConfig[prop]) break case 'defaultCenter': new_prop = L.latLng(userConfig[prop]) break case 'filters': new_prop = [] for (const filter of userConfig[prop]) new_prop.push(new MenuFilter(filter)) break default: new_prop = userConfig[prop] break } gogoConfig[prop] = new_prop } else if (prop == 'colors') this.fillColors(gogoConfig[prop], userConfig[prop]) else this.recursiveFillProperty(gogoConfig[prop], userConfig[prop]) } else if (prop && prop != 'translations') { console.warn("[GoGoCarto] Config option '" + prop + "' does not exist") } } } private fillColors(gogoConfig, userConfig) { for (let prop in userConfig) { const color = tinycolor(userConfig[prop]) if (prop == 'text') prop = color.isDark() ? 'textDark' : 'testLight' if (prop == 'textSoft') prop = color.isDark() ? 'textDarkSoft' : 'testLightSoft' gogoConfig[prop] = color } } private modifyTranslations(gogoConfig, userConfig) { for (const prop in userConfig) { const translations = gogoConfig.i18n[gogoConfig.language] || gogoConfig.i18n['en'] translations[prop] = userConfig[prop] } } }