<!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"
list="saved_colors"
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"
list="saved_colors"
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>
<details class="saved-colors">
<summary>Saved colours</summary>
<p class="note">
Colours you save here appear as preset swatches in every colour picker
on this page. They are stored on your account, so they follow you
across browsers and devices.
</p>
<div id="saved_colors_list" class="saved-colors-list"></div>
<div class="row">
<label
>Add a colour
<input type="color" id="saved_color_new" value="#ff0000" />
</label>
<button type="button" id="saved_color_add">Add</button>
</div>
</details>
<datalist id="saved_colors"></datalist>
<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>
<details class="themes">
<summary>Themes</summary>
<p class="note">
A theme bundles the presentation settings on this page — the fill
colours and toggles, the region-overlay options and label font, the
GLW style overrides and font, and your route colour. Save the current
look to your personal library or to a group, then apply it later.
Group themes can be applied by every member, but only created or
changed by group owners. Theme names must be unique within each
library. Rename and delete saved themes from the
<a href="/library">Library</a> page.
</p>
<div class="row">
<label
>Library
<select id="theme_scope"></select>
</label>
<label
>Theme
<select id="theme_select"></select>
</label>
<button type="button" id="theme_apply">Apply</button>
<button type="button" id="theme_overwrite">Overwrite selected</button>
</div>
<div class="row">
<label
>New theme name
<input type="text" id="theme_name" placeholder="My theme" />
</label>
<button type="button" id="theme_save_new">Save as new theme</button>
</div>
<span id="theme_status" class="status"></span>
</details>
</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="saved_colors"
/>
</label>
<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" list="saved_colors"
/></label>
<label
>Current colour
<input type="color" id="glw_current_color" list="saved_colors"
/></label>
<label
>Wave colour
<input type="color" id="glw_wave_color" list="saved_colors"
/></label>
<label
>Label text colour
<input type="color" id="glw_label_color" list="saved_colors"
/></label>
</div>
<div class="row">
<label
>Area outline
<input
type="color"
id="glw_area_outline_color"
list="saved_colors"
/></label>
<label
>Circle outline
<input
type="color"
id="glw_circle_outline_color"
list="saved_colors"
/></label>
<label
>Margin outline
<input
type="color"
id="glw_margin_outline_color"
list="saved_colors"
/></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"
list="saved_colors"
/>
</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>