Files
dewemoji/app/resources/views/site/layout.blade.php
2026-02-23 05:44:12 +07:00

693 lines
32 KiB
PHP

<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@php
$canonicalPath = $canonicalPath ?? request()->getPathInfo();
$canonicalPath = $canonicalPath === '' ? '/' : $canonicalPath;
if ($canonicalPath !== '/') {
$canonicalPath = '/'.trim($canonicalPath, '/');
}
$canonicalUrl = rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/').$canonicalPath;
$metaDescription = trim($__env->yieldContent('meta_description')) ?: 'Search, copy, and explore emojis with Dewemoji. Free browser extension and API for developers.';
$metaTitle = trim($__env->yieldContent('title')) ?: 'Dewemoji';
@endphp
<title>@yield('title', 'Dewemoji')</title>
<meta name="description" content="{{ $metaDescription }}">
<meta name="csrf-token" content="{{ csrf_token() }}">
<link rel="canonical" href="{{ $canonicalUrl }}">
<meta property="og:title" content="{{ $metaTitle }}">
<meta property="og:description" content="{{ $metaDescription }}">
<meta property="og:url" content="{{ $canonicalUrl }}">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Dewemoji">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ $metaTitle }}">
<meta name="twitter:description" content="{{ $metaDescription }}">
<meta name="theme-color" content="#2053ff">
<link rel="icon" href="/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/logo/logo-mark-128.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/logo/logo-mark-512.png">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/logo/logo-mark-512.png">
<script type="application/ld+json">
{
"@@context": "https://schema.org",
"@@graph": [
{
"@@type": "WebSite",
"name": "Dewemoji",
"url": "{{ rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/') }}/",
"potentialAction": {
"@@type": "SearchAction",
"target": "{{ rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/') }}/?q={search_term_string}",
"query-input": "required name=search_term_string"
}
},
{
"@@type": "Organization",
"name": "Dewemoji",
"url": "{{ rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/') }}/",
"logo": "{{ rtrim(config('app.url', request()->getSchemeAndHttpHost()), '/') }}/assets/logo/logo-mark.svg"
}
]
}
</script>
<script>
(() => {
const stored = localStorage.getItem('dewemoji_theme');
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
const mode = stored || (prefersDark ? 'dark' : 'light');
const root = document.documentElement;
if (mode === 'dark') {
root.classList.add('dark');
root.classList.remove('theme-light');
} else {
root.classList.remove('dark');
root.classList.add('theme-light');
}
})();
</script>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preload" href="/assets/fonts/PlusJakartaSans-Regular.ttf" as="font" type="font/ttf" crossorigin>
<script src="https://unpkg.com/lucide@latest"></script>
@vite(['resources/js/app.js'])
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['"Plus Jakarta Sans"', 'system-ui', 'sans-serif'],
display: ['"Plus Jakarta Sans"', 'system-ui', 'sans-serif'],
},
colors: {
brand: {
sun: '#fcb735',
sunSoft: '#ffda9c',
ocean: '#2053ff',
oceanSoft: '#356cf0',
},
},
animation: {
'float': 'float 6s ease-in-out infinite',
'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
keyframes: {
float: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
}
}
}
}
};
</script>
<style>
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-Light.ttf") format("truetype");
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-Regular.ttf") format("truetype");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-Medium.ttf") format("truetype");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-SemiBold.ttf") format("truetype");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-Bold.ttf") format("truetype");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-LightItalic.ttf") format("truetype");
font-weight: 300;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-Italic.ttf") format("truetype");
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-MediumItalic.ttf") format("truetype");
font-weight: 500;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-SemiBoldItalic.ttf") format("truetype");
font-weight: 600;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/assets/fonts/PlusJakartaSans-BoldItalic.ttf") format("truetype");
font-weight: 700;
font-style: italic;
font-display: swap;
}
:root {
--font-family: "Plus Jakarta Sans", system-ui, sans-serif;
--fs-xl: clamp(2.5rem, 5vw, 3rem);
--fs-l: clamp(1.875rem, 4vw, 2rem);
--fs-m: clamp(1.5rem, 3vw, 1.5rem);
--fs-s: 1.125rem;
--fs-xs: 0.875rem;
--fw-light: 300;
--fw-normal: 400;
--fw-medium: 500;
--fw-semibold: 600;
--fw-bold: 700;
--app-bg: #050505;
--app-fg: #f8fafc;
--muted-text: #9ca3af;
--muted-strong: #e5e7eb;
--muted-border: rgba(148,163,184,0.2);
--panel-bg: rgba(20, 20, 23, 0.6);
--panel-border: rgba(255, 255, 255, 0.08);
--card-bg: linear-gradient(145deg, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.01) 100%);
--card-border: rgba(255, 255, 255, 0.05);
--card-hover-bg: linear-gradient(145deg, rgba(255,255,255,0.07) 0%, rgba(255,255,255,0.02) 100%);
--card-hover-border: rgba(53, 108, 240, 0.35);
--header-bg: rgba(5, 5, 5, 0.85);
--header-border: rgba(255, 255, 255, 0.05);
--surface-bg: #151518;
--surface-border: rgba(255, 255, 255, 0.1);
--nav-bg: rgba(11, 11, 15, 0.95);
--code-bg: rgba(0,0,0,0.3);
--scrollbar-thumb: rgba(255,255,255,.1);
--scrollbar-thumb-hover: rgba(255,255,255,.2);
}
html.theme-light {
--app-bg: #f6f7fb;
--app-fg: #0f172a;
--muted-text: #64748b;
--muted-strong: #1f2937;
--muted-border: rgba(15,23,42,0.12);
--panel-bg: rgba(255, 255, 255, 0.78);
--panel-border: rgba(15, 23, 42, 0.08);
--card-bg: linear-gradient(145deg, rgba(15,23,42,0.04) 0%, rgba(15,23,42,0.02) 100%);
--card-border: rgba(15, 23, 42, 0.08);
--card-hover-bg: linear-gradient(145deg, rgba(15,23,42,0.06) 0%, rgba(15,23,42,0.02) 100%);
--card-hover-border: rgba(32, 83, 255, 0.2);
--header-bg: rgba(255, 255, 255, 0.85);
--header-border: rgba(15, 23, 42, 0.08);
--surface-bg: #ffffff;
--surface-border: rgba(15, 23, 42, 0.12);
--nav-bg: rgba(255, 255, 255, 0.9);
--code-bg: rgba(15,23,42,0.06);
--scrollbar-thumb: rgba(15,23,42,.18);
--scrollbar-thumb-hover: rgba(15,23,42,.28);
}
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
.glass-panel {
background: var(--panel-bg);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid var(--panel-border);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
}
.glass-card {
background: var(--card-bg);
border: 1px solid var(--card-border);
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.glass-card:hover {
background: var(--card-hover-bg);
border-color: var(--card-hover-border);
transform: translateY(-2px);
box-shadow: 0 10px 40px -10px rgba(32, 83, 255, 0.2);
}
.glass-header {
background: var(--header-bg);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--header-border);
}
.theme-surface {
background: var(--surface-bg) !important;
border-color: var(--surface-border) !important;
}
.theme-nav {
background: var(--nav-bg) !important;
border-color: var(--panel-border) !important;
}
.text-gradient {
background: linear-gradient(to right, #fcb735, #2053ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.emoji-name-clamp {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.1;
max-height: 2.2em;
}
h1 { font-size: var(--fs-xl); font-weight: var(--fw-bold); line-height: 1.1; }
h2 { font-size: var(--fs-l); font-weight: var(--fw-semibold); line-height: 1.25; }
h3 { font-size: var(--fs-m); font-weight: var(--fw-semibold); line-height: 1.3; }
html.theme-light .text-white:not(.force-white) { color: var(--app-fg) !important; }
html.theme-light button.text-white,
html.theme-light a.text-white,
html.theme-light [role="button"].text-white,
html.theme-light button .text-white,
html.theme-light a .text-white,
html.theme-light [role="button"] .text-white {
color: #ffffff !important;
}
html.theme-light .text-gray-200 { color: #334155 !important; }
html.theme-light .text-gray-300 { color: #475569 !important; }
html.theme-light .text-gray-400 { color: var(--muted-text) !important; }
html.theme-light .text-gray-500 { color: #94a3b8 !important; }
html.theme-light .text-gray-100 { color: #1f2937 !important; }
html.theme-light .border-white\/5 { border-color: rgba(15,23,42,0.08) !important; }
html.theme-light .border-white\/10 { border-color: rgba(15,23,42,0.12) !important; }
html.theme-light .border-white\/20 { border-color: rgba(15,23,42,0.18) !important; }
html.theme-light .bg-white\/5 { background: rgba(15,23,42,0.04) !important; }
html.theme-light .bg-white\/10 { background: rgba(15,23,42,0.06) !important; }
html.theme-light .bg-white\/20 { background: rgba(15,23,42,0.1) !important; }
html.theme-light .bg-black\/30 { background: var(--code-bg) !important; }
html.theme-light .bg-black\/40 { background: var(--code-bg) !important; }
html.theme-light .copy-btn {
background: rgba(255,255,255,0.9);
border-color: rgba(15,23,42,0.2);
color: var(--app-fg);
}
html.theme-light .copy-btn:hover { background: rgba(32,83,255,0.18); }
html.theme-light .emoji-card {
background: #ffffff;
border-color: rgba(15,23,42,0.12);
box-shadow: 0 6px 18px rgba(15,23,42,0.08);
}
html.theme-light .emoji-card:hover {
border-color: rgba(32,83,255,0.35);
box-shadow: 0 10px 22px rgba(32,83,255,0.18);
}
html.theme-light .emoji-card-bar {
background: rgba(15,23,42,0.04);
border-top-color: rgba(15,23,42,0.1);
}
.offcanvas-panel {
background: var(--panel-bg);
border-color: var(--panel-border);
color: var(--app-fg);
}
.offcanvas-backdrop {
background: rgba(0,0,0,0.4);
}
html.theme-light .offcanvas-backdrop {
background: rgba(15,23,42,0.3);
}
.offcanvas-code {
background: var(--code-bg);
}
.cookie-banner {
background: var(--panel-bg);
border: 1px solid var(--panel-border);
color: var(--app-fg);
box-shadow: 0 12px 40px rgba(0,0,0,0.15);
}
.cookie-btn {
background: rgba(32,83,255,0.14);
border: 1px solid rgba(32,83,255,0.4);
color: var(--app-fg);
}
.cookie-btn:hover { background: rgba(32,83,255,0.22); }
.cookie-btn.secondary {
background: transparent;
border: 1px solid var(--muted-border);
color: var(--muted-text);
}
html.theme-light .cookie-btn.secondary { color: var(--app-fg); }
</style>
@stack('head')
@stack('jsonld')
</head>
<body class="bg-[var(--app-bg)] text-[var(--app-fg)] min-h-screen selection:bg-brand-ocean selection:text-white" style="font-family: var(--font-family); font-size: var(--fs-s); font-weight: var(--fw-normal); line-height: 1.6;">
<div class="fixed top-0 left-0 w-full h-full overflow-hidden -z-10 pointer-events-none">
<div class="absolute top-[-10%] right-[-5%] w-[500px] h-[500px] bg-brand-ocean/20 rounded-full blur-[120px] animate-pulse-slow"></div>
<div class="absolute bottom-[-10%] left-[-10%] w-[600px] h-[600px] bg-blue-900/10 rounded-full blur-[120px]"></div>
</div>
@yield('content')
@hasSection('mobile_nav')
@yield('mobile_nav')
@else
<nav class="lg:hidden fixed bottom-0 inset-x-0 z-50 border-t border-white/10 bg-[#0b0b0f]/95 backdrop-blur px-2 py-2 theme-nav">
<div class="grid grid-cols-4 gap-1 text-[11px]">
<a href="{{ route('home') }}" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 {{ request()->routeIs('home') ? 'text-brand-sun bg-white/5' : 'text-gray-300 hover:bg-white/5' }}">
<i data-lucide="layout-grid" class="w-4 h-4"></i><span>Discover</span>
</a>
<a href="{{ route('api-docs') }}" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 {{ request()->routeIs('api-docs') ? 'text-brand-sun bg-white/5' : 'text-gray-300 hover:bg-white/5' }}">
<i data-lucide="book-open" class="w-4 h-4"></i><span>Docs</span>
</a>
<a href="{{ route('pricing') }}" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 {{ request()->routeIs('pricing') ? 'text-brand-sun bg-white/5' : 'text-gray-300 hover:bg-white/5' }}">
<i data-lucide="badge-dollar-sign" class="w-4 h-4"></i><span>Pricing</span>
</a>
<button id="more-menu-btn" class="flex flex-col items-center justify-center gap-1 rounded-lg py-2 text-gray-300 hover:bg-white/5">
<i data-lucide="more-horizontal" class="w-4 h-4"></i><span>More</span>
</button>
</div>
</nav>
@endif
@hasSection('more_menu')
@yield('more_menu')
@else
<div id="more-menu-backdrop" class="lg:hidden fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden"></div>
<div id="more-menu" class="lg:hidden fixed bottom-16 left-4 right-4 glass-panel rounded-2xl p-4 z-50 hidden">
<div class="flex items-center justify-between mb-3">
<span class="text-sm font-semibold">More</span>
<button id="more-menu-close" class="w-8 h-8 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
<div class="flex flex-col gap-2 text-sm">
@auth
<a href="{{ route('dashboard.overview') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
<i data-lucide="layout-dashboard" class="w-4 h-4"></i><span>Dashboard</span>
</a>
@endauth
@guest
<a href="{{ route('login') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
<i data-lucide="log-in" class="w-4 h-4"></i><span>Login</span>
</a>
@endguest
<a href="{{ route('download') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
<i data-lucide="download" class="w-4 h-4"></i><span>Download</span>
</a>
<a href="{{ route('support') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
<i data-lucide="life-buoy" class="w-4 h-4"></i><span>Support</span>
</a>
<a href="{{ route('privacy') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
<i data-lucide="shield-check" class="w-4 h-4"></i><span>Privacy</span>
</a>
<a href="{{ route('terms') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
<i data-lucide="file-text" class="w-4 h-4"></i><span>Terms</span>
</a>
</div>
</div>
@endif
<div id="cookie-banner" class="cookie-banner fixed bottom-6 left-6 right-6 md:right-8 md:left-auto md:max-w-xl rounded-2xl p-4 md:p-5 hidden z-50">
<div class="flex flex-col gap-3">
<div>
<p class="text-sm font-semibold">Cookies & analytics</p>
<p class="text-xs text-gray-400">We use cookies to measure usage and improve Dewemoji. No tracking on staging. You can change this anytime.</p>
</div>
<div class="flex flex-wrap gap-2">
<button id="cookie-accept" class="cookie-btn rounded-xl px-3 py-2 text-xs font-semibold">Accept analytics</button>
<button id="cookie-decline" class="cookie-btn secondary rounded-xl px-3 py-2 text-xs font-semibold">Decline</button>
<a href="/privacy" class="text-xs text-gray-400 underline underline-offset-2">Privacy</a>
</div>
</div>
</div>
<div id="confirm-dialog" class="fixed inset-0 z-[100] hidden items-center justify-center">
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm"></div>
<div class="relative w-full max-w-md rounded-2xl border border-slate-200 bg-white p-6 text-slate-900 shadow-2xl dark:border-white/10 dark:bg-slate-950 dark:text-white">
<div class="text-xs uppercase tracking-[0.25em] text-slate-400" id="confirm-title">Confirm action</div>
<div class="mt-3 text-lg font-semibold" id="confirm-message">Are you sure?</div>
<div class="mt-6 flex items-center justify-end gap-2">
<button id="confirm-cancel" class="rounded-full border border-slate-200 px-4 py-2 text-sm text-slate-600 hover:bg-slate-100 dark:border-white/10 dark:text-slate-300 dark:hover:bg-white/5">
Cancel
</button>
<button id="confirm-ok" class="rounded-full bg-rose-500 px-4 py-2 text-sm font-semibold text-white hover:bg-rose-600">
Confirm
</button>
</div>
</div>
</div>
<script>lucide.createIcons();</script>
<script>
(() => {
const root = document.documentElement;
const toggle = document.getElementById('theme-toggle');
const iconDark = document.querySelector('[data-theme-icon="dark"]');
const iconLight = document.querySelector('[data-theme-icon="light"]');
const setTheme = (mode) => {
if (mode === 'dark') {
root.classList.add('dark');
root.classList.remove('theme-light');
} else {
root.classList.remove('dark');
root.classList.add('theme-light');
}
localStorage.setItem('dewemoji_theme', mode);
if (iconDark && iconLight) {
iconDark.classList.toggle('hidden', mode !== 'dark');
iconLight.classList.toggle('hidden', mode === 'dark');
}
};
const stored = localStorage.getItem('dewemoji_theme');
if (stored) setTheme(stored);
else setTheme(root.classList.contains('dark') ? 'dark' : 'light');
if (toggle) {
toggle.addEventListener('click', () => {
const next = root.classList.contains('dark') ? 'light' : 'dark';
setTheme(next);
});
}
})();
</script>
<script>
(() => {
const moreMenuBtn = document.getElementById('more-menu-btn');
const moreMenu = document.getElementById('more-menu');
const moreMenuBackdrop = document.getElementById('more-menu-backdrop');
const moreMenuClose = document.getElementById('more-menu-close');
const openMoreMenu = () => {
if (moreMenu) moreMenu.classList.remove('hidden');
if (moreMenuBackdrop) moreMenuBackdrop.classList.remove('hidden');
};
const closeMoreMenu = () => {
if (moreMenu) moreMenu.classList.add('hidden');
if (moreMenuBackdrop) moreMenuBackdrop.classList.add('hidden');
};
if (moreMenuBtn) moreMenuBtn.addEventListener('click', openMoreMenu);
if (moreMenuBackdrop) moreMenuBackdrop.addEventListener('click', closeMoreMenu);
if (moreMenuClose) moreMenuClose.addEventListener('click', closeMoreMenu);
})();
</script>
<script>
(() => {
const GA_ID = 'G-R7FYYRBVJK';
const allowedHosts = new Set(['dewemoji.com', 'www.dewemoji.com']);
const consentKey = 'dewemoji_cookie_consent';
const banner = document.getElementById('cookie-banner');
const acceptBtn = document.getElementById('cookie-accept');
const declineBtn = document.getElementById('cookie-decline');
const loadGA = () => {
if (!allowedHosts.has(window.location.hostname)) return;
if (window.__dewemojiGaLoaded) return;
window.__dewemojiGaLoaded = true;
const script = document.createElement('script');
script.async = true;
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ID}`;
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', GA_ID, { anonymize_ip: true });
};
const consent = localStorage.getItem(consentKey);
if (!consent && banner) banner.classList.remove('hidden');
if (consent === 'accepted') loadGA();
if (acceptBtn) {
acceptBtn.addEventListener('click', () => {
localStorage.setItem(consentKey, 'accepted');
if (banner) banner.classList.add('hidden');
loadGA();
});
}
if (declineBtn) {
declineBtn.addEventListener('click', () => {
localStorage.setItem(consentKey, 'declined');
if (banner) banner.classList.add('hidden');
});
}
})();
</script>
<script>
(() => {
const dialog = document.getElementById('confirm-dialog');
const titleEl = document.getElementById('confirm-title');
const msgEl = document.getElementById('confirm-message');
const okBtn = document.getElementById('confirm-ok');
const cancelBtn = document.getElementById('confirm-cancel');
let resolver = null;
const close = (result) => {
if (!dialog) return;
dialog.classList.add('hidden');
dialog.classList.remove('flex');
if (resolver) resolver(result);
resolver = null;
};
window.dewemojiConfirm = (message, options = {}) => {
if (!dialog) return Promise.resolve(false);
if (titleEl) titleEl.textContent = options.title || 'Confirm action';
if (msgEl) msgEl.textContent = message || 'Are you sure?';
if (okBtn) okBtn.textContent = options.okText || 'Confirm';
if (cancelBtn) cancelBtn.textContent = options.cancelText || 'Cancel';
if (okBtn) {
okBtn.classList.toggle('bg-rose-500', options.danger !== false);
okBtn.classList.toggle('hover:bg-rose-600', options.danger !== false);
okBtn.classList.toggle('bg-brand-ocean', options.danger === false);
okBtn.classList.toggle('hover:bg-brand-ocean/90', options.danger === false);
}
dialog.classList.remove('hidden');
dialog.classList.add('flex');
return new Promise((resolve) => {
resolver = resolve;
});
};
okBtn?.addEventListener('click', () => close(true));
cancelBtn?.addEventListener('click', () => close(false));
dialog?.addEventListener('click', (event) => {
if (event.target === dialog) close(false);
});
dialog?.querySelector(':scope > div.absolute')?.addEventListener('click', () => close(false));
document.addEventListener('keydown', (event) => {
if (dialog?.classList.contains('hidden')) return;
if (event.key === 'Escape') close(false);
});
document.addEventListener('submit', async (event) => {
const form = event.target;
if (!(form instanceof HTMLFormElement)) return;
const message = form.getAttribute('data-confirm');
if (!message || form.dataset.confirmed === 'true') return;
event.preventDefault();
const ok = await window.dewemojiConfirm(message, {
title: form.getAttribute('data-confirm-title') || undefined,
okText: form.getAttribute('data-confirm-ok') || undefined,
cancelText: form.getAttribute('data-confirm-cancel') || undefined,
danger: form.getAttribute('data-confirm-danger') !== 'false',
});
if (ok) {
form.dataset.confirmed = 'true';
form.submit();
}
});
})();
</script>
<script>
(() => {
const alpha = 'abcdefghijklmnopqrstuvwxyz';
const deprecatedAliases = {
in: 'id',
iw: 'he',
ji: 'yi',
jw: 'jv',
};
const makeIso6391Codes = () => {
const codes = [];
for (let i = 0; i < alpha.length; i += 1) {
for (let j = 0; j < alpha.length; j += 1) {
codes.push(alpha[i] + alpha[j]);
}
}
return codes;
};
const canonicalLanguageCode = (raw) => {
const code = String(raw || '').trim().toLowerCase();
if (!code) return '';
return deprecatedAliases[code] || code;
};
window.dewemojiCanonicalLanguageCode = canonicalLanguageCode;
window.dewemojiPopulateLanguageSelect = (target) => {
const select = typeof target === 'string' ? document.getElementById(target) : target;
if (!(select instanceof HTMLSelectElement) || select.dataset.generated === '1') return;
const existing = Array.from(select.options).map((opt) => ({
code: canonicalLanguageCode(opt.value),
label: (opt.textContent || '').trim(),
})).filter((row) => row.code !== '');
const byCode = new Map(existing.map((row) => [row.code.toLowerCase(), row]));
if (typeof Intl !== 'undefined' && typeof Intl.DisplayNames === 'function') {
const dn = new Intl.DisplayNames(['en'], { type: 'language' });
makeIso6391Codes().forEach((code) => {
const name = dn.of(code);
if (!name) return;
// Unknown/unsupported codes are echoed back by many engines.
if (name.toLowerCase() === code) return;
const canonical = canonicalLanguageCode(code);
if (!canonical) return;
byCode.set(canonical, { code: canonical, label: `${name} (${canonical})` });
});
}
const rows = Array.from(byCode.values()).sort((a, b) => {
if (a.code === 'und') return -1;
if (b.code === 'und') return 1;
return a.label.localeCompare(b.label);
});
const previous = (select.value || '').trim().toLowerCase();
select.innerHTML = '';
rows.forEach((row) => {
const option = document.createElement('option');
option.value = row.code;
option.textContent = row.label;
select.appendChild(option);
});
select.value = previous || select.value;
select.dataset.generated = '1';
};
})();
</script>
@stack('scripts')
</body>
</html>