<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>sl-map-web</title>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body>
<header>
<div class="topbar">
<h1>sl-map-web</h1>
<div class="topbar-user">
<span id="logged-in-as"></span>
<button type="button" id="logout-button" class="topbar-logout hidden">
Logout
</button>
</div>
</div>
<nav class="primary-nav">
<a href="/" class="active">Render</a>
<a href="/library">Library</a>
<a href="/groups">Groups</a>
<a href="/invitations" id="invitations-link">Invitations</a>
<a href="/profile">Profile</a>
</nav>
<p>Render Second Life maps from a grid rectangle or a USB notecard.</p>
</header>
<section class="shared-params">
<h2>Output settings</h2>
<div class="row">
<label class="grow">
Save render to
<select id="save_to">
<option value="personal" selected>Personal library</option>
</select>
</label>
</div>
<div class="row">
<label
>Max width (px)
<input type="number" id="max_width" value="2048" min="64" step="64" />
</label>
<label
>Max height (px)
<input
type="number"
id="max_height"
value="2048"
min="64"
step="64"
/>
</label>
<label
>Format
<select id="format">
<option value="png" selected>PNG</option>
<option value="jpeg">JPEG</option>
</select>
</label>
</div>
<div class="row">
<label class="color-toggle">
<input type="checkbox" id="missing_map_tile_enabled" />
Fill missing map tiles:
<input
type="color"
id="missing_map_tile_color"
value="#19485a"
disabled
/>
</label>
<label class="color-toggle">
<input type="checkbox" id="missing_region_enabled" />
Fill missing regions:
<input
type="color"
id="missing_region_color"
value="#000000"
disabled
/>
</label>
</div>
<p class="note">
Enabling "Fill missing regions" significantly increases render time
because each region inside an existing map tile has to be checked
individually against the upstream server.
</p>
<div class="row">
<label class="checkbox">
<input type="checkbox" id="draw_region_rectangles" />
Draw region rectangles
</label>
<label class="checkbox">
<input type="checkbox" id="draw_region_names" />
Draw region names
</label>
<label class="checkbox">
<input type="checkbox" id="draw_region_coordinates" />
Draw region grid coordinates
</label>
<label>
Region label font
<select id="region_label_font_id"></select>
</label>
</div>
<p class="note">
Region names and grid coordinates are only drawn when each region is
rendered large enough for the text to fit and the render covers a
limited number of regions (roughly a 32×32 area) — at smaller sizes or
larger areas the names will not fit and resolving every name would be
too slow, so they are skipped. The rectangle outline is always drawn
when enabled.
</p>
</section>
<nav class="tabs" role="tablist">
<button type="button" class="tab active" data-tab="grid">
From grid rectangle
</button>
<button type="button" class="tab" data-tab="notecard">
From USB notecard
</button>
</nav>
<section id="tab-grid" class="tab-panel active">
<h2>Grid rectangle</h2>
<div class="row">
<label
>Find region
<input type="text" id="region_search" placeholder="Region name" />
</label>
<button type="button" id="region_search_btn">
Set corners to region
</button>
<span id="region_search_status" class="status"></span>
</div>
<div class="row">
<label
>Lower-left X
<input type="number" id="ll_x" value="380" min="0" max="65535" />
</label>
<label
>Lower-left Y
<input type="number" id="ll_y" value="380" min="0" max="65535" />
</label>
<label
>Upper-right X
<input type="number" id="ur_x" value="1500" min="0" max="65535" />
</label>
<label
>Upper-right Y
<input type="number" id="ur_y" value="1500" min="0" max="65535" />
</label>
</div>
</section>
<section id="tab-notecard" class="tab-panel">
<h2>USB notecard</h2>
<nav class="subtabs" role="tablist">
<button type="button" class="subtab active" data-subtab="file">
New notecard from file
</button>
<button type="button" class="subtab" data-subtab="clipboard">
New notecard from clipboard
</button>
<button type="button" class="subtab" data-subtab="reuse">
Reuse saved notecard
</button>
</nav>
<div id="subtab-file" class="subtab-panel active">
<div class="row">
<label class="grow"
>Notecard file
<input type="file" id="notecard_file" accept=".txt,text/plain" />
</label>
<label class="grow">
Saved notecard name
<input type="text" id="notecard_name_file" placeholder="(auto)" />
</label>
</div>
</div>
<div id="subtab-clipboard" class="subtab-panel">
<div class="row">
<label class="grow"
>Paste contents
<textarea
id="notecard_text"
rows="6"
placeholder="Region%20Name/x/y/z one per line"
></textarea>
</label>
</div>
<div class="row">
<label class="grow">
Saved notecard name
<input type="text" id="notecard_name_paste" placeholder="(auto)" />
</label>
</div>
</div>
<div id="subtab-reuse" class="subtab-panel">
<div class="row">
<label class="grow">
Scope
<select id="reuse_scope">
<option value="personal" selected>Personal library</option>
</select>
</label>
<label class="grow">
Notecard
<select id="reuse_notecard_id">
<option value="" selected>(no saved notecards)</option>
</select>
</label>
</div>
</div>
<div class="row">
<label
>Route colour
<input
type="color"
id="route_color"
value="#ff0000"
list="route_color_presets"
/>
</label>
<datalist id="route_color_presets">
<option value="#ff0000"></option>
<option value="#00ff00"></option>
</datalist>
<label class="checkbox">
<input type="checkbox" id="save_without_route" />
Also save a copy without the route
</label>
</div>
<fieldset class="borders">
<legend>Borders (extra regions added around the route)</legend>
<label
>All sides (uniform)
<input type="number" id="border_regions" min="0" max="500" />
</label>
<div class="per-side">
<label
>North (+y)
<input type="number" id="border_north" min="0" max="500" />
</label>
<label
>South (-y)
<input type="number" id="border_south" min="0" max="500" />
</label>
<label
>East (+x)
<input type="number" id="border_east" min="0" max="500" />
</label>
<label
>West (-x)
<input type="number" id="border_west" min="0" max="500" />
</label>
</div>
<p class="note">Setting "All sides" overrides the per-side fields.</p>
</fieldset>
</section>
<section id="glw-fieldset">
<h2>
<label class="checkbox-inline">
<input type="checkbox" id="glw_enabled" />
Add GLW wind/current/wave overlay
</label>
</h2>
<div id="glw-body" hidden>
<div class="row">
<label
>Font
<select id="glw_font_id"></select>
</label>
<p class="note grow">
Place the legend by clicking the legend button on a free slot in the
preview below.
</p>
</div>
<nav class="glw-tabs" role="tablist">
<button type="button" class="glw-tab active" data-glw-tab="event_id">
Event id
</button>
<button type="button" class="glw-tab" data-glw-tab="event_key">
Event key
</button>
<button type="button" class="glw-tab" data-glw-tab="saved">
Saved GLW data
</button>
<button type="button" class="glw-tab" data-glw-tab="pasted">
Paste JSON (advanced)
</button>
</nav>
<div
id="glw-source-event_id"
class="glw-tab-panel active"
data-glw-source="event_id"
>
<label
>Event id
<input type="number" id="glw_event_id" min="0" />
</label>
</div>
<div
id="glw-source-event_key"
class="glw-tab-panel"
data-glw-source="event_key"
>
<label
>Event key
<input type="text" id="glw_event_key" />
</label>
</div>
<div
id="glw-source-saved"
class="glw-tab-panel"
data-glw-source="saved"
>
<label
>Saved GLW data
<select id="glw_saved_id"></select>
</label>
</div>
<div
id="glw-source-pasted"
class="glw-tab-panel"
data-glw-source="pasted"
>
<label
>JSON
<textarea id="glw_pasted" rows="6"></textarea>
</label>
</div>
<div class="row" data-glw-source-not="saved">
<label
>Save as
<input
type="text"
id="glw_save_as"
placeholder="optional, defaults to Event <id> fetched <UTC>"
/>
</label>
</div>
<p class="note" data-glw-source-not="saved">
The auto-saved GLW data row inherits the destination chosen for the
render itself.
</p>
<details>
<summary>Style overrides</summary>
<div class="row">
<label
>Wind colour <input type="color" id="glw_wind_color"
/></label>
<label
>Current colour <input type="color" id="glw_current_color"
/></label>
<label
>Wave colour <input type="color" id="glw_wave_color"
/></label>
<label
>Label text colour <input type="color" id="glw_label_color"
/></label>
</div>
<div class="row">
<label
>Area outline <input type="color" id="glw_area_outline_color"
/></label>
<label
>Circle outline <input type="color" id="glw_circle_outline_color"
/></label>
<label
>Margin outline <input type="color" id="glw_margin_outline_color"
/></label>
<label class="checkbox-inline">
<input type="checkbox" id="glw_margin_band" />
Draw margin band
</label>
</div>
</details>
</div>
</section>
<template id="label-modal-template">
<div class="modal-form">
<label class="grow"
>Text (one line per row)
<textarea class="lm-lines" rows="3"></textarea>
</label>
<div class="row">
<label
>Font
<select class="lm-font"></select>
</label>
<label
>Size (px)
<input type="number" class="lm-size" value="24" min="1" max="512" />
</label>
<label
>Colour
<input type="color" class="lm-color" value="#ffffff" />
</label>
</div>
<div class="row">
<label
>Horizontal
<select class="lm-halign">
<option value="left">Left</option>
<option value="center">Centre</option>
<option value="right">Right</option>
</select>
</label>
<label
>Vertical
<select class="lm-valign">
<option value="top">Top</option>
<option value="center">Centre</option>
<option value="bottom">Bottom</option>
</select>
</label>
</div>
<p class="note lm-fit"></p>
</div>
</template>
<template id="logo-modal-template">
<div class="modal-form">
<nav class="subtabs" role="tablist">
<button
type="button"
class="subtab gm-source-tab active"
data-logo-source="reuse"
>
Reuse existing logo
</button>
<button
type="button"
class="subtab gm-source-tab"
data-logo-source="upload"
>
Upload new logo
</button>
</nav>
<div
class="subtab-panel gm-source-panel active"
data-logo-source="reuse"
>
<div class="row">
<label class="grow"
>Logo
<select class="gm-pick"></select>
</label>
</div>
</div>
<div class="subtab-panel gm-source-panel" data-logo-source="upload">
<div class="row">
<label class="grow"
>New logo name
<input type="text" class="gm-upload-name" maxlength="128" />
</label>
<label
>Image (PNG, JPEG or WebP, max 5 MiB / 2048 px)
<input
type="file"
class="gm-upload-file"
accept="image/png,image/jpeg,image/webp"
/>
</label>
</div>
<p class="status gm-upload-status"></p>
</div>
<img class="logo-thumb gm-preview hidden" alt="logo preview" />
<div class="row">
<label
>Scale
<select class="gm-scale">
<option value="1">1× (native)</option>
<option value="2">2× (nearest)</option>
<option value="4">4× (nearest)</option>
</select>
</label>
<label
>Horizontal
<select class="gm-halign">
<option value="left">Left</option>
<option value="center">Centre</option>
<option value="right">Right</option>
</select>
</label>
<label
>Vertical
<select class="gm-valign">
<option value="top">Top</option>
<option value="center">Centre</option>
<option value="bottom">Bottom</option>
</select>
</label>
</div>
<p class="note gm-fit"></p>
</div>
</template>
<section class="preview-area">
<h2>Preview</h2>
<p class="note">
Tiles are aligned to the SL map CDN's tile grid, so the preview can
include regions outside the requested rectangle — those extra regions
will not appear in the final render. Each free slot is drawn as a green
box (red if its content does not fit); use the buttons in a slot to
place a text label, logo or the GLW legend, the button on a shared edge
to combine two slots, and the split button to break a combined slot
apart.
</p>
<div class="row buttons">
<button type="button" id="preview_btn">Preview</button>
<label class="checkbox-inline">
<input type="checkbox" id="show_slots" checked />
Show free slots
</label>
</div>
<p id="preview-status" class="status"></p>
<p id="preview-region-info" class="status"></p>
<p id="preview-fill-hint" class="status"></p>
<p id="preview-overlay-status" class="status"></p>
<p id="placement-status" class="status"></p>
<p class="note" id="dragzoom-hint" style="display: none">
Drag a rectangle on the map to zoom the selection into that part of the
current area.
</p>
<div id="preview-viewport-wrap" class="preview-viewport-wrap hidden">
<div class="extend-group corner">
<button
type="button"
class="icon-btn extend"
id="extend_all"
title="Expand all sides by one region"
>
⤢
</button>
<button
type="button"
class="icon-btn shrink"
id="shrink_all"
title="Shrink all sides by one region"
>
⤡
</button>
</div>
<div class="extend-group north">
<button
type="button"
class="icon-btn shrink"
id="shrink_n"
title="Shrink the north (+y) side by one region"
>
▼
</button>
<button
type="button"
class="icon-btn extend"
id="extend_n"
title="Extend the north (+y) side by one region"
>
▲
</button>
</div>
<div class="extend-group south">
<button
type="button"
class="icon-btn extend"
id="extend_s"
title="Extend the south (-y) side by one region"
>
▼
</button>
<button
type="button"
class="icon-btn shrink"
id="shrink_s"
title="Shrink the south (-y) side by one region"
>
▲
</button>
</div>
<div class="extend-group west">
<button
type="button"
class="icon-btn extend"
id="extend_w"
title="Extend the west (-x) side by one region"
>
◀
</button>
<button
type="button"
class="icon-btn shrink"
id="shrink_w"
title="Shrink the west (-x) side by one region"
>
▶
</button>
</div>
<div class="extend-group east">
<button
type="button"
class="icon-btn shrink"
id="shrink_e"
title="Shrink the east (+x) side by one region"
>
◀
</button>
<button
type="button"
class="icon-btn extend"
id="extend_e"
title="Extend the east (+x) side by one region"
>
▶
</button>
</div>
<div
id="preview-container"
class="map-container"
aria-hidden="true"
></div>
</div>
</section>
<section class="render-area">
<h2>Render</h2>
<div class="row buttons">
<button type="button" id="generate_btn">Generate</button>
</div>
<div id="render-progress" class="hidden">
<p id="render-status" class="status"></p>
<div class="tile-legend" aria-label="Tile status legend">
<span class="tile-legend-item"
><span class="tile-cell"></span> pending</span
>
<span class="tile-legend-item"
><span class="tile-cell active"></span> in progress</span
>
<span class="tile-legend-item"
><span class="tile-cell memory"></span> in-memory cache</span
>
<span class="tile-legend-item"
><span class="tile-cell disk"></span> on-disk cache</span
>
<span class="tile-legend-item"
><span class="tile-cell network"></span> fetched from network</span
>
<span class="tile-legend-item"
><span class="tile-cell missing"></span> missing upstream</span
>
</div>
<div id="tile-grid" class="tile-grid"></div>
</div>
<div id="render-result" class="hidden">
<h3>Result</h3>
<p id="render-metadata"></p>
<div class="row">
<a id="download-image" class="button">Download image</a>
<a id="download-without-route" class="button hidden"
>Download image (no route)</a
>
<button type="button" id="result-metadata-btn" class="button">
Metadata
</button>
</div>
<img id="result-image" alt="Rendered map" />
</div>
</section>
<script src="/static/app.js"></script>
<script src="/static/modal.js"></script>
</body>
</html>