const OverpassLayer = require('overpass-layer') const tabs = require('modulekit-tabs') const natsort = require('natsort').default const state = require('./state') const Filter = require('overpass-frontend').Filter const getPathFromJSON = require('./getPathFromJSON') const CategoryOverpass = require('./CategoryOverpass') CategoryOverpass.defaultValues.filter = { title: { type: 'text', key: [ 'name', 'name:*', 'operator', 'operator:*', 'ref', 'ref:*' ], name: '{{ trans("filter:title") }}', op: 'strsearch', weight: -1, show_default: true } } class CategoryOverpassFilter { constructor (master) { this.master = master this.data = this.master.data.filter this.tabFilter = new tabs.Tab({ id: 'filter' }) this.master.tools.add(this.tabFilter) this.tabFilter.header.innerHTML = '' this.tabFilter.header.title = lang('filter') this.domFilter = document.createElement('form') this.tabFilter.content.appendChild(this.domFilter) this.tabFilter.on('select', () => { this.formFilter.resize() this.formFilter.focus() }) for (var k in this.data) { let f = this.data[k] if ('name' in f && typeof f.name === 'string') { global.currentCategory = this.master let t = OverpassLayer.twig.twig({ data: f.name, autoescape: true }) f.name = decodeHTML(t.render({}).toString()) } else if (!('name' in f)) { f.name = lang('tag:' + k) } if ('query' in f) { f.queryTemplate = OverpassLayer.twig.twig({ data: f.query, autoescape: false }) } if ('values' in f) { let valueNameTemplate = OverpassLayer.twig.twig({ data: f.valueName || '{{ value }}', autoescape: true }) if (typeof f.values === 'string') { let valuesTemplate = OverpassLayer.twig.twig({ data: f.values, autoescape: true }) let div = document.createElement('div') div.innerHTML = valuesTemplate.render(this.master.data) let options = div.getElementsByTagName('option') f.values = {} for (let i = 0; i < options.length; i++) { let option = options[i] let k = option.value f.values[k] = {} Array.from(option.attributes).forEach(attr => { f.values[k][attr.name] = attr.value }) if (option.textContent) { f.values[k].name = option.textContent } } } if (Array.isArray(f.values) && f.valueName) { let newValues = {} f.values.forEach(value => { newValues[value] = decodeHTML(valueNameTemplate.render({ value }).toString()) }) f.values = newValues } else if (typeof f.values === 'object') { for (var k1 in f.values) { if (typeof f.values[k1] === 'string') { let t = OverpassLayer.twig.twig({ data: f.values[k1], autoescape: true }) f.values[k1] = decodeHTML(t.render({}).toString()) } else if (typeof f.values[k1] === 'object') { if (!('name' in f.values[k1])) { f.values[k1].name = decodeHTML(valueNameTemplate.render({ value: k1 }).toString()) } else if (f.values[k1].name) { let t = OverpassLayer.twig.twig({ data: f.values[k1].name, autoescape: true }) f.values[k1].name = decodeHTML(t.render({})) } } } } if (!('sort' in f) || (f.sort === 'natsort')) { let v = {} let sorter = natsort({ insensitive: true }) let keys = Object.keys(f.values) keys .sort((a, b) => { let weight = (f.values[a].weight || 0) - (f.values[b].weight || 0) if (weight !== 0) { return weight } return sorter(f.values[a].name, f.values[b].name) }) .forEach(k => { v[k] = f.values[k] }) f.values = v } } if ('placeholder' in f && typeof f.placeholder === 'string') { let t = OverpassLayer.twig.twig({ data: f.placeholder, autoescape: true }) f.placeholder = decodeHTML(t.render({}).toString()) } } let masterOptions = { 'change_on_input': true } if (Object.keys(this.data).length > 1) { masterOptions['type'] = 'form_chooser' masterOptions['button:add_element'] = '-- ' + lang('add_filter') + ' --' masterOptions['order'] = false } this.formFilter = new form('filter-' + this.master.id, this.data, masterOptions) this.formFilter.show(this.domFilter) this.formFilter.onchange = () => { let param = JSON.parse(JSON.stringify(this.formFilter.get_data())) this.applyParam(param) state.update() } this.master.on('setParam', this.setParam.bind(this)) this.master.on('applyParam', (param) => { this.applyParam(param) if (!this.tabFilter.isSelected()) { this.tabFilter.select() } }) this.master.on('open', this.openCategory.bind(this)) this.master.on('stateGet', this.stateGet.bind(this)) } setParam (param) { this.formFilter.set_data(param) } applyParam (param) { this.additionalFilter = Object.keys(param).map(k => { let values = param[k] let d = this.data[k] if (values === null) { return d.emptyQuery } if (!Array.isArray(values)) { values = [ values ] } let ret = values.map(value => { if ('values' in d && value in d.values && typeof d.values[value] === 'object' && 'query' in d.values[value]) { let f = new Filter(d.values[value].query) return f.def } else if (d.queryTemplate) { let f = new Filter(decodeHTML(d.queryTemplate.render({ value: value }).toString())) return f.def } var v = { key: 'key' in d ? d.key : k, value: value, op: '=' } if ('op' in d) { if (d.op === 'has_key_value') { v = { key: value, op: 'has_key' } } else { v.op = d.op } } if (Array.isArray(v.key)) { v = { "or": v.key.map( key => { let v1 = { key, value: v.value, op: v.op } let m = key.match(/^(.*)\*(.*)/) if (m) { v1.key = '^' + m[1] + '.*' + m[2] v1.keyRegexp = true } return [ v1 ] } ) } } return [ v ] }).filter(f => f) // remove null values switch (ret.length) { case 0: return null case 1: return ret[0] default: return { or: ret } } }).filter(f => f) // remove null values if (this.additionalFilter.length === 0) { this.additionalFilter = [] } else if (this.additionalFilter.length === 1) { this.additionalFilter = this.additionalFilter[0] } else { this.additionalFilter = { and: this.additionalFilter } } this.master.layer.setFilter(this.additionalFilter) } openCategory () { this.formFilter.resize() let param = JSON.parse(JSON.stringify(this.formFilter.get_data())) this.applyParam(param) } stateGet (param) { let data = this.formFilter.get_data() for (var k in data) { if (data[k]) { param[k] = data[k] } } } } register_hook('category-overpass-init', (category) => { if (category.data.filter) { new CategoryOverpassFilter(category) } }) function decodeHTML (str) { if (typeof str === 'undefined') { return '-- undefined --' } return str .replace(/'/g, '\'') .replace(/"/g, '"') .replace(/>/g, '>') .replace(/</g, '<') }