// The panel's own question and input dialogs in place of the browser's // confirm() and prompt(): UI.confirm(text, {ok, danger}) and // UI.prompt(text, value) answer with promises. A form with data-confirm asks // first and is sent only on «да». (function () { 'use strict'; let dlg = null; function make() { dlg = document.createElement('dialog'); dlg.className = 'uidlg'; dlg.innerHTML = '

' + '
'; document.body.appendChild(dlg); } function ask(text, o) { if (!dlg) make(); if (dlg.open) dlg.close(''); const input = dlg.querySelector('.ui-input'), yes = dlg.querySelector('.ui-yes'); // Long words (a file name) stay on one line, cut with "…"; the window // is as wide as its words, up to the screen's room. const t = dlg.querySelector('.ui-text'); t.textContent = ''; // "{chip}" in the text: the chip stands there, inside the sentence. const parts = String(text).split('{chip}'); text = parts.shift(); String(text).split(/(\s+)/).forEach(w => { if (w.length > 28 && !/\s/.test(w)) { const s = document.createElement('span'); s.className = 'ui-long'; s.textContent = w; s.title = w; t.appendChild(s); } else t.appendChild(document.createTextNode(w)); }); // Extras of a question about an action: where (a chip), the command in // mono, a note under it. if (parts.length && o.chip) { const c = document.createElement('span'); c.className = 'ui-chip'; c.textContent = o.chip; c.title = o.chip; t.append(c, parts.join('')); o = Object.assign({}, o, {chip: ''}); } dlg.querySelectorAll('.ui-extra').forEach(e => e.remove()); if (o.chip || o.code || o.note) { const box = document.createElement('div'); box.className = 'ui-extra'; if (o.chip) { const c = document.createElement('div'); c.className = 'ui-chip'; c.textContent = o.chip; c.title = o.chip; box.appendChild(c); } if (o.code) { const p = document.createElement('pre'); p.className = 'ui-code'; p.textContent = o.code; box.appendChild(p); } if (o.note) { const n = document.createElement('p'); n.className = 'ui-note'; n.textContent = o.note; box.appendChild(n); } t.after(box); } input.hidden = !o.input; input.value = o.value || ''; yes.textContent = o.ok || 'OK'; yes.className = 'ui-yes' + (o.danger ? ' btn danger' : ''); dlg.querySelector('.ui-no').textContent = o.cancel || 'Отмена'; // «Отмена» red, unless «да» is the red one (a delete): never two reds. dlg.querySelector('.ui-no').classList.toggle('close', !o.danger); return new Promise(done => { const end = v => { dlg.onclose = null; done(v); }; dlg.querySelector('.ui-no').onclick = () => dlg.close(''); dlg.querySelector('form').onsubmit = ev => { ev.preventDefault(); dlg.close('ok'); }; dlg.onclose = () => end(dlg.returnValue === 'ok' ? (o.input ? input.value : true) : (o.input ? null : false)); dlg.returnValue = ''; dlg.showModal(); if (o.input) { input.focus(); input.select(); } else yes.focus(); }); } const UI = { confirm: (text, o) => ask(text, Object.assign({ok: 'Да'}, o)), prompt: (text, value) => ask(text, {input: true, value, ok: 'OK'}) }; window.UI = UI; // Forms that ask first: data-confirm, and data-danger for a red «да». document.addEventListener('submit', ev => { const f = ev.target; if (!f.dataset || !f.dataset.confirm) return; if (f.dataset.confirmed) { delete f.dataset.confirmed; return; } ev.preventDefault(); ev.stopImmediatePropagation(); const by = ev.submitter; UI.confirm(f.dataset.confirm, {ok: f.dataset.ok || 'Да', danger: 'danger' in f.dataset}).then(ok => { if (!ok) return; f.dataset.confirmed = '1'; f.requestSubmit(by && f.contains(by) ? by : undefined); }); }, true); })();