<% // Post editor — split into card sections for visual hierarchy: // - Identity : title, slug, excerpt // - Cover : thumbnail + URL + custom upload button // - Content : markdown textarea with attached toolbar // - Meta : status, type, pin, noindex, tags // // All behavior (IDs, names, upload endpoints) is preserved — only layout // and styling changed. const _hasCover = !!post.cover_image_url; // In hub mode, save/create/cancel must carry the /user// prefix, otherwise // the request falls back to the primary site (siteUrlBase empty) and the post // renders headerless (bareChrome) after saving. In solo mode _base is empty. const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : ''; %>

<%= isNew ? t('pedit.title_new') : t('pedit.title_edit') %>

<%# ── TYPE (segmented) + type-specific inputs ──────────────── Post type lives at the TOP (above the body), and the selected type reveals its own input: Audio → inline upload, Video → embed URL, Foto → cover/insert hint. %> <% var ptype = post.type || 'post'; %> <% var TYPE_ICONS = { post: '📝', foto: '📷', video: '🎬', album: '💿', playlist: '📃', mixtape: '📼', audio: '🎵' }; %> <%# Audio is geen keuze meer (shaer-cyg): alles wat muziek is landt op Album of Playlist. De knop blijft alleen staan zolang deze post nog audio IS, zodat een oude post niet stilzwijgend van type verandert bij het openen. %> <% var TYPES = (typeof keuzeTypes !== 'undefined' && keuzeTypes.length) ? keuzeTypes.slice() : ['post','foto','video','album','playlist','mixtape']; %> <% if (ptype === 'audio' && TYPES.indexOf('audio') < 0) TYPES.push('audio'); %>
<%= t('pedit.s_type') %>
<% TYPES.forEach(function (tt) { %> <% }); %>
<%# Muziek: inline upload (transcodes + drops [[track]] into the post). Album, Playlist en Mixtape delen dit paneel -- het verschil zit in de playlist die je insluit, niet in wat je hier uploadt. Audio staat er alleen nog voor oude posts. %> <%# Video: paste a URL → embed chip %> <%# Foto: light hint toward cover + insert-image %>
<%# ── IDENTITY ─────────────────────────────────────────────── %>
<% var _postLang = (typeof post.language !== 'undefined' && post.language) ? post.language : (typeof lang !== 'undefined' ? lang : 'en'); %>
<%# ── COVER ────────────────────────────────────────────────── %>
<%= t('pedit.s_cover') %>
> > <% if (!_hasCover) { %>🖼<% } %>
<%# Muted loop MP4 for an animated cover (auto-made from an animated WebP). Hidden — set by the uploader. %>
<%# ── CONTENT ──────────────────────────────────────────────── %>
<%= t('pedit.s_content') %> <%= t('pedit.content_hint') %>
<%# Sticky "tap to edit" hint (only visible on touch + non-fullscreen via CSS). Purely visual (pointer-events:none); tapping the field opens fullscreen. %> <%# Hidden field is what actually submits to the server. The visible contenteditable's serialized HTML is copied here on submit. %> <%# value = de BESTAANDE inhoud: laadt de module niet (oude cache, js uit), dan slaat het formulier de post ongewijzigd op in plaats van hem te wissen. %> <%# Initial content is injected as a JSON string in a script tag so we can set innerHTML safely from JS. EJS-escaping HTML directly into the editor would render entity-escaped tags as text. %>
0 <%= t('pedit.chars') %>
<%# ── META ─────────────────────────────────────────────────── %>
<%= t('pedit.s_publication') %>
0) ? '' : 'hidden' %>>
<% var _fediOpen = (typeof fediOpenAudio !== 'undefined' && fediOpenAudio); %> <% if (_fediOpen) { %>

<%= t('pedit.fedi_audio_locked') %>

<% } else { %> <%# De scripts van deze pagina staan in assets/js/mod/post-edit.js; de gegevens via partials/page-data.ejs (shaer-bqr). %> <% } %> <% // Poll (federates as an AS2 Question). Free feature. A poll with votes is frozen. var _poll = null; try { _poll = post.poll_json ? JSON.parse(post.poll_json) : null; } catch (e) { _poll = null; } var _pollLocked = (typeof pollLocked !== 'undefined' && pollLocked); var _pollOpts = (_poll && Array.isArray(_poll.options) && _poll.options.length) ? _poll.options : [{ name: '' }, { name: '' }]; %> <%# NB: keep the condition INSIDE the quoted attribute — escaped output (equals-tag) turns a whole emitted style attribute into entity soup (fields showed always). %>
<% if (_pollLocked) { %>

<%= t('pedit.poll_locked') %>

<% } %>
<% _pollOpts.forEach(function (o) { %>
> <% if (!_pollLocked) { %><% } %>
<% }); %>
<% if (!_pollLocked) { %><% } %>
<% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
<% var _scheduled = !!(post.publish_at && (post.status === 'scheduled' || Date.parse(String(post.publish_at).replace(' ', 'T')) > Date.now())); %>
data-iso="<%= post.publish_at || '' %>" value="" style="padding:8px 10px;border-radius:8px;border:1px solid var(--rule,rgba(128,128,128,.4));background:transparent;color:inherit"> <% if (post.status === 'scheduled' && post.publish_at) { %>
⏳ <%= t('pedit.scheduled_for', { d: post.publish_at }) %>
<% } %>
<%= t('pedit.schedule_hint') %>
<% } %>
<%# ── ACTIONS (sticky at bottom) ──────────────────────────── %>
<%= t('pedit.cancel') %>
<% if (!isNew && post.status !== 'published') { %> <% } %>
<%# ── Track picker modal (P59). Mobile-first: full-screen sheet on small viewports, centered modal on ≥640px. Populated lazily on first open. %> <% if (typeof user !== 'undefined' && user) { %> <% } %> <%# Playlist editor modal — included so its global window.openPlaylistEditor is available when the toolbar button fires. Only renders if user can post. %> <% if (typeof user !== 'undefined' && user) { %> <%- include('../partials/playlist-editor', { csrfToken: (typeof csrfToken !== 'undefined' ? csrfToken : '') }) %> <% } %> <%- include('../partials/page-data', { pageData: { _timezone: (typeof timezone !== 'undefined' ? timezone : ''), apply: t('imed.apply'), audio_up_busy: t('pedit.audio_up_busy'), audio_up_done: t('pedit.audio_up_done'), audio_up_fail: t('pedit.audio_up_fail'), cancel: t('imed.cancel'), chip_album: t('pedit.chip_album'), chip_playlist: t('pedit.chip_playlist'), chip_track: t('pedit.chip_track'), flip_h: t('imed.flip_h'), flip_v: t('imed.flip_v'), js_embed_invalid: t('pedit.js_embed_invalid'), js_embed_prompt: t('pedit.js_embed_prompt'), js_failed: t('pedit.js_failed'), js_inserted: t('pedit.js_inserted'), js_link_prompt: t('pedit.js_link_prompt'), js_no_tracks_found: t('pedit.js_no_tracks_found'), js_no_tracks_yet: t('pedit.js_no_tracks_yet'), js_playlist_choose: t('pedit.js_playlist_choose'), js_playlist_editor_missing: t('pedit.js_playlist_editor_missing'), js_playlist_existing: t('pedit.js_playlist_existing'), js_tracks_load_fail: t('pedit.js_tracks_load_fail'), js_tracks_loading: t('pedit.js_tracks_loading'), js_uploaded: t('pedit.js_uploaded'), js_uploading: t('pedit.js_uploading'), pin_nth_suffix: t('pedit.pin_nth_suffix'), pin_top: t('pedit.pin_top'), reset: t('imed.reset'), rotate_left: t('imed.rotate_left'), rotate_right: t('imed.rotate_right'), tb_done: t('pedit.tb_done'), tb_fullscreen: t('pedit.tb_fullscreen'), title: t('imed.title'), zoom_in: t('imed.zoom_in'), zoom_out: t('imed.zoom_out') } }) %>