// Порты, Статус and Изменить of a machine in a dialog over the current page: // the server's pages as fragments, so nothing behind is lost. They open from // the buttons of a machine's row, and from a menu on a tab's title or a // pane's title bar (a right click, or a long press on a phone). Closing the // dialog unmounts the fragment's module: its charts and polling stop. (function () { 'use strict'; const TITLES = {forwards: 'Порты', status: 'Статус', edit: 'Изменить'}; const dlg = document.getElementById('machinedlg'); const area = dlg.querySelector('.frag-area'); const page = /^\/agents\/([^/]+)\/(forwards|status|edit)$/; let changed = false; function open(id, what, name) { dlg.querySelector('.md-title').textContent = (name || id) + ' · ' + TITLES[what]; App.put(area, '
загрузка…
'); if (!dlg.open) (window.FileViews ? FileViews.openModal(dlg) : dlg.showModal()); App.load(area, '/agents/' + encodeURIComponent(id) + '/' + what); } dlg.addEventListener('close', () => { App.put(area, ''); // A form sent in the dialog (a new name, say) shows in the list behind. if (changed && document.querySelector('main#app').dataset.view === 'machines') App.go('/', false); changed = false; }); dlg.querySelector('.md-close').onclick = () => dlg.close(); // «Изменить» drawn again after a new name: the title follows it. new MutationObserver(() => { const h = area.querySelector('[data-module="edit"] .wrap > h1'); if (h) dlg.querySelector('.md-title').textContent = h.textContent.trim() + ' · ' + TITLES.edit; }).observe(area, {childList: true}); dlg.addEventListener('submit', () => { changed = true; }); // The fragment's own links: another page of a machine stays in the dialog, // anything else closes it and goes on as usual. area.addEventListener('click', ev => { const a = ev.target.closest('a[href]'); if (!a || ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey || a.target || a.hasAttribute('download')) return; const u = new URL(a.href, location.href), m = page.exec(u.pathname); if (u.origin !== location.origin) return; if (m) { ev.preventDefault(); ev.stopPropagation(); App.load(area, u.pathname + u.search); return; } dlg.close(); }, true); document.addEventListener('click', ev => { const b = ev.target.closest('[data-dialog]'); if (!b || ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey) return; ev.preventDefault(); open(b.dataset.id, b.dataset.dialog, b.dataset.name); }); // menu: Порты, Статус, Изменить of agent, by the pointer. const menu = document.createElement('div'); menu.className = 'addmenu mdmenu'; menu.hidden = true; document.body.appendChild(menu); function closeMenu() { menu.hidden = true; document.removeEventListener('pointerdown', outside, true); } function outside(ev) { if (!menu.contains(ev.target)) closeMenu(); } function showMenu(ev, agent) { ev.preventDefault(); menu.innerHTML = ''; const head = document.createElement('div'); head.className = 'muted md-head'; head.textContent = agent.name; menu.appendChild(head); Object.keys(TITLES).forEach(k => { const b = document.createElement('button'); b.type = 'button'; b.className = 'item'; b.innerHTML = ''; b.querySelector('b').textContent = TITLES[k]; b.onclick = () => { closeMenu(); open(agent.id, k, agent.name); }; menu.appendChild(b); }); menu.hidden = false; const m = menu.getBoundingClientRect(); menu.style.left = Math.max(8, Math.min(innerWidth - m.width - 8, ev.clientX)) + 'px'; menu.style.top = Math.max(8, Math.min(innerHeight - m.height - 8, ev.clientY)) + 'px'; setTimeout(() => document.addEventListener('pointerdown', outside, true), 0); } document.addEventListener('keydown', ev => { if (ev.key === 'Escape' && !menu.hidden) closeMenu(); }); // A machine page asked for by its address: the machines view with it open. const m = window.APP && window.APP.dialog && page.exec(window.APP.dialog); if (m) { const row = document.querySelector('[data-open][data-id="' + CSS.escape(m[1]) + '"]'); history.replaceState({}, '', '/'); open(m[1], m[2], row ? row.dataset.name : m[1]); } window.MachineDialog = {open, menu: showMenu}; })();