-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
286 lines (261 loc) · 16 KB
/
Copy pathindex.html
File metadata and controls
286 lines (261 loc) · 16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Virelia - Interactive Map</title>
<meta name="description" content="An interactive map of Virelia: twelve regions, their cities and ruins, with the full illustrated history, races and factions — read on the page or aloud.">
<meta name="author" content="Firk1n">
<meta name="theme-color" content="#6d746b">
<!-- Pasted into a chat, the bare URL said nothing about what it was. -->
<meta property="og:type" content="website">
<meta property="og:site_name" content="Virelia">
<meta property="og:title" content="Virelia - An Interactive Map and Encyclopedia">
<meta property="og:description" content="Twelve regions, their cities and ruins, with the full illustrated history, races and factions — read on the page or aloud.">
<meta property="og:url" content="https://vireliamap.vercel.app/">
<meta property="og:image" content="https://vireliamap.vercel.app/assets/social-card.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="The painted world map of Virelia, its regions named.">
<meta name="twitter:card" content="summary_large_image">
<!-- The map marker, so the tab is findable at a glance. Inline because one
circle is not worth a request. -->
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='7' fill='%236d746b'/><circle cx='16' cy='16' r='8.5' fill='%2396373f' stroke='%23fdf6e3' stroke-width='3'/></svg>">
<link rel="apple-touch-icon" href="assets/social-card.jpg">
<link rel="manifest" href="manifest.webmanifest">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Icons, defined once and referenced with <use>. The wiki's mark appears
both on the map button and beside every entry title, and the two have
to be the same drawing or the second one means nothing. Feather icons
(MIT), which suit the map's line-drawn hand. -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon-sprite" aria-hidden="true" focusable="false">
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle><path d="M21 21l-4.35-4.35"></path>
</symbol>
<symbol id="icon-wiki" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
</symbol>
<symbol id="icon-audio" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 18v-6a9 9 0 0 1 18 0v6"></path>
<path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"></path>
</symbol>
<symbol id="icon-map" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
<circle cx="12" cy="10" r="3"></circle>
</symbol>
<symbol id="icon-book" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path>
</symbol>
<symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 6h18M3 12h18M3 18h18"></path>
</symbol>
<symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<path d="M7 10l5 5 5-5M12 15V3"></path>
</symbol>
</svg>
<div id="map"></div>
<!-- Search stays on the map, since it is the one reached for mid-session;
everything else folds into the menu beside it. A disclosure rather than
an ARIA menu: plain buttons in a panel, reached with Tab. -->
<div class="map-buttons">
<div class="map-menu">
<button type="button" id="open-menu" class="map-button map-menu-toggle"
aria-expanded="false" aria-controls="map-menu" aria-label="Menu" title="Menu">
<svg class="map-button-icon" aria-hidden="true"><use href="#icon-menu"></use></svg></button>
<div id="map-menu" class="map-menu-panel" hidden>
<button type="button" id="open-reader" class="map-menu-item" title="Read the wiki">
<svg class="map-button-icon" aria-hidden="true"><use href="#icon-wiki"></use></svg>Wiki</button>
<button type="button" id="open-book" class="map-menu-item" title="Open the book">
<svg class="map-button-icon" aria-hidden="true"><use href="#icon-book"></use></svg>Book</button>
<button type="button" id="open-offline" class="map-menu-item" title="Download for offline use" hidden>
<svg class="map-button-icon" aria-hidden="true"><use href="#icon-download"></use></svg>Offline</button>
</div>
</div>
<button type="button" id="open-search" class="map-button" title="Search Virelia (/)">
<svg class="map-button-icon" aria-hidden="true"><use href="#icon-search"></use></svg>Search</button>
</div>
<div id="offline-dialog" hidden role="dialog" aria-modal="true" aria-label="Download Virelia">
<div class="offline-card"><div class="offline-message">Download the website to make it available offline.</div><button id="offline-close" class="offline-close" type="button" aria-label="Close"><span>×</span></button><p id="offline-progress" hidden></p><div class="offline-bottom"><p class="offline-size-label">Current size: <strong id="offline-size"></strong></p><div class="offline-actions"><button id="offline-download" type="button">Continue</button><button id="offline-cancel" type="button">Cancel</button></div></div></div>
</div>
<div id="sidebar">
<!-- Back mirrors the close control across the top of the panel: the two
are what navigates the panel, so they belong as a pair rather than
one of them living among the audio controls. It carries only an
arrow -- the place it returns to is in its tooltip and its
accessible name, where it costs no room -- and it is absent, not
greyed, when there is nowhere to go back to. -->
<button type="button" id="entry-back" hidden aria-label="Back"><span class="entry-back-glyph">‹</span></button>
<span class="close-btn" onclick="closeSidebar()"><span class="close-x">×</span></span>
<div id="sidebar-content">
<h2>Welcome</h2>
<p>Click a location on the map to begin.</p>
</div>
<!-- One row, and only the player: Back moved to the top of the panel
and the speed slider became a button, which between them freed the
width that used to push this onto a second line. -->
<div id="entry-bar" hidden>
<div class="entry-bar-controls">
<button type="button" id="narration-prev" class="entry-bar-btn narration-jump"
aria-label="Go to the beginning of this paragraph" title="Previous paragraph" disabled>↶</button>
<button type="button" id="narration-play" class="entry-bar-btn narration-play" aria-label="Read this entry aloud" disabled>▶</button>
<button type="button" id="narration-next" class="entry-bar-btn narration-jump"
aria-label="Skip to the next paragraph" title="Next paragraph" disabled>↷</button>
<input type="range" id="narration-seek" min="0" max="1000" value="0" step="1"
aria-label="Seek within the narration" disabled>
<span id="narration-time" class="narration-time">0:00 / --:--</span>
<span class="narration-speed-wrap">
<button type="button" id="narration-speed" class="entry-bar-btn narration-speed"
aria-haspopup="true" aria-expanded="false" aria-controls="narration-speed-popover"
aria-label="Playback speed 1×" title="Playback speed">1×</button>
<div id="narration-speed-popover" class="narration-speed-popover" hidden>
<output id="narration-speed-value" for="narration-speed-range">1×</output>
<input type="range" id="narration-speed-range" min="1" max="3" value="1" step="0.1"
orient="vertical" aria-label="Narration speed">
</div>
</span>
</div>
<!-- Folds the player away for anyone reading rather than listening.
One control for both surfaces, because there is only one bar
and it moves to whichever of them is showing the prose. -->
<button type="button" id="narration-toggle" class="narration-toggle"
aria-expanded="true" aria-controls="entry-bar" title="Hide the audio player">
<svg aria-hidden="true"><use href="#icon-audio"></use></svg>
</button>
<span id="narration-status" class="narration-status" role="status" aria-live="polite"></span>
</div>
</div>
<div id="lightbox" hidden role="dialog" aria-modal="true" aria-label="Image viewer">
<button type="button" id="lightbox-close" class="lightbox-close" aria-label="Close image">×</button>
<img id="lightbox-img" alt="">
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<!-- Data. wiki-data.js is hand-maintained map metadata; the generated files
come from scripts/build-wiki.mjs and scripts/build-region-bounds.mjs and
are merged in, never substituted. Order matters: wiki-runtime.js does
the merging and must see them all.
Two things deliberately absent here:
generated/book.js (417 KB) is fetched by reader.js on first open --
it is the same prose again, for a view most visitors never open; and
region-geometry.js (832 KB) is only needed to *edit* borders, so the
page gets generated/region-bounds.js, the twelve boxes it reads. -->
<script src="wiki-data.js"></script>
<script src="generated/wiki-overrides.js"></script>
<script src="generated/region-bounds.js"></script>
<script src="region-overlays.js"></script>
<script src="wiki-runtime.js"></script>
<script src="main.js"></script>
<script src="regions.js"></script>
<script src="narration.js"></script>
<script src="reader.js"></script>
<script src="book.js"></script>
<script src="search.js"></script>
<script src="generated/offline-manifest.js"></script>
<script src="offline.js"></script>
<script>
// --- LIGHTBOX ---
// Entry and book images open here. It is a modal, so it behaves like the
// other two: Escape closes it, focus goes in and comes back, and the page
// behind it does not scroll away underneath.
(function () {
'use strict';
var box = document.getElementById('lightbox');
var img = document.getElementById('lightbox-img');
var closeButton = document.getElementById('lightbox-close');
var opener = null;
window.showLightbox = function (src, alt) {
opener = document.activeElement;
img.src = src;
// Without this the image is announced as the whole dialog's
// content and a screen reader has nothing to say about it.
img.alt = alt || 'Enlarged view';
box.hidden = false;
document.body.classList.add('lightbox-open');
closeButton.focus();
};
window.closeLightbox = function () {
if (box.hidden) return;
box.hidden = true;
document.body.classList.remove('lightbox-open');
img.removeAttribute('src');
if (opener && opener.focus) opener.focus();
opener = null;
};
// Clicking the backdrop closes; clicking the image itself does not,
// so a mis-drag while looking at a map does not dismiss it.
box.addEventListener('click', function (event) {
if (event.target === box) closeLightbox();
});
closeButton.addEventListener('click', closeLightbox);
box.addEventListener('keydown', function (event) {
if (event.key === 'Escape') { event.preventDefault(); closeLightbox(); }
// Two focusable things, one of which is the image; keep Tab here.
if (event.key === 'Tab') { event.preventDefault(); closeButton.focus(); }
});
})();
// --- MAP MENU ---
(function () {
'use strict';
var toggle = document.getElementById('open-menu');
var panel = document.getElementById('map-menu');
function setOpen(open) {
panel.hidden = !open;
toggle.setAttribute('aria-expanded', String(open));
}
toggle.addEventListener('click', function () { setOpen(panel.hidden); });
// Capture phase, so this runs before the item's own handler: the
// reader, book and offline dialogs remember document.activeElement to
// hand focus back on close, and the item is hidden by then. Moving
// focus to the toggle first gives them somewhere real to return to.
panel.addEventListener('click', function (event) {
if (!event.target.closest('.map-menu-item')) return;
toggle.focus();
setOpen(false);
}, true);
panel.addEventListener('keydown', function (event) {
if (event.key === 'Escape') { event.preventDefault(); toggle.focus(); setOpen(false); }
});
toggle.addEventListener('keydown', function (event) {
if (event.key === 'Escape' && !panel.hidden) { event.preventDefault(); setOpen(false); }
});
// A click or a Tab anywhere else puts it away.
var menu = toggle.parentNode;
document.addEventListener('pointerdown', function (event) {
if (!panel.hidden && !menu.contains(event.target)) setOpen(false);
});
menu.addEventListener('focusout', function (event) {
if (event.relatedTarget && !menu.contains(event.relatedTarget)) setOpen(false);
});
})();
document.getElementById('entry-back').addEventListener('click', function () {
// Back is the History API's back, not a private stack: the sidebar
// control and the browser's own button must do the same thing.
history.back();
});
// Edit mode is for the author, not the reader: 34 KB of tooling plus 832 KB
// of traced geometry are fetched only when ?edit asks for them.
if (/[?&]edit(=|&|$)/.test(window.location.search)) {
// async=false keeps injected scripts in order, so edit.js still finds
// the geometry loaded when it runs.
['region-geometry.js', 'edit.js'].forEach(function (src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.body.appendChild(script);
});
}
</script>
</body>
</html>