(function () { function qs(sel, root) { return (root || document).querySelector(sel) } function qsa(sel, root) { return (root || document).querySelectorAll(sel) } var API_URL = '/ajax/partner_settings_api.php' var MODAL_URL = '/ajax/partner_settings_modal.php' var CK_SRC = '/js/ckeditor/ckeditor.js' function apiPost(fd, cb) { var xhr = new XMLHttpRequest() xhr.open('POST', API_URL, true) xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest') xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return if (xhr.status !== 200) return cb(null) var data = null try { data = JSON.parse(xhr.responseText) } catch (e) {} cb(data) } xhr.send(fd) } function removeModal() { var root = qs('#partnerSettingsRoot') if (root && root._ps_restoreOverflow) { try { root._ps_restoreOverflow() } catch (e) {} } if (root && root.parentNode) root.parentNode.removeChild(root) } function copyText(text) { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text) return } var ta = document.createElement('textarea') ta.value = text ta.style.position = 'fixed' ta.style.left = '-9999px' document.body.appendChild(ta) ta.focus() ta.select() try { document.execCommand('copy') } catch (e) {} document.body.removeChild(ta) } function setHidden(el, hidden) { if (!el) return var cls = el.className || '' var has = cls.indexOf('partner-settings-hidden') !== -1 if (hidden && !has) el.className = (cls + ' partner-settings-hidden').replace(/\s+/g, ' ').trim() if (!hidden && has) el.className = cls.replace(/\bpartner-settings-hidden\b/g, '').replace(/\s+/g, ' ').trim() } function getActivePane(root) { var panes = qsa('[data-ps-pane]', root) var i = 0 while (i < panes.length) { var p = panes[i] if (p.style.display !== 'none') return p i++ } return root } function showOk(root, text) { var pane = getActivePane(root) var ok = qs('[data-ps-ok]', pane) var err = qs('[data-ps-err]', pane) if (err) { err.innerHTML = ''; setHidden(err, true) } if (ok) { ok.textContent = text || ''; setHidden(ok, !text) } } function showErr(root, html) { var pane = getActivePane(root) var ok = qs('[data-ps-ok]', pane) var err = qs('[data-ps-err]', pane) if (ok) { ok.textContent = ''; setHidden(ok, true) } if (err) { err.innerHTML = html || ''; setHidden(err, !html) } } function fullUrlFrom(root, slug) { var base = root.getAttribute('data-ps-base') if (!base) base = 'https://partner.joywork.ru/' var agencyId = root.getAttribute('data-ps-agency-id') || '' slug = (slug || '').trim() if (slug) return base.replace(/\/+$/, '/') + encodeURIComponent(slug) return base.replace(/\/+$/, '/') + '?agency_id=' + encodeURIComponent(String(agencyId)) } function svgTrash18() { return '' } function svgEdit15() { return '' } function svgFile22() { return '' } function svgDownload18() { return '' } function updateOuterPartnerLink(fullUrl) { if (!fullUrl) return var outerRoot = qs('.partner-settings-block') if (!outerRoot) return var a = qs('a', outerRoot) if (!a) return a.setAttribute('href', fullUrl) a.textContent = fullUrl } function bindTabs(root) { var tabs = qs('[data-ps-tabs]', root) if (!tabs) return function activate(tab) { var pane = qs('[data-ps-pane="' + tab + '"]', root) if (!pane) return false var allTabs = qsa('.partner-settings-tab', tabs) for (var i = 0; i < allTabs.length; i++) { allTabs[i].className = allTabs[i].className.replace(/\bis-active\b/g, '').replace(/\s+/g, ' ').trim() } var btn = qs('[data-ps-tab="' + tab + '"]', tabs) if (btn) btn.className = (btn.className + ' is-active').replace(/\s+/g, ' ').trim() var panes = qsa('[data-ps-pane]', root) for (var j = 0; j < panes.length; j++) panes[j].style.display = 'none' pane.style.display = 'block' var ok = qs('[data-ps-ok]', pane) var err = qs('[data-ps-err]', pane) if (ok) { ok.textContent = ''; setHidden(ok, true) } if (err) { err.innerHTML = ''; setHidden(err, true) } if (tab === 'materials') { if (window.partnerSettingsMaterialsLoad) window.partnerSettingsMaterialsLoad() } if (tab === 'news') { if (window.partnerSettingsNewsLoad) window.partnerSettingsNewsLoad() } return true } tabs.addEventListener('click', function (e) { var btn = e.target while (btn && btn !== tabs && !btn.getAttribute('data-ps-tab')) btn = btn.parentNode if (!btn || btn === tabs) return var tab = btn.getAttribute('data-ps-tab') activate(tab) }) var def = root.getAttribute('data-ps-default-tab') if (!def) def = 'url' if (!activate(def)) { var first = qs('[data-ps-tab]', tabs) if (first) { var t = first.getAttribute('data-ps-tab') || '' activate(t) } } } function bindClose(root) { var closeBtns = qsa('[data-ps-close]', root) for (var i = 0; i < closeBtns.length; i++) closeBtns[i].addEventListener('click', removeModal) } function bindUrlRow(root) { var inp = qs('[data-ps-url-input]', root) var copyBtn = qs('[data-ps-url-copy]', root) var urlLink = qs('[data-ps-url-link]', root) if (!inp) return inp.setAttribute('data-ps-original', inp.value || '') root._ps_url_dirty = false inp.addEventListener('input', function () { var orig = inp.getAttribute('data-ps-original') || '' root._ps_url_dirty = (String(inp.value || '').trim() !== String(orig).trim()) }) if (copyBtn) { copyBtn.addEventListener('click', function () { var slugVal = inp ? inp.value : '' var url = fullUrlFrom(root, slugVal) copyText(url) showOk(root, 'Скопировано') }) } root._ps_save_url = function (cb) { showOk(root, '') showErr(root, '') var slug = String(inp.value || '').trim() var fd = new FormData() fd.append('action', 'url_save') fd.append('partner_url', slug) apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error showErr(root, msg) if (cb) cb(false) return } inp.value = data.partner_url || '' inp.setAttribute('data-ps-original', inp.value || '') root._ps_url_dirty = false if (urlLink && data.full_url) { urlLink.setAttribute('href', data.full_url) urlLink.textContent = data.full_url } if (data.full_url) updateOuterPartnerLink(data.full_url) showOk(root, 'URL сохранён') if (cb) cb(true) }) } } function bindBottomSave(root) { var btn = qs('.partner-settings-save', root) if (!btn) return function getActiveTabKey() { var pane = getActivePane(root) if (!pane || !pane.getAttribute) return '' return pane.getAttribute('data-ps-pane') || '' } btn.addEventListener('click', function (e) { e.preventDefault() e.stopPropagation() if (e.stopImmediatePropagation) e.stopImmediatePropagation() var activeTab = getActiveTabKey() if (activeTab === 'url') { if (root._ps_url_dirty && typeof root._ps_save_url === 'function') { root._ps_save_url(function (ok) { if (ok) removeModal() }) return } removeModal() return } if (activeTab === 'materials') { if (typeof root._ps_save_materials === 'function') { root._ps_save_materials(function (ok) { if (ok) removeModal() }) return } removeModal() return } removeModal() }, true) } function bindMaterials(root) { var listBox = qs('[data-ps-mat-list]', root) var empty = qs('[data-ps-mat-empty]', root) var addBtn = qs('[data-ps-mat-add-cat]', root) if (!listBox) return var state = { categories: [], openCatId: 0, loaded: false, editingCatId: 0, editingTitle: '', creating: false, creatingTitle: '', pendingUploads: {} } function renderUploadSummary(okCount, failedItems) { showOk(root, '') showErr(root, '') if (okCount > 0) { showOk(root, 'Загружено файлов: ' + okCount) } if (failedItems && failedItems.length) { var html = 'Не загружено файлов: ' + failedItems.length var i = 0 while (i < failedItems.length) { var row = failedItems[i] || {} var fname = String(row.file_name || 'Файл') var msg = String(row.message || 'Ошибка загрузки файла') html += '
• ' + fname.replace(//g, '>') + ': ' + msg.replace(//g, '>') i++ } showErr(root, html) } } function showDropError(catId, text) { showErr(root, text || '') } function clearDropError(catId) { showErr(root, '') } function extFromName(name) { name = String(name || '') var p = name.lastIndexOf('.') if (p < 0) return '' return name.slice(p + 1).toLowerCase().replace(/[^a-z0-9]/g, '') } function isAllowedExtFront(ext) { var allowed = { pdf:1, doc:1, docx:1, rtf:1, txt:1, xls:1, xlsx:1, csv:1, jpg:1, jpeg:1, png:1, webp:1 } return !!allowed[ext] } function validateFilesFront(catId, files) { var max = 20 * 1024 * 1024 var out = [] var i = 0 while (i < files.length) { var f = files[i] var ext = extFromName(f.name) if (!isAllowedExtFront(ext)) { showDropError(catId, 'Формат .' + (ext || 'unknown') + ' не поддерживается') return null } if (Number(f.size || 0) > max) { showDropError(catId, 'Файл слишком большой. Максимум 20 МБ') return null } out.push(f) i++ } clearDropError(catId) return out } function findCatById(id) { var i = 0 while (i < state.categories.length) { if (state.categories[i].id === id) return state.categories[i] i++ } return null } function ensurePendingBucket(catId) { catId = String(catId) if (!state.pendingUploads[catId]) state.pendingUploads[catId] = [] return state.pendingUploads[catId] } function addPendingUpload(catId, fileName) { var uid = 'u_' + String(Date.now()) + '_' + String(Math.floor(Math.random() * 100000)) var bucket = ensurePendingBucket(catId) bucket.push({ uid: uid, name: String(fileName || 'Файл'), percent: 0 }) return uid } function updatePendingUpload(catId, uid, percent) { var bucket = ensurePendingBucket(catId) var i = 0 while (i < bucket.length) { if (bucket[i].uid === uid) { bucket[i].percent = percent return } i++ } } function removePendingUpload(catId, uid) { var bucket = ensurePendingBucket(catId) var next = [] var i = 0 while (i < bucket.length) { if (bucket[i].uid !== uid) next.push(bucket[i]) i++ } state.pendingUploads[String(catId)] = next } function clearPendingUploads(catId) { state.pendingUploads[String(catId)] = [] } function stopEditing() { state.editingCatId = 0 state.editingTitle = '' } function startEditing(catId) { var cat = findCatById(catId) if (!cat) return state.editingCatId = catId state.editingTitle = cat.title || '' state.creating = false state.creatingTitle = '' } function startCreating() { state.creating = true state.creatingTitle = '' stopEditing() } function stopCreating() { state.creating = false state.creatingTitle = '' } function setEmptyVisible(v) { if (!empty) return if (v) empty.style.display = 'block' if (!v) empty.style.display = 'none' } function focusEditingInput() { if (!state.editingCatId) return var inp = qs('input[data-cat-input="' + String(state.editingCatId) + '"]', listBox) if (!inp) return inp.focus() inp.select() } function focusCreatingInput() { if (!state.creating) return var inp = qs('input[data-cat-create-input="1"]', listBox) if (!inp) return inp.focus() inp.select() } function render() { listBox.innerHTML = '' if (!state.categories.length && !state.creating) { setEmptyVisible(true) } else { setEmptyVisible(false) } var i = 0 while (i < state.categories.length) { var c = state.categories[i] var opened = false if (c.id === state.openCatId) opened = true var cat = document.createElement('div') cat.className = 'ps-mat2__cat' var row = document.createElement('div') row.className = 'ps-mat2__row' row.setAttribute('data-cat-id', String(c.id)) var name = document.createElement('div') name.className = 'ps-mat2__name' var actions = document.createElement('div') actions.className = 'ps-mat2__actions' if (c.id === state.editingCatId) { var inp = document.createElement('input') inp.type = 'text' inp.className = 'ps-mat2__input' inp.value = state.editingTitle inp.setAttribute('data-cat-input', String(c.id)) name.appendChild(inp) var save = document.createElement('button') save.type = 'button' save.className = 'ps-mat2__icon ps-mat2__icon--save' save.textContent = '✓' save.setAttribute('data-cat-save', String(c.id)) var cancel = document.createElement('button') cancel.type = 'button' cancel.className = 'ps-mat2__icon' cancel.textContent = '×' cancel.setAttribute('data-cat-cancel', String(c.id)) actions.appendChild(save) actions.appendChild(cancel) } else { name.textContent = c.title name.setAttribute('data-cat-open', String(c.id)) name.style.cursor = 'pointer' var edit = document.createElement('button') edit.type = 'button' edit.className = 'ps-mat2__icon' edit.setAttribute('data-cat-edit', String(c.id)) edit.innerHTML = svgEdit15() var del = document.createElement('button') del.type = 'button' del.className = 'ps-mat2__icon ps-mat2__icon--danger' del.setAttribute('data-cat-del', String(c.id)) del.innerHTML = svgTrash18() var op = document.createElement('button') op.type = 'button' op.className = 'ps-mat2__icon' if (opened) op.textContent = '-' if (!opened) op.textContent = '+' op.setAttribute('data-cat-open', String(c.id)) actions.appendChild(edit) actions.appendChild(del) actions.appendChild(op) } row.appendChild(name) row.appendChild(actions) cat.appendChild(row) if (opened) { var body = document.createElement('div') body.className = 'ps-mat2__body' var files = document.createElement('div') files.className = 'ps-mat2__files' var pList = state.pendingUploads[String(c.id)] || [] var pListForEmpty = state.pendingUploads[String(c.id)] || [] if ((!c.files || !c.files.length) && !pListForEmpty.length) { var fr = document.createElement('div') fr.className = 'ps-mat2__file' fr.style.paddingBottom = '10px' var fn = document.createElement('div') fn.className = 'ps-mat2__file-name' fn.style.textDecoration = 'none' fn.textContent = 'Файлы отсутствуют' fr.appendChild(fn) files.appendChild(fr) } else { var j = 0 while (j < c.files.length) { var f = c.files[j] var fr2 = document.createElement('div') fr2.className = 'ps-mat2__file' fr2.style.paddingBottom = '10px' var open = document.createElement('a') open.className = 'ps-mat2__file-open' open.innerHTML = svgFile22() if (f.file_url) { open.href = f.file_url open.target = '_blank' open.rel = 'noopener' } else { open.href = 'javascript:void(0)' } var title = document.createElement('div') title.className = 'ps-mat2__file-title' title.textContent = f.file_name var fa = document.createElement('div') fa.className = 'ps-mat2__file-actions' var dl = document.createElement('a') dl.className = 'ps-mat2__file-download' dl.innerHTML = svgDownload18() if (f.file_url) { dl.href = API_URL + '?action=materials_file_download&file_id=' + encodeURIComponent(String(f.id)) dl.setAttribute('download', f.file_name || 'file') } else { dl.href = 'javascript:void(0)' } var fd = document.createElement('button') fd.type = 'button' fd.className = 'ps-mat2__icon ps-mat2__icon--danger' fd.setAttribute('data-file-del', String(f.id)) fd.innerHTML = svgTrash18() fa.appendChild(dl) fa.appendChild(fd) fr2.appendChild(open) fr2.appendChild(title) fr2.appendChild(fa) files.appendChild(fr2) j++ } } var upl = document.createElement('div') upl.className = 'ps-upl' var uplInput = document.createElement('input') uplInput.type = 'file' uplInput.multiple = true uplInput.className = 'ps-upl__input' uplInput.setAttribute('data-upl-input', String(c.id)) var drop = document.createElement('div') drop.className = 'ps-upl__drop' drop.setAttribute('data-upl-drop', String(c.id)) var ic = document.createElement('div') ic.className = 'ps-upl__icon' ic.textContent = '↑' var tt = document.createElement('div') tt.className = 'ps-upl__title' tt.textContent = 'Нажмите на эту область или перенесите в нее файл' var hint = document.createElement('div') hint.className = 'ps-upl__hint' hint.textContent = 'Форматы: PDF, DOC, DOCX, RTF, TXT, XLS, XLSX, CSV, JPG, JPEG, PNG, WEBP. Максимум 20 МБ' drop.appendChild(ic) drop.appendChild(tt) drop.appendChild(hint) upl.appendChild(uplInput) upl.appendChild(drop) var pendingWrap = document.createElement('div') pendingWrap.className = 'ps-mat2__files' pendingWrap.style.marginTop = '8px' var pList = state.pendingUploads[String(c.id)] || [] var pi = 0 while (pi < pList.length) { var p = pList[pi] var prow = document.createElement('div') prow.className = 'ps-mat2__file' prow.style.background = 'rgba(76, 175, 80, 0.12)' prow.style.paddingBottom = '10px' var ptitle = document.createElement('div') ptitle.className = 'ps-mat2__file-title' ptitle.textContent = p.name var pRight = document.createElement('div') pRight.className = 'ps-mat2__file-actions' pRight.style.minWidth = '140px' pRight.style.justifyContent = 'flex-end' var pPercent = document.createElement('div') pPercent.style.fontWeight = '700' pPercent.style.fontSize = '12px' pPercent.style.color = 'black' pPercent.textContent = String(Math.max(0, Math.min(100, parseInt(p.percent, 10) || 0))) + '%' pRight.appendChild(pPercent) prow.appendChild(ptitle) prow.appendChild(pRight) pendingWrap.appendChild(prow) pi++ } if (pList.length) { body.appendChild(pendingWrap) } body.appendChild(files) body.appendChild(upl) cat.appendChild(body) } listBox.appendChild(cat) i++ } if (state.creating) { var catNew = document.createElement('div') catNew.className = 'ps-mat2__cat' var rowNew = document.createElement('div') rowNew.className = 'ps-mat2__row' var nameNew = document.createElement('div') nameNew.className = 'ps-mat2__name' var inpNew = document.createElement('input') inpNew.type = 'text' inpNew.className = 'ps-mat2__input' inpNew.value = state.creatingTitle inpNew.setAttribute('data-cat-create-input', '1') nameNew.appendChild(inpNew) var actionsNew = document.createElement('div') actionsNew.className = 'ps-mat2__actions' var saveNew = document.createElement('button') saveNew.type = 'button' saveNew.className = 'ps-mat2__icon ps-mat2__icon--save' saveNew.textContent = '✓' saveNew.setAttribute('data-cat-create-save', '1') var cancelNew = document.createElement('button') cancelNew.type = 'button' cancelNew.className = 'ps-mat2__icon' cancelNew.textContent = '×' cancelNew.setAttribute('data-cat-create-cancel', '1') actionsNew.appendChild(saveNew) actionsNew.appendChild(cancelNew) rowNew.appendChild(nameNew) rowNew.appendChild(actionsNew) catNew.appendChild(rowNew) listBox.appendChild(catNew) } focusEditingInput() focusCreatingInput() } function load() { var fd = new FormData() fd.append('action', 'materials_list') apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error showErr(root, msg) return } state.categories = data.categories || [] // если открытая категория удалена — закрыть if (state.openCatId) { var exists = false var i = 0 while (i < state.categories.length) { if (state.categories[i].id === state.openCatId) { exists = true break } i++ } if (!exists) state.openCatId = 0 } state.loaded = true render() }) } function createCategory(title, cb) { showOk(root, '') showErr(root, '') title = String(title || '').trim() if (!title) { if (cb) cb(false); return } var fd = new FormData() fd.append('action', 'materials_category_create') fd.append('title', title) apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error showErr(root, msg) if (cb) cb(false) return } stopCreating() state.openCatId = data.id state.loaded = false load() if (cb) cb(true) }) } function saveCategoryTitle(catId, title, cb) { showOk(root, '') showErr(root, '') title = String(title || '').trim() if (!title) { if (cb) cb(false); return } var fd = new FormData() fd.append('action', 'materials_category_rename') fd.append('category_id', String(catId)) fd.append('title', title) apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error showErr(root, msg) if (cb) cb(false) return } stopEditing() state.loaded = false load() if (cb) cb(true) }) } function deleteCategory(catId) { showOk(root, '') showErr(root, '') var fd = new FormData() fd.append('action', 'materials_category_delete') fd.append('category_id', String(catId)) apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error showErr(root, msg) return } if (state.openCatId === catId) state.openCatId = 0 if (state.editingCatId === catId) stopEditing() state.loaded = false load() }) } function uploadFiles(catId, files) { showOk(root, '') showErr(root, '') clearDropError(catId) if (!catId) return if (!files || !files.length) return // Открываем категорию и сразу показываем pending-элементы state.openCatId = catId var pendings = [] var i = 0 while (i < files.length) { var f = files[i] var uid = addPendingUpload(catId, f.name || 'Файл') pendings.push({ uid: uid, file: f }) i++ } render() // Грузим КАЖДЫЙ файл отдельно, чтобы у каждого был свой % var finished = 0 var uploadedOk = 0 var failedItems = [] function doneOne() { finished++ if (finished >= pendings.length) { state.loaded = false load() renderUploadSummary(uploadedOk, failedItems) } } function uploadOne(item) { var fd = new FormData() fd.append('action', 'materials_files_upload') fd.append('category_id', String(catId)) fd.append('files[]', item.file) var xhr = new XMLHttpRequest() xhr.open('POST', API_URL, true) xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest') xhr.upload.onprogress = function (e) { if (!e || !e.lengthComputable) return var percent = 0 if (e.total > 0) { percent = Math.round((e.loaded / e.total) * 100) } updatePendingUpload(catId, item.uid, percent) render() } xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return var data = null try { data = JSON.parse(xhr.responseText) } catch (e) {} // Убираем pending-элемент этого файла removePendingUpload(catId, item.uid) if (xhr.status !== 200 || !data || !data.ok) { var msg = 'Ошибка загрузки файла' if (data && data.error) msg = data.error if (data && data.errors && data.errors.length && data.errors[0].message) { msg = data.errors[0].message } failedItems.push({ file_name: (item.file && item.file.name) ? item.file.name : 'Файл', message: msg }) render() doneOne() return } if (data.errors && data.errors.length) { var ei = 0 while (ei < data.errors.length) { failedItems.push({ file_name: data.errors[ei].file_name || ((item.file && item.file.name) ? item.file.name : 'Файл'), message: data.errors[ei].message || 'Ошибка загрузки файла' }) ei++ } } if (data.created && data.created.length) { uploadedOk += data.created.length } render() doneOne() } xhr.onerror = function () { removePendingUpload(catId, item.uid) failedItems.push({ file_name: (item.file && item.file.name) ? item.file.name : 'Файл', message: 'Ошибка сети при загрузке файла' }) render() doneOne() } xhr.send(fd) } var k = 0 while (k < pendings.length) { uploadOne(pendings[k]) k++ } } function deleteFile(fileId) { showOk(root, '') showErr(root, '') var fd = new FormData() fd.append('action', 'materials_file_delete') fd.append('file_id', String(fileId)) apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error showErr(root, msg) return } state.loaded = false load() }) } function toggleOpen(catId) { if (state.editingCatId) return if (state.creating) return if (state.openCatId === catId) { state.openCatId = 0 } else { state.openCatId = catId } render() } function closestAttr(el, attrName) { var cur = el while (cur && cur !== listBox) { if (cur.getAttribute && cur.getAttribute(attrName)) return cur cur = cur.parentNode } return null } window.partnerSettingsMaterialsLoad = function () { if (!state.loaded) load() } root._ps_save_materials = function (cb) { if (state.editingCatId) { var cid = state.editingCatId var inp = qs('input[data-cat-input="' + String(cid) + '"]', listBox) var val = inp ? inp.value : state.editingTitle saveCategoryTitle(cid, val || '', function (ok) { if (cb) cb(!!ok) }) return } if (state.creating) { var inpNew = qs('input[data-cat-create-input="1"]', listBox) var valNew = inpNew ? inpNew.value : state.creatingTitle createCategory(valNew || '', function (ok) { if (cb) cb(!!ok) }) return } if (cb) cb(true) } if (addBtn) { addBtn.addEventListener('click', function () { showOk(root, '') showErr(root, '') startCreating() render() }) } listBox.addEventListener('click', function (e) { var t = e.target var dz = closestAttr(t, 'data-upl-drop') if (dz) { var cidDz = parseInt(dz.getAttribute('data-upl-drop'), 10) if (!cidDz) return var inp = qs('input[data-upl-input="' + String(cidDz) + '"]', listBox) if (inp) inp.click() return } var btn = t while (btn && btn !== listBox && !btn.getAttribute('data-cat-open') && !btn.getAttribute('data-cat-edit') && !btn.getAttribute('data-cat-del') && !btn.getAttribute('data-cat-save') && !btn.getAttribute('data-cat-cancel') && !btn.getAttribute('data-cat-create-save') && !btn.getAttribute('data-cat-create-cancel') && !btn.getAttribute('data-file-del')) { btn = btn.parentNode } if (!btn || btn === listBox) return var v v = btn.getAttribute('data-cat-create-save') if (v) { var inpNew = qs('input[data-cat-create-input="1"]', listBox) if (!inpNew) return createCategory(inpNew.value || '') return } v = btn.getAttribute('data-cat-create-cancel') if (v) { stopCreating() render() return } v = btn.getAttribute('data-cat-save') if (v) { var cidSave = parseInt(v, 10) var inpSave = qs('input[data-cat-input="' + String(cidSave) + '"]', listBox) if (!inpSave) return saveCategoryTitle(cidSave, inpSave.value || '') return } v = btn.getAttribute('data-cat-cancel') if (v) { stopEditing() render() return } v = btn.getAttribute('data-cat-edit') if (v) { var cidEdit = parseInt(v, 10) startEditing(cidEdit) render() return } v = btn.getAttribute('data-cat-del') if (v) { var cidDel = parseInt(v, 10) if (!cidDel) return openConfirmModal({ title: 'Удаление категории', text: 'Удалить категорию и все файлы внутри?', okText: 'Удалить', cancelText: 'Отмена', onConfirm: function () { deleteCategory(cidDel) } }) return } v = btn.getAttribute('data-cat-open') if (v) { var cidOpen = parseInt(v, 10) toggleOpen(cidOpen) return } v = btn.getAttribute('data-file-del') if (v) { var fid = parseInt(v, 10) if (!fid) return openConfirmModal({ title: 'Удаление файла', text: 'Вы действительно хотите удалить файл?', okText: 'Удалить', cancelText: 'Отмена', onConfirm: function () { deleteFile(fid) } }) return } }) listBox.addEventListener('change', function (e) { var t = e.target if (!t || !t.getAttribute) return var v = t.getAttribute('data-upl-input') if (!v) return var cid = parseInt(v, 10) if (!cid) return if (!t.files) return var picked = [] var i = 0 while (i < t.files.length) { picked.push(t.files[i]) i++ } var validPicked = validateFilesFront(cid, picked) t.value = '' if (!validPicked) return uploadFiles(cid, validPicked) }) listBox.addEventListener('dragover', function (e) { var dz = closestAttr(e.target, 'data-upl-drop') if (!dz) return e.preventDefault() dz.className = (String(dz.className) + ' is-drag').replace(/\s+/g, ' ').trim() }) listBox.addEventListener('dragleave', function (e) { var dz = closestAttr(e.target, 'data-upl-drop') if (!dz) return dz.className = String(dz.className).replace(/\bis-drag\b/g, '').replace(/\s+/g, ' ').trim() }) listBox.addEventListener('drop', function (e) { var dz = closestAttr(e.target, 'data-upl-drop') if (!dz) return e.preventDefault() dz.className = String(dz.className).replace(/\bis-drag\b/g, '').replace(/\s+/g, ' ').trim() var cid = parseInt(dz.getAttribute('data-upl-drop'), 10) if (!cid) return var dt = e.dataTransfer if (!dt) return if (!dt.files) return var dropped = [] var di = 0 while (di < dt.files.length) { dropped.push(dt.files[di]) di++ } var validDropped = validateFilesFront(cid, dropped) if (!validDropped) return uploadFiles(cid, validDropped) }) listBox.addEventListener('keydown', function (e) { var t = e.target if (!t || !t.getAttribute) return var catInputId = t.getAttribute('data-cat-input') var createInput = t.getAttribute('data-cat-create-input') if (createInput) { if (e.keyCode === 13) { e.preventDefault() createCategory(t.value || '') return } if (e.keyCode === 27) { e.preventDefault() stopCreating() render() return } return } if (!catInputId) return var cid = parseInt(catInputId, 10) if (!cid) return if (e.keyCode === 13) { e.preventDefault() saveCategoryTitle(cid, t.value || '') return } if (e.keyCode === 27) { e.preventDefault() stopEditing() render() } }) } function loadScriptOnce(src, cb) { if (!src) { cb(false); return } if (!window._ps_scripts) window._ps_scripts = {} var st = window._ps_scripts[src] if (st && st.state === 'loaded') { cb(true); return } if (st && st.state === 'loading') { st.cbs.push(cb); return } window._ps_scripts[src] = { state: 'loading', cbs: [cb] } var s = document.createElement('script') s.src = src s.async = true s.onload = function () { var a = window._ps_scripts[src].cbs || [] window._ps_scripts[src].cbs = [] window._ps_scripts[src].state = 'loaded' var i = 0 while (i < a.length) { a[i](true); i++ } } s.onerror = function () { var a = window._ps_scripts[src].cbs || [] window._ps_scripts[src].cbs = [] window._ps_scripts[src].state = 'error' var i = 0 while (i < a.length) { a[i](false); i++ } } document.head.appendChild(s) } function ensureCkEditor(cb) { if (window.CKEDITOR) { cb(true); return } loadScriptOnce(CK_SRC, function (ok) { if (!ok) { cb(false); return } if (!window.CKEDITOR) { cb(false); return } cb(true) }) } function openNewsCreateModal(root, onSaved, editItem) { if (qs('.ps-news-modal')) return var isEdit = !!(editItem && editItem.id) var overlayEl = document.createElement('div') overlayEl.className = 'ps-news-modal' var boxEl = document.createElement('div') boxEl.className = 'ps-news-modal__box' var headerEl = document.createElement('div') headerEl.className = 'ps-news-modal__head' var titleEl = document.createElement('div') titleEl.className = 'ps-news-modal__title' titleEl.textContent = isEdit ? 'Редактировать новость' : 'Новая новость' var closeBtn = document.createElement('button') closeBtn.type = 'button' closeBtn.className = 'ps-news-modal__close' closeBtn.textContent = '×' headerEl.appendChild(titleEl) headerEl.appendChild(closeBtn) var bodyEl = document.createElement('div') bodyEl.className = 'ps-news-modal__body' var msgOk = document.createElement('div') msgOk.className = 'ps-news-modal__msg ps-news-modal__msg--ok partner-settings-hidden' msgOk.setAttribute('data-news-modal-ok', '1') var msgErr = document.createElement('div') msgErr.className = 'ps-news-modal__msg ps-news-modal__msg--err partner-settings-hidden' msgErr.setAttribute('data-news-modal-err', '1') function modalShowOk(text) { if (msgErr) { msgErr.innerHTML = ''; setHidden(msgErr, true) } if (msgOk) { msgOk.textContent = text || ''; setHidden(msgOk, !text) } } function modalShowErr(html) { if (msgOk) { msgOk.textContent = ''; setHidden(msgOk, true) } if (msgErr) { msgErr.innerHTML = html || ''; setHidden(msgErr, !html) } } var titleField = document.createElement('div') titleField.className = 'ps-news-modal__field' var titleLabel = document.createElement('div') titleLabel.className = 'ps-news-modal__label' titleLabel.textContent = 'Заголовок' var titleInput = document.createElement('input') titleInput.type = 'text' titleInput.className = 'ps-news-modal__input' titleInput.value = editItem && editItem.title ? String(editItem.title) : '' titleField.appendChild(titleLabel) titleField.appendChild(titleInput) var importantBox = document.createElement('div') importantBox.className = 'checkbox' var importantId = 'news_important_' + String((new Date()).getTime()) var importantInput = document.createElement('input') importantInput.name = 'is_important' importantInput.id = importantId importantInput.type = 'checkbox' importantInput.value = '1' if (editItem && parseInt(editItem.is_important, 10) === 1) { importantInput.checked = true } var importantLabel = document.createElement('label') importantLabel.className = 'label' importantLabel.setAttribute('for', importantId) importantLabel.textContent = 'Важная новость' importantBox.appendChild(importantInput) importantBox.appendChild(importantLabel) var editorId = 'ps_news_body_' + String((new Date()).getTime()) var textLabel = document.createElement('div') textLabel.className = 'ps-news-modal__label' textLabel.textContent = 'Текст новости' var textarea = document.createElement('textarea') textarea.className = 'ps-news-modal__textarea' textarea.id = editorId textarea.value = editItem && editItem.body_html ? String(editItem.body_html) : '' bodyEl.appendChild(msgOk) bodyEl.appendChild(msgErr) bodyEl.appendChild(titleField) bodyEl.appendChild(importantBox) bodyEl.appendChild(textLabel) bodyEl.appendChild(textarea) var actionsEl = document.createElement('div') actionsEl.className = 'ps-news-modal__actions' var cancelBtn = document.createElement('button') cancelBtn.type = 'button' cancelBtn.className = 'partner-settings-btn' cancelBtn.textContent = 'Отмена' var saveBtn = document.createElement('button') saveBtn.type = 'button' saveBtn.className = 'ps-mat2__create' saveBtn.textContent = isEdit ? 'Сохранить изменения' : 'Сохранить' actionsEl.appendChild(cancelBtn) actionsEl.appendChild(saveBtn) boxEl.appendChild(headerEl) boxEl.appendChild(bodyEl) boxEl.appendChild(actionsEl) overlayEl.appendChild(boxEl) document.body.appendChild(overlayEl) var editor = null function closeModal() { if (editor) { try { editor.destroy(true) } catch (e) {} editor = null } if (overlayEl && overlayEl.parentNode) overlayEl.parentNode.removeChild(overlayEl) } closeBtn.addEventListener('click', closeModal) cancelBtn.addEventListener('click', closeModal) ensureCkEditor(function (ok) { if (!ok) { modalShowErr('Не удалось загрузить редактор') return } editor = window.CKEDITOR.replace(editorId, { height: 320, resize_enabled: false, removePlugins: 'elementspath,uploadimage,uploadwidget,uploadfile,filetools,exportpdf', removeDialogTabs: 'link:upload;image:Upload;image2:Upload', toolbar: [ { name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'RemoveFormat'] }, { name: 'paragraph', items: ['Format', 'NumberedList', 'BulletedList', 'Outdent', 'Indent', 'Blockquote'] }, { name: 'links', items: ['Link', 'Unlink'] }, { name: 'insert', items: ['Table', 'HorizontalRule', 'Smiley', 'SpecialChar'] }, { name: 'document', items: ['Source'] } ] }) editor.on('instanceReady', function () { if (editItem && editItem.body_html) { try { editor.setData(String(editItem.body_html)) } catch (e) {} } }) }) saveBtn.addEventListener('click', function () { modalShowOk('') modalShowErr('') var title = String(titleInput.value || '').trim() if (!title) { modalShowErr('Введите заголовок') return } var html = '' if (editor) html = editor.getData() else html = String(textarea.value || '') var importantVal = (importantInput && importantInput.checked) ? '1' : '0' var fd = new FormData() fd.append('action', isEdit ? 'news_update' : 'news_create') if (isEdit) fd.append('news_id', String(editItem.id)) fd.append('title', title) fd.append('body_html', html) fd.append('is_important', importantVal) apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error modalShowErr(msg) return } closeModal() if (onSaved) onSaved() showOk(root, isEdit ? 'Новость успешно обновлена' : 'Новость успешно создана') }) }) setTimeout(function () { try { titleInput.focus() } catch (e) {} }, 50) } function openConfirmModal(opts) { opts = opts || {} var title = String(opts.title || 'Подтверждение') var text = String(opts.text || 'Вы уверены?') var okText = String(opts.okText || 'Удалить') var cancelText = String(opts.cancelText || 'Отмена') var onConfirm = typeof opts.onConfirm === 'function' ? opts.onConfirm : function () {} // чтобы не открыть две сразу if (qs('.ps-confirm-modal')) return var overlay = document.createElement('div') overlay.className = 'ps-confirm-modal' var box = document.createElement('div') box.className = 'ps-confirm-modal__box' var head = document.createElement('div') head.className = 'ps-confirm-modal__head' var titleEl = document.createElement('div') titleEl.className = 'ps-confirm-modal__title' titleEl.textContent = title var closeBtn = document.createElement('button') closeBtn.type = 'button' closeBtn.className = 'ps-confirm-modal__close' closeBtn.textContent = '×' head.appendChild(titleEl) head.appendChild(closeBtn) var body = document.createElement('div') body.className = 'ps-confirm-modal__body' body.textContent = text var actions = document.createElement('div') actions.className = 'ps-confirm-modal__actions' var cancelBtn = document.createElement('button') cancelBtn.type = 'button' cancelBtn.className = 'partner-settings-btn' cancelBtn.textContent = cancelText var okBtn = document.createElement('button') okBtn.type = 'button' okBtn.className = 'ps-confirm-modal__danger' okBtn.textContent = okText actions.appendChild(cancelBtn) actions.appendChild(okBtn) box.appendChild(head) box.appendChild(body) box.appendChild(actions) overlay.appendChild(box) document.body.appendChild(overlay) function closeModal() { if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay) document.removeEventListener('keydown', onKeyDown, true) } function onKeyDown(e) { if (e.keyCode === 27) { e.preventDefault() closeModal() } if (e.keyCode === 13) { e.preventDefault() closeModal() onConfirm() } } closeBtn.addEventListener('click', closeModal) cancelBtn.addEventListener('click', closeModal) okBtn.addEventListener('click', function () { closeModal() onConfirm() }) overlay.addEventListener('click', function (e) { if (e.target === overlay) closeModal() }) document.addEventListener('keydown', onKeyDown, true) setTimeout(function () { try { okBtn.focus() } catch (e) {} }, 20) } function bindNews(root) { var listBox = qs('[data-ps-news-list]', root) var empty = qs('[data-ps-news-empty]', root) var addBtn = qs('[data-ps-news-add]', root) if (!listBox) return var state = { items: [], loaded: false, openId: 0 } function setEmptyVisible(v) { if (!empty) return if (v) empty.style.display = 'block' if (!v) empty.style.display = 'none' } function render() { listBox.innerHTML = '' if (!state.items.length) setEmptyVisible(true) if (state.items.length) setEmptyVisible(false) var i = 0 while (i < state.items.length) { var it = state.items[i] var item = document.createElement('div') item.className = 'ps-news__item' var row = document.createElement('div') row.className = 'ps-news__row' var name = document.createElement('div') name.className = 'ps-news__name' name.innerHTML = '' if (it.is_important && parseInt(it.is_important, 10) === 1) { var badge = document.createElement('span') badge.className = 'ps-news__badge' badge.textContent = 'ВАЖНО' name.appendChild(badge) } var titleSpan = document.createElement('span') titleSpan.textContent = it.title || '' name.appendChild(titleSpan) var meta = document.createElement('div') meta.className = 'ps-news__meta' meta.textContent = it.created_at || '' var actions = document.createElement('div') actions.className = 'ps-news__actions' var edit = document.createElement('button') edit.type = 'button' edit.className = 'ps-mat2__icon' edit.setAttribute('data-news-edit', String(it.id)) edit.innerHTML = svgEdit15() var del = document.createElement('button') del.type = 'button' del.className = 'ps-mat2__icon ps-mat2__icon--danger' del.setAttribute('data-news-del', String(it.id)) del.innerHTML = svgTrash18() var op = document.createElement('button') op.type = 'button' op.className = 'ps-mat2__icon' op.setAttribute('data-news-open', String(it.id)) if (state.openId === it.id) op.textContent = '-' if (state.openId !== it.id) op.textContent = '+' actions.appendChild(edit) actions.appendChild(del) actions.appendChild(op) row.appendChild(name) row.appendChild(meta) row.appendChild(actions) item.appendChild(row) if (state.openId === it.id) { var body = document.createElement('div') body.className = 'ps-news__body' body.innerHTML = it.body_html || '' item.appendChild(body) } listBox.appendChild(item) i++ } } function load() { var fd = new FormData() fd.append('action', 'news_list') apiPost(fd, function (data) { if (!data || !data.ok) return state.items = data.items || [] var keep = false if (state.openId) { var i = 0 while (i < state.items.length) { if (state.items[i].id === state.openId) { keep = true; i = state.items.length; continue } i++ } } if (!keep) state.openId = 0 state.loaded = true render() }) } function deleteNews(newsId) { showOk(root, '') showErr(root, '') var fd = new FormData() fd.append('action', 'news_delete') fd.append('news_id', String(newsId)) apiPost(fd, function (data) { if (!data || !data.ok) { var msg = 'Ошибка' if (data && data.error) msg = data.error showErr(root, msg) return } if (state.openId === newsId) state.openId = 0 state.loaded = false load() }) } window.partnerSettingsNewsLoad = function () { if (!state.loaded) load() } if (addBtn) { addBtn.addEventListener('click', function () { openNewsCreateModal(root, function () { state.loaded = false load() }) }) } listBox.addEventListener('click', function (e) { var t = e.target var btn = t while ( btn && btn !== listBox && !btn.getAttribute('data-news-open') && !btn.getAttribute('data-news-del') && !btn.getAttribute('data-news-edit') ) { btn = btn.parentNode } if (!btn || btn === listBox) return var v v = btn.getAttribute('data-news-open') if (v) { var idOpen = parseInt(v, 10) if (!idOpen) return if (state.openId === idOpen) { state.openId = 0 } else { state.openId = idOpen } render() return } v = btn.getAttribute('data-news-edit') if (v) { var idEdit = parseInt(v, 10) if (!idEdit) return var editItem = null var k = 0 while (k < state.items.length) { if (parseInt(state.items[k].id, 10) === idEdit) { editItem = state.items[k] break } k++ } if (!editItem) return openNewsCreateModal(root, function () { state.loaded = false load() }, editItem) return } v = btn.getAttribute('data-news-del') if (v) { var idDel = parseInt(v, 10) if (!idDel) return openConfirmModal({ title: 'Удаление новости', text: 'Вы действительно хотите удалить эту новость?', okText: 'Удалить', cancelText: 'Отмена', onConfirm: function () { deleteNews(idDel) } }) return } }) } function bindModal(root) { bindClose(root) bindUrlRow(root) bindMaterials(root) bindNews(root) bindTabs(root) bindBottomSave(root) } window.partnerSettingsModalBind = function () { var root = qs('#partnerSettingsRoot') if (root) bindModal(root) } // function openModal() { // if (qs('#partnerSettingsRoot')) return // // var prevOverflow = document.documentElement.style.overflow // var prevBodyOverflow = document.body.style.overflow // // document.documentElement.style.overflow = 'hidden' // document.body.style.overflow = 'hidden' // // function restoreOverflow() { // document.documentElement.style.overflow = prevOverflow // document.body.style.overflow = prevBodyOverflow // } // // var xhr = new XMLHttpRequest() // xhr.open('GET', MODAL_URL, true) // xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest') // xhr.onreadystatechange = function () { // if (xhr.readyState !== 4) return // // if (xhr.status !== 200) { // restoreOverflow() // return // } // // var wrap = document.createElement('div') // wrap.innerHTML = xhr.responseText // while (wrap.firstChild) document.body.appendChild(wrap.firstChild) // // var root = qs('#partnerSettingsRoot') // if (root) { // root._ps_restoreOverflow = restoreOverflow // } else { // restoreOverflow() // } // // if (window.partnerSettingsModalBind) window.partnerSettingsModalBind() // } // xhr.send(null) // } // function bindOpenBtn() { // var btn = qs('.partners-settings') // if (!btn) return // btn.addEventListener('click', openModal) // } // // if (document.readyState === 'loading') { // document.addEventListener('DOMContentLoaded', bindOpenBtn) // } else { // bindOpenBtn() // } window.openPartnerSettingsModal = function() { if (document.getElementById('partnerSettingsRoot')) { return; } var prevOverflow = document.documentElement.style.overflow; var prevBodyOverflow = document.body.style.overflow; document.documentElement.style.overflow = 'hidden'; document.body.style.overflow = 'hidden'; function restoreOverflow() { document.documentElement.style.overflow = prevOverflow; document.body.style.overflow = prevBodyOverflow; } var xhr = new XMLHttpRequest(); xhr.open('GET', MODAL_URL, true); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.onreadystatechange = function() { if (xhr.readyState !== 4) return; if (xhr.status !== 200) { restoreOverflow(); return; } var wrap = document.createElement('div'); wrap.innerHTML = xhr.responseText; while (wrap.firstChild) document.body.appendChild(wrap.firstChild); var root = qs('#partnerSettingsRoot'); if (root) { root._ps_restoreOverflow = restoreOverflow; } else { restoreOverflow(); } if (window.partnerSettingsModalBind) window.partnerSettingsModalBind(); }; xhr.send(null); }; })()