'use strict'; /** Get first element by selector * @param string * @param [HTMLElement] defaults to document * @return HTMLElement */ function qs(selector, context) { return (context || document).querySelector(selector); } /** Get last element by selector * @param string * @param [HTMLElement] defaults to document * @return HTMLElement */ function qsl(selector, context) { const els = qsa(selector, context); return els[els.length - 1]; } /** Get all elements by selector * @param string * @param [HTMLElement] defaults to document * @return NodeList */ function qsa(selector, context) { return (context || document).querySelectorAll(selector); } /** Return a function calling fn with the next arguments * @param function * @param ... * @return function with preserved this */ function partial(fn) { const args = Array.apply(null, arguments).slice(1); return function () { return fn.apply(this, args); }; } /** Return a function calling fn with the first parameter and then the next arguments * @param function * @param ... * @return function with preserved this */ function partialArg(fn) { const args = Array.apply(null, arguments); return function (arg) { args[0] = arg; return fn.apply(this, args); }; } /** Assign values from source to target * @param Object * @param Object */ function mixin(target, source) { for (const key in source) { target[key] = source[key]; } } /** Add or remove CSS class * @param HTMLElement * @param string * @param [boolean] */ function alterClass(el, className, enable) { if (el) { el.classList[enable ? 'add' : 'remove'](className); } } /** Toggle visibility * @param string * @return boolean false */ function toggle(id) { const el = qs('#' + id); el && el.classList.toggle('hidden'); return false; } /** Set permanent cookie * @param string * @param number */ function cookie(assign, days) { const date = new Date(); date.setDate(date.getDate() + days); document.cookie = assign + '; expires=' + date; } /** Verify current Adminer version * @param string * @param string own URL base * @param string */ function verifyVersion(current, url, token) { cookie('adminer_version=0', 1); const iframe = document.createElement('iframe'); iframe.src = 'https://www.adminer.org/version/?current=' + current; iframe.frameBorder = 0; iframe.marginHeight = 0; iframe.scrolling = 'no'; iframe.style.width = '7ex'; iframe.style.height = '1.25em'; iframe.style.display = 'none'; addEventListener('message', event => { if (event.origin == 'https://www.adminer.org') { const match = /version=(.+)/.exec(event.data); if (match) { cookie('adminer_version=' + match[1], 1); ajax(url + 'script=version', () => { }, event.data + '&token=' + token); } } }, false); qs('#version').appendChild(iframe); } /** Get value of select * @param HTMLElement * @return string */ function selectValue(select) { if (!select.selectedIndex) { return select.value; } const selected = select.options[select.selectedIndex]; return ((selected.attributes.value || {}).specified ? selected.value : selected.text); } /** Verify if element has a specified tag name * @param HTMLElement * @param string regular expression * @return boolean */ function isTag(el, tag) { const re = new RegExp('^(' + tag + ')$', 'i'); return el && re.test(el.tagName); } /** Get parent node with specified tag name * @param HTMLElement * @param string regular expression * @return HTMLElement */ function parentTag(el, tag) { while (el && !isTag(el, tag)) { el = el.parentNode; } return el; } /** Set checked class * @param HTMLInputElement */ function trCheck(el) { const tr = parentTag(el, 'tr'); alterClass(tr, 'checked', el.checked); if (el.form && el.form['all'] && el.form['all'].onclick) { // Opera treats form.all as document.all el.form['all'].onclick(); } } /** Fill number of selected items * @param string * @param string * @uses thousandsSeparator */ function selectCount(id, count) { setHtml(id, (count === '' ? '' : '(' + (count + '').replace(/\B(?=(\d{3})+$)/g, thousandsSeparator) + ')')); const el = qs('#' + id); if (el) { for (const input of qsa('input', el.parentNode.parentNode)) { if (input.type == 'submit') { input.disabled = (count == '0'); } } } } /** Check all elements matching given name * @param RegExp * @this HTMLInputElement */ function formCheck(name) { for (const elem of this.form.elements) { if (name.test(elem.name)) { elem.checked = this.checked; trCheck(elem); } } } /** Check all rows in */ function tableCheck() { for (const input of qsa('table.checkable td:first-child input')) { trCheck(input); } } /** Uncheck single element * @param string */ function formUncheck(id) { const el = qs('#' + id); el.checked = false; trCheck(el); } /** Get number of checked elements matching given name * @param HTMLInputElement * @param RegExp * @return number */ function formChecked(input, name) { let checked = 0; for (const el of input.form.elements) { if (name.test(el.name) && el.checked) { checked++; } } return checked; } /** Select clicked row * @param MouseEvent * @param [boolean] force click */ function tableClick(event, click) { const td = parentTag(event.target, 'td'); let text; if (td && (text = td.getAttribute('data-text'))) { if (selectClick.call(td, event, +text, td.getAttribute('data-warning'))) { return; } } click = (click || !window.getSelection || getSelection().isCollapsed); let el = event.target; while (!isTag(el, 'tr')) { if (isTag(el, 'table|a|input|textarea')) { if (el.type != 'checkbox') { return; } checkboxClick.call(el, event); click = false; } el = el.parentNode; if (!el) { // Ctrl+click on text fields hides the element return; } } el = el.firstChild.firstChild; if (click) { el.checked = !el.checked; el.onclick && el.onclick(); } if (el.name == 'check[]') { el.form['all'].checked = false; formUncheck('all-page'); } if (/^(tables|views)\[\]$/.test(el.name)) { formUncheck('check-all'); } trCheck(el); } let lastChecked; /** Shift-click on checkbox for multiple selection. * @param MouseEvent * @this HTMLInputElement */ function checkboxClick(event) { if (!this.name) { return; } if (event.shiftKey && (!lastChecked || lastChecked.name == this.name)) { const checked = (lastChecked ? lastChecked.checked : true); let checking = !lastChecked; for (const input of qsa('input', parentTag(this, 'table'))) { if (input.name === this.name) { if (checking) { input.checked = checked; trCheck(input); } if (input === this || input === lastChecked) { if (checking) { break; } checking = true; } } } } else { lastChecked = this; } } /** Set HTML code of an element * @param string * @param string undefined to set parentNode to empty string */ function setHtml(id, html) { const el = qs('[id="' + id.replace(/[\\"]/g, '\\$&') + '"]'); // database name is used as ID if (el) { if (html == null) { el.parentNode.innerHTML = ''; } else { el.innerHTML = html; } } } /** Find node position * @param Node * @return number */ function nodePosition(el) { let pos = 0; while ((el = el.previousSibling)) { pos++; } return pos; } /** Go to the specified page * @param string * @param string */ function pageClick(href, page) { if (!isNaN(page) && page) { location.href = href + (page != 1 ? '&page=' + (page - 1) : ''); } } /** Display items in menu * @param MouseEvent * @this HTMLElement */ function menuOver(event) { const a = event.target; if (isTag(a, 'a|span') && a.offsetLeft + a.offsetWidth > a.parentNode.offsetWidth - 15) { // 15 - ellipsis this.style.overflow = 'visible'; } } /** Hide items in menu * @this HTMLElement */ function menuOut() { this.style.overflow = 'hidden'; } /** Add row in select fieldset * @this HTMLSelectElement */ function selectAddRow() { const field = this; const row = cloneNode(field.parentNode); field.onchange = selectFieldChange; field.onchange(); for (const select of qsa('select', row)) { select.name = select.name.replace(/[a-z]\[\d+/, '$&1'); select.selectedIndex = 0; } for (const input of qsa('input', row)) { input.name = input.name.replace(/[a-z]\[\d+/, '$&1'); input.className = ''; if (input.type == 'checkbox') { input.checked = false; } else { input.value = ''; } } field.parentNode.parentNode.appendChild(row); } /** Prevent onsearch handler on Enter * @param KeyboardEvent * @this HTMLInputElement */ function selectSearchKeydown(event) { if (event.keyCode == 13 || event.keyCode == 10) { this.onsearch = () => { }; } } /** Clear column name after resetting search * @this HTMLInputElement */ function selectSearchSearch() { if (!this.value) { this.parentNode.firstChild.selectedIndex = 0; } } /** Toggle column context menu * @param [string] extra class name * @this HTMLElement */ function columnMouse(className) { for (const span of qsa('span', this)) { if (/column/.test(span.className)) { span.className = 'column' + (className || ''); } } } /** Fill column in search field * @param string * @return boolean false */ function selectSearch(name) { let el = qs('#fieldset-search'); el.className = ''; const divs = qsa('div', el); let i, div; for (i=0; i < divs.length; i++) { div = divs[i]; el = qs('[name$="[col]"]', div); if (el && selectValue(el) == name) { break; } } if (i == divs.length) { div.firstChild.value = name; div.firstChild.onchange(); } qs('[name$="[val]"]', div).focus(); return false; } /** Check if Ctrl key (Command key on Mac) was pressed * @param KeyboardEvent|MouseEvent * @return boolean */ function isCtrl(event) { return (event.ctrlKey || event.metaKey) && !event.altKey; // shiftKey allowed } /** Send form by Ctrl+Enter on