Files
openfreemap/website/public/debug/lang/mix.js
Zsolt Ero c4aecd01f6 work
2025-10-22 14:36:29 +02:00

214 lines
5.9 KiB
JavaScript

// ============================================
// 1. MAIN EXECUTION (Entry Point)
// ============================================
const map = new maplibregl.Map({
container: 'map',
style: 'https://tiles.openfreemap.org/styles/liberty',
center: [0, 0],
zoom: 2,
hash: true,
})
const line1Input = document.getElementById('line1')
const line2Input = document.getElementById('line2')
const langInput = document.getElementById('lang')
const line1ExprInput = document.getElementById('line1-expr')
const line2ExprInput = document.getElementById('line2-expr')
map.on('load', () => {
const params = new URLSearchParams(window.location.search)
// Set defaults if no params present
if (!params.has('line1') && !params.has('line2') && !params.has('lang')) {
const url = new URL(window.location)
url.searchParams.set('line1', 'colon,underscore,name,latin')
url.searchParams.set('line2', 'nonlatin')
url.searchParams.set('lang', 'en')
window.history.replaceState({}, '', url)
}
syncInputsFromParams()
applyConfiguration()
initializeInputListeners()
initializeModal()
initializeResetButton()
})
// ============================================
// 2. UI INITIALIZATION
// ============================================
function initializeInputListeners() {
const debouncedApplyConfig = debounce(applyConfiguration, 500)
const handleInput = () => {
updateParamsFromInputs()
updateExpressionDisplays()
debouncedApplyConfig()
}
line1Input.addEventListener('input', handleInput)
line2Input.addEventListener('input', handleInput)
langInput.addEventListener('input', handleInput)
}
function initializeModal() {
const modal = document.getElementById('shareModal')
document.getElementById('shareBtn').addEventListener('click', () => {
modal.classList.remove('hidden')
})
document.getElementById('closeModalBtn').addEventListener('click', () => {
modal.classList.add('hidden')
})
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
modal.classList.add('hidden')
}
})
}
function initializeResetButton() {
document.getElementById('resetBtn').addEventListener('click', () => {
const hash = window.location.hash
window.location.href = `${window.location.pathname}${hash}`
})
}
// ============================================
// 3. CONFIGURATION & SYNC
// ============================================
function applyConfiguration() {
const { line1, line2, lang } = parseParams()
if (!map.getStyle()) return
const style = map.getStyle()
modifyStyle({
style,
line1Config: line1 ?? '',
line2Config: line2 ?? '',
langCode: lang,
})
map.setStyle(style, { diff: true })
updateExpressionDisplays()
}
function syncInputsFromParams() {
const { line1, line2, lang } = parseParams()
line1Input.value = line1 ?? ''
line2Input.value = line2 ?? ''
langInput.value = lang ?? ''
}
function updateParamsFromInputs() {
const params = new URLSearchParams(window.location.search)
params.set('line1', line1Input.value)
params.set('line2', line2Input.value)
params.set('lang', langInput.value)
const queryString = params.toString()
const hash = window.location.hash
const newUrl = `${window.location.pathname}?${queryString}${hash}`
window.history.replaceState({}, '', newUrl)
}
function updateExpressionDisplays() {
const { line1, line2, lang } = parseParams()
const langCode = lang
const line1Expr = buildFieldAccessor(line1 ?? '', langCode)
const line2Expr = buildFieldAccessor(line2 ?? '', langCode)
line1ExprInput.value = line1Expr ? JSON.stringify(line1Expr) : ''
line2ExprInput.value = line2Expr ? JSON.stringify(line2Expr) : ''
}
// ============================================
// 4. STYLE MODIFICATION
// ============================================
function modifyStyle({ style, line1Config, line2Config, langCode }) {
for (const layer of style.layers) {
if (layer.source !== 'openmaptiles') continue
if (!layer.layout) continue
const textField = layer.layout['text-field']
if (!textField) continue
if (JSON.stringify(textField) === JSON.stringify(['to-string', ['get', 'ref']])) continue
const id = layer.id
const separator = id.includes('line') || id.includes('highway') ? ' ' : '\n'
const line1Expr = buildFieldAccessor(line1Config, langCode)
const line2Expr = buildFieldAccessor(line2Config, langCode)
if (line1Expr && line2Expr) {
layer.layout['text-field'] = ['concat', line1Expr, separator, line2Expr]
} else if (line1Expr) {
layer.layout['text-field'] = line1Expr
} else if (line2Expr) {
layer.layout['text-field'] = line2Expr
} else {
layer.layout['text-field'] = ['get', 'name']
}
}
}
// ============================================
// 5. UTILITY FUNCTIONS
// ============================================
function debounce(func, delay) {
let timeoutId
return function (...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
func.apply(this, args)
}, delay)
}
}
function parseParams() {
const params = new URLSearchParams(window.location.search)
return {
line1: params.get('line1'),
line2: params.get('line2'),
lang: params.get('lang') || 'en',
}
}
function buildFieldAccessor(config, langCode) {
if (!config) return null
const parts = []
const fields = config
.split(',')
.map(f => f.trim())
.filter(f => f)
for (const field of fields) {
if (field === 'underscore') {
parts.push(['get', `name_${langCode}`])
} else if (field === 'colon') {
parts.push(['get', `name:${langCode}`])
} else if (field === 'latin') {
parts.push(['get', 'name:latin'])
} else if (field === 'nonlatin') {
parts.push(['get', 'name:nonlatin'])
} else if (field === 'name') {
parts.push(['get', 'name'])
} else {
parts.push(['get', field])
}
}
return parts.length > 0 ? ['coalesce', ...parts] : null
}