require.config({ paths: { bootstrap: './vendor/bootstrap.min', diffMatchPatch: './vendor/diff_match_patch.min', handlebars: './vendor/handlebars.min', handlebarsExtended: './utils/handlebars_helper', jquery: './vendor/jquery.min', locales: './locales/locale', lodash: './vendor/lodash.custom.min', pathToRegexp: './vendor/path-to-regexp/index', prismjs: './vendor/prism', semver: './vendor/semver.min', utilsSampleRequest: './utils/send_sample_request', webfontloader: './vendor/webfontloader', list: './vendor/list.min', apiData: './api_data', apiProject: './api_project', }, shim: { bootstrap: { deps: ['jquery'] }, diffMatchPatch: { exports: 'diff_match_patch' }, handlebars: { exports: 'Handlebars' }, handlebarsExtended: { deps: ['jquery', 'handlebars'], exports: 'Handlebars' }, prismjs: { exports: 'Prism' }, }, urlArgs: 'v=' + (new Date()).getTime(), waitSeconds: 150 }); require([ 'jquery', 'lodash', 'locales', 'handlebarsExtended', 'apiProject', 'apiData', 'prismjs', 'utilsSampleRequest', 'semver', 'webfontloader', 'bootstrap', 'pathToRegexp', 'list' ], function($, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver, WebFont) { // Load google web fonts. WebFont.load({ active: function() { // Only init after fonts are loaded. init($, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver); }, inactive: function() { // Run init, even if loading fonts fails init($, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver); }, google: { families: ['Source Code Pro', 'Source Sans Pro:n4,n6,n7'] } }); }); function init($, _, locale, Handlebars, apiProject, apiData, Prism, sampleRequest, semver) { var api = apiData.api; // // Templates // var templateHeader = Handlebars.compile( $('#template-header').html() ); var templateFooter = Handlebars.compile( $('#template-footer').html() ); var templateArticle = Handlebars.compile( $('#template-article').html() ); var templateCompareArticle = Handlebars.compile( $('#template-compare-article').html() ); var templateGenerator = Handlebars.compile( $('#template-generator').html() ); var templateProject = Handlebars.compile( $('#template-project').html() ); var templateSections = Handlebars.compile( $('#template-sections').html() ); var templateSidenav = Handlebars.compile( $('#template-sidenav').html() ); // // Default host url used if no sampleUrl is present in config // var baseURL = window.location.origin; // // apiProject defaults // if ( ! apiProject.template) apiProject.template = {}; if (apiProject.template.withCompare == null) apiProject.template.withCompare = true; if (apiProject.template.withGenerator == null) apiProject.template.withGenerator = true; if (apiProject.template.forceLanguage) locale.setLanguage(apiProject.template.forceLanguage); if (apiProject.template.aloneDisplay == null) apiProject.template.aloneDisplay = false; // Setup jQuery Ajax $.ajaxSetup(apiProject.template.jQueryAjaxSetup); // // Data transform // // grouped by group var apiByGroup = _.groupBy(api, function(entry) { return entry.group; }); // grouped by group and name var apiByGroupAndName = {}; $.each(apiByGroup, function(index, entries) { apiByGroupAndName[index] = _.groupBy(entries, function(entry) { return entry.name; }); }); // // sort api within a group by title ASC and custom order // var newList = []; var umlauts = { 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // TODO: remove in version 1.0 $.each (apiByGroupAndName, function(index, groupEntries) { // get titles from the first entry of group[].name[] (name has versioning) var titles = []; $.each (groupEntries, function(titleName, entries) { var title = entries[0].title; if(title !== undefined) { title.toLowerCase().replace(/[äöüß]/g, function($0) { return umlauts[$0]; }); titles.push(title + '#~#' + titleName); // '#~#' keep reference to titleName after sorting } }); // sort by name ASC titles.sort(); // custom order if (apiProject.order) titles = sortByOrder(titles, apiProject.order, '#~#'); // add single elements to the new list titles.forEach(function(name) { var values = name.split('#~#'); var key = values[1]; groupEntries[key].forEach(function(entry) { newList.push(entry); }); }); }); // api overwrite with ordered list api = newList; // // Group- and Versionlists // var apiGroups = {}; var apiGroupTitles = {}; var apiVersions = {}; apiVersions[apiProject.version] = 1; $.each(api, function(index, entry) { apiGroups[entry.group] = 1; apiGroupTitles[entry.group] = entry.groupTitle || entry.group; apiVersions[entry.version] = 1; }); // sort groups apiGroups = Object.keys(apiGroups); apiGroups.sort(); // custom order if (apiProject.order) apiGroups = sortByOrder(apiGroups, apiProject.order); // sort versions DESC apiVersions = Object.keys(apiVersions); apiVersions.sort(semver.compare); apiVersions.reverse(); // // create Navigationlist // var nav = []; apiGroups.forEach(function(group) { // Mainmenu entry nav.push({ group: group, isHeader: true, title: apiGroupTitles[group] }); // Submenu var oldName = ''; api.forEach(function(entry) { if (entry.group === group) { if (oldName !== entry.name) { nav.push({ title: entry.title, group: group, name: entry.name, type: entry.type, version: entry.version, url: entry.url }); } else { nav.push({ title: entry.title, group: group, hidden: true, name: entry.name, type: entry.type, version: entry.version, url: entry.url }); } oldName = entry.name; } }); }); /** * Add navigation items by analyzing the HTML content and searching for h1 and h2 tags * @param nav Object the navigation array * @param content string the compiled HTML content * @param index where to insert items * @return boolean true if any good-looking (i.e. with a group identifier)
<script>
import OutClick from "svelte-outclick";
import { banner, newPayment } from "$lib/store";
import { goto } from "$app/navigation";
import Avatar from "$comp/Avatar.svelte";
import Icon from "$comp/Icon.svelte";
import { loading, t } from "$lib/translations";
import { page } from "$app/stores";
export let data;
let { subject, user } = data;
$: update(data);
let update = (data) => ({ subject, user } = data);
let menuButtons;
$: if (user)
menuButtons = [
{
key: "nav.settings",
icon: "settings",
href: `/${user.username}/settings`,
},
{ key: "nav.support", icon: "support", href: `/support` },
{ key: "nav.map", icon: "map", href: `/${user.username}/map` },
{
key: "nav.messages",
icon: "message",
href: `/${user.username}/messages`,
},
{ key: "nav.merch", icon: "shop", href: `https://coinosmerch.com` },
{ key: "nav.signOut", icon: "logout", href: `/logout` },
];
let showMenu = false;
$: bg =
$banner?.id && $banner.id === subject.id
? `url(${$banner.src})`
: subject?.banner
? `url(/api/public/${subject.banner}.webp)`
: undefined;
$: $page && (showMenu = false);
</script>
{#if !$loading}
<header
class="bg-gradient-to-r from-primary to-gradient h-[175px] w-full relative bg-cover mb-20"
style:background-image={bg}
>
<nav class="flex justify-end items-center space-x-4 p-5">
{#if user}
<a href={`/${user.username}`}>
<button
class="bg-white p-2 rounded-full w-12 h-12 sm:w-16 sm:h-16 drop-shadow-xl border border-black/10 {$page
.url.pathname === `/${user.username}`
? 'opacity-100'
: 'opacity-70 hover:opacity-80'}"
><Icon icon="home" style="mx-auto w-6 sm:w-8" />
</button>
</a>
<a href={`/${user.username}/receive`}>
<button
class="bg-white p-2 rounded-full w-12 h-12 sm:w-16 sm:h-16 drop-shadow-xl border border-black/10 {$page
.url.pathname === `/${user.username}/receive`
? 'opacity-100'
: 'opacity-70 hover:opacity-80'}"
><Icon icon="numpad" style="mx-auto w-6 sm:w-8" />
</button>
</a>
<a href={`/${user.username}/payments`}>
<button
class="bg-white p-2 rounded-full w-12 h-12 sm:w-16 sm:h-16 drop-shadow-xl border border-black/10 {$page
.url.pathname === `/${user.username}/payments`
? 'opacity-100'
: 'opacity-70 hover:opacity-80'} relative"
><Icon icon="clock" style="mx-auto w-6 sm:w-8" />
{#if $newPayment}
<span class="absolute top-0 right-0">
<span class="flex h-3 w-3">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#F7931A] opacity-75"
/>
<span
class="relative inline-flex rounded-full h-3 w-3 bg-[#F7931A]"
/>
</span>
</span>
{/if}
</button>
</a>
<a href={`/send`}>
<button
class="bg-white p-2 rounded-full w-12 h-12 sm:w-16 sm:h-16 drop-shadow-xl border border-black/10 {$page
.url.pathname === `/send`
? 'opacity-100'
: 'opacity-70 hover:opacity-80'}"
><Icon icon="send" style="mx-auto w-6 sm:w-8" />
</button>
</a>
<div class="relative">
<OutClick on:outclick={() => (showMenu = false)}>
<button
class="bg-white p-2 rounded-full w-12 h-12 sm:w-16 sm:h-16 drop-shadow-xl border border-black/10 {$page
.url.pathname === `/${user.username}` ||
$page.url.pathname === `/${user.username}/settings` ||
$page.url.pathname === `/support`
? 'opacity-100'
: `opacity-70 hover:opacity-80 ${
showMenu ? 'opacity-80' : ''
}`}"
on:click={() => (showMenu = !showMenu)}
><Icon icon="menu" style="mx-auto w-6 sm:w-8" />
</button>
<div
class="{showMenu
? 'block'
: 'hidden'} absolute top-14 right-0 bg-white rounded-3xl p-8 shadow-xl z-50"
>
<ul class="space-y-5 w-48">
{#each menuButtons as { href, icon, key }}
<li>
<a {href} data-sveltekit-prefetch="off">
<button
class="flex justify-center items-center font-semibold hover:opacity-80"
><Icon {icon} style="mr-2 w-6 sm:w-8" /> {$t(key)}
</button>
</a>
</li>
{/each}
</ul>
<hr class="my-4" />
<a href="/?stay=true"
><img src="/images/logo.svg" alt="Coinos" class="w-32" /></a
>
</div>
</OutClick>
</div>
{:else}
<a href={`/login?redirect=${$page.url.pathname}`}>
<button class="bg-white px-5 py-2 rounded-xl font-semibold text-sm"
>{$t("nav.signIn")}</button
>
</a>
{/if}
</nav>
<div
class="absolute md:w-[64px] md:mx-auto lg:left-[154px] xl:left-[194px] left-[calc(50vw-64px)] -bottom-[64px] z-30"
>
<Avatar user={subject} />
</div>
</header>
{/if}
<style>
header {
z-index: 50;
view-transition-name: header;
}
</style>