diff --git a/bin/download_dependencies b/bin/download_dependencies index b9275a79..ff1a6eac 100755 --- a/bin/download_dependencies +++ b/bin/download_dependencies @@ -1,3 +1,7 @@ #!/bin/sh +curl -H "Accept: application/json" -H "Content-Type: application/sparql-query" -H "User-Agent: OpenStreetBrowser" -XPOST -d @'lib/tag2link-wikidata.qry' https://query.wikidata.org/sparql > data/tag2link-wikidata.json +curl -H "Accept: application/json" -H "Content-Type: application/sparql-query" -H "User-Agent: OpenStreetBrowser" -XPOST -d @'lib/tag2link-sophox.qry' https://sophox.org/sparql > data/tag2link-sophox.json +bin/tag2link-converter + bin/download_geoip2 diff --git a/bin/tag2link-converter b/bin/tag2link-converter new file mode 100755 index 00000000..2d3c26d5 --- /dev/null +++ b/bin/tag2link-converter @@ -0,0 +1,52 @@ +#!/usr/bin/php + $d) { + $tag2link[$key]['formatter'][$i]['operator'] = $entry['operatorLabel']['value']; + } + } + + continue; + } + } + else { + $tag2link[$key] = array( + 'label' => $entry['itemLabel']['value'], + 'formatter' => array(), + ); + } + + $formatter = array( + 'link' => $link, + ); + + if (array_key_exists('operatorLabel', $entry)) { + $formatter['operator'] = $entry['operatorLabel']['value']; + print "{$formatter['operator']}\n"; + } + else if (preg_match("/^https?:\/\/([^\/]*)(\/.*|)$/", $link, $m)) { + $formatter['operator'] = $m[1]; + } + + $tag2link[$key]['formatter'][] = $formatter; + } +} + +file_put_contents('dist/tag2link.json', json_encode($tag2link, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)); diff --git a/lib/tag2link-sophox.qry b/lib/tag2link-sophox.qry new file mode 100644 index 00000000..1e1c4ca4 --- /dev/null +++ b/lib/tag2link-sophox.qry @@ -0,0 +1,7 @@ +SELECT ?item ?itemLabel (CONCAT("Key:", ?permanent_key_ID) as ?OSM_key) ?formatter_URL WHERE { + FILTER(?permanent_key_ID NOT IN ('image', 'url', 'website', 'wikidata', 'wikimedia_commons')). + ?item osmdt:P2 osmd:Q7. + SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } + ?item osmdt:P16 ?permanent_key_ID. + ?item osmdt:P8 ?formatter_URL. +} diff --git a/lib/tag2link-wikidata.qry b/lib/tag2link-wikidata.qry new file mode 100644 index 00000000..87bdaf57 --- /dev/null +++ b/lib/tag2link-wikidata.qry @@ -0,0 +1,15 @@ +SELECT ?itemLabel ?OSM_key ?formatter_URL ?operatorLabel WHERE { + ?item wdt:P1282 ?OSM_key . + FILTER(?OSM_key NOT IN("Key:image", "Key:url", "Key:website", "Key:wikidata", "Key:wikimedia_commons")) + SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } + { + ?item p:P1630 ?statement. + ?statement ps:P1630 ?formatter_URL. + } + UNION + { + ?item p:P3303 ?statement. + ?statement ps:P3303 ?formatter_URL. + } + OPTIONAL { ?statement pq:P137 ?operator. } +} diff --git a/src/category.css b/src/category.css index 93ef0d8f..31da4eb7 100644 --- a/src/category.css +++ b/src/category.css @@ -161,3 +161,26 @@ .info .infoShowDetails .summary { display: none; } + +dl > dd { + position: relative; +} +.tag2link { + position: absolute; + top: 1em; + left: 0; + border: 1px solid black; + padding: 0.25em; + background: white; + z-index: 1; +} +.tag2link > .closeButton { + float: right; +} +.tag2link > ul { + padding-left: 0; + margin: 0; +} +.tag2link > ul > li { + list-style: none; +} diff --git a/src/index.js b/src/index.js index 806a55d9..6d7a7df1 100644 --- a/src/index.js +++ b/src/index.js @@ -42,6 +42,8 @@ require('./GeoInfo') require('./PluginMeasure') require('./PluginGeoLocate') let exportAll = require('./exportAll') +const tagsDisplay = require('./tagsDisplay').display +require('./tagsDisplay-tag2link') window.onload = function () { var initState = config.defaultView @@ -309,18 +311,7 @@ window.showDetails = function (data, category) { h.innerHTML = lang('header:attributes') dom.appendChild(h) - div = document.createElement('dl') - div.className = 'tags' - for (k in data.object.tags) { - dt = document.createElement('dt') - dt.appendChild(document.createTextNode(k)) - div.appendChild(dt) - - dd = document.createElement('dd') - dd.appendChild(document.createTextNode(data.object.tags[k])) - div.appendChild(dd) - } - dom.appendChild(div) + dom.appendChild(tagsDisplay(data.object.tags)) h = document.createElement('h3') h.innerHTML = lang('header:osm_meta') diff --git a/src/tagsDisplay-tag2link.js b/src/tagsDisplay-tag2link.js new file mode 100644 index 00000000..3143d2b4 --- /dev/null +++ b/src/tagsDisplay-tag2link.js @@ -0,0 +1,63 @@ +const httpGet = require('./httpGet') +const formatter = require('./tagsDisplay').formatter + +let tag2link + +register_hook('init_callback', (initState, callback) => { + httpGet('dist/tag2link.json', {}, (err, result) => { + if (err) { + console.error('Can\'t read dist/tag2link.json - execute bin/download_dependencies') + return callback() + } + + tag2link = JSON.parse(result.body) + + Object.keys(tag2link).forEach(key => { + let tag = tag2link[key] + let link = tag.formatter[0].link.replace('$1', '{{ value }}') + + if (tag.formatter.length > 1) { + link = "#\" onclick=\"return tag2link(this, " + JSON.stringify(key).replace(/"/g, '"') + ", {{ value|json_encode }})" + } + + formatter.push({ + regexp: new RegExp("^" + key + "$"), + link + }) + }) + + callback() + }) +}) + +global.tag2link = function (dom, key, value) { + let div = document.createElement('div') + div.className = 'tag2link' + dom.parentNode.appendChild(div) + + let closeButton = document.createElement('div') + closeButton.className = 'closeButton' + closeButton.innerHTML = '❌' + closeButton.onclick = () => { + dom.parentNode.removeChild(div) + } + div.appendChild(closeButton) + + let selector = document.createElement('ul') + div.appendChild(selector) + + let tag = tag2link[key] + tag.formatter.forEach(formatter => { + let li = document.createElement('li') + + let a = document.createElement('a') + a.target = '_blank' + a.href = formatter.link.replace('$1', value) + a.appendChild(document.createTextNode(formatter.operator)) + + li.appendChild(a) + selector.appendChild(li) + }) + + return false +} diff --git a/src/tagsDisplay.js b/src/tagsDisplay.js new file mode 100644 index 00000000..3a69db38 --- /dev/null +++ b/src/tagsDisplay.js @@ -0,0 +1,96 @@ +const OverpassLayer = require('overpass-layer') + +const formatter = [ + { + regexp: /^(.*:)?wikidata$/, + link: 'https://wikidata.org/wiki/{{ value }}' + }, + { + regexp: /^(.*:)?wikipedia$/, + link: '{% set v = value|split(":") %}https://{{ v[0] }}.wikipedia.org/wiki/{{ v[1]|replace({" ": "_"}) }}' + }, + { + regexp: /^(.*:)?wikipedia:([a-zA-Z]+)$/, + link: '{% set v = key|matches(":([a-zA-Z]+)") %}https://{{ v[1] }}.wikipedia.org/wiki/{{ value|replace({" ": "_"}) }}' + }, + { + regexp: /^((.*:)?website(:.*)?|(.*:)?url(:.*)?|contact:website)$/, + link: '{{ value|websiteUrl }}' + }, + { + regexp: /^(image|wikimedia_commons)$/, + link: '{% if value matches "/^(File|Category):/" %}' + + 'https://commons.wikimedia.org/wiki/{{ value|replace({" ": "_"}) }}' + + '{% else %}' + + '{{ value|websiteUrl }}' + + '{% endif %}' + }, + { + regexp: /^(species)$/, + link: 'https://species.wikimedia.org/wiki/{{ value|replace({" ": "_"}) }}' + }, + { + regexp: /^(phone|contact:phone|fax|contact:fax)(:.*|)$/, + link: 'tel:{{ value }}' + }, + { + regexp: /^(email|contact:email)(:.*|)$/, + link: 'mailto:{{ value }}' + } +] + +let compiled = false +let defaultTemplate + +function tagsDisplay (tags) { + if (!compiled) { + defaultTemplate = OverpassLayer.twig.twig({ data: '{{ value }}', autoescape: true }) + for (let i in formatter) { + if (formatter[i].format) { + formatter[i].template = OverpassLayer.twig.twig({ data: formatter[i].format, autoescape: true }) + } else { + formatter[i].template = OverpassLayer.twig.twig({ data: '{{ value }}', autoescape: true }) + } + } + + compiled = true + } + + const div = document.createElement('dl') + div.className = 'tags' + for (let k in tags) { + const dt = document.createElement('dt') + dt.appendChild(document.createTextNode(k)) + div.appendChild(dt) + + let template = defaultTemplate + + const dd = document.createElement('dd') + for (let i = 0; i < formatter.length; i++) { + if (k.match(formatter[i].regexp)) { + template = formatter[i].template + break + } + } + + let value = tags[k].split(/;/g) + value = value.map(v => { + // trim whitespace (but add it around the formatted value later) + let m = v.match(/^( *)([^ ].*[^ ]|[^ ])( *)$/) + if (m) { + return m[1] + template.render({ key: k, value: m[2] }) + m[3] + } + return v + }).join(';') + + dd.innerHTML = value + div.appendChild(dd) + } + + return div +} + +module.exports = { + display: tagsDisplay, + formatter +} diff --git a/src/wikipedia.js b/src/wikipedia.js index 55929385..42964353 100644 --- a/src/wikipedia.js +++ b/src/wikipedia.js @@ -128,6 +128,8 @@ register_hook('show-details', function (data, category, dom, callback) { foundPrefixes.push('') ob.tags.wikipedia.split(/;/g).forEach(value => { + value = value.trim() + found++ showWikipedia(value, div, done) }) @@ -145,6 +147,8 @@ register_hook('show-details', function (data, category, dom, callback) { foundPrefixes.push(prefix) ob.tags[k].split(/;/g).forEach(value => { + value = value.trim() + found++ showWikipedia(value, div, done) }) @@ -180,6 +184,8 @@ register_hook('show-details', function (data, category, dom, callback) { foundPrefixes.push('') ob.tags.wikidata.split(/;/g).forEach(value => { + value = value.trim() + found++ wikidata.load(value, function (err, result) { @@ -228,6 +234,8 @@ register_hook('show-details', function (data, category, dom, callback) { div.appendChild(h) ob.tags[k].split(/;/g).forEach(value => { + value = value.trim() + wikidata.load(value, (err, result) => { var x