mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-01 08:45:44 +00:00
576 lines
23 KiB
JavaScript
576 lines
23 KiB
JavaScript
/**
|
||
* JS раздела «Медиа»: файловый браузер, upload и простой crop-редактор.
|
||
*/
|
||
(function (window, document) {
|
||
'use strict';
|
||
|
||
var Adminx = window.Adminx || (window.Adminx = {});
|
||
|
||
function esc(s) {
|
||
return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) {
|
||
return { '&': '&', '<': '<', '>': '>', '"': '"' }[c];
|
||
});
|
||
}
|
||
|
||
function submitAction(url, data, onDone) {
|
||
var form = document.getElementById('mediaActionForm');
|
||
var fd;
|
||
if (!form) { return; }
|
||
fd = new FormData(form);
|
||
Object.keys(data || {}).forEach(function (key) {
|
||
fd.set(key, data[key]);
|
||
});
|
||
Adminx.Loader.show();
|
||
Adminx.Ajax.post(url, fd).then(function (payload) {
|
||
Adminx.Loader.hide();
|
||
var d = payload.data || {};
|
||
if (d.success && typeof onDone === 'function') {
|
||
onDone(d);
|
||
}
|
||
Adminx.Ajax.handle(payload);
|
||
if (d.success && !d.redirect) {
|
||
window.location.reload();
|
||
}
|
||
}).catch(function () {
|
||
Adminx.Loader.hide();
|
||
Adminx.Toast.show('Ошибка сети', 'error');
|
||
});
|
||
}
|
||
|
||
Adminx.Media = {
|
||
pendingPreview: '',
|
||
|
||
init: function () {
|
||
var self = this;
|
||
this.bindUpload();
|
||
this.bindCrop();
|
||
this.bindLightbox();
|
||
this.bindLivePreview();
|
||
|
||
document.addEventListener('click', function (e) {
|
||
var folder = e.target.closest('[data-media-folder]');
|
||
var rename = e.target.closest('[data-media-rename]');
|
||
var del = e.target.closest('[data-media-delete]');
|
||
var clearThumbs = e.target.closest('[data-media-clear-thumbs]');
|
||
var copy = e.target.closest('[data-copy]');
|
||
var webp = e.target.closest('[data-media-webp]');
|
||
var zoom = e.target.closest('[data-media-result-zoom]');
|
||
|
||
if (folder) { self.createFolder(); return; }
|
||
if (rename) { self.rename(rename); return; }
|
||
if (del) { self.remove(del); return; }
|
||
if (clearThumbs) { self.clearThumbnails(clearThumbs); return; }
|
||
if (webp) { self.convertWebp(webp); return; }
|
||
if (zoom) { e.preventDefault(); self.openResultLarge(); return; }
|
||
if (copy) { self.copy(copy.getAttribute('data-copy')); }
|
||
});
|
||
},
|
||
|
||
base: function () {
|
||
return Adminx.base();
|
||
},
|
||
|
||
modalInput: function (cfg, done) {
|
||
var overlay = document.createElement('div');
|
||
overlay.className = 'overlay';
|
||
overlay.innerHTML =
|
||
'<div class="modal media-prompt" role="dialog" aria-modal="true">' +
|
||
'<div class="modal-header">' +
|
||
'<span class="dialog-icon info"><i class="ti ti-pencil"></i></span>' +
|
||
'<div style="flex:1"><h3>' + esc(cfg.title) + '</h3>' +
|
||
'<p class="text-secondary" style="margin-top:4px">' + esc(cfg.message || '') + '</p></div>' +
|
||
'<button class="modal-close" type="button" data-cancel aria-label="Закрыть"><i class="ti ti-x"></i></button>' +
|
||
'</div>' +
|
||
'<div class="modal-body"><input class="input" type="text" value="' + esc(cfg.value || '') + '" data-input></div>' +
|
||
'<div class="modal-footer"><button class="btn btn-ghost" type="button" data-cancel>Отмена</button>' +
|
||
'<button class="btn btn-primary" type="button" data-ok style="margin-left:auto">' + esc(cfg.ok || 'Сохранить') + '</button></div>' +
|
||
'</div>';
|
||
document.body.appendChild(overlay);
|
||
requestAnimationFrame(function () { overlay.classList.add('show'); });
|
||
var input = overlay.querySelector('[data-input]');
|
||
var close = function () {
|
||
overlay.classList.remove('show');
|
||
setTimeout(function () { overlay.remove(); document.removeEventListener('keydown', onKey); }, 180);
|
||
};
|
||
var apply = function () {
|
||
var value = (input.value || '').trim();
|
||
close();
|
||
if (value) { done(value); }
|
||
};
|
||
var onKey = function (e) {
|
||
if (e.key === 'Escape') { close(); }
|
||
if (e.key === 'Enter') { apply(); }
|
||
};
|
||
overlay.addEventListener('click', function (e) {
|
||
if (e.target === overlay || e.target.closest('[data-cancel]')) { close(); }
|
||
if (e.target.closest('[data-ok]')) { apply(); }
|
||
});
|
||
document.addEventListener('keydown', onKey);
|
||
input.focus();
|
||
input.select();
|
||
},
|
||
|
||
createFolder: function () {
|
||
var dir = document.querySelector('input[name="dir"]');
|
||
var self = this;
|
||
this.modalInput({
|
||
title: 'Создать папку',
|
||
message: 'Папка будет создана в текущем каталоге.',
|
||
value: '',
|
||
ok: 'Создать'
|
||
}, function (name) {
|
||
submitAction(self.base() + '/media/folders', {
|
||
dir: dir ? dir.value : '/uploads',
|
||
name: name
|
||
});
|
||
});
|
||
},
|
||
|
||
rename: function (button) {
|
||
var self = this;
|
||
var kind = button.getAttribute('data-kind') === 'folder' ? 'папку' : 'файл';
|
||
this.modalInput({
|
||
title: 'Переименовать ' + kind,
|
||
message: button.getAttribute('data-path') || '',
|
||
value: button.getAttribute('data-name') || '',
|
||
ok: 'Переименовать'
|
||
}, function (name) {
|
||
submitAction(self.base() + '/media/rename', {
|
||
path: button.getAttribute('data-path') || '',
|
||
name: name
|
||
});
|
||
});
|
||
},
|
||
|
||
remove: function (button) {
|
||
var self = this;
|
||
var name = button.getAttribute('data-name') || button.getAttribute('data-path') || '';
|
||
var kind = button.getAttribute('data-kind') === 'folder' ? 'папку и всё содержимое' : 'файл';
|
||
Adminx.Confirm.open({
|
||
kind: 'error',
|
||
title: 'Удалить ' + kind + '?',
|
||
message: '«' + name + '» будет удалён с диска.',
|
||
confirmLabel: 'Удалить',
|
||
confirmClass: 'btn-danger',
|
||
onConfirm: function () {
|
||
submitAction(self.base() + '/media/delete', {
|
||
path: button.getAttribute('data-path') || ''
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
clearThumbnails: function (button) {
|
||
var self = this;
|
||
var name = button.getAttribute('data-name') || button.getAttribute('data-path') || '';
|
||
Adminx.Confirm.open({
|
||
kind: 'warning',
|
||
title: 'Удалить все превью?',
|
||
message: 'Во всех вложенных папках «' + name + '» будут удалены только служебные каталоги превью. Исходные файлы останутся на месте.',
|
||
confirmLabel: 'Удалить превью',
|
||
confirmClass: 'btn-danger',
|
||
onConfirm: function () {
|
||
submitAction(self.base() + '/media/clear-thumbnails', {
|
||
path: button.getAttribute('data-path') || ''
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
copy: function (text) {
|
||
if (!text) { return; }
|
||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||
navigator.clipboard.writeText(text).then(function () {
|
||
Adminx.Toast.show('Путь скопирован', 'success');
|
||
});
|
||
} else {
|
||
Adminx.Toast.show(text, 'info');
|
||
}
|
||
},
|
||
|
||
bindUpload: function () {
|
||
var form = document.getElementById('mediaUploadForm');
|
||
if (!form) { return; }
|
||
var input = form.querySelector('input[type="file"]');
|
||
var drop = form.querySelector('[data-media-drop]');
|
||
var trigger = document.querySelector('[data-media-upload-trigger]');
|
||
var status = form.querySelector('[data-media-upload-status]');
|
||
var self = this;
|
||
var depth = 0;
|
||
|
||
function setStatus(text, cls) {
|
||
if (!status) { return; }
|
||
status.textContent = text || '';
|
||
status.className = cls ? 'is-' + cls : '';
|
||
}
|
||
|
||
function upload(files) {
|
||
var fd;
|
||
var i;
|
||
if (!files || !files.length) {
|
||
setStatus('Файлы не выбраны', 'error');
|
||
return;
|
||
}
|
||
fd = new FormData(form);
|
||
fd.delete('files[]');
|
||
for (i = 0; i < files.length; i++) {
|
||
fd.append('files[]', files[i]);
|
||
}
|
||
form.classList.add('is-loading');
|
||
setStatus('Загрузка: ' + files.length, 'progress');
|
||
Adminx.Ajax.post(self.base() + '/media/upload', fd).then(function (payload) {
|
||
form.classList.remove('is-loading');
|
||
var d = payload.data || {};
|
||
if (d.success) {
|
||
setStatus(d.message || 'Готово', 'success');
|
||
Adminx.Ajax.handle(payload);
|
||
} else {
|
||
setStatus(d.message || 'Ошибка загрузки', 'error');
|
||
Adminx.Toast.show(d.message || 'Ошибка загрузки', 'error');
|
||
}
|
||
}).catch(function () {
|
||
form.classList.remove('is-loading');
|
||
setStatus('Ошибка сети', 'error');
|
||
});
|
||
}
|
||
|
||
if (trigger) {
|
||
trigger.addEventListener('click', function () { input.click(); });
|
||
}
|
||
drop.addEventListener('click', function () { input.click(); });
|
||
input.addEventListener('change', function () { upload(input.files); });
|
||
drop.addEventListener('dragenter', function (e) {
|
||
e.preventDefault(); depth++; drop.classList.add('is-dragover'); setStatus('Отпустите файлы', 'progress');
|
||
});
|
||
drop.addEventListener('dragover', function (e) {
|
||
e.preventDefault(); drop.classList.add('is-dragover');
|
||
});
|
||
drop.addEventListener('dragleave', function (e) {
|
||
e.preventDefault(); depth = Math.max(0, depth - 1); if (depth === 0) { drop.classList.remove('is-dragover'); }
|
||
});
|
||
drop.addEventListener('drop', function (e) {
|
||
e.preventDefault(); depth = 0; drop.classList.remove('is-dragover'); upload(e.dataTransfer ? e.dataTransfer.files : null);
|
||
});
|
||
},
|
||
|
||
bindLightbox: function () {
|
||
var self = this;
|
||
var modal = document.querySelector('[data-media-lightbox]');
|
||
if (!modal) { return; }
|
||
this.lightbox = {
|
||
modal: modal,
|
||
image: modal.querySelector('[data-media-lightbox-image]'),
|
||
title: modal.querySelector('[data-media-lightbox-title]'),
|
||
meta: modal.querySelector('[data-media-lightbox-meta]')
|
||
};
|
||
|
||
document.addEventListener('click', function (e) {
|
||
var trigger = e.target.closest('[data-media-preview]');
|
||
if (trigger) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
self.openLightbox(
|
||
trigger.getAttribute('data-media-preview') || '',
|
||
trigger.getAttribute('data-media-preview-name') || 'Предпросмотр',
|
||
trigger.getAttribute('data-media-preview-meta') || ''
|
||
);
|
||
return;
|
||
}
|
||
if (e.target === modal || e.target.closest('[data-media-lightbox-close]')) {
|
||
self.closeLightbox();
|
||
}
|
||
});
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape' && !modal.hasAttribute('hidden')) {
|
||
self.closeLightbox();
|
||
}
|
||
});
|
||
// Уходя со страницы с открытым превью — подчистить временный файл.
|
||
window.addEventListener('pagehide', function () { self.clearPreview(true); });
|
||
},
|
||
|
||
openLightbox: function (url, title, meta) {
|
||
var lb = this.lightbox;
|
||
if (!lb) { return; }
|
||
if (lb.image) { lb.image.src = url || ''; lb.image.alt = title || ''; }
|
||
if (lb.title) { lb.title.textContent = title || 'Предпросмотр'; }
|
||
if (lb.meta) { lb.meta.textContent = meta || ''; }
|
||
lb.modal.removeAttribute('hidden');
|
||
document.documentElement.classList.add('media-lightbox-open');
|
||
},
|
||
|
||
closeLightbox: function () {
|
||
var lb = this.lightbox;
|
||
if (!lb) { return; }
|
||
lb.modal.setAttribute('hidden', 'hidden');
|
||
if (lb.image) { lb.image.src = ''; }
|
||
document.documentElement.classList.remove('media-lightbox-open');
|
||
},
|
||
|
||
// Встроенный «Результат»: живой серверный рендер по настройкам + выделению.
|
||
bindLivePreview: function () {
|
||
var self = this;
|
||
var form = document.querySelector('.media-transform-form');
|
||
var result = document.querySelector('[data-media-result]');
|
||
if (!form || !result) { return; }
|
||
this.result = {
|
||
img: result.querySelector('[data-media-result-img]'),
|
||
meta: result.querySelector('[data-media-result-meta]'),
|
||
empty: result.querySelector('[data-media-result-empty]')
|
||
};
|
||
var timer = null;
|
||
var onChange = function () {
|
||
clearTimeout(timer);
|
||
timer = setTimeout(function () { self.refreshResult(); }, 300);
|
||
};
|
||
form.addEventListener('change', onChange);
|
||
form.addEventListener('input', onChange);
|
||
// Первый рендер результата при открытии страницы.
|
||
setTimeout(function () { self.refreshResult(); }, 60);
|
||
},
|
||
|
||
// Сгенерировать серверный результат по текущим настройкам и показать во встроенном блоке.
|
||
refreshResult: function () {
|
||
var self = this;
|
||
var form = document.querySelector('.media-transform-form');
|
||
if (!form || !this.result) { return; }
|
||
var reqId = (this.resultReq = (this.resultReq || 0) + 1);
|
||
Adminx.Ajax.post(this.base() + '/media/preview', new FormData(form)).then(function (payload) {
|
||
if (reqId !== self.resultReq) { return; } // пришёл ответ на устаревший запрос
|
||
var d = payload.data || {};
|
||
if (!d.success) { return; }
|
||
var data = d.data || {};
|
||
self.clearPreview(false);
|
||
self.pendingPreview = data.path || '';
|
||
self.resultUrl = (data.url || '') + '?t=' + Date.now();
|
||
self.resultMeta = data.meta || '';
|
||
if (self.result.img) { self.result.img.src = self.resultUrl; self.result.img.style.display = ''; }
|
||
if (self.result.empty) { self.result.empty.style.display = 'none'; }
|
||
if (self.result.meta) { self.result.meta.textContent = data.meta || ''; }
|
||
// Если открыт крупный просмотр — обновить и его.
|
||
if (self.lightbox && !self.lightbox.modal.hasAttribute('hidden') && self.lightbox.image) {
|
||
self.lightbox.image.src = self.resultUrl;
|
||
if (self.lightbox.meta) { self.lightbox.meta.textContent = data.meta || ''; }
|
||
}
|
||
}).catch(function () {});
|
||
},
|
||
|
||
// Открыть текущий результат крупно (лупа/кнопка «Крупно»).
|
||
openResultLarge: function () {
|
||
if (this.resultUrl) {
|
||
this.openLightbox(this.resultUrl, 'Результат по настройкам', this.resultMeta || '');
|
||
} else {
|
||
this.refreshResult();
|
||
Adminx.Toast.show('Готовлю результат…', 'info');
|
||
}
|
||
},
|
||
|
||
// Удалить временный превью-файл (при закрытии окна или уходе со страницы).
|
||
clearPreview: function (useBeacon) {
|
||
var path = this.pendingPreview;
|
||
if (!path) { return; }
|
||
this.pendingPreview = '';
|
||
var url = this.base() + '/media/preview-clear';
|
||
var fd = new FormData();
|
||
fd.append('_csrf', Adminx.csrf());
|
||
fd.append('path', path);
|
||
if (useBeacon && navigator.sendBeacon) {
|
||
navigator.sendBeacon(url, fd);
|
||
return;
|
||
}
|
||
Adminx.Ajax.post(url, fd).catch(function () {});
|
||
},
|
||
|
||
convertWebp: function (button) {
|
||
var form = button.closest('form');
|
||
var fd;
|
||
if (!form) { return; }
|
||
Adminx.Confirm.open({
|
||
kind: 'info',
|
||
title: 'Создать WebP-копию?',
|
||
message: 'Будет создан соседний WebP-файл с тем же именем исходника.',
|
||
confirmLabel: 'Создать WebP',
|
||
confirmClass: 'btn-primary',
|
||
onConfirm: function () {
|
||
fd = new FormData();
|
||
fd.append('_csrf', Adminx.csrf());
|
||
fd.append('path', button.getAttribute('data-path') || '');
|
||
fd.append('quality', form.querySelector('[name="quality"]') ? form.querySelector('[name="quality"]').value : '82');
|
||
Adminx.Loader.show();
|
||
Adminx.Ajax.post(Adminx.Media.base() + '/media/convert-webp', fd).then(function (payload) {
|
||
Adminx.Loader.hide();
|
||
Adminx.Ajax.handle(payload);
|
||
}).catch(function () {
|
||
Adminx.Loader.hide();
|
||
Adminx.Toast.show('Ошибка сети', 'error');
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
bindCrop: function () {
|
||
var root = document.querySelector('[data-media-editor]');
|
||
if (!root) { return; }
|
||
var img = root.querySelector('[data-crop-image]');
|
||
var frame = root.querySelector('[data-crop-frame]');
|
||
var box = root.querySelector('[data-crop-box]');
|
||
var form = root.querySelector('.media-transform-form');
|
||
if (!img || !frame || !box || !form) { return; }
|
||
|
||
var state = { x: 12, y: 12, w: 76, h: 76, drag: null, sx: 0, sy: 0 };
|
||
var sizeBadge = box.querySelector('[data-crop-size]');
|
||
var ratioSelect = form.querySelector('[data-crop-ratio]');
|
||
var inputs = {
|
||
enabled: form.querySelector('[data-crop-enabled]'),
|
||
x: form.querySelector('[data-crop-input="x"]'),
|
||
y: form.querySelector('[data-crop-input="y"]'),
|
||
w: form.querySelector('[data-crop-input="w"]'),
|
||
h: form.querySelector('[data-crop-input="h"]'),
|
||
outputW: form.querySelector('[name="width"]'),
|
||
outputH: form.querySelector('[name="height"]')
|
||
};
|
||
|
||
function aspectLabel(w, h) {
|
||
function gcd(a, b) { return b ? gcd(b, a % b) : a; }
|
||
var g = gcd(w, h) || 1;
|
||
var rw = Math.round(w / g);
|
||
var rh = Math.round(h / g);
|
||
if (rw <= 40 && rh <= 40) { return rw + ':' + rh; }
|
||
return (w / Math.max(1, h)).toFixed(2) + ':1';
|
||
}
|
||
|
||
function sync(syncOutputSize) {
|
||
var rect = img.getBoundingClientRect();
|
||
var naturalW = img.naturalWidth || 1;
|
||
var naturalH = img.naturalHeight || 1;
|
||
var full = state.x === 0 && state.y === 0 && state.w === 100 && state.h === 100;
|
||
var px = Math.round((state.x / 100) * naturalW);
|
||
var py = Math.round((state.y / 100) * naturalH);
|
||
var pw = Math.max(1, Math.round((state.w / 100) * naturalW));
|
||
var ph = Math.max(1, Math.round((state.h / 100) * naturalH));
|
||
box.style.left = state.x + '%';
|
||
box.style.top = state.y + '%';
|
||
box.style.width = state.w + '%';
|
||
box.style.height = state.h + '%';
|
||
if (inputs.enabled) { inputs.enabled.value = full ? '0' : '1'; }
|
||
if (inputs.x) { inputs.x.value = px; }
|
||
if (inputs.y) { inputs.y.value = py; }
|
||
if (inputs.w) { inputs.w.value = pw; }
|
||
if (inputs.h) { inputs.h.value = ph; }
|
||
if (syncOutputSize && inputs.outputW && inputs.outputH) {
|
||
var outputScale = Math.min(1, 2400 / pw, 2400 / ph);
|
||
inputs.outputW.value = Math.max(1, Math.round(pw * outputScale));
|
||
inputs.outputH.value = Math.max(1, Math.round(ph * outputScale));
|
||
}
|
||
if (sizeBadge) {
|
||
sizeBadge.textContent = (full ? 'Весь кадр · ' : 'Выделение · ') + pw + '×' + ph + ' px · ' + aspectLabel(pw, ph);
|
||
}
|
||
// Кроп-инпуты меняются программно — уведомим живое превью вручную.
|
||
if (inputs.w) { inputs.w.dispatchEvent(new Event('input', { bubbles: true })); }
|
||
return rect;
|
||
}
|
||
|
||
function clamp(preserveSize) {
|
||
if (preserveSize) {
|
||
state.w = Math.max(1, Math.min(100, state.w));
|
||
state.h = Math.max(1, Math.min(100, state.h));
|
||
state.x = Math.max(0, Math.min(100 - state.w, state.x));
|
||
state.y = Math.max(0, Math.min(100 - state.h, state.y));
|
||
return;
|
||
}
|
||
state.x = Math.max(0, Math.min(99, state.x));
|
||
state.y = Math.max(0, Math.min(99, state.y));
|
||
state.w = Math.max(1, Math.min(100 - state.x, state.w));
|
||
state.h = Math.max(1, Math.min(100 - state.y, state.h));
|
||
}
|
||
|
||
function ratio(value) {
|
||
if (!value || value === 'free' || value === 'original') { return 0; }
|
||
var p = value.split(':');
|
||
return p.length === 2 ? (parseFloat(p[0]) / parseFloat(p[1])) : 0;
|
||
}
|
||
|
||
function setRatio(value) {
|
||
var r = ratio(value);
|
||
var rect = img.getBoundingClientRect();
|
||
var imageRatio = rect.height > 0 ? rect.width / rect.height : ((img.naturalWidth || 1) / (img.naturalHeight || 1));
|
||
if (!r || !imageRatio) {
|
||
state = { x: 0, y: 0, w: 100, h: 100, drag: null, sx: 0, sy: 0 };
|
||
} else if (r >= imageRatio) {
|
||
state.w = 76;
|
||
state.h = (state.w * imageRatio) / r;
|
||
state.x = 12;
|
||
state.y = (100 - state.h) / 2;
|
||
} else {
|
||
state.h = 76;
|
||
state.w = (state.h * r) / imageRatio;
|
||
state.y = 12;
|
||
state.x = (100 - state.w) / 2;
|
||
}
|
||
clamp();
|
||
sync(true);
|
||
}
|
||
|
||
box.addEventListener('mousedown', function (e) {
|
||
var handle = e.target.getAttribute('data-crop-handle');
|
||
state.drag = handle || 'move';
|
||
state.sx = e.clientX;
|
||
state.sy = e.clientY;
|
||
e.preventDefault();
|
||
});
|
||
document.addEventListener('mousemove', function (e) {
|
||
var rect;
|
||
var dx;
|
||
var dy;
|
||
if (!state.drag) { return; }
|
||
rect = img.getBoundingClientRect();
|
||
dx = ((e.clientX - state.sx) / rect.width) * 100;
|
||
dy = ((e.clientY - state.sy) / rect.height) * 100;
|
||
state.sx = e.clientX;
|
||
state.sy = e.clientY;
|
||
if (state.drag === 'move') {
|
||
state.x += dx; state.y += dy;
|
||
clamp(true);
|
||
sync(false);
|
||
} else {
|
||
if (state.drag.indexOf('e') !== -1) { state.w += dx; }
|
||
if (state.drag.indexOf('s') !== -1) { state.h += dy; }
|
||
if (state.drag.indexOf('w') !== -1) { state.x += dx; state.w -= dx; }
|
||
if (state.drag.indexOf('n') !== -1) { state.y += dy; state.h -= dy; }
|
||
if (ratioSelect) { ratioSelect.value = 'free'; }
|
||
clamp(false);
|
||
sync(true);
|
||
}
|
||
});
|
||
document.addEventListener('mouseup', function () { state.drag = null; });
|
||
|
||
if (ratioSelect) {
|
||
ratioSelect.addEventListener('change', function (e) { setRatio(e.target.value); });
|
||
}
|
||
form.querySelector('[data-crop-reset]').addEventListener('click', function () {
|
||
state = { x: 0, y: 0, w: 100, h: 100, drag: null, sx: 0, sy: 0 };
|
||
sync(true);
|
||
});
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault();
|
||
Adminx.Loader.show();
|
||
Adminx.Ajax.post(form.action, new FormData(form)).then(function (payload) {
|
||
Adminx.Loader.hide();
|
||
Adminx.Ajax.handle(payload);
|
||
}).catch(function () {
|
||
Adminx.Loader.hide();
|
||
Adminx.Toast.show('Ошибка сети', 'error');
|
||
});
|
||
});
|
||
window.addEventListener('resize', sync);
|
||
if (img.complete) { sync(); } else { img.addEventListener('load', sync); }
|
||
}
|
||
};
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', function () { Adminx.Media.init(); });
|
||
} else {
|
||
Adminx.Media.init();
|
||
}
|
||
})(window, document);
|