xref: /plugin/openlayersmap/script.js (revision 7390889d9aa535aa9bdfffc90e2fe7f66d696221)
1/*
2 * Copyright (c) 2008-2022 Mark C. Prins <mprins@users.sf.net>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17
18/**
19 * Test for css support in the browser by sniffing for a css class we added
20 * using javascript added by the action plugin; this is an edge case because
21 * browsers that support javascript generally support css as well.
22 *
23 * @returns {Boolean} true when the browser supports css (and implicitly
24 *          javascript)
25 */
26function olTestCSSsupport() {
27    return (jQuery('.olCSSsupported').length > 0);
28}
29
30/**
31 * Creates a DocumentFragment to insert into the dom.
32 *
33 * @param mapid
34 *            id for the map div
35 * @param width
36 *            width for the map div
37 * @param height
38 *            height for the map div
39 * @returns a {DocumentFragment} element that can be injected into the dom
40 */
41function olCreateMaptag(mapid, width, height) {
42    var mEl = '<div id="' + mapid + '-olContainer" class="olContainer olWebOnly">'
43            // map
44            + '<div id="' + mapid + '" tabindex="0" style="width:' + width + ';height:' + height + ';" class="olMap"></div>'
45            + '</div>',
46        // fragment
47        frag = document.createDocumentFragment(),
48        // temp node
49        temp = document.createElement('div');
50    temp.innerHTML = mEl;
51    while (temp.firstChild) {
52        frag.appendChild(temp.firstChild);
53    }
54    return frag;
55}
56
57/**
58 * Create the map based on the params given.
59 *
60 * @param {Object}
61 *            mapOpts MapOptions hash {id:'olmap', width:500px, height:500px,
62 *            lat:6710200, lon:506500, zoom:13, statusbar:1, controls:1,
63 *            poihoverstyle:1, baselyr:'', kmlfile:'', gpxfile:'', geojsonfile,
64 *            summary:''}
65 * @param {Array}
66 *            OLmapPOI array with POI's [ {lat:6710300,lon:506000,txt:'instap
67 *            punt',angle:180,opacity:.9,img:'', rowId:n},... ]);
68 *
69 * @return {OpenLayers.Map} the created map
70 */
71function createMap(mapOpts, poi) {
72
73    // const mapOpts = olMapData[0].mapOpts;
74    // const poi = olMapData[0].poi;
75
76    if (!olEnable) {
77        return;
78    }
79    if (!olTestCSSsupport()) {
80        olEnable = false;
81        return;
82    }
83
84    // find map element location
85    var cleartag = document.getElementById(mapOpts.id + '-clearer');
86    if (cleartag === null) {
87        return;
88    }
89    // create map element and add to document
90    var fragment = olCreateMaptag(mapOpts.id, mapOpts.width, mapOpts.height);
91    cleartag.parentNode.insertBefore(fragment, cleartag);
92
93    /** dynamic map extent. */
94    let extent = ol.extent.createEmpty();
95    overlayGroup = new ol.layer.Group({title: 'Overlays', fold: 'open', layers: []});
96    const baseLyrGroup = new ol.layer.Group({'title': 'Base maps', layers: []});
97
98    const map = new ol.Map({
99        target:   document.getElementById(mapOpts.id),
100        layers:   [baseLyrGroup, overlayGroup],
101        view:     new ol.View({
102            center:     ol.proj.transform([mapOpts.lon, mapOpts.lat], 'EPSG:4326', 'EPSG:3857'),
103            zoom:       mapOpts.zoom,
104            projection: 'EPSG:3857'
105        }),
106        controls: [new ol.control.Zoom()]
107    });
108
109    if (osmEnable) {
110        baseLyrGroup.getLayers().push(
111            new ol.layer.Tile({
112                visible: true,
113                title:   'OSM',
114                type:    'base',
115                source:  new ol.source.OSM()
116            }));
117
118        baseLyrGroup.getLayers().push(
119            new ol.layer.Tile({
120                visible: mapOpts.baselyr === "cycle map",
121                title:   'cycle map',
122                type:    'base',
123                source:  new ol.source.OSM({
124                    url:          'https://{a-c}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=' + tfApiKey,
125                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
126                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
127                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
128                })
129            }));
130
131        baseLyrGroup.getLayers().push(
132            new ol.layer.Tile({
133                visible: mapOpts.baselyr === "transport",
134                title:   'transport',
135                type:    'base',
136                source:  new ol.source.OSM({
137                    url:          'https://{a-c}.tile.thunderforest.com/transport/{z}/{x}/{y}.png?apikey=' + tfApiKey,
138                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
139                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
140                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
141                })
142            }));
143
144        baseLyrGroup.getLayers().push(
145            new ol.layer.Tile({
146                visible: mapOpts.baselyr === "landscape",
147                title:   'landscape',
148                type:    'base',
149                source:  new ol.source.OSM({
150                    url:          'https://{a-c}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png?apikey=' + tfApiKey,
151                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
152                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
153                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
154                })
155            }));
156
157        baseLyrGroup.getLayers().push(
158            new ol.layer.Tile({
159                visible: mapOpts.baselyr === "outdoors",
160                title:   'outdoors',
161                type:    'base',
162                source:  new ol.source.OSM({
163                    url:          'https://{a-c}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png?apikey=' + tfApiKey,
164                    attributions: 'Data &copy;ODbL <a href="https://openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>, '
165                                      + 'Tiles &copy;<a href="https://www.thunderforest.com/" target="_blank">Thunderforest</a>'
166                                      + '<img src="https://www.thunderforest.com/favicon.ico" alt="Thunderforest logo"/>'
167                })
168            }));
169    }
170
171    if (bEnable && bApiKey !== '') {
172        baseLyrGroup.getLayers().push(
173            new ol.layer.Tile({
174                visible: mapOpts.baselyr === "bing road",
175                title:   'bing road',
176                type:    'base',
177                source:  new ol.source.BingMaps({
178                    key:        bApiKey,
179                    imagerySet: 'Road'
180                })
181            }));
182
183        baseLyrGroup.getLayers().push(
184            new ol.layer.Tile({
185                visible: mapOpts.baselyr === "bing sat",
186                title:   'bing sat',
187                type:    'base',
188                source:  new ol.source.BingMaps({
189                    key:        bApiKey,
190                    imagerySet: 'Aerial'
191                })
192            }));
193
194        baseLyrGroup.getLayers().push(
195            new ol.layer.Tile({
196                visible: mapOpts.baselyr === "bing hybrid",
197                title:   'bing hybrid',
198                type:    'base',
199                source:  new ol.source.BingMaps({
200                    key:        bApiKey,
201                    imagerySet: 'AerialWithLabels'
202                })
203            }));
204    }
205
206    if (stamenEnable) {
207        baseLyrGroup.getLayers().push(
208            new ol.layer.Tile({
209                visible: false,
210                type:    'base',
211                title:   'toner',
212                source:  new ol.source.Stamen({layer: 'toner'})
213            })
214        );
215        baseLyrGroup.getLayers().push(
216            new ol.layer.Tile({
217                visible: false,
218                type:    'base',
219                title:   'terrain',
220                source:  new ol.source.Stamen({layer: 'terrain'})
221            })
222        );
223    }
224
225    extent = ol.extent.extend(extent, map.getView().calculateExtent());
226
227    const iconScale = 1.0;
228    const vectorSource = new ol.source.Vector();
229    poi.forEach((p) => {
230        const f = new ol.Feature({
231            geometry:    new ol.geom.Point(ol.proj.fromLonLat([p.lon, p.lat])),
232            description: p.txt,
233            img:         p.img,
234            rowId:       p.rowId,
235            lat:         p.lat,
236            lon:         p.lon,
237            angle:       p.angle,
238            alt:         p.img.substring(0, p.img.lastIndexOf("."))
239        });
240        f.setId(p.rowId);
241        f.setStyle(new ol.style.Style({
242            text:      new ol.style.Text({
243                text:           "" + p.rowId,
244                textAlign:      'left',
245                textBaseline:   'bottom',
246                offsetX:        8,
247                offsetY:        -8,
248                scale:          iconScale,
249                fill:           new ol.style.Fill({color: 'rgb(0,0,0)'}),
250                font:           '12px monospace bold',
251                backgroundFill: new ol.style.Fill({color: 'rgba(255,255,255,.4)'})
252            }), image: new ol.style.Icon({
253                src:         DOKU_BASE + "lib/plugins/openlayersmap/icons/" + p.img,
254                crossOrigin: '',
255                opacity:     p.opacity,
256                scale:       iconScale,
257                rotation:    p.angle * Math.PI / 180,
258            }),
259        }));
260        vectorSource.addFeature(f);
261    });
262
263    const vectorLayer = new ol.layer.Vector({title: 'POI', visible: true, source: vectorSource});
264    overlayGroup.getLayers().push(vectorLayer);
265    if (mapOpts.autozoom) {
266        extent = ol.extent.extend(extent, vectorSource.getExtent());
267        map.getView().fit(extent);
268    }
269
270    map.addControl(new ol.control.ScaleLine({bar: true, text: true}));
271    map.addControl(new ol.control.MousePosition({
272        coordinateFormat: ol.coordinate.createStringXY(4), projection: 'EPSG:4326',
273    }));
274    map.addControl(new ol.control.FullScreen({label: '✈'}));
275    map.addControl(new ol.control.OverviewMap({
276        label:  '+',
277        layers: [new ol.layer.Tile({
278            source: new ol.source.OSM()
279        })]
280    }));
281    map.addControl(new ol.control.LayerSwitcher({
282        activationMode: 'click',
283        label:          '\u2630',
284        collapseLabel:  '\u00BB',
285    }));
286    map.addControl(new ol.control.Attribution({
287        collapsible: true,
288        collapsed:   true
289    }));
290
291    if (mapOpts.kmlfile.length > 0) {
292        try {
293            const kmlSource = new ol.source.Vector({
294                url:    DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.kmlfile,
295                format: new ol.format.KML(),
296            });
297            overlayGroup.getLayers().push(new ol.layer.Vector({title: 'KML file', visible: true, source: kmlSource}));
298
299            if (mapOpts.autozoom) {
300                kmlSource.once('change', function () {
301                    extent = ol.extent.extend(extent, kmlSource.getExtent());
302                    map.getView().fit(extent);
303                });
304            }
305        } catch (e) {
306            console.error(e);
307        }
308    }
309
310    if (mapOpts.geojsonfile.length > 0) {
311        try {
312            const geoJsonSource = new ol.source.Vector({
313                url:    DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.geojsonfile,
314                format: new ol.format.GeoJSON(),
315            });
316            overlayGroup.getLayers().push(new ol.layer.Vector({
317                title: 'GeoJSON file', visible: true, source: geoJsonSource,
318                // TODO
319                // style:  {
320                //     strokeColor:   "#FF00FF",
321                //     strokeWidth:   3,
322                //     strokeOpacity: 0.7,
323                //     pointRadius:   4,
324                //     fillColor:     "#FF99FF",
325                //     fillOpacity:   0.7
326                // }
327            }));
328
329            if (mapOpts.autozoom) {
330                geoJsonSource.once('change', function () {
331                    extent = ol.extent.extend(extent, geoJsonSource.getExtent());
332                    map.getView().fit(extent);
333                });
334            }
335        } catch (e) {
336            console.error(e);
337        }
338    }
339
340    if (mapOpts.gpxfile.length > 0) {
341        try {
342            const gpxSource = new ol.source.Vector({
343                url:    DOKU_BASE + "lib/exe/fetch.php?media=" + mapOpts.gpxfile,
344                format: new ol.format.GPX(),
345            });
346            overlayGroup.getLayers().push(new ol.layer.Vector({
347                title: 'GPS track', visible: true, source: gpxSource,
348                // TODO
349                // style:  {
350                //     strokeColor:   "#0000FF",
351                //     strokeWidth:   3,
352                //     strokeOpacity: 0.7,
353                //     pointRadius:   4,
354                //     fillColor:     "#0099FF",
355                //     fillOpacity:   0.7
356                // }
357            }));
358
359            if (mapOpts.autozoom) {
360                gpxSource.once('change', function () {
361                    extent = ol.extent.extend(extent, gpxSource.getExtent());
362                    map.getView().fit(extent);
363                });
364            }
365        } catch (e) {
366            console.error(e);
367        }
368    }
369
370    const container = document.getElementById('popup');
371    const content = document.getElementById('popup-content');
372    const closer = document.getElementById('popup-closer');
373
374    const overlay = new ol.Overlay({
375        element: container, autoPan: true, autoPanAnimation: {
376            duration: 250,
377        }, //stopEvent: false,
378    });
379    map.addOverlay(overlay);
380
381    /**
382     * Add a click handler to hide the popup.
383     * @return {boolean} Don't follow the href.
384     */
385    closer.onclick = function () {
386        overlay.setPosition(undefined);
387        closer.blur();
388        return false;
389    };
390
391    // display popup on click
392    map.on('singleclick', function (evt) {
393        const selFeature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
394            return feature;
395        });
396        if (selFeature) {
397            overlay.setPosition(evt.coordinate);
398
399            let pContent = '<div class="spacer">&nbsp;</div>';
400            let locDesc = '';
401
402            if (selFeature.get('rowId') !== undefined) {
403                pContent += '<span class="rowId">' + selFeature.get('rowId') + ': </span>';
404            }
405            if (selFeature.get('name') !== undefined) {
406                pContent += '<span class="txt">' + selFeature.get('name') + '</span>';
407                locDesc = selFeature.get('name');
408                // TODO strip <p> tag from locDesc
409                // locDesc = selFeature.get('name').split(/\s+/).slice(0,2).join('+');
410            }
411            if (selFeature.get('ele') !== undefined) {
412                pContent += '<div class="ele">elevation: ' + selFeature.get('ele') + '</div>';
413            }
414            if (selFeature.get('type') !== undefined) {
415                pContent += '<div>' + selFeature.get('type') + '</div>';
416            }
417            if (selFeature.get('time') !== undefined) {
418                pContent += '<div class="time">time: ' + selFeature.get('time') + '</div>';
419            }
420            if (selFeature.get('description') !== undefined) {
421                pContent += '<div class="desc">' + selFeature.get('description') + '</div>';
422            }
423            if (selFeature.get('img') !== undefined) {
424                pContent += '<div class="coord" title="lat;lon">' +
425                    '<img alt="" src="' + DOKU_BASE + 'lib/plugins/openlayersmap/icons/' + selFeature.get('img') +
426                    '" width="16" height="16" ' + 'style="transform:rotate(' + selFeature.get('angle') + 'deg)" />&nbsp;' +
427                    '<a href="geo:' + selFeature.get('lat') + ',' + selFeature.get('lon') + '?q=' + selFeature.get('lat') +
428                    ',' + selFeature.get('lon') + '(' + selFeature.get('alt') + ')" title="Open in navigation app">' +
429                    ol.coordinate.format([selFeature.get('lon'), selFeature.get('lat')], '{x}º; {y}º', 4) + '</a></div>';
430            }
431            content.innerHTML = pContent;
432        } else {
433            // do nothing...
434        }
435    });
436
437    // change mouse cursor when over marker
438    map.on('pointermove', function (e) {
439        const pixel = map.getEventPixel(e.originalEvent);
440        const hit = map.hasFeatureAtPixel(pixel);
441        map.getTarget().style.cursor = hit ? 'pointer' : '';
442    });
443
444    return map;
445}
446
447/**
448 * add layers to the map based on the olMapOverlays object.
449 */
450function olovAddToMap() {
451    for (const key in olMapOverlays) {
452        const overlay = olMapOverlays[key];
453        const m = olMaps[overlay.id];
454
455        switch (overlay.type) {
456            case 'osm':
457                m.addLayer(new ol.layer.Tile({
458                    title:   overlay.name,
459                    visible: (overlay.visible).toLowerCase() === 'true',
460                    opacity: parseFloat(overlay.opacity),
461                    source:  new ol.source.OSM({
462                        url:          overlay.url,
463                        crossOrigin:  'Anonymous',
464                        attributions: overlay.attribution
465                    })
466                }));
467                break;
468            case 'wms':
469                m.addLayer(new ol.layer.Image({
470                    title:   overlay.name,
471                    opacity: parseFloat(overlay.opacity),
472                    visible: (overlay.visible).toLowerCase() === 'true',
473                    source:  new ol.source.ImageWMS({
474                        url:          overlay.url,
475                        params:       {
476                            'LAYERS':      overlay.layers,
477                            'VERSION':     overlay.version,
478                            'TRANSPARENT': overlay.transparent,
479                            'FORMAT':      overlay.format
480                        },
481                        ratio:        1,
482                        crossOrigin:  'Anonymous',
483                        attributions: overlay.attribution
484                    })
485                }));
486                break;
487            case 'ags':
488                m.addLayer(new ol.layer.Image({
489                    title:   overlay.name,
490                    opacity: parseFloat(overlay.opacity),
491                    visible: (overlay.visible).toLowerCase() === 'true',
492                    source:  new ol.source.ImageArcGISRest({
493                        url:          overlay.url,
494                        params:       {
495                            'LAYERS':      overlay.layers,
496                            'TRANSPARENT': overlay.transparent,
497                            'FORMAT':      overlay.format
498                        },
499                        ratio:        1,
500                        crossOrigin:  'Anonymous',
501                        attributions: overlay.attribution
502                    })
503                }));
504                break;
505            // case 'mapillary':
506            //     var mUrl = 'http://api.mapillary.com/v1/im/search?';
507            //     if (overlay.skey !== '') {
508            //         mUrl = 'http://api.mapillary.com/v1/im/sequence?';
509            //     }
510            //     var mLyr = new OpenLayers.Layer.Vector(
511            //         "Mapillary", {
512            //             projection:  new OpenLayers.Projection("EPSG:4326"),
513            //             strategies:  [new OpenLayers.Strategy.BBOX({
514            //                 ratio:     1.1,
515            //                 resFactor: 1.5
516            //             }) /* ,new OpenLayers.Strategy.Cluster({}) */],
517            //             protocol:    new OpenLayers.Protocol.HTTP({
518            //                 url:            mUrl,
519            //                 format:         new OpenLayers.Format.GeoJSON(),
520            //                 params:         {
521            //                     // default to max. 250 locations
522            //                     'max-results': 250,
523            //                     'geojson':     true,
524            //                     'skey':        overlay.skey
525            //                 },
526            //                 filterToParams: function (filter, params) {
527            //                     if (filter.type === OpenLayers.Filter.Spatial.BBOX) {
528            //                         // override the bbox serialization of
529            //                         // the filter to give the Mapillary
530            //                         // specific bounds
531            //                         params['min-lat'] = filter.value.bottom;
532            //                         params['max-lat'] = filter.value.top;
533            //                         params['min-lon'] = filter.value.left;
534            //                         params['max-lon'] = filter.value.right;
535            //                         // if the width of our bbox width is
536            //                         // less than 0.15 degrees drop the max
537            //                         // results
538            //                         if (filter.value.top - filter.value.bottom < .15) {
539            //                             OpenLayers.Console.debug('dropping max-results parameter, width is: ',
540            //                                 filter.value.top - filter.value.bottom);
541            //                             params['max-results'] = null;
542            //                         }
543            //                     }
544            //                     return params;
545            //                 }
546            //             }),
547            //             styleMap:    new OpenLayers.StyleMap({
548            //                 'default': {
549            //                     cursor:          'help',
550            //                     rotation:        '${ca}',
551            //                     externalGraphic: DOKU_BASE + 'lib/plugins/openlayersmapoverlays/icons/arrow-up-20.png',
552            //                     graphicHeight:   20,
553            //                     graphicWidth:    20,
554            //                 },
555            //                 'select':  {
556            //                     externalGraphic: DOKU_BASE + 'lib/plugins/openlayersmapoverlays/icons/arrow-up-20-select.png',
557            //                     label:           '${location}',
558            //                     fontSize:        '1em',
559            //                     fontFamily:      'monospace',
560            //                     labelXOffset:    '0.5',
561            //                     labelYOffset:    '0.5',
562            //                     labelAlign:      'lb',
563            //                 }
564            //             }),
565            //             attribution: '<a href="http://www.mapillary.com/legal.html">' +
566            //                              '<img src="http://mapillary.com/favicon.ico" ' +
567            //                              'alt="Mapillary" height="16" width="16" />Mapillary (CC-BY-SA)',
568            //             visibility:  (overlay.visible).toLowerCase() == 'true',
569            //         });
570            //     m.addLayer(mLyr);
571            //     selectControl.addLayer(mLyr);
572            //     break;
573            // case 'search':
574            //     m.addLayer(new OpenLayers.Layer.Vector(
575            //         overlay.name,
576            //         overlay.url,
577            //         {
578            //             layers:      overlay.layers,
579            //             version:     overlay.version,
580            //             transparent: overlay.transparent,
581            //             format:      overlay.format
582            //         }, {
583            //             opacity:     parseFloat(overlay.opacity),
584            //             visibility:  (overlay.visible).toLowerCase() == 'true',
585            //             isBaseLayer: !1,
586            //             attribution: overlay.attribution
587            //         }
588            //     ));
589            //     break;
590        }
591    }
592}
593
594/** init. */
595function olInit() {
596    if (olEnable) {
597        // add info window to DOM
598        const frag = document.createDocumentFragment(),
599            temp = document.createElement('div');
600        temp.innerHTML = '<div id="popup" class="olPopup"><a href="#" id="popup-closer" class="olPopupCloseBox"></a><div id="popup-content"></div></div>';
601        while (temp.firstChild) {
602            frag.appendChild(temp.firstChild);
603        }
604        document.body.appendChild(frag);
605
606        let _i = 0;
607        // create the maps in the page
608        for (_i = 0; _i < olMapData.length; _i++) {
609            var _id = olMapData[_i].mapOpts.id;
610            olMaps[_id] = createMap(olMapData[_i].mapOpts, olMapData[_i].poi);
611
612            // set max-width on help pop-over
613            jQuery('#' + _id).parent().parent().find('.olMapHelp').css('max-width', olMapData[_i].mapOpts.width);
614
615            // shrink the map width to fit inside page container
616            var _w = jQuery('#' + _id + '-olContainer').parent().innerWidth();
617            if (parseInt(olMapData[_i].mapOpts.width) > _w) {
618                jQuery('#' + _id).width(_w);
619                jQuery('#' + _id + '-olStatusBar').width(_w);
620                jQuery('#' + _id).parent().parent().find('.olMapHelp').width(_w);
621                olMaps[_id].updateSize();
622            }
623        }
624
625        // add overlays
626        olovAddToMap();
627
628        let resizeTimer;
629        jQuery(window).on('resize', function (e) {
630            clearTimeout(resizeTimer);
631            resizeTimer = setTimeout(function () {
632                for (_i = 0; _i < olMapData.length; _i++) {
633                    var _id = olMapData[_i].mapOpts.id;
634                    var _w = jQuery('#' + _id + '-olContainer').parent().innerWidth();
635                    if (parseInt(olMapData[_i].mapOpts.width) > _w) {
636                        jQuery('#' + _id).width(_w);
637                        jQuery('#' + _id + '-olStatusBar').width(_w);
638                        jQuery('#' + _id).parent().parent().find('.olMapHelp').width(_w);
639                        olMaps[_id].updateSize();
640                    }
641                }
642            }, 250);
643        });
644
645        // hide the table(s) with POI by giving it a print-only style
646        jQuery('.olPOItableSpan').addClass('olPrintOnly');
647        // hide the static map image(s) by giving it a print only style
648        jQuery('.olStaticMap').addClass('olPrintOnly');
649        // add help button with toggle.
650        jQuery('.olWebOnly > .olMap')
651            .prepend(
652                '<div class="olMapHelpButtonDiv">'
653                + '<button onclick="jQuery(\'.olMapHelp\').toggle(500);" class="olMapHelpButton olHasTooltip"><span>'
654                + 'Show or hide help</span>?</button></div>');
655        // toggle to switch dynamic vs. static map
656        jQuery('.olMapHelp').before(
657            '<div class="a11y"><button onclick="jQuery(\'.olPrintOnly\').toggle();jQuery(\'.olWebOnly\').toggle();">'
658            + 'Hide or show the dynamic map</button></div>');
659    }
660}
661
662/**
663 * CSS support flag.
664 *
665 * @type {Boolean}
666 */
667let olCSSEnable = true;
668
669/* register olInit to run with onload event. */
670jQuery(olInit);
671