diff --git a/README.md b/README.md index e03454ce..089bbfd0 100644 --- a/README.md +++ b/README.md @@ -118,3 +118,6 @@ With the function `register_hook` you can hook into several functions. The follo * `state-get`: modules can add values into the current state. Parameters: `state`: an object, which can be modified by modules. * `state-apply`: when a state is applied to the app. Parameters: `state`: state which should be applied. +* `show-details`: called when details are being displayed. Parameters: data (see properties in doc/TwigJS.md), category, dom, callback. +* `show-popup`: called when a popup is being displayed. Parameters: data (see properties in doc/TwigJS.md), category, dom, callback. +* `options_save`: called when options are saved. Parameters: options (the new object), old_options (before save) diff --git a/ajax.php b/ajax.php index 85e6f675..0d50087e 100644 --- a/ajax.php +++ b/ajax.php @@ -22,6 +22,6 @@ if ($postdata) { } $fun = "ajax_{$_REQUEST['__func']}"; -$return = $fun($_REQUEST["param"], $postdata); +$return = $fun($_REQUEST, $postdata); print json_encode($return); diff --git a/doc/TwigJS.md b/doc/TwigJS.md index d9f39645..7e5bf831 100644 --- a/doc/TwigJS.md +++ b/doc/TwigJS.md @@ -24,6 +24,7 @@ There are several extra functions defined for the TwigJS language: Extra filters: * filter websiteUrl: return a valid http link. Example: `{{ "www.google.com"|websiteUrl }}` -> "http://www.google.com"; `{{ "https://google.com"|websiteUrl }}` -> "https://google.com" +* filter `matches`: regular expression match. e.g. `{{ "test"|matches("e(st)$") }}` returns `[ "est", "st" ]`. Returns null if it does not match. Notes: * Variables will automatically be HTML escaped, if not the filter raw is used, e.g.: {{ tags.name|raw }} diff --git a/lang/ast.json b/lang/ast.json index 2daa5b6c..d6d91532 100644 --- a/lang/ast.json +++ b/lang/ast.json @@ -1,5 +1,6 @@ { "main:options": "Opciones", + "more": "más", "options:data_lang": "Llingua de los datos", "options:data_lang:local": "Llingua llocal", "options:ui_lang": "Llingua de la interfaz", diff --git a/lang/ca.json b/lang/ca.json index 51ca2664..125c6797 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -1,4 +1,5 @@ { "main:options": "Opcions", + "more": "més", "save": "Guardar" } \ No newline at end of file diff --git a/lang/cs.json b/lang/cs.json index f292521b..9ee96b8f 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -1,5 +1,6 @@ { "main:options": "Nastavení", + "more": "více", "options:data_lang": "Jazyk dat", "options:data_lang:local": "Místní jazyk", "options:ui_lang": "Jazyk rozhraní", diff --git a/lang/da.json b/lang/da.json index 9a4149f3..8145e1e2 100644 --- a/lang/da.json +++ b/lang/da.json @@ -1,5 +1,6 @@ { "main:options": "Indstillinger", + "more": "mere", "options:data_lang": "Data sprog", "options:data_lang:local": "Lokalt sprog", "options:ui_lang": "Brugerfladesprog", diff --git a/lang/de.json b/lang/de.json index eb262bf6..5d7ca9c1 100644 --- a/lang/de.json +++ b/lang/de.json @@ -1,8 +1,13 @@ { "closed": "geschlossen", "default": "Standard", + "error": { + "message": "Fehler", + "!=1": "Fehler" + }, "facilities": "Einrichtungen", "main:options": "Optionen", + "more": "mehr", "open": "geöffnet", "options:data_lang": "Datensprache", "options:data_lang:local": "Lokale Sprache", @@ -13,5 +18,6 @@ "show details": "zeige Details", "toggle_fullscreen": "(De-)aktiviere Vollbildmodus", "unknown": "unbekannt", - "unnamed": "Namenlos" + "unnamed": "Namenlos", + "wikipedia:no-url-parse": "Konnte Wikipedia Adresse nicht erkennen" } diff --git a/lang/el.json b/lang/el.json index f990b744..c909aa30 100644 --- a/lang/el.json +++ b/lang/el.json @@ -1,5 +1,6 @@ { "main:options": "Επιλογές", + "more": "περισσότερα", "options:data_lang": "Γλωσσα δεδομένων", "options:data_lang:local": "Τοπική γλώσσα", "options:ui_lang": "Γλώσσα διεπαφής", diff --git a/lang/en.json b/lang/en.json index abab282c..c8e7c5cb 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,8 +1,13 @@ { "closed": "closed", "default": "default", + "error": { + "message": "Error", + "!=1": "Errors" + }, "facilities": "Facilities", "main:options": "Options", + "more": "more", "open": "open", "options:data_lang": "Data language", "options:data_lang:desc": "Many map features have their name (and other tags) translated to different languages (e.g. with 'name:en', 'name:de'). Specify which language should be used for displaying, or 'Local language' so that always the untranslated value (e.g. 'name') will be used", @@ -14,5 +19,6 @@ "show details": "show details", "toggle_fullscreen": "Toggle full screen mode", "unknown": "unknown", - "unnamed": "unnamed" + "unnamed": "unnamed", + "wikipedia:no-url-parse": "Could not parse Wikipedia URL" } diff --git a/lang/es.json b/lang/es.json index 6040895d..0f4a7c32 100644 --- a/lang/es.json +++ b/lang/es.json @@ -1,5 +1,6 @@ { "main:options": "Opciones", + "more": "más", "options:data_lang": "Idioma de datos", "options:data_lang:local": "Idioma local", "options:ui_lang": "Idioma de interfaz", diff --git a/lang/et.json b/lang/et.json index 0b73477c..7028b216 100644 --- a/lang/et.json +++ b/lang/et.json @@ -1,5 +1,6 @@ { "main:options": "Valikud", + "more": "lisaks", "options:data_lang": "Andmete keel", "options:data_lang:local": "Kohalik keel", "options:ui_lang": "Kasutajaliidese keel", diff --git a/lang/fr.json b/lang/fr.json index d84d9a1f..e9c15df0 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -1,5 +1,6 @@ { "main:options": "Options", + "more": "plus", "options:data_lang": "Langue des données", "options:data_lang:local": "Langue locale", "options:ui_lang": "Langue de l'interface", diff --git a/lang/hu.json b/lang/hu.json index 279c05f7..0d5c91da 100644 --- a/lang/hu.json +++ b/lang/hu.json @@ -1,5 +1,6 @@ { "main:options": "Beállítások", + "more": "több", "options:data_lang": "Adatnyelv", "options:data_lang:local": "Helyi nyelv", "options:ui_lang": "Menünyelv", diff --git a/lang/it.json b/lang/it.json index 26dde9f6..3746ee15 100644 --- a/lang/it.json +++ b/lang/it.json @@ -1,5 +1,6 @@ { "main:options": "Opzioni", + "more": "altri", "options:data_lang": "Lingua dei dati", "options:data_lang:local": "Lingua del tuo browser", "options:ui_lang": "Lingua dell'interfaccia", diff --git a/lang/ja.json b/lang/ja.json index 6f87f45c..7c37bd2f 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -1,5 +1,6 @@ { "main:options": "オプション設定", + "more": "もっと", "options:data_lang": "データ表示", "options:data_lang:local": "ブラウザの設定言語", "options:ui_lang": "インタフェース表示", diff --git a/lang/nl.json b/lang/nl.json index 17abe618..7bdb8e7b 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -1,5 +1,6 @@ { "main:options": "Opties", + "more": "meer", "options:data_lang": "Taal voor data", "options:data_lang:local": "Lokale taal", "options:ui_lang": "Interfacetaal", diff --git a/lang/pl.json b/lang/pl.json index 5a62a30d..1b281fb5 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -1,5 +1,6 @@ { "main:options": "Opcje", + "more": "więcej", "options:data_lang": "Język danych", "options:data_lang:local": "Język lokalny", "options:ui_lang": "Język interfejsu", diff --git a/lang/pt-br.json b/lang/pt-br.json index 9b7b5d9b..8bfccb59 100644 --- a/lang/pt-br.json +++ b/lang/pt-br.json @@ -1,4 +1,5 @@ { + "more": "mais", "save": "Salvar", "unnamed": "sem nome" } \ No newline at end of file diff --git a/lang/ro.json b/lang/ro.json index b5428f66..b2faa188 100644 --- a/lang/ro.json +++ b/lang/ro.json @@ -1,5 +1,6 @@ { "main:options": "Optiuni", + "more": "Mai mult", "options:data_lang": "Limba date", "options:data_lang:local": "Limba locala", "options:ui_lang": "Limba interfata", diff --git a/lang/ru.json b/lang/ru.json index 415f3e16..66dac2a2 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -1,5 +1,6 @@ { "main:options": "Настройки", + "more": "Ещё", "options:data_lang": "Язык информации на карте", "options:data_lang:local": "Определить язык автоматически", "options:ui_lang": "Язык интерфейса", diff --git a/lang/sr.json b/lang/sr.json index f889c77d..4a52d62d 100644 --- a/lang/sr.json +++ b/lang/sr.json @@ -1,5 +1,6 @@ { "main:options": "Опције", + "more": "још", "options:data_lang": "Језик подетака", "options:data_lang:local": "Локални језик", "options:ui_lang": "Језик интерфејса", diff --git a/lang/template.json b/lang/template.json index 81e36564..8bdbd5bf 100644 --- a/lang/template.json +++ b/lang/template.json @@ -1,6 +1,7 @@ { "default": "", "main:options": "", + "more": "", "options:data_lang": "", "options:data_lang:desc": "", "options:data_lang:local": "", diff --git a/lang/uk.json b/lang/uk.json index 3020b659..93ab3b75 100644 --- a/lang/uk.json +++ b/lang/uk.json @@ -1,5 +1,6 @@ { "main:options": "Налаштування", + "more": "Ще", "options:data_lang": "Мова мапи", "options:data_lang:local": "Місцева мова", "options:ui_lang": "Мова інтерфейсу", diff --git a/modulekit.php b/modulekit.php index f22fca9e..e552ff51 100644 --- a/modulekit.php +++ b/modulekit.php @@ -14,6 +14,7 @@ $include = array( 'src/options.php', 'src/language.php', 'src/ip-location.php', + 'src/wikipedia.php', ), 'css' => array( 'style.css', diff --git a/src/CategoryOverpass.js b/src/CategoryOverpass.js index 2ea70076..e4559bdc 100644 --- a/src/CategoryOverpass.js +++ b/src/CategoryOverpass.js @@ -94,6 +94,10 @@ function CategoryOverpass (id, data) { } this.updatePopupContent(ob, ob.popup) + + if (document.getElementById('content').className === 'details') { + showDetails(ob, this) + } }.bind(this) p = document.createElement('div') @@ -219,6 +223,14 @@ CategoryOverpass.prototype.notifyPopupOpen = function (object, popup) { } CategoryOverpass.prototype.updatePopupContent = function (object, popup) { + call_hooks_callback('show-popup', object, this, popup._contentNode, + function (err) { + if (err.length) { + console.log('show-popup produced errors:', err) + } + } + ) + if (object.data.popupDescription || object.data.description) { var div = document.createElement('div') div.className = 'description' diff --git a/src/index.js b/src/index.js index f104df58..5963a2d5 100644 --- a/src/index.js +++ b/src/index.js @@ -26,12 +26,18 @@ require('./fullscreen') require('./mapLayers') require('./twigFunctions') require('./categories') +require('./wikipedia') window.onload = function () { initState = config.defaultView map = L.map('map') + // due to php export, options may be an array -> fix + if (Array.isArray(options)) { + options = {} + } + call_hooks('init') call_hooks_callback('init_callback', initState, onload2.bind(this, initState)) } @@ -139,17 +145,17 @@ window.setPath = function (path) { return } - options = { + var param = { showDetails: !!path.match(/\/details$/) } - show(path, options, function (err) { + show(path, param, function (err) { if (err) { alert(err) return } - call_hooks('show', path, options) + call_hooks('show', path, param) }) } @@ -199,7 +205,7 @@ function show (id, options, callback) { }) } -function showDetails (data, category) { +window.showDetails = function (data, category) { var div, h, dt, dd var k var dom = document.getElementById('contentDetails') @@ -228,6 +234,15 @@ function showDetails (data, category) { div.innerHTML = result }.bind(this, div)) + + call_hooks_callback('show-details', data, category, dom, + function (err) { + if (err.length) { + console.log('show-details produced errors:', err) + } + } + ) + h = document.createElement('h3') h.innerHTML = 'Attributes' dom.appendChild(h) diff --git a/src/language.js b/src/language.js index 06383eb6..aaa651b0 100644 --- a/src/language.js +++ b/src/language.js @@ -79,12 +79,15 @@ function langName (code) { } register_hook('init_callback', function (initState, callback) { - if ('data_lang' in options) { - tagTranslations.setTagLanguage(options.data_lang) - } else { - tagTranslations.setTagLanguage(getPreferredDataLanguage()) + if (!('ui_lang' in options)) { + options.ui_lang = ui_lang } + if (!('data_lang' in options)) { + options.data_lang = getPreferredDataLanguage() + } + tagTranslations.setTagLanguage(options.data_lang) + callback(null) }) @@ -108,10 +111,10 @@ register_hook('options_form', function (def) { } }) -register_hook('options_save', function (data) { - if ('data_lang' in data) { - if ('data_lang' in options && options.data_lang !== data.data_lang) { - tagTranslations.setTagLanguage(data.data_lang) +register_hook('options_save', function (options, old_options) { + if ('data_lang' in options) { + if (old_options.data_lang !== options.data_lang) { + tagTranslations.setTagLanguage(options.data_lang) baseCategory.recalc() } } diff --git a/src/options.js b/src/options.js index 8a760646..59fc6e8c 100644 --- a/src/options.js +++ b/src/options.js @@ -1,5 +1,6 @@ /* globals form, ajax, options:true, showRootContent */ var moduleOptions = {} +var prevPage register_hook('init', function () { var menu = document.getElementById('menu') @@ -28,6 +29,7 @@ moduleOptions.open = function () { call_hooks('options_form', def) var optionsForm = new form('options', def) + prevPage = document.getElementById('content').className document.getElementById('content').className = 'options' var dom = document.getElementById('contentOptions') dom.innerHTML = '' @@ -57,16 +59,17 @@ moduleOptions.submit = function (optionsForm) { } } - if (reload) { - location.reload() - } - ajax('options_save', null, data, function (ret) { - call_hooks('options_save', data) - + old_options = options options = data - showRootContent() + document.getElementById('content').className = prevPage + + call_hooks('options_save', data, old_options) + + if (reload) { + location.reload() + } }) return false diff --git a/src/twigFunctions.js b/src/twigFunctions.js index a0c40b58..f5e4cd83 100644 --- a/src/twigFunctions.js +++ b/src/twigFunctions.js @@ -36,3 +36,6 @@ OverpassLayer.twig.extendFilter('websiteUrl', function (value) { return 'http://' + value }) +OverpassLayer.twig.extendFilter('matches', function (value, match) { + return value.match(match) +}) diff --git a/src/wikipedia.js b/src/wikipedia.js new file mode 100644 index 00000000..31ddac5b --- /dev/null +++ b/src/wikipedia.js @@ -0,0 +1,175 @@ +var cache = {} + +function stripLinks (dom) { + var as = dom.getElementsByTagName('a') + var as = Array.prototype.slice.call(as) + + as.forEach(function (current) { + var c + + while (c = current.firstChild) { + current.parentNode.insertBefore(c, current) + } + + current.parentNode.removeChild(current) + }) +} + +function prepare (text) { + var ret = '' + var i + + var div = document.createElement('div') + div.innerHTML = text + + var contents = div.getElementsByTagName('div') + for (i = 0; i < contents.length; i++) { + if (contents[i].id === 'mw-content-text') { + var content = contents[i] + break + } + } + + if (!content) { + return null + } + + var p = content.firstChild.firstChild + while (p && p.tagName !== 'P') { + p = p.nextSibling + } + + if (!p) { + return null + } + + stripLinks(p) + + // first image + var imgs = div.getElementsByTagName('img') + for (i = 0; i < imgs.length; i++) { + var img = imgs[i] + + // ignore icons + if (img.width <= 64 && img.height <= 64) { + continue + } + + img.removeAttribute('width') + img.removeAttribute('height') + p.insertBefore(img, p.firstChild) + + break; + } + + return p.innerHTML +} + +function get (value, callback) { + var cacheId = options.data_lang + ':' + value + if (cacheId in cache) { + return callback(null, cache[cacheId]) + } + + ajax('wikipedia', + { + page: value, + lang: options.data_lang + }, + function (result) { + if (!result.content) { + return callback('error', null) + } + + var text = prepare(result.content) + text += ' ' + lang('more') + '' + + cache[cacheId] = text + + callback(null, text) + } + ) +} + +register_hook('show-details', function (data, category, dom, callback) { + var ob = data.object + var found = 0 + var finished = 0 + var errs = [] + var h + var div = document.createElement('div') + div.className = 'wikipedia' + + if ('wikipedia' in ob.tags) { + found++ + showWikipedia(ob.tags.wikipedia, div, done) + } + + for (var k in ob.tags) { + var m + if (m = k.match(/^(.*):wikipedia$/)) { + h = document.createElement('h4') + h.appendChild(document.createTextNode(lang('tag:' + m[1]))) + div.appendChild(h) + + found++ + showWikipedia(ob.tags[k], div, done) + } + + if (m = k.match(/^((.*):)?wikipedia:(.*)$/)) { + if (m[1]) { + h = document.createElement('h4') + h.appendChild(document.createTextNode(lang('tag:' + m[1]))) + div.appendChild(h) + } + + found++ + showWikipedia(m[3] + ':' + ob.tags[k], div, done) + } + } + + if (found) { + h = document.createElement('h3') + h.appendChild(document.createTextNode(lang('tag:wikipedia'))) + dom.appendChild(h) + + dom.appendChild(div) + } + + function done (err) { + finished++ + + if (err) { + errs.push(err) + } + + if (found === finished) { + callback(errs.length ? errs : null) + } + } +}) + +function showWikipedia (tagValue, dom, callback) { + var block = document.createElement('div') + block.className = 'loading' + dom.appendChild(block) + + var l = document.createElement('div') + l.innerHTML = 'Loading...' + l.className = 'loadingIndicator' + block.appendChild(l) + + get(tagValue, function (err, text) { + if (!text) { + block.appendChild(document.createTextNode(lang('wikipedia:no-url-parse'))) + } + + var div = document.createElement('div') + div.innerHTML = text + block.appendChild(div) + + block.className = '' + + callback(err) + }) +} diff --git a/src/wikipedia.php b/src/wikipedia.php new file mode 100644 index 00000000..55c2ca76 --- /dev/null +++ b/src/wikipedia.php @@ -0,0 +1,41 @@ + "_"))); + + $content = file_get_contents($wp_url); + + $langList = array($wp_lang => $wp_url); + + $dom = new DOMDocument(); + $dom->loadHTML($content); + + $langDiv = $dom->getElementsByTagName('li');//interlanguage-link interwiki-bar'); + for ($i = 0; $i < $langDiv->length; $i++) { + $li = $langDiv->item($i); + + if (preg_match('/^interlanguage-link interwiki-(.*)$/', $li->getAttribute('class'), $m)) { + $a = $li->firstChild; + $langList[$m[1]] = $a->getAttribute('href'); + } + } + + if ($wp_lang !== $param['lang'] && array_key_exists($param['lang'], $langList)) { + $content = file_get_contents($langList[$param['lang']]); + $wp_lang = $param['lang']; + } + + return array( + 'content' => $content, + 'languages' => $langList, + 'language' => $wp_lang, + ); +} diff --git a/style.css b/style.css index 8bc28b8f..252cc339 100644 --- a/style.css +++ b/style.css @@ -13,9 +13,7 @@ body { top: 0px; bottom: 0px; left: 0px; - width: 250px; - padding-left:10px; - padding-right:10px; + width: 270px; position: absolute; } #map { @@ -43,6 +41,7 @@ body { } #sidebar > #header { padding-top: 27px; + padding-left: 10px; height:110px; font-size:18px; color:#333; @@ -74,6 +73,8 @@ body { flex: 1; flex-shrink: 0; overflow: auto; + padding-left: 10px; + padding-right: 10px; } #sidebar > #footer { @@ -488,3 +489,33 @@ a.showDetails { .leaflet-popup-content ul > li > .key { font-weight: bold; } + +#contentDetails div > .loadingIndicator { + display: none; +} +#contentDetails div.loading > .loadingIndicator { + float: right; + display: block; +} + +/* Wikipedia */ +.wikipedia img { + max-width: 100px; + max-height: 100px; + float: right; + margin-left: 0.5em; + margin-bottom: 0.5em; +} +.wikipedia div { + text-align: justify; +} +#contentDetails h3 { + border-bottom: 1px solid black; + clear: both; +} +#contentDetails h4 { + clear: both; +} +.wikipedia .reference { + display: none; +}