aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2025-10-17 00:26:39 +0200
committerGuilhem Moulin <guilhem@fripost.org>2025-10-18 14:10:14 +0200
commit354e8aa6abfcc37452c8bcf8bab2d481612bb750 (patch)
treef72d8f2e9fe57283c4373122311771e9e361dcac
parent7f74020caf44589bcd68a07378823848c6daea03 (diff)
Add an help modal with instructions how to use the map.
-rw-r--r--index.html143
-rw-r--r--main.js521
-rw-r--r--style.css66
3 files changed, 510 insertions, 220 deletions
diff --git a/index.html b/index.html
index b5d4381..b3a8748 100644
--- a/index.html
+++ b/index.html
@@ -53,6 +53,149 @@
</div>
</div>
</div>
+ <div class="modal" id="help-modal" tabindex="-1" aria-hidden="true">
+ <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="btn-close" data-bs-dismiss="modal" title="Stäng" aria-label="Stäng"></button>
+ </div>
+ <div id="help-body" class="modal-body">
+ <h3>Navigering</h3>
+ <p>Kartan är interaktiv och kontrolleras med musen. Tryck ner på
+ musens första knapp och dra kartan för att byta koordinater.</p>
+ <p>Zoomnivån kan styras med musens rullhjul, eller alternativt med
+ knapparna och skjutreglaget längst upp till vänster. Den aktuella
+ skalan visas längst ner till vänster på kartan.</p>
+ <p>Både koordinater och zoomnivå sparas i URL:en, så om du delar
+ URL:en med någon annan eller sparar den i din webbläsares bokmärken
+ kommer senare besök att landa på samma plats samt zoomnivå på
+ kartan.</p>
+ <p>Klickar du på ett objekt på kartan – exempelvis ett
+ undersökningstillstånd eller ett naturreservat (läs mer om lagerval
+ nedan) – så visar det valda objektet med en cyanfärgad kantlinje
+ samt dyker ett fönster upp med information om just det objektet.
+ Både informationen och geometrier kommer från ansvarig myndighet.
+ I fall det finns flera objekt i närheten av just platsen du klickade
+ på, så antalet träffar visas högst upp på informationsfönstret
+ (exempelvis ”<span class="fst-italic text-muted">Träff 1 av 3</span>”) och du
+ kan välja mellan dem genom att klicka på pilarna
+ <span class="popover-header">
+ <button class="popover-button popover-button-prev text-muted help-button"></button>/<button class="popover-button popover-button-next text-muted help-button"></button>
+ </span> bredvid.
+ Informationsfönstret kan flyttas runt om det står i vägen, samt att
+ det förstoras om rutan är för liten.</p>
+
+ <h3>Funktionsknappar</h3>
+ <p>Det finns ett antal funktionsknappar längst upp till
+ höger på kartan. Om du låter muspekaren vila på varje
+ knapp, dyker en hjälptext upp.</p>
+
+ <ol id='help-describe-functions'>
+ <li data-for-button='layer-selection-button'>
+ <p>Här kan du välja mellan olika lager att ha på kartan.
+ Det finns massor med lager, och de grupperas ihop så att
+ det är enkelt att välja hela gruppen på en gång. Klickar
+ du på pilen
+ <span class="accordion"><span class="accordion-button collapsed help-button text-muted"></span></span>
+ kan du istället välja ett specifikt lager.</p>
+ <p>Längst ned finns knappar där du kan välja att se
+ administrativa gränser, samt välja mellan nedtonad eller färgad
+ bakgrundskarta.</p>
+ <p>Lager, precis som koordinater och zoomnivå, sparas i URL:en.
+ Så om du delar URL:en med någon annan eller sparar den i din
+ webbläsares bokmärken kommer senare besök att landa på samma vy
+ (eventuellt med uppdaterat underlag).</p>
+ </li>
+
+ <li data-for-button='map-legend-button'>
+ <p>Här ser du symboler för alla lager som valts ut.</p>
+ </li>
+
+ <li data-for-button='measure-button'>
+ <p>Med det här verktyget kan du rita direkt på kartan och mäta
+ distanser eller ytor. Till exempel kan du lätt mäta avståndet
+ mellan en viss exploatering och ett skyddat område eller
+ kommungräns.</p>
+ </li>
+
+ <li data-for-button='age-filter-button'>
+ <p>Med det här verktyget kan du filtrera bort gamla
+ exploateringar, vilket underlättar övervakning. Det är bra att
+ kunna fokusera på nya exploateringen, då det annars snabbt kan
+ kännas överväldigande.</p>
+
+ <p>Om filtret är aktivt så blir knappen svart. Filterparametrar
+ sparas i URL:en, så en övervaknings-URL till ett specifik område
+ kan enkelt delas med andra eller bokmärkas.</p>
+ </li>
+
+ <li data-for-button='fullscreen-toggle'>
+ <p>Genom att klicka på knappen kan du aktivera eller inaktivera
+ helskärmsläget.</p>
+ <li>
+
+ <li data-for-button='export-to-image'>
+ <p>Genom att klicka på knappen kan du ladda ner den aktuella
+ kartvyn som en bild. Bilden kan då användas i en rapport eller
+ ett yttrande.</p>
+ </li>
+
+ <li data-for-button='info-button'>
+ <p>Här kan du se källor och licensvillkor för varje lager samt
+ själva kartverktyget.
+ För de flesta lager finns det också en produktlänk till den
+ ansvariga myndighetens webbplats.</p>
+ <p>Bredvid varje källa kan du se när den senast hämtades av
+ kartverktyget, samt när skikten generades på kartan (det vill
+ säga hur gammalt underlaget är på kartan).
+ Nedladdning samt uppdatering av underlag för kartan sker
+ automatiskt varje dag.</p>
+ </li>
+
+ <li data-for-button='help-button'>
+ <p>Om du klickar på den här knappen ser du det här
+ hjälpfönstret.</p>
+ </li>
+ </ol>
+
+ <h3>Målgrupp och syftet</h3>
+ <p class="mb-2">Kartan kan användas som en hjälp i att välja vilka
+ exploateringar som är viktigast att bekämpa. Målgruppen för kartan
+ är individer och organisationer som vill:</p>
+ <ol>
+ <li>se kumulativa effekter av olika exploateringar i
+ norr</li>
+ <li>se var en viss exploatering ligger i förhållande
+ till ett område av intresse (ex formellt skydd, höga
+ naturvärden, riksintresse med flera)</li>
+ <li>övervaka de senaste exploateringarna i ett visst
+ område.</li>
+ </ol>
+
+ <p>Viktigt att komma ihåg är att allt underlag i kartan
+ kommer från olika myndigheter (Bergsstaten, Skogsstyrelsen,
+ Länsstyrelsen, Naturvårdsverket med flera) och ingen modell
+ finns för att assistera i själva avvägningen.
+ Med andra ord så samlar kartan in befintliga underlag från
+ olika myndigheter istället för att själv presentera något
+ nytt, och den data kartan presenterar är därmed information
+ som kan ingå i rapporter och yttranden (och ingen kan hävda
+ att underlaget är påhittat).</p>
+
+ <p>Underlagen för kartan uppdateras automatiskt varje dag.
+ Se ovan för detaljer om hur gammalt varje underlag är.</p>
+
+ <h3>Buggrapporter och feedback</h3>
+ <p>Tveka inte att skicka ett
+ <a href="mailto:Z3VpbGhlbQ __AT__ ZnJpcG9zdA __DOT__ b3Jn" target="_blank" class="link-secondary email-address-b64">mejl
+ <i class="bi bi-envelope-at"></i></a>
+ med önskemål, buggrapporter, förslag till
+ förbättring med flera.
+ Kartverktyget samt tillhörande verktyg är alla fri programvara.</p>
+ </div>
+ </div>
+ </div>
+ </div>
<script type="module" src="./main.js"></script>
<noscript>Den här sidan kräver JavaScript men din webbläsare stöder inte det (eller så är skript blockerade).</noscript>
</body>
diff --git a/main.js b/main.js
index 59f6143..93edd14 100644
--- a/main.js
+++ b/main.js
@@ -168,11 +168,16 @@ const CONTAINER_STOPEVENT = MAP.getViewport().getElementsByClassName('ol-overlay
CONTAINER_STOPEVENT.appendChild(document.getElementById('zoom-control'));
CONTAINER_STOPEVENT.appendChild(CONTAINER_MAP);
CONTAINER_STOPEVENT.appendChild(document.getElementById('info-modal'));
+ CONTAINER_STOPEVENT.appendChild(document.getElementById('help-modal'));
const info_backdrop = document.createElement('div');
CONTAINER_STOPEVENT.appendChild(info_backdrop);
info_backdrop.id = 'info-modal-backdrop';
+ const help_backdrop = document.createElement('div');
+ CONTAINER_STOPEVENT.appendChild(help_backdrop);
+ help_backdrop.id = 'help-modal-backdrop';
+
const age_filter = document.createElement('div');
age_filter.id = 'age-filter-modal';
age_filter.classList.add('modal');
@@ -340,6 +345,7 @@ if (window.location === window.parent.location) {
btn.classList.add('btn', classInactive);
btn.setAttribute('aria-label', btn.title);
MAP.addControl(control);
+ control.element.id = 'fullscreen-toggle'; /* for the help dialog */
control.addEventListener('enterfullscreen', function() {
/* dispose popover as entering fullscreen messes up its position */
@@ -423,260 +429,341 @@ if (window.location === window.parent.location) {
};
}
-const decodeEmailAddress = function(element) {
- const MAILTO = 'mailto:';
- const CLASSNAME = 'email-address-b64';
- for (const a of element.getElementsByClassName(CLASSNAME)) {
- if (a.tagName.toLowerCase() === 'a' && a.href.toLowerCase().startsWith(MAILTO)) {
- let href = MAILTO;
- for (const part of a.href.substr(MAILTO.length).split(/\s+/)) {
- switch (part) {
- case '__AT__':
- href += '@';
- break;
- case '__DOT__':
- href += '.';
- break;
- default:
- href += atob(part);
- }
- }
- a.href = href;
- a.classList.remove(CLASSNAME);
- }
- }
-};
-
-/* info button */
+/* info and help buttons */
(function() {
- const div = document.createElement('div');
- MENU.appendChild(div);
- div.id = 'info-button';
- div.classList.add('ol-unselectable', 'ol-control');
+ const add_button = function(x) {
+ const div = document.createElement('div');
+ MENU.appendChild(div);
+ div.id = x.id + '-button';
+ div.classList.add('ol-unselectable', 'ol-control');
- const btn = document.createElement('button');
- div.appendChild(btn);
- btn.type = 'button';
- btn.setAttribute('aria-expanded', 'false');
- btn.title = 'Källor och licensinformation';
- btn.setAttribute('aria-label', btn.title);
- btn.classList.add('btn', 'btn-light');
+ const btn = document.createElement('button');
+ div.appendChild(btn);
+ btn.type = 'button';
+ btn.setAttribute('aria-expanded', 'false');
+ btn.title = x.title;
+ btn.setAttribute('aria-label', btn.title);
+ btn.classList.add('btn', 'btn-light');
- const i = document.createElement('i');
- btn.appendChild(i);
- i.classList.add('bi', 'bi-info-lg');
+ const i = document.createElement('i');
+ btn.appendChild(i);
+ i.classList.add('bi', 'bi-' + x.bi);
- const panel = document.getElementById('info-modal');
- const modal = new Modal(panel, {
- backdrop: false,
- });
- decodeEmailAddress(panel);
+ const panel = document.getElementById(x.id + '-modal');
+ const modal = new Modal(panel, {
+ backdrop: false,
+ });
- const backdrop = document.getElementById('info-modal-backdrop');
- backdrop.onclick = function() {
- modal.hide();
- };
+ const backdrop = document.getElementById(x.id + '-modal-backdrop');
+ backdrop.onclick = function() {
+ modal.hide();
+ };
- panel.addEventListener('show.bs.modal', function() {
- backdrop.classList.add('modal-backdrop', 'show');
- btn.setAttribute('aria-expanded', 'true');
- btn.classList.replace('btn-light', 'btn-dark');
- });
- panel.addEventListener('hide.bs.modal', function() {
- /* XXX workaround for https://github.com/twbs/bootstrap/issues/41005#issuecomment-2585390544 */
- const activeElement = document.activeElement;
- if (activeElement instanceof HTMLElement) {
- activeElement.blur();
- }
- });
- panel.addEventListener('hidden.bs.modal', function() {
- btn.classList.replace('btn-dark', 'btn-light');
- btn.setAttribute('aria-expanded', 'false');
- backdrop.classList.remove('modal-backdrop', 'show');
- infoMetadataAccordions.forEach(function(x, idx) {
- /* collapse all accordions */
- const body = x.element.parentNode.parentNode;
- const name = 'info-accordion-collapse-' + idx;
- if (body.id === name) {
- body.classList.remove('show');
+ panel.addEventListener('show.bs.modal', function() {
+ backdrop.classList.add('modal-backdrop', 'show');
+ btn.setAttribute('aria-expanded', 'true');
+ btn.classList.replace('btn-light', 'btn-dark');
+ });
+ panel.addEventListener('hide.bs.modal', function() {
+ /* XXX workaround for https://github.com/twbs/bootstrap/issues/41005#issuecomment-2585390544 */
+ const activeElement = document.activeElement;
+ if (activeElement instanceof HTMLElement) {
+ activeElement.blur();
}
- if (body.parentNode !== null) {
- const headers = body.parentNode.getElementsByClassName('accordion-header');
- for (let i = 0; i < headers.length; i++) {
- const buttons = headers[i].getElementsByClassName('accordion-button');
- for (let j = 0; j < buttons.length; j++) {
- const btn = buttons[j];
- if (btn.getAttribute('data-bs-target') === '#' + name) {
- btn.setAttribute('aria-expanded', 'false');
- btn.classList.add('collapsed');
- }
+ });
+
+ panel.addEventListener('hidden.bs.modal', function() {
+ btn.classList.replace('btn-dark', 'btn-light');
+ btn.setAttribute('aria-expanded', 'false');
+ backdrop.classList.remove('modal-backdrop', 'show');
+ });
+
+ btn.onclick = function() {
+ modal.show();
+ };
+
+ /* de-obfuscate email address */
+ const MAILTO = 'mailto:';
+ const CLASSNAME = 'email-address-b64';
+ for (const a of panel.getElementsByClassName(CLASSNAME)) {
+ if (a.tagName.toLowerCase() === 'a' && a.href.toLowerCase().startsWith(MAILTO)) {
+ let href = MAILTO;
+ for (const part of a.href.substr(MAILTO.length).split(/\s+/)) {
+ switch (part) {
+ case '__AT__':
+ href += '@';
+ break;
+ case '__DOT__':
+ href += '.';
+ break;
+ default:
+ href += atob(part);
}
}
+ a.href = href;
+ a.classList.remove(CLASSNAME);
}
+ }
+
+ return [panel, btn, modal];
+ };
+
+ /* info button */
+ (function() {
+ const [panel, btn, modal] = add_button({
+ id: 'info',
+ title: 'Källor och licensinformation',
+ bi: 'info-lg',
});
- });
- const dateFormatter = new Intl.DateTimeFormat(LOCALE);
- btn.onclick = function() {
- infoMetadataAccordions.forEach((x) => x.element.replaceChildren());
- modal.show();
- Promise.allSettled(Object.entries(mapLayers).map(function([grp,lyr]) {
- const baseurl = lyr?.getSource?.()?.get?.('baseurl');
- if (baseurl == null) {
- return new Promise(() => { throw new Error(`Unknown source for "${grp}"`); });
- }
- return fetch(new URL('metadata.json', baseurl))
- .then(function(resp0) {
- if (resp0.status === 200) {
- return resp0.json().then((x) => [grp,x]);
- } else {
- throw new Error(`${resp0.url} [${resp0.status}]`);
- }
- });
- }))
- .then(function(rs) {
- const metadata = Object.fromEntries(rs.filter(function(r) {
- if (r.status === 'fulfilled') {
- return true;
- } else if (r.status === 'rejected') {
- console.log(r.reason);
- }
- return false;
- }).map((r) => r.value));
-
- infoMetadataAccordions.forEach(function(x) {
- const ul = x.element;
- const groupnames = new Set();
- const last_updated = [];
- x.items.forEach(function([groupname]) {
- const layer_group = metadata[groupname];
- if (layer_group == null) {
- return;
- }
- if (!groupnames.has(groupname)) {
- groupnames.add(groupname);
- if (layer_group.last_updated != null) {
- last_updated.push(layer_group.last_updated);
+ panel.addEventListener('hidden.bs.modal', function() {
+ infoMetadataAccordions.forEach(function(x, idx) {
+ /* collapse all accordions */
+ const body = x.element.parentNode.parentNode;
+ const name = 'info-accordion-collapse-' + idx;
+ if (body.id === name) {
+ body.classList.remove('show');
+ }
+ if (body.parentNode !== null) {
+ const headers = body.parentNode.getElementsByClassName('accordion-header');
+ for (let i = 0; i < headers.length; i++) {
+ const buttons = headers[i].getElementsByClassName('accordion-button');
+ for (let j = 0; j < buttons.length; j++) {
+ const btn = buttons[j];
+ if (btn.getAttribute('data-bs-target') === '#' + name) {
+ btn.setAttribute('aria-expanded', 'false');
+ btn.classList.add('collapsed');
}
}
- });
- if (last_updated.length > 0) {
- /* show creation time of the MVT layers */
- const li = document.createElement('li');
- li.classList.add('list-group-item', 'text-muted');
- ul.appendChild(li);
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-map');
- li.appendChild(i);
- const t = document.createTextNode(
- ' Lokalt skikt (vectiler) genererades ' +
- last_updated
- .sort()
- .map((ts) => dateFormatter.format(new Date(ts)))
- .join('; ') + '.'
- );
- li.appendChild(t);
}
+ }
+ });
+ });
- const source_files = new Set();
- x.items.forEach(function([groupname, layername]) {
- /* for each source file associated with the accordion header, show copyright, license and timing information */
- const layer_group = metadata[groupname];
- if (layer_group?.layers == null || layer_group?.source_files == null) {
- return;
+ const dateFormatter = new Intl.DateTimeFormat(LOCALE);
+ btn.onclick = function() {
+ infoMetadataAccordions.forEach((x) => x.element.replaceChildren());
+ modal.show();
+ Promise.allSettled(Object.entries(mapLayers).map(function([grp,lyr]) {
+ const baseurl = lyr?.getSource?.()?.get?.('baseurl');
+ if (baseurl == null) {
+ return new Promise(() => { throw new Error(`Unknown source for "${grp}"`); });
+ }
+ return fetch(new URL('metadata.json', baseurl))
+ .then(function(resp0) {
+ if (resp0.status === 200) {
+ return resp0.json().then((x) => [grp,x]);
+ } else {
+ throw new Error(`${resp0.url} [${resp0.status}]`);
}
- const def = layer_group.layers[layername];
- if (def?.source_files == null) {
- return;
+ });
+ }))
+ .then(function(rs) {
+ const metadata = Object.fromEntries(rs.filter(function(r) {
+ if (r.status === 'fulfilled') {
+ return true;
+ } else if (r.status === 'rejected') {
+ console.log(r.reason);
}
- def.source_files.forEach(function(source_file) {
- if (source_files.has(source_file)) {
+ return false;
+ }).map((r) => r.value));
+
+ infoMetadataAccordions.forEach(function(x) {
+ const ul = x.element;
+ const groupnames = new Set();
+ const last_updated = [];
+ x.items.forEach(function([groupname]) {
+ const layer_group = metadata[groupname];
+ if (layer_group == null) {
return;
}
- const x = layer_group.source_files[source_file];
- source_files.add(source_file);
-
+ if (!groupnames.has(groupname)) {
+ groupnames.add(groupname);
+ if (layer_group.last_updated != null) {
+ last_updated.push(layer_group.last_updated);
+ }
+ }
+ });
+ if (last_updated.length > 0) {
+ /* show creation time of the MVT layers */
const li = document.createElement('li');
- li.classList.add('list-group-item');
+ li.classList.add('list-group-item', 'text-muted');
ul.appendChild(li);
- const h = document.createElement('h6');
- li.appendChild(h);
- if (x.description != null) {
- const t = document.createTextNode(x.description);
- h.appendChild(t);
- }
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-map');
+ li.appendChild(i);
+ const t = document.createTextNode(
+ ' Lokalt skikt (vectiler) genererades ' +
+ last_updated
+ .sort()
+ .map((ts) => dateFormatter.format(new Date(ts)))
+ .join('; ') + '.'
+ );
+ li.appendChild(t);
+ }
- if (x.copyright != null) {
- const p = document.createElement('p');
- li.appendChild(p);
- const t = document.createTextNode(x.copyright);
- p.appendChild(t);
+ const source_files = new Set();
+ x.items.forEach(function([groupname, layername]) {
+ /* for each source file associated with the accordion header, show copyright, license and timing information */
+ const layer_group = metadata[groupname];
+ if (layer_group?.layers == null || layer_group?.source_files == null) {
+ return;
}
+ const def = layer_group.layers[layername];
+ if (def?.source_files == null) {
+ return;
+ }
+ def.source_files.forEach(function(source_file) {
+ if (source_files.has(source_file)) {
+ return;
+ }
+ const x = layer_group.source_files[source_file];
+ source_files.add(source_file);
+
+ const li = document.createElement('li');
+ li.classList.add('list-group-item');
+ ul.appendChild(li);
+ const h = document.createElement('h6');
+ li.appendChild(h);
+ if (x.description != null) {
+ const t = document.createTextNode(x.description);
+ h.appendChild(t);
+ }
- if (x.license != null) {
- const p = document.createElement('p');
- li.appendChild(p);
- p.appendChild(document.createTextNode('Licensvillkor: '));
- const t = document.createTextNode(x.license.name);
- if (x.license.url == null) {
+ if (x.copyright != null) {
+ const p = document.createElement('p');
+ li.appendChild(p);
+ const t = document.createTextNode(x.copyright);
p.appendChild(t);
- } else {
+ }
+
+ if (x.license != null) {
+ const p = document.createElement('p');
+ li.appendChild(p);
+ p.appendChild(document.createTextNode('Licensvillkor: '));
+ const t = document.createTextNode(x.license.name);
+ if (x.license.url == null) {
+ p.appendChild(t);
+ } else {
+ const a = document.createElement('a');
+ a.href = x.license.url;
+ a.target = '_blank';
+ a.appendChild(t);
+ p.appendChild(a);
+ }
+ }
+
+ if (x.product_url != null) {
+ const p = document.createElement('p');
+ li.appendChild(p);
+ const t = document.createTextNode('Produktlänk ');
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-box-arrow-up-right');
const a = document.createElement('a');
- a.href = x.license.url;
+ a.href = x.product_url;
a.target = '_blank';
a.appendChild(t);
+ a.appendChild(i);
p.appendChild(a);
}
- }
-
- if (x.product_url != null) {
- const p = document.createElement('p');
- li.appendChild(p);
- const t = document.createTextNode('Produktlänk ');
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- const a = document.createElement('a');
- a.href = x.product_url;
- a.target = '_blank';
- a.appendChild(t);
- a.appendChild(i);
- p.appendChild(a);
- }
- if (x.last_modified != null) {
- const p = document.createElement('p');
- p.classList.add('small', 'text-muted');
- li.appendChild(p);
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-file-earmark-code');
- p.appendChild(i);
- p.appendChild(document.createTextNode(' '));
- const t0 = document.createTextNode('Källfil');
- if (x.url == null) {
- p.appendChild(t0);
- } else {
- const a = document.createElement('a');
- p.appendChild(a);
+ if (x.last_modified != null) {
+ const p = document.createElement('p');
+ p.classList.add('small', 'text-muted');
+ li.appendChild(p);
const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(t0);
- a.appendChild(document.createTextNode(' '));
- a.appendChild(i);
- a.href = x.url;
- a.target = '_blank';
+ i.classList.add('bi', 'bi-file-earmark-code');
+ p.appendChild(i);
+ p.appendChild(document.createTextNode(' '));
+ const t0 = document.createTextNode('Källfil');
+ if (x.url == null) {
+ p.appendChild(t0);
+ } else {
+ const a = document.createElement('a');
+ p.appendChild(a);
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-box-arrow-up-right');
+ a.appendChild(t0);
+ a.appendChild(document.createTextNode(' '));
+ a.appendChild(i);
+ a.href = x.url;
+ a.target = '_blank';
+ }
+ const t1 = document.createTextNode(' ändrades senast ');
+ p.appendChild(t1);
+ const td = document.createTextNode(dateFormatter.format(new Date(x.last_modified)));
+ p.appendChild(td);
+ const t2 = document.createTextNode('.');
+ p.appendChild(t2);
}
- const t1 = document.createTextNode(' ändrades senast ');
- p.appendChild(t1);
- const td = document.createTextNode(dateFormatter.format(new Date(x.last_modified)));
- p.appendChild(td);
- const t2 = document.createTextNode('.');
- p.appendChild(t2);
- }
+ });
});
});
});
- });
- };
+ };
+ })();
+
+ /* help button */
+ (function() {
+ const [panel] = add_button({
+ id: 'help',
+ title: 'Hjälp med att använda kartan',
+ bi: 'question-circle',
+ });
+
+ /* Use the text from the .html file but ensure that buttons are
+ * listed in the same order as the menu, and spell titles out. This
+ * avoids duplication and avoids that things would get out of sync */
+ const button_map = {};
+ const ol = panel.querySelector('#help-describe-functions');
+ if (ol != null && ol.tagName.toLowerCase() === 'ol') {
+ for (const li of ol.children) {
+ const id = li.getAttribute('data-for-button');
+ if (id == null || id === '') {
+ continue;
+ }
+ button_map[id] = li;
+ }
+ }
+
+ for (const node of MENU.children) {
+ if (node.id == null || node.id === '') {
+ continue
+ }
+ const btn = node.getElementsByTagName('button')[0];
+ if (btn == null || btn.tagName.toLowerCase() !== 'button') {
+ continue;
+ }
+ const btn2 = btn.cloneNode(true);
+ const title = btn2.title;
+ btn2.id = btn2.title = '';
+ for (const attr of btn.attributes) {
+ if (attr.name.toLowerCase().startsWith('aria-') || attr.name === 'id' || attr.name === 'title') {
+ btn2.removeAttribute(attr.name);
+ }
+ }
+
+ const h = document.createElement('h6');
+ h.classList.add('help-button-description');
+ h.appendChild(btn2);
+ if (title != null && title != '') {
+ const t = document.createTextNode(title)
+ h.appendChild(t);
+ }
+ btn2.classList.add('help-button');
+
+ ol.insertAdjacentElement('beforebegin', h);
+
+ const li = button_map[node.id];
+ if (li != null) {
+ /* move <li>'s children (paragraphs) to the main text */
+ while (li.children.length > 0) {
+ ol.insertAdjacentElement('beforebegin', li.firstElementChild);
+ }
+ }
+ }
+ ol.remove();
+ })();
})();
/* we're all set, show the control container now */
diff --git a/style.css b/style.css
index fb413d7..8bb4033 100644
--- a/style.css
+++ b/style.css
@@ -157,7 +157,7 @@ body.inprogress {
border: none;
outline: none; /* eslint-disable-line css/use-baseline */
}
-.ol-control button.btn {
+.ol-control button.btn, #help-body button.btn.help-button {
--bs-btn-padding-x: 0.5rem;
--bs-btn-padding-y: 0.5rem;
display: block;
@@ -299,7 +299,7 @@ body.inprogress {
--bs-btn-padding-y: 0.4rem;
}
}
-#info-modal {
+#info-modal, #help-modal {
/* close the modal when clicking the backdrop */
pointer-events: none;
-webkit-user-select: text;
@@ -310,7 +310,12 @@ body.inprogress {
--modal-info-padding-y: .5rem;
--modal-info-bg-light: rgba(0, 0, 0, .08);
}
-#info-modal .modal-header {
+#help-modal {
+ --modal-info-padding-x: 1rem;
+ --modal-info-padding-y: 1rem;
+}
+#info-modal .modal-header,
+#help-modal .modal-header {
padding: var(--modal-info-padding-y) var(--modal-info-padding-x);
}
#info-modal .list-group-item,
@@ -383,6 +388,61 @@ body.inprogress {
padding: var(--modal-info-padding-y) var(--modal-info-padding-x) 0 var(--modal-info-padding-x);
margin: 0;
}
+#help-body {
+ padding: var(--modal-info-padding-y) var(--modal-info-padding-x);
+ hyphens: auto; /* eslint-disable-line css/use-baseline */
+}
+#help-body .popover-header {
+ -webkit-user-select: inherit;
+ -moz-user-select: inherit;
+ user-select: inherit; /* eslint-disable-line css/use-baseline */
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+#help-body .popover-header .popover-button {
+ height: var(--bs-body-font-size);
+ width: var(--bs-body-font-size);
+ padding: 0;
+ border: none;
+ vertical-align: middle;
+}
+#help-body button.btn.help-button {
+ --bs-btn-padding-x: .1rem;
+ --bs-btn-padding-y: .1rem;
+ width: 1.75rem;
+ height: 1.75rem;
+ display: inline-block;
+ font-size: var(--bs-body-font-size);
+ font-weight: var(--bs-body-font-weight);
+ line-height: var(--bs-body-line-height);
+ margin: 0;
+}
+#help-body .help-button {
+ pointer-events: none;
+}
+#help-body .accordion .accordion-button {
+ display: inline;
+ padding: 0;
+ opacity: .75;
+}
+#help-body .accordion .accordion-button::after {
+ display: inline-block;
+ margin: 0;
+ height: 1rem;
+ vertical-align: middle;
+}
+#help-body .help-button-description > button.btn.help-button {
+ margin-right: .5rem;
+}
+#help-body p:last-child {
+ margin-bottom: 0;
+}
+@media (min-width: 992px) {
+ #help-body {
+ text-align: justify;
+ }
+}
.ol-overlaycontainer-stopevent .modal-backdrop.show {
pointer-events: auto;