PageSourceSearch

https://openstreetmap.cz/theme/js/poi-popup.js?626e3cb3

js openstreetmap.cz collected 2026-09-24 09:37:46 UTC 21,292 bytes, 554 lines download raw bytes

1// (c) 2016-2022 osmcz-app, https://github.com/osmcz/osmcz
2
3
4var osmcz = osmcz || {};
5osmcz.poiPopup = function (map) {
6    // -- constructor --
7
8    osmcz._map = map;
9};
10
11// static fields
12osmcz.permanentlyDisplayed = false;
13osmcz._marker = L.circleMarker([0, 0]);
14osmcz._map = false;
15osmcz.poiPopupOpen = false;
16
17// static methods
18osmcz.poiPopup.load = function (object) {
19
20    // exit when osm object does not exists or osmid is null
21    if ((!object) || (!"id" in object)) {
22        return;
23    }
24
25    $.ajax({
26        url: 'https://www.openstreetmap.org' + OSM.apiUrl({type: object.type, id: object.id}),
27        dataType: 'xml',
28        jsonp: false,
29        global: false,
30        success: function (data) {
31            console.log("loaded xml", data);
32
33            osmcz.permanentlyDisplayed = true;
34
35            var geojson = osm_geojson.osm2geojson(data);
36            var feature = geojson.features[0];
37            var tags = feature.properties;
38            feature.properties = {  //poloha.net style
39                tags: tags,
40                osm_id: object.id,
41                osm_type: object.type
42            };
43
44
45            if (feature.geometry.type !== 'Point') { // take centroid of a Polygon
46                var centroid;
47                try {
48                    centroid = L.polygon(feature.geometry.coordinates[0]).getBounds().getCenter();
49                } catch(e) {
50                    centroid = L.polygon(feature.geometry.coordinates).getBounds().getCenter();
51                }
52
53                feature.geometry = {
54                    type: 'Point',
55                    coordinates: [centroid.lat, centroid.lng]
56                };
57            }
58
59            if (feature.properties.tags.name) {
60                document.title = feature.properties.tags.name + ' ~ OpenStreetMap.cz';
61            }
62
63            console.log("geojson feature:", feature);
64
65            // zoom to feature
66            var lon = feature.geometry.coordinates[0];
67            var lat = feature.geometry.coordinates[1];
68            osmcz._map.setView([lat, lon]);
69
70            //set icon and show
71            var icon = osmcz.iconsService.get(feature.properties.tags);
72            poiSidebar.setContent(osmcz.poiPopup.getHtml(feature, icon.options.iconUrl));
73            poiSidebar.show();
74            osmcz.poiPopupOpen = true;
75        }
76    });
77
78};
79
80osmcz.poiPopup.open = function (feature, icon) {  //currently from active-layer
81    poiSidebar.setContent(osmcz.poiPopup.getHtml(feature, icon));
82    poiSidebar.show();
83    osmcz.poiPopupOpen = true;
84    document.title = 'OpenStreetMap.cz';
85    if (feature.properties.tags.name) {
86        document.title = feature.properties.tags.name + ' ~ OpenStreetMap.cz';
87    }
88};
89
90
91osmcz.poiPopup.close = function () {
92    console.log('poi-popup: close');
93
94    if (!osmcz.poiPopupOpen) {
95        return;
96    }
97
98    osmcz._map.removeLayer(osmcz._marker);
99    osmcz.permanentlyDisplayed = false;
100    osmcz.poiSidebar.hide();
101    osmcz.poiPopupOpen = false;
102
103    document.title = 'OpenStreetMap.cz';
104
105    var path = (location.host === 'openstreetmap.cz' || location.host === 'osm.localhost')
106        ? '/'
107        : location.pathname;
108
109    history.replaceState('', '', path + location.hash);
110};
111
112osmcz.poiPopup.setUrl = function (p) {
113    var path = (location.host === 'openstreetmap.cz' || location.host === 'osm.localhost')
114        ? ('/' + p.osm_type + '/' + p.osm_id)
115        : ('?' + p.osm_type + '=' + p.osm_id);
116
117    history.replaceState('', '', path + location.hash);
118};
119
120
121// ------- POI panel template  -------
122osmcz.poiPopup.getHtml = function (feature, icon, embedded) {
123
124    //TODO refactor
125
126    var id = feature.properties.osm_id;
127    var osm_type = feature.properties.osm_type;
128    var general = []; // store k,v objects in array to allow sort them
129    var name = {};
130    var contact = {};
131    var openingHours = '';
132    var payment = {};
133    var building = {};
134    var wikidata = {};
135    var wikimedia = {};
136    var wikipedia = {};
137    var guidepost = false;
138    var lon = feature.geometry.coordinates[0];
139    var lat = feature.geometry.coordinates[1];
140
141    // Get preferred user language and use it on wikipedia
142    var userLang = (window.navigator.userLanguage || window.navigator.language).split("-")[0];
143    var wikiLang = (userLang ? "?uselang=" + userLang : "" );
144
145    // show circle marker
146    if (osmcz.permanentlyDisplayed) {
147        osmcz._marker.setLatLng([lat, lon]).addTo(osmcz._map);
148    }
149
150    var tpl = [];
151
152    // Not needed when we are inside popup
153    if (!embedded) {
154//       tpl.push(osmcz.permanentlyDisplayed ? '<a class="close">&times;</a>' : '');
155        tpl.push('<h4>');
156        tpl.push('<img class="poi-icon" src="' + icon + '">&nbsp;');
157        tpl.push('<span class="h4-text">' + (feature.properties.tags.name || 'Bod zájmu') + '</span>');
158        tpl.push('</h4>');
159    }
160
161    $.each(feature.properties.tags, function (k, v) {
162        k = k + "";
163        v = v + "";
164
165        if (k.match(/^name:/))
166            name[k] = v;
167        else if (k.match(/^payment:/))
168            payment[k] = v;
169        else if (k.match(/^contact:/) || k.match(/^phone/) || k.match(/^email/))
170            contact[k] = v;
171        else if (k.match(/^building/))
172            building[k] = v;
173        else if (k.match(/^opening_hours$/))
174            openingHours = v;
175        else if (k.match(/^addr:|^ref:ruian:/)) //skip TODO show in expert mode
176            undefined;
177        else
178            general.push({k: k, v: v});
179
180        // global flags
181        if (k.match(/^wikipedia/)) {
182            // Prefer wikipedia over wikipedia:cs over first one
183            if (k.match(/^wikipedia$/))
184                wikipedia = {k: k, v: v};
185            else if (k.match(/^wikipedia:cs$/) &&
186                (!wikipedia.k || (wikipedia.k && !wikipedia.k == "wikipedia")))
187                wikipedia = {k: k, v: k.split(":").pop() + ":" + v};
188            else if (!wikipedia.k && k.match(/^wikipedia:/))
189                wikipedia = {k: k, v: k.split(":").pop() + ":" + v};
190        }
191        else if (k.match(/^wikimedia_commons/) && v.match(/^File:/))
192            wikimedia = {k: k, v: v};
193        else if (k.match(/^wikidata/))
194            wikidata = {k: k, v: v};
195        else if (k.match(/^image$/) && v.match(/^File:/))
196            wikimedia = {k: 'wikimedia_commons', v: v};
197    });
198
199    // sort the array
200    general.sort(function (a, b) {
201        return a.k.localeCompare(b.k);
202    });
203
204    if (general.length) {
205        for (var i in general) {
206            var k = general[i].k;
207            var v = general[i].v;
208            tpl.push('<b>' + k + '</b> = ');
209            // wikipedia=* or xxx:wikipedia=*
210            if (k.match(/^wikipedia$/) || k.match(/:wikipedia$/)) {
211                if (v.match(/:/)) {
212                    tpl.push('<a href="https://' + v.split(":")[0] + '.wikipedia.org/wiki/' + v.split(":")[1] + wikiLang + '">' + v + '</a>');
213                } else {
214                    tpl.push('<a href="https://wikipedia.org/wiki/' + v + wikiLang + '">' + v + '</a>');
215                }
216            }
217            // wikipedia:<country>=* or xxx:wikipedia:<country>=*
218            else if (k.match(/^wikipedia:/) || k.match(/:wikipedia:/))
219                tpl.push('<a href="https://' + k.split(":").pop() + '.wikipedia.org/wiki/' + v + wikiLang + '">' + v + '</a>');
220            // wikidata=*
221            else if (k.match(/^wikidata$/))
222                tpl.push('<a href="https://www.wikidata.org/wiki/' + v + wikiLang + '">' + v + '</a>');
223            // wikimedia commons=*
224            else if (k.match(/^wikimedia_commons$/))
225                tpl.push('<a href="https://commons.wikimedia.org/wiki/' + v + wikiLang + '">' + v + '</a>');
226            else if (k.match(/^image$/) && v.match(/^File:/))
227            // handle image as wikimedia_commons
228                tpl.push('<a href="https://commons.wikimedia.org/wiki/' + v + wikiLang + '">' + v + '</a>');
229            // Katalog národního památkového ústavu
230            else if (k.match(/^ref:npu$/))
231                tpl.push('<a href="http://pamatkovykatalog.cz/?mode=parametric&isProtected=1&presenter=ElementsResults&indexId=' + encodeURIComponent(v) + '">' + v + '</a>'); // @TODO: upravit, až bude HTTPS verze
232            else
233            // Just standard url
234                tpl.push(v.match(/^https?:\/\/.+/) ? ('<a href="' + v + '">' + v + '</a>') : v);
235            tpl.push('<br>');
236        }
237    }
238
239
240    var section = function (obj, label, hide) {
241        if (Object.keys(obj).length) {
242            hide && tpl.push('<p><b>' + label + '</b> <a href="#" onclick="$(this).parent().hide().next().show();return false">zobrazit</a></p>');
243            hide && tpl.push('<div style="display:none">');
244            hide && tpl.push('<h5>' + label + ' <a href="#" onclick="$(this).parent().parent().hide().prev().show();return false">skrýt</a></h5>');
245
246            hide || tpl.push('<h5>' + label + '</h5>');
247            $.each(obj, function (k, v) {
248                tpl.push('<b>' + k + '</b> = ');
249                tpl.push(v.match(/^https?:\/\/.+/) ? ('<a href="' + v + '">' + v + '</a>') : v);
250                tpl.push('<br>');
251            });
252            tpl.pop(); //remove last br
253
254            hide && tpl.push('</div>');
255        }
256    };
257
258    section(name, 'Další jména:', true);
259    try {
260        if (openingHours != '') {
261            console.log(openingHours);
262            tpl = tpl.concat(osmcz.openingHoursService.getHtml(openingHours));
263        }
264    } catch (err) {
265        tpl.push("<b>opening_hours</b> = " + openingHours + "<br> <em>(Nepodařilo se naparsovat: " + err + ")</em>");
266    }
267    section(payment, 'Možnosti platby:');
268    section(contact, 'Kontakty:');
269    section(building, 'Budova:');
270
271    // Finish there when we are inside popup
272    if (embedded) {
273        return tpl.join('');
274    }
275
276    //tpl.push('<div class="osmid"><a href="http://osm.org/' + osm_type + '/' + id + '">osm ID: ' + osm_type + '/' + id + '</a></div>');
277    tpl.push('<div class="osmid"><a href="https://osmap.cz/' + osm_type + '/' + id + '">osmap.cz/' + osm_type + '/' + id + '</a></div>');
278    tpl.push('<div id="wikimedia-commons" data-osm-id="' + id + '"></div>');
279    tpl.push('<div id="guidepost" data-osm-id="' + id + '"></div>');
280    tpl.push('<div id="mapillary-photo" data-osm-id="' + id + '"></div>');
281
282
283    // ---------------------------------------- images ------------------------------------------
284    // TODO refactor
285
286    // -----------------  WP & WM -----------------
287
288    if (feature.wikimedia || feature.wikipedia) {
289        setTimeout(function () { //after dom is created
290            showWikimediaCommons();
291        }, 0);
292    }
293
294    // url of ajax proxy server for wikipedia and wikimedia (CORS not HTTPS)
295    var xhd_proxy_url = 'https://openstreetmap.cz/xhr_proxy.php';
296
297    // Show picture from wikimedia commons if there is `wikidata` or `wikimedia_commons` or `wikipedia` tag
298    // Priorities:
299    //      1) wikidata
300    //      2) wikimedia_commons
301    //      3) wikipedia
302    //
303    // There is also fallback - if wikidata reply does not contains image and wikimedia_commons or wikipedia
304    // tags exists, they will be checked as well.
305
306    if (wikimedia.k || wikipedia.k || wikidata.k) {
307        var url;
308        if (wikidata.k)
309            url = getWikiApiUrl(wikidata);
310        else if (wikimedia.k)
311            url = getWikiApiUrl(wikimedia);
312        else
313            url = getWikiApiUrl(wikipedia);
314
315        $.ajax({
316            url: xhd_proxy_url,
317            data: {
318                url: url
319            },
320            dataType: 'json',
321            success: function (data) {
322                var replyType = getWikiType(data);
323                if (replyType == 'wikimedia') {
324                    feature.wikimedia = data;
325                    showWikimediaCommons();
326                }
327                else if (replyType == 'wikipedia') {
328                    feature.wikipedia = data;
329                    showWikimediaCommons();
330                }
331                else if (replyType == 'wikidata') {
332                    var reply = processWikidataReply(data);
333                    if (!reply.k) {
334                        // Wikidata entry does not contains image
335                        // try other tags
336                        if (wikimedia.k)
337                            reply = wikimedia;
338                        else if (wikipedia.k)
339                            reply = wikipedia;
340                    }
341
342                    // get image thumbnail url or try wikimedia/wikipedia if there is no image on wikidata
343                    if (reply.k) {
344                        var url = getWikiApiUrl(reply);
345                        $.ajax({
346                            url: xhd_proxy_url,
347                            data: {
348                                url: url
349                            },
350                            dataType: 'json',
351                            success: function (data) {
352                                if (getWikiType(data) == 'wikimedia') {
353                                    feature.wikimedia = data;
354                                    showWikimediaCommons();
355                                } else if (getWikiType(data) == 'wikipedia') {
356                                    feature.wikipedia = data;
357                                    showWikimediaCommons();
358                                }
359                            }
360                        });
361                    }
362                }
363            }
364        });
365    }
366
367    // Show image from wikimedia commons
368    function showWikimediaCommons() {
369
370        // Template
371        var wcmTpl = '<h5><a href="https://commons.wikimedia.org/">'
372            + '<img class="commons_logo" src="' + osmcz.basePath + 'img/commons_logo.png" height="24"></a>'
373            + 'Foto z Wikipedie</h5>'
374            + '<a href="_descriptionshorturl">'
375            + '<img src="_thumburl" >'
376            + '</a>';
377
378        var wcm = $('#wikimedia-commons');
379        if (id == wcm.attr('data-osm-id') && (feature.wikimedia || feature.wikipedia)) {
380            if (feature.wikimedia) {
381                var k = Object.keys(feature.wikimedia.query.pages)[0];
382                if (!feature.wikimedia.query.pages[k].imageinfo.length)
383                    return;
384                var descriptionshorturl = feature.wikimedia.query.pages[k].imageinfo[0].descriptionshorturl;
385                var thumburl = feature.wikimedia.query.pages[k].imageinfo[0].thumburl;
386            } else {
387                var k = Object.keys(feature.wikipedia.query.pages)[0];
388                if (!feature.wikipedia.query.pages[k].pageimage)
389                    return;
390                var descriptionshorturl = 'https://commons.wikimedia.org/wiki/File:' + feature.wikipedia.query.pages[k].pageimage;
391                var thumburl = feature.wikipedia.query.pages[k].thumbnail.source;
392            }
393            wcm.html(wcmTpl.replace(/_thumburl/g, thumburl).replace(/_descriptionshorturl/g, descriptionshorturl));
394        }
395    }
396
397    // -----------------  GUIDEPOST -----------------
398
399
400    // show guidepost picture from openstreetmap.cz
401    if (feature.properties.tags.information == 'guidepost') {
402        if (feature.guidepost) {
403            setTimeout(function () { //after dom is created
404                showGuidepost();
405            }, 0);
406        }
407        else {
408            var ref = feature.properties.tags.ref;
409            $.ajax({
410                url: osmcz.photoDbUrl + 'api/close',
411                data: {
412                    lat: lat,
413                    lon, lon,
414                    limit: '1',
415                    distance: '50'
416                },
417                dataType: 'json',
418                success: function (data) {
419                    feature.guidepost = data;
420                    showGuidepost();
421                }
422            });
423        }
424    }
425
426    var gpTpl = '<h5>Foto rozcestníku</h5>'
427        + '<a href="_imgUrl">'
428        + '<div id="thumbnailLoadSpinner" class="text-center"><span class="glyphicon glyphicon-refresh text-info gly-spin"></span><br></div>'
429        + '<img id="thumbnailImage" src="" class="center-block"/>'
430        + '</a>'
431        + '<div class="margin-top-05"><b>Fotografii poskytl: </b> _autor'
432        + '<span style="margin: 0.5em"/>'
433        + ' <a href="'+osmcz.photoDbUrl+'?id=_id" target="_blank" class="btn btn-default btn-xs">'
434        + '   <span class="glyphicon glyphicon-pencil" title="upravit"></span> upravit</a>'
435        + '</div>'
436
437    function showGuidepost() {
438        var gp = $('#guidepost');
439        if (id == gp.attr('data-osm-id') && feature.guidepost) {
440            // TODO: show all guideposts
441            if (!feature.guidepost.features.length)
442                return;
443            var autor = feature.guidepost.features[0].properties.author;
444            var gpostId = feature.guidepost.features[0].properties.id;
445            var fullImgUrl = osmcz.photoDbUrl + 'files/' + gpostId + '.jpg';
446            gp.html(gpTpl.replace(/_autor/g, autor).replace(/_imgUrl/g, fullImgUrl).replace(/_id/g, gpostId));
447
448            // get guidepost thumbnail from photodb cache server first
449            // if it fails, request it from phpThumb
450            var tb = new Image();
451            tb.src = osmcz.photoDbUrl + "files/250px/" + gpostId + '.jpg';
452            tb.onload = function () {
453                $('#thumbnailLoadSpinner').hide();
454                $('#thumbnailImage').attr('src', tb.src);
455            };
456            tb.onerror = function () {
457                $('#thumbnailLoadSpinner').html('<br><span class="glyphicon glyphicon-picture bigger semigrey thumbnail crossed" title="Náhled není k dispozici."><span><br>');
458                $('#thumbnailLoadSpinner').attr('class', 'text-nowrap text-center');
459
460            };
461
462        }
463    }
464
465    // -----------------  MAPILLARY -----------------
466
467    // show closest mapillary photo if no photo so far
468    if (osmcz.permanentlyDisplayed) {
469        if (!feature.wikimedia && !feature.wikipedia && !feature.guidepost) {
470            //TODO WP tag != existing WP photo --> chain mapillary after the WP request?
471
472            if (feature.mapillary) {
473                setTimeout(function () { //after dom is created
474                    showMapillary();
475                }, 0);
476            }
477            else {
478                //TODO - limit=10 & choose the best oriented photo
479                $.ajax({
480                    url: 'https://api.mapillary.com/v1/im/close?lat=' + lat + '&lon=' + lon + '&distance=30&limit=1',
481                    dataType: 'json',
482                    jsonp: false,
483                    global: false,
484                    success: function (data) {
485                        feature.mapillary = data;
486                        showMapillary();
487                    }
488                });
489            }
490        }
491    }
492
493    // "_key" se nahrazuje mapillary IDčkem
494    var mpTpl = '<h5>Nejbližší street-view</h5>'
495        + '<a href="https://www.mapillary.com/map/im/_key/photo">'
496        + '<img src="' + osmcz.fakeHttps + 'images.mapillary.com/_key/thumb-320.jpg" height="187">'  //@todo https viz https://github.com/mapillary/mapillary_issues/issues/2419
497        + '</a>';
498
499    function showMapillary() {
500        var mp = $('#mapillary-photo');
501        if (id == mp.attr('data-osm-id') && feature.mapillary.length) {
502            mp.html(mpTpl.replace(/_key/g, feature.mapillary[0].key));
503        }
504    }
505
506    return tpl.join('');
507
508
509    // ------- Wikidata, wikimedia commons and wikipedia functions -------
510    //
511    // Prepare correct api request
512    function getWikiApiUrl(we) {
513        if (we.k == "wikimedia_commons") { // wikimedia commons tag
514            return 'https://commons.wikimedia.org/w/api.php?action=query&prop=imageinfo&iiprop=url&iiurlwidth=240&format=json'
515                + '&titles=' + encodeURIComponent(we.v);
516        } else if (we.k == "wikidata") { // wikidata tag
517            return 'https://www.wikidata.org/w/api.php?action=wbgetclaims&property=P18&format=json'
518                + '&entity=' + encodeURIComponent(we.v);
519        } else { // wikipedia tag
520            var country = we.v.split(":")[0];
521            if (country === we.v)
522                country = "en";
523            return 'https://' + country + '.wikipedia.org/w/api.php?action=query&prop=pageimages&pithumbsize=240&format=json'
524                + '&titles=' + encodeURIComponent(we.v);
525        }
526    }
527
528    // Analyze reply and identify wikidata / wikimedia / wikipedia content
529    function getWikiType(d) {
530        if (d.query) {
531            if (Object.keys(d.query.pages)[0]) {
532                var k = Object.keys(d.query.pages)[0];
533                if (d.query.pages[k].imageinfo)
534                    return 'wikimedia';
535                if (d.query.pages[k].pageimage)
536                    return 'wikipedia';
537            }
538        }
539        if (d.claims)
540            return 'wikidata';
541    }
542
543    // In wikidata entry is image name. but we need thumbnail,
544    // so we will convert wikidata reply to wikimedia_commons tag
545    function processWikidataReply(d) {
546        var w = {};
547        if (d.claims.P18) {
548            w.k = 'wikimedia_commons';
549            w.v = 'File:' + d.claims.P18[0].mainsnak.datavalue.value;
550        }
551        return w;
552    }
553
554};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.