/**
* NeoCMS administration client.
*
* The public page is loaded into a same-origin iframe, edited in memory, and only written to disk
* through authenticated API calls. Keeping that distinction explicit prevents a modal's Save
* button from accidentally becoming a rather enthusiastic Publish button.
*/
(function ($) {
'use strict';
// References to the active iframe document and currently selected editable element.
let iframeDoc = null;
let currentElement = null;
let currentImage = null;
// Dirty state protects unpublished browser changes from accidental navigation.
let dirty = false;
// Permissions are replaced by the authoritative dashboard response after initialisation.
let permissions = {draft: true, publish: false, schedule: false, manage: false};
// Remember offered drafts so an iframe rewrite does not prompt for the same draft twice.
const loadedDrafts = new Set();
// True once the page shown has been saved as a server-side draft (cleared by any navigation).
let draftSaved = false;
// Path of the page shown in the preview. Writing a draft into the frame (document.open) makes the frame's own URL
// become the admin page's, so the real page path is captured when the page loads instead.
let framePath = '/';
// Set while a saved draft is written into the preview, so its load event is not treated as navigation.
let writingDraft = false;
// URL of a preview page that was reloaded to bypass the browser cache; its next load is the fresh one.
let revalidatedHref = null;
// Server-rendered values keep selectors and mutating requests aligned with configuration.
const csrfToken = $('meta[name="csrf-token"]').attr('content') || '';
const editableClass = $('meta[name="neo-editable-class"]').attr('content') || 'editable';
const editableSelector = '.' + editableClass;
// A styled box standing in for a photo: role="img" on something other than an or