(function (window, document) { 'use strict'; var Adminx = window.Adminx || (window.Adminx = {}); Adminx.PublicPresentations = { form: null, drawer: null, dirty: false, loading: false, needsReload: false, currentRevision: 0, activeEditor: 'draft_item_markup', targetCatalog: {}, init: function () { this.form = document.getElementById('publicPresentationForm'); this.drawer = document.getElementById('publicPresentationDrawer'); if (!this.form || !this.drawer) { return; } this.targetCatalog = this.readJson('[data-presentation-targets]'); this.targetTypeChanged(); var self = this; document.addEventListener('click', function (event) { var target = event.target; if (target.closest('[data-presentation-new]')) { self.openNew(); return; } var edit = target.closest('[data-presentation-edit]'); if (edit) { self.open(Number(edit.getAttribute('data-presentation-edit'))); return; } var history = target.closest('[data-presentation-revisions]'); if (history) { self.open(Number(history.getAttribute('data-presentation-revisions')), 'revisions'); return; } var copy = target.closest('[data-presentation-copy]'); if (copy) { self.copy(Number(copy.getAttribute('data-presentation-copy'))); return; } var remove = target.closest('[data-presentation-delete]'); if (remove) { self.remove(Number(remove.getAttribute('data-presentation-delete'))); return; } var tab = target.closest('[data-presentation-tab]'); if (tab) { self.tab(tab.getAttribute('data-presentation-tab')); return; } if (target.closest('[data-presentation-save]')) { self.save().catch(function () {}); return; } if (target.closest('[data-presentation-publish]')) { self.publish(); return; } if (target.closest('[data-presentation-lint]')) { self.lint(); return; } if (target.closest('[data-presentation-preview]')) { self.preview(); return; } if (target.closest('[data-presentation-diagnose]')) { self.diagnose(); return; } var fullscreen = target.closest('[data-presentation-fullscreen]'); if (fullscreen) { self.fullscreen(fullscreen.getAttribute('data-presentation-fullscreen')); return; } var variable = target.closest('[data-presentation-variable]'); if (variable) { self.insertVariable(variable.getAttribute('data-presentation-variable')); return; } if (target.closest('[data-presentation-assignment-save]')) { self.saveAssignment(); return; } var assignmentDelete = target.closest('[data-presentation-assignment-delete]'); if (assignmentDelete) { self.deleteAssignment(Number(assignmentDelete.getAttribute('data-presentation-assignment-delete'))); return; } var revision = target.closest('[data-presentation-revision]'); if (revision) { self.showRevision(Number(revision.getAttribute('data-presentation-revision'))); return; } if (target.closest('[data-presentation-revision-restore]')) { self.restoreRevision(); return; } if (target.closest('[data-presentation-revision-delete]')) { self.deleteRevision(); return; } if (target.closest('[data-presentation-revisions-clear]')) { self.clearRevisions(); } }); document.addEventListener('click', function (event) { var close = event.target.closest('[data-close-drawer]'); if (!close || !self.drawer.contains(close)) { return; } if (self.dirty) { event.preventDefault(); event.stopImmediatePropagation(); self.confirmDiscard(); return; } if (self.needsReload) { window.location.reload(); } }, true); document.addEventListener('keydown', function (event) { if ((event.ctrlKey || event.metaKey) && String(event.key).toLowerCase() === 's' && self.drawer.classList.contains('show')) { event.preventDefault(); self.save().catch(function () {}); return; } if (event.key === 'Escape' && self.dirty && self.drawer.classList.contains('show')) { event.preventDefault(); event.stopImmediatePropagation(); self.confirmDiscard(); } }, true); this.form.addEventListener('input', function (event) { if (!self.loading && !event.target.closest('.public-presentation-assignment-form')) { self.setDirty(true); } }); this.form.addEventListener('change', function (event) { if (!self.loading && !event.target.closest('.public-presentation-assignment-form') && event.target.name !== 'preview_document_id') { self.setDirty(true); } if (event.target.name === 'assignment_target_type') { self.targetTypeChanged(); } }); this.form.addEventListener('submit', function (event) { event.preventDefault(); self.save().catch(function () {}); }); setTimeout(function () { self.bindEditors(); }, 120); }, base: function () { return this.form.getAttribute('data-base') || ''; }, resource: function () { return this.base() + '/public-site/presentations'; }, revisionResource: function () { return this.base() + '/public-site/presentation-revisions'; }, assignmentResource: function () { return this.base() + '/public-site/presentation-assignments'; }, id: function () { return Number(this.field('id').value) || 0; }, field: function (name) { return this.form.querySelector('[name="' + name + '"]'); }, editor: function (name) { var field = this.field(name); return field && field._adminxCodeMirror ? field._adminxCodeMirror : null; }, request: function (url, options) { return Adminx.Ajax.request(url, options || {}).then(function (response) { var payload = response.data || {}; if (!response.ok || !payload.success) { var error = new Error(payload.message || ('HTTP ' + response.status)); error.payload = payload; throw error; } return payload; }); }, bindEditors: function () { var self = this; ['draft_item_markup', 'draft_wrapper_markup', 'draft_empty_markup', 'draft_css'].forEach(function (name) { var editor = self.editor(name); if (!editor || editor._publicPresentationBound) { return; } editor._publicPresentationBound = true; editor.on('focus', function () { self.activeEditor = name; }); editor.on('change', function () { if (!self.loading) { self.setDirty(true); } }); }); }, openNew: function () { this.loading = true; this.form.reset(); this.field('id').value = ''; this.field('title').value = 'Новое представление'; this.field('code').value = 'presentation_' + String(Date.now()).slice(-8); this.field('kind').value = 'card'; this.setCode('draft_item_markup', this.defaultValue('item')); this.setCode('draft_wrapper_markup', this.defaultValue('wrapper')); this.setCode('draft_empty_markup', this.defaultValue('empty')); this.setCode('draft_css', ''); this.loading = false; this.needsReload = false; this.currentRevision = 0; this.setStatus({is_published: false, version: 0, has_changes: true}); this.renderAssignments([]); this.clearRevisionView(); this.clearPreview(); this.clearDiagnostics(); this.setDirty(false); this.tab('item'); Adminx.Drawer.open('publicPresentationDrawer'); this.refreshEditors(); }, open: function (id, panel) { var self = this; Adminx.Loader.show(); this.request(this.resource() + '/' + id).then(function (payload) { self.fill(payload.data || {}); self.tab(panel || 'item'); Adminx.Drawer.open('publicPresentationDrawer'); self.refreshEditors(); if (panel === 'revisions') { self.loadRevisions(); } }).catch(function (error) { self.fail(error); }).finally(function () { Adminx.Loader.hide(); }); }, fill: function (item) { this.loading = true; ['id', 'title', 'code', 'kind', 'description', 'draft_settings_json'].forEach(function (name) { var field = this.field(name); if (field) { field.value = item[name] == null ? (name === 'draft_settings_json' ? '{}' : '') : item[name]; } }, this); ['draft_item_markup', 'draft_wrapper_markup', 'draft_empty_markup', 'draft_css'].forEach(function (name) { this.setCode(name, item[name] || ''); }, this); this.loading = false; this.needsReload = false; this.currentRevision = 0; this.setStatus(item); this.renderAssignments(item.assignments || []); this.clearRevisionView(); this.clearPreview(); this.clearDiagnostics(); this.setDirty(false); }, defaultValue: function (name) { var field = document.querySelector('[data-presentation-default="' + name + '"]'); return field ? field.value : ''; }, setCode: function (name, value) { var field = this.field(name), editor = this.editor(name); if (field) { field.value = value || ''; } if (editor) { editor.setValue(value || ''); } }, sync: function () { if (Adminx.CodeEditor) { Adminx.CodeEditor.syncAll(this.form); } }, refreshEditors: function () { this.bindEditors(); setTimeout(function () { if (Adminx.CodeEditor) { Adminx.CodeEditor.refreshAll(); } }, 80); }, tab: function (name) { this.drawer.querySelectorAll('[data-presentation-tab]').forEach(function (button) { var active = button.getAttribute('data-presentation-tab') === name; button.classList.toggle('is-active', active); button.setAttribute('aria-selected', active ? 'true' : 'false'); }); this.drawer.querySelectorAll('[data-presentation-panel]').forEach(function (panel) { panel.hidden = panel.getAttribute('data-presentation-panel') !== name; }); if (name === 'item') { this.activeEditor = 'draft_item_markup'; } if (name === 'wrapper') { this.activeEditor = 'draft_wrapper_markup'; } if (name === 'empty') { this.activeEditor = 'draft_empty_markup'; } if (name === 'css') { this.activeEditor = 'draft_css'; } if (name === 'revisions') { this.loadRevisions(); } this.refreshEditors(); }, save: function (quiet) { var self = this; this.sync(); var id = this.id(); this.setSaveState('Сохранение...', ''); return this.request(this.resource() + (id ? '/' + id : ''), { method: 'POST', body: new FormData(this.form) }).then(function (payload) { var savedId = Number((payload.data || {}).id) || id; self.field('id').value = savedId; self.needsReload = true; self.setDirty(false); self.setSaveState('Черновик сохранён', 'ok'); if (!quiet) { Adminx.Toast.show(payload.message || 'Черновик сохранён', 'success'); } return savedId; }).catch(function (error) { self.setSaveState('Не сохранено', 'error'); self.fail(error); throw error; }); }, publish: function () { var self = this; this.save(true).then(function (id) { var data = new FormData(); data.set('_csrf', Adminx.csrf()); return self.request(self.resource() + '/' + id + '/publish', {method: 'POST', body: data}); }).then(function (payload) { self.setStatus(payload.data || {}); self.setSaveState('Опубликовано', 'ok'); self.needsReload = true; Adminx.Toast.show(payload.message || 'Представление опубликовано', 'success'); }).catch(function () {}); }, lint: function () { var self = this; this.sync(); var data = new FormData(this.form); this.setLint('Проверка...', ''); this.request(this.resource() + '/lint', {method: 'POST', body: data}).then(function (payload) { self.setLint(payload.message || 'Twig-синтаксис корректен', 'ok'); }).catch(function (error) { self.setLint(error.message, 'error'); }); }, preview: function () { var self = this; this.sync(); Adminx.Loader.show(); this.request(this.resource() + '/preview', {method: 'POST', body: new FormData(this.form)}).then(function (payload) { var result = payload.data || {}; self.drawer.querySelector('[data-presentation-preview-frame]').srcdoc = result.html || ''; self.drawer.querySelector('[data-presentation-preview-json]').textContent = result.json || '{}'; var state = self.drawer.querySelector('[data-presentation-preview-state] span'); if (state) { state.textContent = result.document && result.document.title ? 'Показан материал «' + result.document.title + '» · ID ' + result.document.id : 'Предпросмотр обновлён.'; } }).catch(function (error) { self.fail(error); }).finally(function () { Adminx.Loader.hide(); }); }, clearPreview: function () { var frame = this.drawer.querySelector('[data-presentation-preview-frame]'); if (frame) { frame.removeAttribute('srcdoc'); } var json = this.drawer.querySelector('[data-presentation-preview-json]'); if (json) { json.textContent = 'После запуска здесь появятся данные, доступные шаблону.'; } var state = this.drawer.querySelector('[data-presentation-preview-state] span'); if (state) { state.textContent = 'Выберите материал и запустите предпросмотр.'; } }, fullscreen: function (name) { var editor = this.editor(name); if (editor && Adminx.CodeEditor) { Adminx.CodeEditor.toggleFullscreen(editor); } }, insertVariable: function (value) { var editorName = this.activeEditor === 'draft_css' ? 'draft_item_markup' : this.activeEditor; var tab = {draft_item_markup: 'item', draft_wrapper_markup: 'wrapper', draft_empty_markup: 'empty'}[editorName] || 'item'; this.tab(tab); var editor = this.editor(editorName); if (editor) { editor.replaceSelection(value || ''); editor.focus(); editor.save(); } }, saveAssignment: function () { var id = this.id(), self = this; if (!id) { Adminx.Toast.show('Сначала сохраните представление', 'warning'); return; } var data = new FormData(); data.set('_csrf', Adminx.csrf()); data.set('context_code', this.field('assignment_context').value); data.set('target_type', this.field('assignment_target_type').value); data.set('target_key', this.field('assignment_target_key').value || '0'); data.set('mode', this.field('assignment_mode').value); this.request(this.resource() + '/' + id + '/assignments', {method: 'POST', body: data}).then(function (payload) { self.renderAssignments((payload.data || {}).assignments || []); self.needsReload = true; Adminx.Toast.show(payload.message || 'Назначение сохранено', 'success'); }).catch(function (error) { self.fail(error); }); }, deleteAssignment: function (id) { var self = this; Adminx.Confirm.open({ kind: 'warning', title: 'Удалить назначение?', message: 'Само представление и публичный шаблон не изменятся.', confirmLabel: 'Удалить', onConfirm: function () { var data = new FormData(); data.set('_csrf', Adminx.csrf()); self.request(self.assignmentResource() + '/' + id + '/delete', {method: 'POST', body: data}).then(function () { self.reloadAssignments(); self.needsReload = true; }).catch(function (error) { self.fail(error); }); } }); }, reloadAssignments: function () { var self = this, id = this.id(); if (!id) { return; } this.request(this.resource() + '/' + id).then(function (payload) { self.renderAssignments((payload.data || {}).assignments || []); }).catch(function (error) { self.fail(error); }); }, renderAssignments: function (items) { var list = this.drawer.querySelector('[data-presentation-assignment-list]'); var contexts = { content_list: 'Список материалов', compact_list: 'Компактный список', slider: 'Слайдер', search_results: 'Результаты поиска', related: 'Похожие материалы', favorites: 'Избранное', viewed: 'Просмотренные материалы', catalog_filters: 'Фильтры каталога', document_page: 'Страница материала' }; if (!items.length) { list.innerHTML = '