Compare commits

...

17 Commits
v4.10 ... v4.11

Author SHA1 Message Date
Peter Knut
81e337bed1 Release 4.11 2024-10-30 22:14:44 +01:00
Peter Knut
18ddbf1d60 MySQL: Fix highlighting current table in menu on macOS 2024-10-30 22:12:59 +01:00
Peter Knut
e4235d21e5 Clean up JS code 2024-10-30 21:16:39 +01:00
Peter Knut
10dfc54bf4 Show help popup after a short delay, refactoring 2024-10-30 21:16:39 +01:00
Peter Knut
af3c863be3 Fix drag-n-drop moving of function parameters 2024-10-30 21:16:39 +01:00
Peter Knut
6cd5495c1b PostgreSQL: Show list of schemas in database, unify lists of sequences and user types 2024-10-30 21:16:39 +01:00
Peter Knut
78ad8381ab Update CS and SK translations 2024-10-30 21:16:39 +01:00
Peter Knut
87fc82f88e Fix printing error message while validating server URL 2024-10-30 21:16:39 +01:00
Peter Knut
0519a0b985 Devel: Add modification time to CSS and JS links for easier development 2024-10-30 21:16:39 +01:00
Peter Knut
f61cf1ba0c Support drag-n-drop moving on touch screens 2024-10-30 21:16:39 +01:00
HyP3r
fc2e025603 MS SQL: Prefix Unicode strings with 'N' so they are treated correctly 2024-10-30 21:16:39 +01:00
Peter Knut
659e72b692 MariaDB: Fix several links to documentation pages 2024-10-30 21:16:39 +01:00
Peter Knut
123373ca82 Switch to custom JUSH library 2024-10-30 21:16:39 +01:00
Peter Knut
e99f2d90cf Update project information 2024-10-30 21:16:39 +01:00
Peter Knut
bdabc5aa13 Small CSS tuning 2024-10-30 21:16:39 +01:00
Peter Knut
92f3e0ca00 Reset the style of sortable row after dragging 2024-10-22 23:29:03 +02:00
Peter Knut
aa1266e4f2 Bump version to 4.10.1-dev 2024-10-22 23:29:03 +02:00
34 changed files with 454 additions and 293 deletions

View File

@@ -1,5 +1,18 @@
# Changelog # Changelog
## Adminer 4.11 (2024-10-30)
- Support drag-n-drop moving on touch screens
- Fix drag-n-drop moving of function parameters
- Update project information in comments
- Update CS and SK translations
- Show help popup after a short delay
- PostgreSQL: Show list of schemas in database, unify lists of sequences and user types
- MariaDB: Fix several links to documentation pages
- MySQL: Fix highlighting current table in menu on macOS
- MS SQL: Prefix Unicode strings with 'N' so they are treated correctly
- Fix printing error message while validating server URL
- Small CSS tuning
## Adminer 4.10 (2024-10-22) ## Adminer 4.10 (2024-10-22)
### UI changes and improvements ### UI changes and improvements

View File

@@ -182,8 +182,8 @@ foreach ($engines as $engine) {
<p> <p>
<?php if (support("columns") || $TABLE == "") { ?> <?php if (support("columns") || $TABLE == "") { ?>
<?php echo lang('Table name'); ?>: <input name="name" data-maxlength="64" value="<?php echo h($row["name"]); ?>" autocapitalize="off"> <?php echo lang('Table name'); ?>: <input name="name" data-maxlength="64" value="<?php echo h($row["name"]); ?>" autocapitalize="off">
<?php if ($TABLE == "" && !$_POST) { echo script("focus(qs('#form')['name']);"); } ?> <?php if ($TABLE == "" && !$_POST) { echo script("focus(gid('form')['name']);"); } ?>
<?php echo ($engines ? "<select name='Engine'>" . optionlist(array("" => "(" . lang('engine') . ")") + $engines, $row["Engine"]) . "</select>" . on_help("getTarget(event).value", 1) . script("qsl('select').onchange = helpClose;") : ""); ?> <?php echo ($engines ? "<select name='Engine'>" . optionlist(array("" => "(" . lang('engine') . ")") + $engines, $row["Engine"]) . "</select>" . help_script_command("value", true) : ""); ?>
<?php echo ($collations && !preg_match("~sqlite|mssql~", $jush) ? html_select("Collation", array("" => "(" . lang('collation') . ")") + $collations, $row["Collation"]) : ""); ?> <?php echo ($collations && !preg_match("~sqlite|mssql~", $jush) ? html_select("Collation", array("" => "(" . lang('collation') . ")") + $collations, $row["Collation"]) : ""); ?>
<input type="submit" value="<?php echo lang('Save'); ?>"> <input type="submit" value="<?php echo lang('Save'); ?>">
<?php } ?> <?php } ?>
@@ -221,7 +221,7 @@ if (support("partitioning")) {
print_fieldset("partition", lang('Partition by'), $row["partition_by"]); print_fieldset("partition", lang('Partition by'), $row["partition_by"]);
?> ?>
<p> <p>
<?php echo "<select name='partition_by'>" . optionlist(array("" => "") + $partition_by, $row["partition_by"]) . "</select>" . on_help("getTarget(event).value.replace(/./, 'PARTITION BY \$&')", 1) . script("qsl('select').onchange = partitionByChange;"); ?> <?php echo "<select name='partition_by'>" . optionlist(array("" => "") + $partition_by, $row["partition_by"]) . "</select>" . help_script_command("value.replace(/./, 'PARTITION BY \$&')", true) . script("qsl('select').onchange = partitionByChange;"); ?>
(<input name="partition" value="<?php echo h($row["partition"]); ?>">) (<input name="partition" value="<?php echo h($row["partition"]); ?>">)
<?php echo lang('Partitions'); ?>: <input type="number" name="partitions" class="size<?php echo ($partition_table || !$row["partition_by"] ? " hidden" : ""); ?>" value="<?php echo h($row["partitions"]); ?>"> <?php echo lang('Partitions'); ?>: <input type="number" name="partitions" class="size<?php echo ($partition_table || !$row["partition_by"] ? " hidden" : ""); ?>" value="<?php echo h($row["partitions"]); ?>">
<table cellspacing="0" id="partition-table"<?php echo ($partition_table ? "" : " class='hidden'"); ?>> <table cellspacing="0" id="partition-table"<?php echo ($partition_table ? "" : " class='hidden'"); ?>>

View File

@@ -66,7 +66,7 @@ echo ($_POST["add_x"] || strpos($name, "\n")
'mariadb' => "supported-character-sets-and-collations/", 'mariadb' => "supported-character-sets-and-collations/",
'mssql' => "ms187963.aspx", 'mssql' => "ms187963.aspx",
)) : ""); )) : "");
echo script("focus(qs('#name'));"); echo script("focus(gid('name'));");
?> ?>
<input type="submit" value="<?php echo lang('Save'); ?>"> <input type="submit" value="<?php echo lang('Save'); ?>">
<?php <?php

View File

@@ -47,7 +47,28 @@ if ($tables_views && !$error && !$_POST["search"]) {
page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true); page_header(($_GET["ns"] == "" ? lang('Database') . ": " . h(DB) : lang('Schema') . ": " . h($_GET["ns"])), $error, true);
if ($adminer->homepage()) { if ($adminer->homepage()) {
if ($_GET["ns"] !== "") { if ($_GET["ns"] === "") {
echo "<h3 id='schemas'>" . lang('Schemas') . "</h3>\n";
$schemas = schemas();
if (!$schemas) {
echo "<p class='message'>" . lang('No schemas.') . "\n";
} else {
// TODO: Checkboxes for batch dropping of schemas.
echo "<div class='scrollable'>\n",
"<table class='nowrap'>\n",
'<thead><tr class="wrap">',
'<th>', lang('Schema'), "</th>",
'</tr></thead>';
foreach ($schemas as $name) {
echo "<tr", odd(), ">",
"<th><a href='", h(ME), "ns=" . urlencode($name), "' title='", lang('Show schema'), "'>" . h($name) . "</a></th>",
"</tr>";
}
echo '</table></div>';
}
} else {
echo "<h3 id='tables-views'>" . lang('Tables and views') . "</h3>\n"; echo "<h3 id='tables-views'>" . lang('Tables and views') . "</h3>\n";
$tables_list = tables_list(); $tables_list = tables_list();
if (!$tables_list) { if (!$tables_list) {
@@ -73,7 +94,7 @@ if ($adminer->homepage()) {
echo "<table cellspacing='0' class='nowrap checkable'>\n"; echo "<table cellspacing='0' class='nowrap checkable'>\n";
echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"); echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");
echo '<thead><tr class="wrap">'; echo '<thead><tr class="wrap">';
echo '<td><input id="check-all" type="checkbox" class="jsonly">' . script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);", ""); echo '<td><input id="check-all" type="checkbox" class="jsonly">' . script("gid('check-all').onclick = partial(formCheck, /^(tables|views)\[/);", "");
echo '<th>' . lang('Table'); echo '<th>' . lang('Table');
echo '<td>' . lang('Engine') . doc_link(array('sql' => 'storage-engines.html')); echo '<td>' . lang('Engine') . doc_link(array('sql' => 'storage-engines.html'));
echo '<td>' . lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/')); echo '<td>' . lang('Collation') . doc_link(array('sql' => 'charset-charsets.html', 'mariadb' => 'supported-character-sets-and-collations/'));
@@ -126,17 +147,17 @@ if ($adminer->homepage()) {
echo "</div>\n"; echo "</div>\n";
if (!information_schema(DB)) { if (!information_schema(DB)) {
echo "<div class='footer'><div>\n"; echo "<div class='footer'><div>\n";
$vacuum = "<input type='submit' value='" . lang('Vacuum') . "'> " . on_help("'VACUUM'"); $vacuum = "<input type='submit' value='" . lang('Vacuum') . "'> " . help_script("VACUUM");
$optimize = "<input type='submit' name='optimize' value='" . lang('Optimize') . "'> " . on_help($jush == "sql" ? "'OPTIMIZE TABLE'" : "'VACUUM OPTIMIZE'"); $optimize = "<input type='submit' name='optimize' value='" . lang('Optimize') . "'> " . help_script($jush == "sql" ? "OPTIMIZE TABLE" : "VACUUM OPTIMIZE");
echo "<fieldset><legend>" . lang('Selected') . " <span id='selected'></span></legend><div>" echo "<fieldset><legend>" . lang('Selected') . " <span id='selected'></span></legend><div>"
. ($jush == "sqlite" ? $vacuum . ($jush == "sqlite" ? $vacuum
: ($jush == "pgsql" ? $vacuum . $optimize : ($jush == "pgsql" ? $vacuum . $optimize
: ($jush == "sql" ? "<input type='submit' value='" . lang('Analyze') . "'> " . on_help("'ANALYZE TABLE'") . $optimize : ($jush == "sql" ? "<input type='submit' value='" . lang('Analyze') . "'> " . help_script("ANALYZE TABLE") . $optimize
. "<input type='submit' name='check' value='" . lang('Check') . "'> " . on_help("'CHECK TABLE'") . "<input type='submit' name='check' value='" . lang('Check') . "'> " . help_script("CHECK TABLE")
. "<input type='submit' name='repair' value='" . lang('Repair') . "'> " . on_help("'REPAIR TABLE'") . "<input type='submit' name='repair' value='" . lang('Repair') . "'> " . help_script("REPAIR TABLE")
: ""))) : "")))
. "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . on_help($jush == "sqlite" ? "'DELETE'" : "'TRUNCATE" . ($jush == "pgsql" ? "'" : " TABLE'")) . confirm() . "<input type='submit' name='truncate' value='" . lang('Truncate') . "'> " . help_script($jush == "sqlite" ? "DELETE" : ("TRUNCATE" . ($jush == "pgsql" ? "" : " TABLE"))) . confirm()
. "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . on_help("'DROP TABLE'") . confirm() . "\n"; . "<input type='submit' name='drop' value='" . lang('Drop') . "'>" . help_script("DROP TABLE") . confirm() . "\n";
$databases = (support("scheme") ? $adminer->schemas() : $adminer->databases()); $databases = (support("scheme") ? $adminer->schemas() : $adminer->databases());
if (count($databases) != 1 && $jush != "sqlite") { if (count($databases) != 1 && $jush != "sqlite") {
$db = (isset($_POST["target"]) ? $_POST["target"] : (support("scheme") ? $_GET["ns"] : DB)); $db = (isset($_POST["target"]) ? $_POST["target"] : (support("scheme") ? $_GET["ns"] : DB));
@@ -186,12 +207,16 @@ if ($adminer->homepage()) {
echo "<h3 id='sequences'>" . lang('Sequences') . "</h3>\n"; echo "<h3 id='sequences'>" . lang('Sequences') . "</h3>\n";
$sequences = get_vals("SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = current_schema() ORDER BY sequence_name"); $sequences = get_vals("SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = current_schema() ORDER BY sequence_name");
if ($sequences) { if ($sequences) {
echo "<table>\n"; echo "<table>\n",
echo "<thead><tr><th>" . lang('Name') . "</thead>\n"; "<thead><tr><th>", lang('Name'), "</th><td></td></tr></thead>\n";
odd(''); odd('');
foreach ($sequences as $val) { foreach ($sequences as $val) {
echo "<tr" . odd() . "><th><a href='" . h(ME) . "sequence=" . urlencode($val) . "'>" . h($val) . "</a>\n"; echo "<tr", odd(), ">",
"<th>", h($val), "</th>",
"<td><a href='", h(ME), "sequence=", urlencode($val), "'>", lang('Alter'), "</a></td>\n";
} }
echo "</table>\n"; echo "</table>\n";
} }
echo "<p class='links'><a href='" . h(ME) . "sequence='>" . lang('Create sequence') . "</a>\n"; echo "<p class='links'><a href='" . h(ME) . "sequence='>" . lang('Create sequence') . "</a>\n";
@@ -201,12 +226,16 @@ if ($adminer->homepage()) {
echo "<h3 id='user-types'>" . lang('User types') . "</h3>\n"; echo "<h3 id='user-types'>" . lang('User types') . "</h3>\n";
$user_types = types(); $user_types = types();
if ($user_types) { if ($user_types) {
echo "<table>\n"; echo "<table>\n",
echo "<thead><tr><th>" . lang('Name') . "</thead>\n"; "<thead><tr><th>", lang('Name'), "</th><td></td></tr></thead>\n";
odd(''); odd('');
foreach ($user_types as $val) { foreach ($user_types as $val) {
echo "<tr" . odd() . "><th><a href='" . h(ME) . "type=" . urlencode($val) . "'>" . h($val) . "</a>\n"; echo "<tr", odd(), ">",
"<th>", h($val), "</th>",
"<td><a href='", h(ME), "type=", urlencode($val), "'>", lang('Alter'), "</a></td>\n";
} }
echo "</table>\n"; echo "</table>\n";
} }
echo "<p class='links'><a href='" . h(ME) . "type='>" . lang('Create type') . "</a>\n"; echo "<p class='links'><a href='" . h(ME) . "type='>" . lang('Create type') . "</a>\n";

View File

@@ -40,7 +40,8 @@ if (isset($_GET["mssql"])) {
} }
function quote($string) { function quote($string) {
return "'" . str_replace("'", "''", $string) . "'"; $unicode = strlen($string) != strlen(utf8_decode($string));
return ($unicode ? "N" : "") . "'" . str_replace("'", "''", $string) . "'";
} }
function select_db($database) { function select_db($database) {
@@ -163,7 +164,8 @@ if (isset($_GET["mssql"])) {
} }
function quote($string) { function quote($string) {
return "'" . str_replace("'", "''", $string) . "'"; $unicode = strlen($string) != strlen(utf8_decode($string));
return ($unicode ? "N" : "") . "'" . str_replace("'", "''", $string) . "'";
} }
function select_db($database) { function select_db($database) {

View File

@@ -500,11 +500,14 @@ if (!defined("DRIVER")) {
* @return array array($name => array("Name" => , "Engine" => , "Comment" => , "Oid" => , "Rows" => , "Collation" => , "Auto_increment" => , "Data_length" => , "Index_length" => , "Data_free" => )) or only inner array with $name * @return array array($name => array("Name" => , "Engine" => , "Comment" => , "Oid" => , "Rows" => , "Collation" => , "Auto_increment" => , "Data_length" => , "Index_length" => , "Data_free" => )) or only inner array with $name
*/ */
function table_status($name = "", $fast = false) { function table_status($name = "", $fast = false) {
$return = array(); if ($fast && min_version(5)) {
foreach (get_rows($fast && min_version(5) $query = "SELECT TABLE_NAME AS Name, ENGINE AS Engine, CREATE_OPTIONS AS Create_options, TABLE_COMMENT AS Comment FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() " . ($name != "" ? "AND TABLE_NAME = " . q($name) : "ORDER BY Name");
? "SELECT TABLE_NAME AS Name, ENGINE AS Engine, CREATE_OPTIONS AS Create_options, TABLE_COMMENT AS Comment FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() " . ($name != "" ? "AND TABLE_NAME = " . q($name) : "ORDER BY Name") } else {
: "SHOW TABLE STATUS" . ($name != "" ? " LIKE " . q(addcslashes($name, "%_\\")) : "") $query = "SHOW TABLE STATUS" . ($name != "" ? " LIKE " . q(addcslashes($name, "%_\\")) : "");
) as $row) { }
$tables = [];
foreach (get_rows($query) as $row) {
if ($row["Engine"] == "InnoDB") { if ($row["Engine"] == "InnoDB") {
// ignore internal comment, unnecessary since MySQL 5.1.21 // ignore internal comment, unnecessary since MySQL 5.1.21
$row["Comment"] = preg_replace('~(?:(.+); )?InnoDB free: .*~', '\1', $row["Comment"]); $row["Comment"] = preg_replace('~(?:(.+); )?InnoDB free: .*~', '\1', $row["Comment"]);
@@ -513,11 +516,15 @@ if (!defined("DRIVER")) {
$row["Comment"] = ""; $row["Comment"] = "";
} }
if ($name != "") { if ($name != "") {
// MariaDB: Table name is returned as lowercase on macOS, so we fix it here.
$row["Name"] = $name;
return $row; return $row;
} }
$return[$row["Name"]] = $row;
$tables[$row["Name"]] = $row;
} }
return $return;
return $tables;
} }
/** Find out whether the identifier is view /** Find out whether the identifier is view

View File

@@ -182,8 +182,8 @@ $prefixes = array();
if (DB != "") { if (DB != "") {
$checked = ($TABLE != "" ? "" : " checked"); $checked = ($TABLE != "" ? "" : " checked");
echo "<thead><tr>"; echo "<thead><tr>";
echo "<th style='text-align: left;'><label class='block'><input type='checkbox' id='check-tables'$checked>" . lang('Tables') . "</label>" . script("qs('#check-tables').onclick = partial(formCheck, /^tables\\[/);", ""); echo "<th style='text-align: left;'><label class='block'><input type='checkbox' id='check-tables'$checked>" . lang('Tables') . "</label>" . script("gid('check-tables').onclick = partial(formCheck, /^tables\\[/);", "");
echo "<th style='text-align: right;'><label class='block'>" . lang('Data') . "<input type='checkbox' id='check-data'$checked></label>" . script("qs('#check-data').onclick = partial(formCheck, /^data\\[/);", ""); echo "<th style='text-align: right;'><label class='block'>" . lang('Data') . "<input type='checkbox' id='check-data'$checked></label>" . script("gid('check-data').onclick = partial(formCheck, /^data\\[/);", "");
echo "</thead>\n"; echo "</thead>\n";
$views = ""; $views = "";
@@ -208,7 +208,7 @@ if (DB != "") {
} else { } else {
echo "<thead><tr><th style='text-align: left;'>"; echo "<thead><tr><th style='text-align: left;'>";
echo "<label class='block'><input type='checkbox' id='check-databases'" . ($TABLE == "" ? " checked" : "") . ">" . lang('Database') . "</label>"; echo "<label class='block'><input type='checkbox' id='check-databases'" . ($TABLE == "" ? " checked" : "") . ">" . lang('Database') . "</label>";
echo script("qs('#check-databases').onclick = partial(formCheck, /^databases\\[/);", ""); echo script("gid('check-databases').onclick = partial(formCheck, /^databases\\[/);", "");
echo "</thead>\n"; echo "</thead>\n";
$databases = $adminer->databases(); $databases = $adminer->databases();
if ($databases) { if ($databases) {

View File

@@ -129,7 +129,7 @@ class Adminer {
echo "<table cellspacing='0' class='layout'>\n"; echo "<table cellspacing='0' class='layout'>\n";
echo $this->loginFormField('driver', '<tr><th>' . lang('System') . '<td>', html_select("auth[driver]", $drivers, DRIVER, "loginDriver(this);") . "\n"); echo $this->loginFormField('driver', '<tr><th>' . lang('System') . '<td>', html_select("auth[driver]", $drivers, DRIVER, "loginDriver(this);") . "\n");
echo $this->loginFormField('server', '<tr><th>' . lang('Server') . '<td>', '<input name="auth[server]" value="' . h(SERVER) . '" title="hostname[:port]" placeholder="localhost" autocapitalize="off">' . "\n"); echo $this->loginFormField('server', '<tr><th>' . lang('Server') . '<td>', '<input name="auth[server]" value="' . h(SERVER) . '" title="hostname[:port]" placeholder="localhost" autocapitalize="off">' . "\n");
echo $this->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input name="auth[username]" id="username" value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("focus(qs('#username')); qs('#username').form['auth[driver]'].onchange();")); echo $this->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input name="auth[username]" id="username" value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("focus(gid('username')); gid('username').form['auth[driver]'].onchange();"));
echo $this->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">' . "\n"); echo $this->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">' . "\n");
echo $this->loginFormField('db', '<tr><th>' . lang('Database') . '<td>', '<input name="auth[db]" value="' . h($_GET["db"]) . '" autocapitalize="off">' . "\n"); echo $this->loginFormField('db', '<tr><th>' . lang('Database') . '<td>', '<input name="auth[db]" value="' . h($_GET["db"]) . '" autocapitalize="off">' . "\n");
echo "</table>\n"; echo "</table>\n";
@@ -424,8 +424,8 @@ class Adminer {
echo "<select name='columns[$i][fun]'>", echo "<select name='columns[$i][fun]'>",
optionlist([-1 => ""] + array_filter([lang('Functions') => $functions, lang('Aggregation') => $grouping]), $val["fun"]), optionlist([-1 => ""] + array_filter([lang('Functions') => $functions, lang('Aggregation') => $grouping]), $val["fun"]),
"</select>", "</select>",
on_help("getTarget(event).value && getTarget(event).value.replace(/ |\$/, '(') + ')'", 1), help_script_command("value && value.replace(/ |\$/, '(') + ')'", true),
script("qsl('select').onchange = (event) => { helpClose();" . ($key !== "" ? "" : " qsl('select, input:not(.remove)', event.target.parentNode).onchange();") . " };", ""), script("qsl('select').onchange = (event) => { " . ($key !== "" ? "" : " qsl('select, input:not(.remove)', event.target.parentNode).onchange();") . " };", ""),
"($column)"; "($column)";
} else { } else {
echo $column; echo $column;
@@ -557,7 +557,7 @@ class Adminer {
json_row($key); json_row($key);
} }
echo ";\n"; echo ";\n";
echo "selectFieldChange.call(qs('#form')['select']);\n"; echo "selectFieldChange.call(gid('form')['select']);\n";
echo "</script>\n"; echo "</script>\n";
echo "</div></fieldset>\n"; echo "</div></fieldset>\n";
} }
@@ -1073,7 +1073,7 @@ class Adminer {
} }
} }
if ($output) { if ($output) {
echo "<ul id='logins'>\n$output</ul>\n" . script("mixin(qs('#logins'), {onmouseover: menuOver, onmouseout: menuOut});"); echo "<ul id='logins'>\n$output</ul>\n" . script("mixin(gid('logins'), {onmouseover: menuOver, onmouseout: menuOut});");
} }
} else { } else {
$tables = array(); $tables = array();
@@ -1156,7 +1156,7 @@ bodyLoad('<?php echo (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '
if ($databases) { if ($databases) {
echo "<select id='database-select' name='db'>" . optionlist(["" => lang('Database')] + $databases, DB) . "</select>" echo "<select id='database-select' name='db'>" . optionlist(["" => lang('Database')] + $databases, DB) . "</select>"
. script("mixin(qs('#database-select'), {onmousedown: dbMouseDown, onchange: dbChange});"); . script("mixin(gid('database-select'), {onmousedown: dbMouseDown, onchange: dbChange});");
} else { } else {
echo "<input id='database-select' name='db' value='" . h(DB) . "' autocapitalize='off'>\n"; echo "<input id='database-select' name='db' value='" . h(DB) . "' autocapitalize='off'>\n";
} }
@@ -1164,7 +1164,7 @@ bodyLoad('<?php echo (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '
if (support("scheme") && $missing != "db" && DB != "" && $connection->select_db(DB)) { if (support("scheme") && $missing != "db" && DB != "" && $connection->select_db(DB)) {
echo "<br><select id='scheme-select' name='ns'>" . optionlist(["" => lang('Schema')] + $adminer->schemas(), $_GET["ns"]) . "</select>" echo "<br><select id='scheme-select' name='ns'>" . optionlist(["" => lang('Schema')] + $adminer->schemas(), $_GET["ns"]) . "</select>"
. script("mixin(qs('#scheme-select'), {onmousedown: dbMouseDown, onchange: dbChange});"); . script("mixin(gid('scheme-select'), {onmousedown: dbMouseDown, onchange: dbChange});");
if ($_GET["ns"] != "") { if ($_GET["ns"] != "") {
set_schema($_GET["ns"]); set_schema($_GET["ns"]);
@@ -1200,7 +1200,7 @@ bodyLoad('<?php echo (is_object($connection) ? preg_replace('~^(\d\.?\d).*~s', '
* @return null * @return null
*/ */
function tablesPrint($tables) { function tablesPrint($tables) {
echo "<ul id='tables'>" . script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});"); echo "<ul id='tables'>" . script("mixin(gid('tables'), {onmouseover: menuOver, onmouseout: menuOut});");
foreach ($tables as $table => $status) { foreach ($tables as $table => $status) {
$name = $this->tableName($status); $name = $this->tableName($status);

View File

@@ -71,7 +71,7 @@ if (!function_exists('is_server_host_valid')) {
*/ */
function build_http_url($server, $username, $password, $defaultServer, $defaultPort = null) { function build_http_url($server, $username, $password, $defaultServer, $defaultPort = null) {
if (!preg_match('~^(https?://)?([^:]*)(:\d+)?$~', rtrim($server, '/'), $matches)) { if (!preg_match('~^(https?://)?([^:]*)(:\d+)?$~', rtrim($server, '/'), $matches)) {
$this->error = lang('Invalid server or credentials.'); auth_error(lang('Invalid server or credentials.'));
return false; return false;
} }

View File

@@ -29,9 +29,9 @@ function page_header($title, $error = "", $breadcrumb = [], $title2 = "") {
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="robots" content="noindex"> <meta name="robots" content="noindex">
<title><?php echo $title_page; ?></title> <title><?php echo $title_page; ?></title>
<link rel="stylesheet" type="text/css" href="../adminer/static/default.css"> <link rel="stylesheet" type="text/css" href="../adminer/static/default.css?<?php echo filemtime("../adminer/static/default.css"); ?>">
<?php echo script_src("../adminer/static/functions.js"); ?> <?php echo script_src("../adminer/static/functions.js?" . filemtime("../adminer/static/functions.js")); ?>
<?php echo script_src("static/editing.js"); ?> <?php echo script_src("static/editing.js?" . filemtime("../adminer/static/editing.js")); ?>
<?php if ($adminer->head()) { ?> <?php if ($adminer->head()) { ?>
<link rel="shortcut icon" type="image/x-icon" href="../adminer/static/favicon.ico"> <link rel="shortcut icon" type="image/x-icon" href="../adminer/static/favicon.ico">
<link rel="apple-touch-icon" href="../adminer/static/favicon.ico"> <link rel="apple-touch-icon" href="../adminer/static/favicon.ico">
@@ -54,7 +54,7 @@ function page_header($title, $error = "", $breadcrumb = [], $title2 = "") {
</script> </script>
<div id="help" class="jush-<?php echo $jush; ?> jsonly hidden"></div> <div id="help" class="jush-<?php echo $jush; ?> jsonly hidden"></div>
<?php echo script("mixin(qs('#help'), {onmouseover: function () { helpOpen = 1; }, onmouseout: helpMouseout});"); ?> <?php echo script("initHelpPopup();"); ?>
<div id="content"> <div id="content">
<?php <?php

View File

@@ -297,7 +297,7 @@ function edit_fields(array $fields, array $collations, $type = "TABLE", $foreign
} }
?> ?>
<th id="label-name"><?php echo ($type == "TABLE" ? lang('Column name') : lang('Parameter name')); ?></th> <th id="label-name"><?php echo ($type == "TABLE" ? lang('Column name') : lang('Parameter name')); ?></th>
<td id="label-type"><?php echo lang('Type'); ?><textarea id="enum-edit" rows="4" cols="12" wrap="off" style="display: none;"></textarea><?php echo script("qs('#enum-edit').onblur = editingLengthBlur;"); ?></td> <td id="label-type"><?php echo lang('Type'); ?><textarea id="enum-edit" rows="4" cols="12" wrap="off" style="display: none;"></textarea><?php echo script("gid('enum-edit').onblur = editingLengthBlur;"); ?></td>
<td id="label-length"><?php echo lang('Length'); ?></td> <td id="label-length"><?php echo lang('Length'); ?></td>
<td><?php echo lang('Options'); /* no label required, options have their own label */ ?></td> <td><?php echo lang('Options'); /* no label required, options have their own label */ ?></td>
<?php if ($type == "TABLE") { ?> <?php if ($type == "TABLE") { ?>
@@ -621,7 +621,7 @@ function doc_link(array $paths, $text = "<sup>?</sup>") {
]; ];
if (preg_match('~MariaDB~', $server_info)) { if (preg_match('~MariaDB~', $server_info)) {
$urls['sql'] = "https://mariadb.com/kb/en/library/"; $urls['sql'] = "https://mariadb.com/kb/en/";
$paths['sql'] = (isset($paths['mariadb']) ? $paths['mariadb'] : str_replace(".html", "/", $paths['sql'])); $paths['sql'] = (isset($paths['mariadb']) ? $paths['mariadb'] : str_replace(".html", "/", $paths['sql']));
} }

View File

@@ -967,7 +967,7 @@ function input($field, $value, $function) {
$has_function = (in_array($function, $functions) || isset($functions[$function])); $has_function = (in_array($function, $functions) || isset($functions[$function]));
echo (count($functions) > 1 echo (count($functions) > 1
? "<select name='function[$name]' $disabled>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>" ? "<select name='function[$name]' $disabled>" . optionlist($functions, $function === null || $has_function ? $function : "") . "</select>"
. on_help("getTarget(event).value.replace(/^SQL\$/, '')", 1) . help_script_command("value.replace(/^SQL\$/, '')", true)
. script("qsl('select').onchange = functionChange;", "") . script("qsl('select').onchange = functionChange;", "")
: h(reset($functions)) : h(reset($functions))
) . '<td>'; ) . '<td>';
@@ -1486,13 +1486,26 @@ function lzw_decompress($binary) {
return $return; return $return;
} }
/** Return events to display help on mouse over /**
* @param string JS expression * @param string $text Help text.
* @param bool JS expression * @param bool $side Side position.
* @return string *
*/ * @return string
function on_help($command, $side = 0) { */
return script("mixin(qsl('select, input'), {onmouseover: function (event) { helpMouseover.call(this, event, $command, $side) }, onmouseout: helpMouseout});", ""); function help_script($text, $side = false)
{
return script("initHelpFor(qsl('select, input'), '" . h($text) . "', $side);", "");
}
/**
* @param string $command JS expression for returning the help text.
* @param bool $side Side position.
*
* @return string
*/
function help_script_command($command, $side = false)
{
return script("initHelpFor(qsl('select, input'), (value) => { return $command; }, $side);", "");
} }
/** Print edit data form /** Print edit data form
@@ -1586,7 +1599,7 @@ function edit_form($table, $fields, $row, $update) {
} }
} }
echo ($update ? "<input type='submit' name='delete' value='" . lang('Delete') . "'>" . confirm() . "\n" echo ($update ? "<input type='submit' name='delete' value='" . lang('Delete') . "'>" . confirm() . "\n"
: ($_POST || !$fields ? "" : script("focus(qsa('td', qs('#form'))[1].firstChild);")) : ($_POST || !$fields ? "" : script("focus(qsa('td', gid('form'))[1].firstChild);"))
); );
if (isset($_GET["select"])) { if (isset($_GET["select"])) {
hidden_fields(array("check" => (array) $_POST["check"], "clone" => $_POST["clone"], "all" => $_POST["all"])); hidden_fields(array("check" => (array) $_POST["check"], "clone" => $_POST["clone"], "all" => $_POST["all"]));

View File

@@ -1,2 +1,2 @@
<?php <?php
$VERSION = "4.10"; $VERSION = "4.11";

View File

@@ -1,11 +1,14 @@
<?php <?php
/** Adminer - Compact database management /**
* @link https://www.adminer.org/ * Adminer - Database management in a single PHP file
* @author Jakub Vrana, https://www.vrana.cz/ *
* @copyright 2007 Jakub Vrana * @link https://github.com/pematon/adminer
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 * @author Jakub Vrana (https://www.vrana.cz/)
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other) * @author Peter Knut
*/ * @copyright 2007-2021 Jakub Vrana, 2024 Peter Knut
* @license Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
* @license GNU General Public License, version 2 (https://www.gnu.org/licenses/gpl-2.0.html)
*/
include "./include/bootstrap.inc.php"; include "./include/bootstrap.inc.php";
include "./include/tmpfile.inc.php"; include "./include/tmpfile.inc.php";

View File

@@ -259,8 +259,8 @@ $translations = array(
'%d row(s)' => array('%d řádek', '%d řádky', '%d řádků'), '%d row(s)' => array('%d řádek', '%d řádky', '%d řádků'),
'Page' => 'Stránka', 'Page' => 'Stránka',
'last' => 'poslední', 'last' => 'poslední',
'Load more data' => 'Nahrát další data', 'Load more data' => 'Načíst další data',
'Loading' => 'Nahrává se', 'Loading' => 'Načítá se',
'Whole result' => 'Celý výsledek', 'Whole result' => 'Celý výsledek',
'%d byte(s)' => array('%d bajt', '%d bajty', '%d bajtů'), '%d byte(s)' => array('%d bajt', '%d bajty', '%d bajtů'),

View File

@@ -277,8 +277,8 @@ $translations = array(
'DB' => 'DB', 'DB' => 'DB',
'File must be in UTF-8 encoding.' => 'Súbor musí byť v kódovaní UTF-8.', 'File must be in UTF-8 encoding.' => 'Súbor musí byť v kódovaní UTF-8.',
'Modify' => 'Zmeniť', 'Modify' => 'Zmeniť',
'Load more data' => 'Nahráť ďalšie dáta', 'Load more data' => 'Načítať ďalšie dáta',
'Loading' => 'Nahráva sa', 'Loading' => 'Načítava sa',
'ATTACH queries are not supported.' => 'Dotazy ATTACH nie sú podporované.', 'ATTACH queries are not supported.' => 'Dotazy ATTACH nie sú podporované.',
'Warnings' => 'Varovania', 'Warnings' => 'Varovania',
'%d / ' => '%d / ', '%d / ' => '%d / ',

View File

@@ -39,12 +39,16 @@ $routine_languages = routine_languages();
<?php echo ($routine_languages ? lang('Language') . ": " . html_select("language", $routine_languages, $row["language"]) . "\n" : ""); ?> <?php echo ($routine_languages ? lang('Language') . ": " . html_select("language", $routine_languages, $row["language"]) . "\n" : ""); ?>
<input type="submit" value="<?php echo lang('Save'); ?>"> <input type="submit" value="<?php echo lang('Save'); ?>">
<div class="scrollable"> <div class="scrollable">
<table cellspacing="0" class="nowrap"> <table cellspacing="0" class="nowrap" id="edit-fields">
<?php <?php
edit_fields($row["fields"], $collations, $routine); edit_fields($row["fields"], $collations, $routine);
if (isset($_GET["function"])) { if (isset($_GET["function"])) {
echo "<tr><td>" . lang('Return type'); echo "<tbody><tr><th></th>",
"<th>", lang('Return type'), "</th>";
edit_type("returns", $row["returns"], $collations, array(), ($jush == "pgsql" ? array("void", "trigger") : array())); edit_type("returns", $row["returns"], $collations, array(), ($jush == "pgsql" ? array("void", "trigger") : array()));
echo "<td></td></tr></tbody>\n";
} }
?> ?>
</table> </table>

View File

@@ -50,9 +50,9 @@ foreach (table_status('', true) as $table => $table_status) {
?> ?>
<div id="schema" style="height: <?php echo $top; ?>em;"> <div id="schema" style="height: <?php echo $top; ?>em;">
<script<?php echo nonce(); ?>> <script<?php echo nonce(); ?>>
qs('#schema').onselectstart = function () { return false; }; gid('schema').onselectstart = function () { return false; };
var tablePos = {<?php echo implode(",", $table_pos_js) . "\n"; ?>}; var tablePos = {<?php echo implode(",", $table_pos_js) . "\n"; ?>};
var em = qs('#schema').offsetHeight / <?php echo $top; ?>; var em = gid('schema').offsetHeight / <?php echo $top; ?>;
document.onmousemove = schemaMousemove; document.onmousemove = schemaMousemove;
document.onmouseup = partialArg(schemaMouseup, '<?php echo js_escape(DB); ?>'); document.onmouseup = partialArg(schemaMouseup, '<?php echo js_escape(DB); ?>');
</script> </script>

View File

@@ -27,7 +27,7 @@ if (!$row) {
<form action="" method="post"> <form action="" method="post">
<p><input name="name" id="name" value="<?php echo h($row["name"]); ?>" autocapitalize="off"> <p><input name="name" id="name" value="<?php echo h($row["name"]); ?>" autocapitalize="off">
<?php echo script("focus(qs('#name'));"); ?> <?php echo script("focus(gid('name'));"); ?>
<input type="submit" value="<?php echo lang('Save'); ?>"> <input type="submit" value="<?php echo lang('Save'); ?>">
<?php <?php
if ($_GET["ns"] != "") { if ($_GET["ns"] != "") {

View File

@@ -322,10 +322,10 @@ if (!$columns && support("table")) {
echo "<div class='scrollable'>"; echo "<div class='scrollable'>";
echo "<table id='table' cellspacing='0' class='nowrap checkable'>"; echo "<table id='table' cellspacing='0' class='nowrap checkable'>";
echo script("mixin(qs('#table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true), onkeydown: editingKeydown});"); echo script("mixin(gid('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true), onkeydown: editingKeydown});");
echo "<thead><tr>" . (!$group && $select echo "<thead><tr>" . (!$group && $select
? "" ? ""
: "<td><input type='checkbox' id='all-page' class='jsonly'>" . script("qs('#all-page').onclick = partial(formCheck, /check/);", "") : "<td><input type='checkbox' id='all-page' class='jsonly'>" . script("gid('all-page').onclick = partial(formCheck, /check/);", "")
. " <a href='" . h($_GET["modify"] ? remove_from_uri("modify") : $_SERVER["REQUEST_URI"] . "&modify=1") . "'>" . lang('Modify') . "</a>"); . " <a href='" . h($_GET["modify"] ? remove_from_uri("modify") : $_SERVER["REQUEST_URI"] . "&modify=1") . "'>" . lang('Modify') . "</a>");
$names = array(); $names = array();
$functions = array(); $functions = array();
@@ -353,10 +353,10 @@ if (!$columns && support("table")) {
} }
echo "<span class='column hidden'>"; echo "<span class='column hidden'>";
if ($sortable) { if ($sortable) {
echo "<a href='" . h($href . $desc) . "' title='" . lang('descending') . "' class='text'> ↓</a>"; echo "<a href='" . h($href . $desc) . "' title='" . lang('descending') . "' class='text'> ↓</a>";
} }
if (!$val["fun"] && isset($field["privileges"]["where"])) { if (!$val["fun"] && isset($field["privileges"]["where"])) {
echo '<a href="#fieldset-search" title="' . lang('Search') . '" class="text jsonly"> =</a>'; echo '<a href="#fieldset-search" title="' . lang('Search') . '" class="text jsonly"> =</a>';
echo script("qsl('a').onclick = partial(selectSearch, '" . js_escape($key) . "');"); echo script("qsl('a').onclick = partial(selectSearch, '" . js_escape($key) . "');");
} }
echo "</span>"; echo "</span>";

View File

@@ -243,7 +243,7 @@ if (!isset($_GET["import"])) {
} }
echo "<p>"; echo "<p>";
textarea("query", $q, 20); textarea("query", $q, 20);
echo script(($_POST ? "" : "qs('textarea').focus();\n") . "qs('#form').onsubmit = partial(sqlSubmit, qs('#form'), '" . js_escape(remove_from_uri("sql|limit|error_stops|only_errors|history")) . "');"); echo script(($_POST ? "" : "qs('textarea').focus();\n") . "gid('form').onsubmit = partial(sqlSubmit, gid('form'), '" . js_escape(remove_from_uri("sql|limit|error_stops|only_errors|history")) . "');");
echo "<p>$execute\n"; echo "<p>$execute\n";
echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n"; echo lang('Limit rows') . ": <input type='number' name='limit' class='size' value='" . h($_POST ? $_POST["limit"] : $_GET["limit"]) . "'>\n";

View File

@@ -1,7 +1,7 @@
/** @author Ondrej Valka, http://valka.info */ /** @author Ondrej Valka, http://valka.info */
body { color: #000; background: #fff; font: 90%/1.25 Verdana, Arial, Helvetica, sans-serif; margin: 0; width: -moz-fit-content; width: fit-content; } body { color: #000; background: #fff; font: 90%/1.25 Verdana, Arial, Helvetica, sans-serif; margin: 0; width: -moz-fit-content; width: fit-content; }
a { color: blue; text-decoration: none; } a { color: blue; text-decoration: none; }
a:hover { color: red; text-decoration: underline; } a:link:hover { color: red; text-decoration: underline; }
a.text:hover { text-decoration: none; } a.text:hover { text-decoration: none; }
a.jush-help:hover { color: inherit; } a.jush-help:hover { color: inherit; }
h1 { font-size: 150%; margin: 0; padding: .8em 1em; border-bottom: 1px solid #999; font-weight: normal; color: #777; background: #eee; } h1 { font-size: 150%; margin: 0; padding: .8em 1em; border-bottom: 1px solid #999; font-weight: normal; color: #777; background: #eee; }
@@ -25,7 +25,7 @@ pre code { display: block; font-size: 100%; }
pre, textarea { font: 110%/1.25 monospace; } pre, textarea { font: 110%/1.25 monospace; }
pre.jush { background: #fff; } pre.jush { background: #fff; }
input, textarea, select { box-sizing: border-box; } input, textarea, select { box-sizing: border-box; }
input:not([type="radio"]), select { vertical-align: middle; } input[type="image"] { vertical-align: middle; margin-top: -3px; }
input[type="number"] { -moz-appearance: textfield; } input[type="number"] { -moz-appearance: textfield; }
input::-webkit-inner-spin-button { -webkit-appearance: none; } input::-webkit-inner-spin-button { -webkit-appearance: none; }
input.default { box-shadow: 1px 1px 1px #777; } input.default { box-shadow: 1px 1px 1px #777; }
@@ -76,7 +76,8 @@ p.nowrap { white-space: pre; }
.handle:before { content: "="; display: inline-block; width: 18px; height: 18px; overflow: hidden; font-size: 130%; text-align: center; line-height: 16px; opacity: 0.2; } .handle:before { content: "="; display: inline-block; width: 18px; height: 18px; overflow: hidden; font-size: 130%; text-align: center; line-height: 16px; opacity: 0.2; }
span.handle { display: inline-block; width: 18px; height: 18px; padding-right: .3em; } span.handle { display: inline-block; width: 18px; height: 18px; padding-right: .3em; }
th.handle:before { vertical-align: middle; } th.handle:before { vertical-align: middle; }
.no-sort .handle { cursor: default; opacity: 0; } .no-sort .handle { cursor: default; }
.no-sort .handle:before { content: "•"; font-size: 100%; }
.placeholder { opacity: 0; } .placeholder { opacity: 0; }
.dragging { position: absolute; margin: 0; background: #fff; } .dragging { position: absolute; margin: 0; background: #fff; }
.dragging * { cursor: grabbing; } .dragging * { cursor: grabbing; }

View File

@@ -16,17 +16,22 @@ function bodyLoad(version, maria) {
if (maria) { if (maria) {
for (var i = 1; i < obj.length; i++) { for (var i = 1; i < obj.length; i++) {
obj[i] = obj[i] obj[i] = obj[i]
.replace(/\.html/, '/') .replace('.html', '/')
.replace(/-type-syntax/, '-data-types') .replace('-type-syntax', '-data-types')
.replace(/numeric-(data-types)/, '$1-$&') .replace(/numeric-(data-types)/, '$1-$&')
.replace(/#statvar_.*/, '#$$1') .replace(/replication-options-(master|binary-log)\//, 'replication-and-binary-log-system-variables/')
.replace('server-options/', 'server-system-variables/')
.replace('innodb-parameters/', 'innodb-system-variables/')
.replace(/#(statvar|sysvar|option_mysqld)_(.*)/, '#$2')
.replace(/#sysvar_(.*)/, '#$1')
; ;
} }
} }
} }
obj[key] = (maria ? obj[key].replace(/dev\.mysql\.com\/doc\/mysql\/en\//, 'mariadb.com/kb/en/library/') : obj[key]) // MariaDB
.replace(/\/doc\/mysql/, '/doc/refman/' + version) // MySQL obj[key] = (maria ? obj[key].replace('dev.mysql.com/doc/mysql/en/', 'mariadb.com/kb/en/') : obj[key]) // MariaDB
.replace(/\/docs\/current/, '/docs/' + version) // PostgreSQL .replace('/doc/mysql/', '/doc/refman/' + version) // MySQL
.replace('/docs/current/', '/docs/' + version) // PostgreSQL
; ;
} }
} }
@@ -83,13 +88,17 @@ function messagesPrint(el) {
/** Hide or show some login rows for selected driver /**
* @param HTMLSelectElement * Hides or shows some login rows for selected driver.
*/ *
function loginDriver(driver) { * @param {HTMLSelectElement} driverSelect
var trs = parentTag(driver, 'table').rows; */
var disabled = /sqlite/.test(selectValue(driver)); function loginDriver(driverSelect) {
alterClass(trs[1], 'hidden', disabled); // 1 - row with server const trs = parentTag(driverSelect, 'table').rows;
const disabled = /sqlite/.test(selectValue(driverSelect));
// 1 - row with server
trs[1].classList.toggle('hidden', disabled);
trs[1].getElementsByTagName('input')[0].disabled = disabled; trs[1].getElementsByTagName('input')[0].disabled = disabled;
} }
@@ -231,11 +240,13 @@ function editFields() {
els = qsa('[name$="[type]"]'); els = qsa('[name$="[type]"]');
for (var i = 0; i < els.length; i++) { for (var i = 0; i < els.length; i++) {
mixin(els[i], { mixin(els[i], {
onfocus: function () { lastType = selectValue(this); }, onfocus: () => {
lastType = selectValue(this);
},
onchange: editingTypeChange, onchange: editingTypeChange,
onmouseover: function (event) { helpMouseover.call(this, event, getTarget(event).value, 1) },
onmouseout: helpMouseout
}); });
initHelpFor(els[i], (value) => { return value; }, true);
} }
} }
@@ -244,7 +255,7 @@ function editFields() {
* @return boolean false to cancel action * @return boolean false to cancel action
*/ */
function editingClick(event) { function editingClick(event) {
var el = getTarget(event); var el = event.target;
if (!isTag(el, 'input')) { if (!isTag(el, 'input')) {
el = parentTag(el, 'label'); el = parentTag(el, 'label');
el = el && qs('input', el); el = el && qs('input', el);
@@ -273,7 +284,7 @@ function editingClick(event) {
* @param InputEvent * @param InputEvent
*/ */
function editingInput(event) { function editingInput(event) {
var el = getTarget(event); var el = event.target;
if (/\[default]$/.test(el.name)) { if (/\[default]$/.test(el.name)) {
el.previousSibling.checked = true; el.previousSibling.checked = true;
} }
@@ -398,26 +409,26 @@ function editingTypeChange() {
el.checked = false; el.checked = false;
} }
if (el.name === name + '[collation]') { if (el.name === name + '[collation]') {
alterClass(el, 'hidden', !/(char|text|enum|set)$/.test(text)); el.classList.toggle('hidden', !/(char|text|enum|set)$/.test(text));
} }
if (el.name === name + '[unsigned]') { if (el.name === name + '[unsigned]') {
alterClass(el, 'hidden', !/(^|[^o])int(?!er)|numeric|real|float|double|decimal|money/.test(text)); el.classList.toggle('hidden', !/(^|[^o])int(?!er)|numeric|real|float|double|decimal|money/.test(text));
} }
if (el.name === name + '[on_update]') { if (el.name === name + '[on_update]') {
alterClass(el, 'hidden', !/timestamp|datetime/.test(text)); // MySQL supports datetime since 5.6.5 // MySQL supports datetime since 5.6.5.
el.classList.toggle('hidden', !/timestamp|datetime/.test(text));
} }
if (el.name === name + '[on_delete]') { if (el.name === name + '[on_delete]') {
alterClass(el, 'hidden', !/`/.test(text)); el.classList.toggle('hidden', !/`/.test(text));
} }
} }
helpClose();
} }
/** Mark length as required /** Mark length as required
* @this HTMLInputElement * @this HTMLInputElement
*/ */
function editingLengthChange() { function editingLengthChange() {
alterClass(this, 'required', !this.value.length && /var(char|binary)$/.test(selectValue(this.parentNode.previousSibling.firstChild))); this.classList.toggle('required', !this.value.length && /var(char|binary)$/.test(selectValue(this.parentNode.previousSibling.firstChild)));
} }
/** Edit enum or set /** Edit enum or set
@@ -426,7 +437,7 @@ function editingLengthChange() {
function editingLengthFocus() { function editingLengthFocus() {
var td = this.parentNode; var td = this.parentNode;
if (/(enum|set)$/.test(selectValue(td.previousSibling.firstChild))) { if (/(enum|set)$/.test(selectValue(td.previousSibling.firstChild))) {
var edit = qs('#enum-edit'); var edit = gid('enum-edit');
edit.value = enumValues(this.value); edit.value = enumValues(this.value);
td.appendChild(edit); td.appendChild(edit);
this.style.display = 'none'; this.style.display = 'none';
@@ -470,9 +481,9 @@ function editingLengthBlur() {
* @param number * @param number
*/ */
function columnShow(checked, column) { function columnShow(checked, column) {
var trs = qsa('tr', qs('#edit-fields')); var trs = qsa('tr', gid('edit-fields'));
for (var i=0; i < trs.length; i++) { for (var i=0; i < trs.length; i++) {
alterClass(qsa('td', trs[i])[column], 'hidden', !checked); qsa('td', trs[i])[column].classList.toggle('hidden', !checked);
} }
} }
@@ -481,9 +492,9 @@ function columnShow(checked, column) {
*/ */
function partitionByChange() { function partitionByChange() {
var partitionTable = /RANGE|LIST/.test(selectValue(this)); var partitionTable = /RANGE|LIST/.test(selectValue(this));
alterClass(this.form['partitions'], 'hidden', partitionTable || !this.selectedIndex);
alterClass(qs('#partition-table'), 'hidden', !partitionTable); this.form['partitions'].classList.toggle('hidden', partitionTable || !this.selectedIndex);
helpClose(); gid('partition-table').classList.toggle('hidden', !partitionTable);
} }
/** Add next partition row /** Add next partition row
@@ -503,7 +514,7 @@ function partitionNameChange() {
function editingCommentsClick(el, focus) { function editingCommentsClick(el, focus) {
var comment = el.form['Comment']; var comment = el.form['Comment'];
columnShow(el.checked, 6); columnShow(el.checked, 6);
alterClass(comment, 'hidden', !el.checked); comment.classList.toggle('hidden', !el.checked);
if (focus && el.checked) { if (focus && el.checked) {
comment.focus(); comment.focus();
} }
@@ -516,7 +527,7 @@ function editingCommentsClick(el, focus) {
* @this HTMLTableElement * @this HTMLTableElement
*/ */
function dumpClick(event) { function dumpClick(event) {
var el = parentTag(getTarget(event), 'label'); var el = parentTag(event.target, 'label');
if (el) { if (el) {
el = qs('input', el); el = qs('input', el);
var match = /(.+)\[]$/.exec(el.name); var match = /(.+)\[]$/.exec(el.name);
@@ -646,7 +657,7 @@ function triggerChange(tableRe, table, form) {
if (tableRe.test(form['Trigger'].value)) { if (tableRe.test(form['Trigger'].value)) {
form['Trigger'].value = table + '_' + (selectValue(form['Timing']).charAt(0) + formEvent.charAt(0)).toLowerCase(); form['Trigger'].value = table + '_' + (selectValue(form['Timing']).charAt(0) + formEvent.charAt(0)).toLowerCase();
} }
alterClass(form['Of'], 'hidden', !/ OF/.test(formEvent)); form['Of'].classList.toggle('hidden', !/ OF/.test(formEvent));
} }
@@ -720,55 +731,114 @@ function schemaMouseup(event, db) {
s += '_' + key + ':' + Math.round(tablePos[key][0] * 10000) / 10000 + 'x' + Math.round(tablePos[key][1] * 10000) / 10000; s += '_' + key + ':' + Math.round(tablePos[key][0] * 10000) / 10000 + 'x' + Math.round(tablePos[key][1] * 10000) / 10000;
} }
s = encodeURIComponent(s.substr(1)); s = encodeURIComponent(s.substr(1));
var link = qs('#schema-link'); var link = gid('schema-link');
link.href = link.href.replace(/[^=]+$/, '') + s; link.href = link.href.replace(/[^=]+$/, '') + s;
cookie('adminer_schema-' + db + '=' + s, 30); //! special chars in db cookie('adminer_schema-' + db + '=' + s, 30); //! special chars in db
} }
} }
// Help.
(function() {
let openTimeout = null;
let closeTimeout = null;
let helpVisible = false;
var helpOpen, helpIgnore; // when mouse outs <option> then it mouse overs border of <select> - ignore it window.initHelpPopup = function () {
const help = gid("help");
/** Display help help.addEventListener("mouseenter", () => {
* @param MouseEvent clearTimeout(closeTimeout);
* @param string closeTimeout = null;
* @param bool display on left side (otherwise on top) });
* @this HTMLElement
*/
function helpMouseover(event, text, side) {
var target = getTarget(event);
if (!text) {
helpClose();
} else if (window.jush && (!helpIgnore || this !== target)) {
helpOpen = 1;
var help = qs('#help');
help.innerHTML = text;
jush.highlight_tag([ help ]);
alterClass(help, 'hidden');
var rect = target.getBoundingClientRect();
var body = document.documentElement;
help.style.top = (body.scrollTop + rect.top - (side ? (help.offsetHeight - target.offsetHeight) / 2 : help.offsetHeight)) + 'px';
help.style.left = (body.scrollLeft + rect.left - (side ? help.offsetWidth : (help.offsetWidth - target.offsetWidth) / 2)) + 'px';
}
}
/** Close help after timeout help.addEventListener("mouseleave", hideHelp);
* @param MouseEvent };
* @this HTMLElement
*/ /**
function helpMouseout(event) { * @param {HTMLElement} element
helpOpen = 0; * @param {string|function} content
helpIgnore = (this !== getTarget(event)); * @param {boolean} side Displays on left side (otherwise on top).
setTimeout(function () { */
if (!helpOpen) { window.initHelpFor = function(element, content, side = false) {
helpClose(); const withCallback = typeof content === "function";
element.addEventListener("mouseenter", (event) => {
showHelp(event.target, withCallback ? content(event.target.value) : content, side)
});
element.addEventListener("mouseleave", hideHelp);
element.addEventListener("blur", hideHelp);
if (withCallback) {
element.addEventListener("change", hideHelp);
} }
}, 200); };
}
/** Close help /**
*/ * Displays help popup after a small delay.
function helpClose() { *
alterClass(qs('#help'), 'hidden', true); * @param {HTMLElement} element
} * @param {string} text
* @param {boolean} side display on left side (otherwise on top)
*/
function showHelp(element, text, side) {
if (!text) {
hideHelp();
return;
}
if (isSorting() || !window.jush) {
return;
}
clearTimeout(openTimeout);
openTimeout = null;
clearTimeout(closeTimeout);
closeTimeout = null;
const help = gid("help");
help.innerHTML = text;
jush.highlight_tag([help]);
// Display help briefly to calculate position properly.
help.classList.remove("hidden");
const rect = element.getBoundingClientRect();
const root = document.documentElement;
help.style.top = (root.scrollTop + rect.top - (side ? (help.offsetHeight - element.offsetHeight) / 2 : help.offsetHeight)) + 'px';
help.style.left = (root.scrollLeft + rect.left - (side ? help.offsetWidth : (help.offsetWidth - element.offsetWidth) / 2)) + 'px';
if (helpVisible) {
return;
}
help.classList.add("hidden");
openTimeout = setTimeout(() => {
gid("help").classList.remove("hidden");
helpVisible = true;
openTimeout = null;
}, 600);
}
/**
* Closes the help popup after a small delay.
*/
function hideHelp() {
if (openTimeout) {
clearTimeout(openTimeout);
openTimeout = null;
return;
}
closeTimeout = setTimeout(() => {
gid("help").classList.add("hidden");
helpVisible = false;
closeTimeout = null;
}, 200);
}
})();

View File

@@ -73,17 +73,6 @@ function mixin(target, source) {
} }
} }
/** Add or remove CSS class
* @param HTMLElement
* @param string
* @param [bool]
*/
function alterClass(el, className, enable) {
if (el) {
el.className = el.className.replace(RegExp('(^|\\s)' + className + '(\\s|$)'), '$2') + (enable ? ' ' + className : '');
}
}
/** /**
* Toggles visibility of element with ID. * Toggles visibility of element with ID.
* *
@@ -129,7 +118,7 @@ function verifyVersion(currentVersion, baseUrl, token) {
ajax(baseUrl + 'script=version', function () {}, data); ajax(baseUrl + 'script=version', function () {}, data);
if (currentVersion !== version) { if (currentVersion !== version) {
qs('#version').innerText = version; gid('version').innerText = version;
} }
}); });
} }
@@ -173,7 +162,7 @@ function parentTag(el, tag) {
*/ */
function trCheck(el) { function trCheck(el) {
var tr = parentTag(el, 'tr'); var tr = parentTag(el, 'tr');
alterClass(tr, 'checked', el.checked); tr.classList.toggle('checked', el.checked);
if (el.form && el.form['all'] && el.form['all'].onclick) { // Opera treats form.all as document.all if (el.form && el.form['all'] && el.form['all'].onclick) { // Opera treats form.all as document.all
el.form['all'].onclick(); el.form['all'].onclick();
} }
@@ -186,7 +175,7 @@ function trCheck(el) {
*/ */
function selectCount(id, count) { function selectCount(id, count) {
setHtml(id, (count === '' ? '' : '(' + (count + '').replace(/\B(?=(\d{3})+$)/g, thousandsSeparator) + ')')); setHtml(id, (count === '' ? '' : '(' + (count + '').replace(/\B(?=(\d{3})+$)/g, thousandsSeparator) + ')'));
var el = qs('#' + id); var el = gid(id);
if (el) { if (el) {
var inputs = qsa('input', el.parentNode.parentNode); var inputs = qsa('input', el.parentNode.parentNode);
for (var i = 0; i < inputs.length; i++) { for (var i = 0; i < inputs.length; i++) {
@@ -259,7 +248,7 @@ function formChecked(el, name) {
* @param [boolean] force click * @param [boolean] force click
*/ */
function tableClick(event, click) { function tableClick(event, click) {
var td = parentTag(getTarget(event), 'td'); var td = parentTag(event.target, 'td');
var text; var text;
if (td && (text = td.getAttribute('data-text'))) { if (td && (text = td.getAttribute('data-text'))) {
if (selectClick.call(td, event, +text, td.getAttribute('data-warning'))) { if (selectClick.call(td, event, +text, td.getAttribute('data-warning'))) {
@@ -267,7 +256,7 @@ function tableClick(event, click) {
} }
} }
click = (click || !window.getSelection || getSelection().isCollapsed); click = (click || !window.getSelection || getSelection().isCollapsed);
var el = getTarget(event); var el = event.target;
while (!isTag(el, 'tr')) { while (!isTag(el, 'tr')) {
if (isTag(el, 'table|a|input|textarea')) { if (isTag(el, 'table|a|input|textarea')) {
if (el.type !== 'checkbox') { if (el.type !== 'checkbox') {
@@ -374,7 +363,7 @@ function initTablesFilter(dbName) {
if (sessionStorage) { if (sessionStorage) {
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
if (dbName === sessionStorage.getItem('adminer_tables_filter_db') && sessionStorage.getItem('adminer_tables_filter')) { if (dbName === sessionStorage.getItem('adminer_tables_filter_db') && sessionStorage.getItem('adminer_tables_filter')) {
qs('#tables-filter').value = sessionStorage.getItem('adminer_tables_filter'); gid('tables-filter').value = sessionStorage.getItem('adminer_tables_filter');
filterTables(); filterTables();
} else { } else {
sessionStorage.removeItem('adminer_tables_filter'); sessionStorage.removeItem('adminer_tables_filter');
@@ -384,7 +373,7 @@ function initTablesFilter(dbName) {
}); });
} }
const filterInput = qs('#tables-filter'); const filterInput = gid('tables-filter');
filterInput.addEventListener('input', function () { filterInput.addEventListener('input', function () {
window.clearTimeout(tablesFilterTimeout); window.clearTimeout(tablesFilterTimeout);
tablesFilterTimeout = window.setTimeout(filterTables, 200); tablesFilterTimeout = window.setTimeout(filterTables, 200);
@@ -401,7 +390,7 @@ function initTablesFilter(dbName) {
} }
function filterTables() { function filterTables() {
const value = qs('#tables-filter').value.toLowerCase(); const value = gid('tables-filter').value.toLowerCase();
if (value === tablesFilterValue) { if (value === tablesFilterValue) {
return; return;
} }
@@ -445,7 +434,7 @@ function filterTables() {
* @this HTMLElement * @this HTMLElement
*/ */
function menuOver(event) { function menuOver(event) {
var a = getTarget(event); var a = event.target;
if (isTag(a, 'a|span') && a.offsetLeft + a.offsetWidth > a.parentNode.offsetWidth - 15) { // 15 - ellipsis if (isTag(a, 'a|span') && a.offsetLeft + a.offsetWidth > a.parentNode.offsetWidth - 15) { // 15 - ellipsis
this.style.overflow = 'visible'; this.style.overflow = 'visible';
} }
@@ -544,13 +533,13 @@ function selectSearchSearch() {
// Sorting. // Sorting.
(function() { (function() {
let placeholderRow, nextRow, dragHelper; let placeholderRow = null, nextRow = null, dragHelper = null;
let startY, minY, maxY; let startY, minY, maxY;
/** /**
* Initializes sortable list of DIV elements. * Initializes sortable list of DIV elements.
* *
* @param {string} parentId * @param {string} parentSelector
*/ */
window.initSortable = function(parentSelector) { window.initSortable = function(parentSelector) {
const parent = qs(parentSelector); const parent = qs(parentSelector);
@@ -572,65 +561,73 @@ function selectSearchSearch() {
row.classList.remove("no-sort"); row.classList.remove("no-sort");
const handle = qs(".handle", row); const handle = qs(".handle", row);
handle.addEventListener("mousedown", (event) => { handle.addEventListener("mousedown", (event) => { startSorting(row, event) });
event.preventDefault(); handle.addEventListener("touchstart", (event) => { startSorting(row, event) });
const parent = row.parentNode;
startY = event.clientY - getOffsetTop(row);
minY = getOffsetTop(parent);
maxY = minY + parent.offsetHeight - row.offsetHeight;
placeholderRow = row.cloneNode(true);
placeholderRow.classList.add("placeholder");
parent.insertBefore(placeholderRow, row);
nextRow = row.nextElementSibling;
let top = event.clientY - startY;
let left = getOffsetLeft(row);
let width = row.getBoundingClientRect().width;
if (row.tagName === "TR") {
const firstChild = row.firstElementChild;
const borderWidth = (firstChild.offsetWidth - firstChild.clientWidth) / 2;
const borderHeight = (firstChild.offsetHeight - firstChild.clientHeight) / 2;
minY -= borderHeight;
maxY -= borderHeight;
top -= borderHeight;
left -= borderWidth;
width += 2 * borderWidth;
for (const child of row.children) {
child.style.width = child.getBoundingClientRect().width + "px";
}
dragHelper = document.createElement("table");
dragHelper.appendChild(row);
} else {
dragHelper = row;
}
dragHelper.style.top = `${top}px`;
dragHelper.style.left = `${left}px`;
dragHelper.style.width = `${width}px`;
dragHelper.classList.add("dragging");
document.body.appendChild(dragHelper);
window.addEventListener("mousemove", updateSorting);
window.addEventListener("mouseup", () => {
dragHelper.classList.remove("dragging");
parent.insertBefore(dragHelper.tagName === "TABLE" ? dragHelper.firstChild : dragHelper, placeholderRow);
placeholderRow.remove();
window.removeEventListener("mousemove", updateSorting);
}, { once: true });
});
}; };
window.isSorting = function () {
return dragHelper !== null;
};
function startSorting(row, event) {
event.preventDefault();
const pointerY = getPointerY(event);
const parent = row.parentNode;
startY = pointerY - getOffsetTop(row);
minY = getOffsetTop(parent);
maxY = minY + parent.offsetHeight - row.offsetHeight;
placeholderRow = row.cloneNode(true);
placeholderRow.classList.add("placeholder");
parent.insertBefore(placeholderRow, row);
nextRow = row.nextElementSibling;
let top = pointerY - startY;
let left = getOffsetLeft(row);
let width = row.getBoundingClientRect().width;
if (row.tagName === "TR") {
const firstChild = row.firstElementChild;
const borderWidth = (firstChild.offsetWidth - firstChild.clientWidth) / 2;
const borderHeight = (firstChild.offsetHeight - firstChild.clientHeight) / 2;
minY -= borderHeight;
maxY -= borderHeight;
top -= borderHeight;
left -= borderWidth;
width += 2 * borderWidth;
for (const child of row.children) {
child.style.width = child.getBoundingClientRect().width + "px";
}
dragHelper = document.createElement("table");
dragHelper.appendChild(row);
} else {
dragHelper = row;
}
dragHelper.style.top = `${top}px`;
dragHelper.style.left = `${left}px`;
dragHelper.style.width = `${width}px`;
dragHelper.classList.add("dragging");
document.body.appendChild(dragHelper);
window.addEventListener("mousemove", updateSorting);
window.addEventListener("touchmove", updateSorting);
window.addEventListener("mouseup", finishSorting);
window.addEventListener("touchend", finishSorting);
window.addEventListener("touchcancel", finishSorting);
}
function updateSorting(event) { function updateSorting(event) {
let top = Math.min(Math.max(event.clientY - startY, minY), maxY); const pointerY = getPointerY(event);
let top = Math.min(Math.max(pointerY - startY, minY), maxY);
dragHelper.style.top = `${top}px`; dragHelper.style.top = `${top}px`;
const parent = placeholderRow.parentNode; const parent = placeholderRow.parentNode;
@@ -656,6 +653,34 @@ function selectSearchSearch() {
} }
} }
} }
function finishSorting() {
dragHelper.classList.remove("dragging");
dragHelper.style.top = null;
dragHelper.style.left = null;
dragHelper.style.width = null;
placeholderRow.parentNode.insertBefore(dragHelper.tagName === "TABLE" ? dragHelper.firstChild : dragHelper, placeholderRow);
placeholderRow.remove();
placeholderRow = nextRow = dragHelper = null;
window.removeEventListener("mousemove", updateSorting);
window.removeEventListener("touchmove", updateSorting);
window.removeEventListener("mouseup", finishSorting);
window.removeEventListener("touchend", finishSorting);
window.removeEventListener("touchcancel", finishSorting);
}
function getPointerY(event) {
if (event.type.includes("touch")) {
const touch = event.touches[0] || event.changedTouches[0];
return touch.clientY;
} else {
return event.clientY;
}
}
})(); })();
@@ -681,7 +706,7 @@ function columnMouse(className) {
* @return boolean false * @return boolean false
*/ */
function selectSearch(name) { function selectSearch(name) {
var el = qs('#fieldset-search'); var el = gid('fieldset-search');
el.className = ''; el.className = '';
var divs = qsa('div', el); var divs = qsa('div', el);
for (var i=0; i < divs.length; i++) { for (var i=0; i < divs.length; i++) {
@@ -708,16 +733,6 @@ function isCtrl(event) {
return (event.ctrlKey || event.metaKey) && !event.altKey; // shiftKey allowed return (event.ctrlKey || event.metaKey) && !event.altKey; // shiftKey allowed
} }
/** Return event target
* @param Event
* @return HTMLElement
*/
function getTarget(event) {
return event.target || event.srcElement;
}
/** Send form by Ctrl+Enter on <select> and <textarea> /** Send form by Ctrl+Enter on <select> and <textarea>
* @param KeyboardEvent * @param KeyboardEvent
* @param [string] * @param [string]
@@ -725,7 +740,7 @@ function getTarget(event) {
*/ */
function bodyKeydown(event, button) { function bodyKeydown(event, button) {
eventStop(event); eventStop(event);
var target = getTarget(event); var target = event.target;
if (target.jushTextarea) { if (target.jushTextarea) {
target = target.jushTextarea; target = target.jushTextarea;
} }
@@ -749,7 +764,7 @@ function bodyKeydown(event, button) {
* @param MouseEvent * @param MouseEvent
*/ */
function bodyClick(event) { function bodyClick(event) {
var target = getTarget(event); var target = event.target;
if ((isCtrl(event) || event.shiftKey) && target.type === 'submit' && isTag(target, 'input')) { if ((isCtrl(event) || event.shiftKey) && target.type === 'submit' && isTag(target, 'input')) {
target.form.target = '_blank'; target.form.target = '_blank';
setTimeout(function () { setTimeout(function () {
@@ -767,7 +782,7 @@ function bodyClick(event) {
*/ */
function editingKeydown(event) { function editingKeydown(event) {
if ((event.keyCode === 40 || event.keyCode === 38) && isCtrl(event)) { // 40 - Down, 38 - Up if ((event.keyCode === 40 || event.keyCode === 38) && isCtrl(event)) { // 40 - Down, 38 - Up
var target = getTarget(event); var target = event.target;
var sibling = (event.keyCode === 40 ? 'nextSibling' : 'previousSibling'); var sibling = (event.keyCode === 40 ? 'nextSibling' : 'previousSibling');
var el = target.parentNode.parentNode[sibling]; var el = target.parentNode.parentNode[sibling];
if (el && (isTag(el, 'tr') || (el = el[sibling])) && isTag(el, 'tr') && (el = el.childNodes[nodePosition(target.parentNode)]) && (el = el.childNodes[nodePosition(target)])) { if (el && (isTag(el, 'tr') || (el = el[sibling])) && isTag(el, 'tr') && (el = el.childNodes[nodePosition(target.parentNode)]) && (el = el.childNodes[nodePosition(target)])) {
@@ -792,7 +807,6 @@ function functionChange() {
// Undefined with the set data type. // Undefined with the set data type.
if (!input) { if (!input) {
helpClose();
return; return;
} }
@@ -822,8 +836,6 @@ function functionChange() {
} }
oninput({target: input}); oninput({target: input});
helpClose();
} }
/** /**
@@ -868,7 +880,7 @@ function fieldChange() {
function ajax(url, callback, data, message) { function ajax(url, callback, data, message) {
var request = (window.XMLHttpRequest ? new XMLHttpRequest() : (window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : false)); var request = (window.XMLHttpRequest ? new XMLHttpRequest() : (window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : false));
if (request) { if (request) {
var ajaxStatus = qs('#ajaxstatus'); var ajaxStatus = gid('ajaxstatus');
if (message) { if (message) {
ajaxStatus.innerHTML = '<div class="message">' + message + '</div>'; ajaxStatus.innerHTML = '<div class="message">' + message + '</div>';
ajaxStatus.className = ajaxStatus.className.replace(/ hidden/g, ''); ajaxStatus.className = ajaxStatus.className.replace(/ hidden/g, '');
@@ -938,9 +950,9 @@ function ajaxForm(form, message, button) {
return ajax(url, function (request) { return ajax(url, function (request) {
setHtml('ajaxstatus', request.responseText); setHtml('ajaxstatus', request.responseText);
if (window.jush) { if (window.jush) {
jush.highlight_tag(qsa('code', qs('#ajaxstatus')), 0); jush.highlight_tag(qsa('code', gid('ajaxstatus')), 0);
} }
messagesPrint(qs('#ajaxstatus')); messagesPrint(gid('ajaxstatus'));
}, data, message); }, data, message);
} }
@@ -955,7 +967,7 @@ function ajaxForm(form, message, button) {
*/ */
function selectClick(event, text, warning) { function selectClick(event, text, warning) {
var td = this; var td = this;
var target = getTarget(event); var target = event.target;
if (!isCtrl(event) || isTag(td.firstChild, 'input|textarea') || isTag(target, 'a')) { if (!isCtrl(event) || isTag(td.firstChild, 'input|textarea') || isTag(target, 'a')) {
return; return;
} }
@@ -1042,7 +1054,7 @@ function selectLoadMore(limit, loading) {
return !ajax(href, function (request) { return !ajax(href, function (request) {
var tbody = document.createElement('tbody'); var tbody = document.createElement('tbody');
tbody.innerHTML = request.responseText; tbody.innerHTML = request.responseText;
qs('#table').appendChild(tbody); gid('table').appendChild(tbody);
if (tbody.children.length < limit) { if (tbody.children.length < limit) {
a.parentNode.removeChild(a); a.parentNode.removeChild(a);
} else { } else {
@@ -1096,9 +1108,9 @@ function setupSubmitHighlightInput(input) {
* @this HTMLInputElement * @this HTMLInputElement
*/ */
function inputFocus() { function inputFocus() {
var submit = findDefaultSubmit(this); const submit = findDefaultSubmit(this);
if (submit) { if (submit) {
alterClass(submit, 'default', true); submit.classList.toggle('default', true);
} }
} }
@@ -1106,9 +1118,9 @@ function inputFocus() {
* @this HTMLInputElement * @this HTMLInputElement
*/ */
function inputBlur() { function inputBlur() {
var submit = findDefaultSubmit(this); const submit = findDefaultSubmit(this);
if (submit) { if (submit) {
alterClass(submit, 'default'); submit.classList.toggle('default', false);
} }
} }
@@ -1190,7 +1202,9 @@ function getOffsetLeft(element) {
} }
oninput = function (event) { oninput = function (event) {
var target = event.target; const target = event.target;
var maxLength = target.getAttribute('data-maxlength'); const maxLength = target.getAttribute('data-maxlength');
alterClass(target, 'maxlength', target.value && maxLength != null && target.value.length > maxLength); // maxLength could be 0
// maxLength could be 0
target.classList.toggle('maxlength', target.value && maxLength != null && target.value.length > maxLength);
}; };

View File

@@ -40,7 +40,7 @@ page_header(($name != "" ? lang('Alter trigger') . ": " . h($name) : lang('Creat
<tr><th><?php echo lang('Type'); ?><td><?php echo html_select("Type", $trigger_options["Type"], $row["Type"]); ?> <tr><th><?php echo lang('Type'); ?><td><?php echo html_select("Type", $trigger_options["Type"], $row["Type"]); ?>
</table> </table>
<p><?php echo lang('Name'); ?>: <input name="Trigger" value="<?php echo h($row["Trigger"]); ?>" data-maxlength="64" autocapitalize="off"> <p><?php echo lang('Name'); ?>: <input name="Trigger" value="<?php echo h($row["Trigger"]); ?>" data-maxlength="64" autocapitalize="off">
<?php echo script("qs('#form')['Timing'].onchange();"); ?> <?php echo script("gid('form')['Timing'].onchange();"); ?>
<p><?php textarea("Statement", $row["Statement"]); ?> <p><?php textarea("Statement", $row["Statement"]); ?>
<p> <p>
<input type="submit" value="<?php echo lang('Save'); ?>"> <input type="submit" value="<?php echo lang('Save'); ?>">

View File

@@ -142,7 +142,7 @@ if ($_POST) {
<tr><th><?php echo lang('Server'); ?><td><input name="host" data-maxlength="60" value="<?php echo h($row["host"]); ?>" autocapitalize="off"> <tr><th><?php echo lang('Server'); ?><td><input name="host" data-maxlength="60" value="<?php echo h($row["host"]); ?>" autocapitalize="off">
<tr><th><?php echo lang('Username'); ?><td><input name="user" data-maxlength="80" value="<?php echo h($row["user"]); ?>" autocapitalize="off"> <tr><th><?php echo lang('Username'); ?><td><input name="user" data-maxlength="80" value="<?php echo h($row["user"]); ?>" autocapitalize="off">
<tr><th><?php echo lang('Password'); ?><td><input name="pass" id="pass" value="<?php echo h($row["pass"]); ?>" autocomplete="new-password"> <tr><th><?php echo lang('Password'); ?><td><input name="pass" id="pass" value="<?php echo h($row["pass"]); ?>" autocomplete="new-password">
<?php if (!$row["hashed"]) { echo script("typePassword(qs('#pass'));"); } ?> <?php if (!$row["hashed"]) { echo script("typePassword(gid('pass'));"); } ?>
<?php echo (min_version(8) ? "" : checkbox("hashed", 1, $row["hashed"], lang('Hashed'), "typePassword(this.form['pass'], this.checked);")); ?> <?php echo (min_version(8) ? "" : checkbox("hashed", 1, $row["hashed"], lang('Hashed'), "typePassword(this.form['pass'], this.checked);")); ?>
</table> </table>

View File

@@ -439,13 +439,14 @@ if ($_SESSION["lang"]) {
$file = str_replace("<?php switch_lang(); ?>\n", "", $file); $file = str_replace("<?php switch_lang(); ?>\n", "", $file);
$file = str_replace('<?php echo $LANG; ?>', $_SESSION["lang"], $file); $file = str_replace('<?php echo $LANG; ?>', $_SESSION["lang"], $file);
} }
$file = str_replace('<?php echo script_src("static/editing.js"); ?>' . "\n", "", $file); $file = str_replace('<?php echo script_src("static/editing.js?" . filemtime("../adminer/static/editing.js")); ?>' . "\n", "", $file);
$file = preg_replace('~\s+echo script_src\("\.\./vendor/vrana/jush/modules/jush-(textarea|txt|js|\$jush)\.js"\);~', '', $file); $file = preg_replace('~\s+echo script_src\("\.\./vendor/vrana/jush/modules/jush-(textarea|txt|js|\$jush)\.js"\);~', '', $file);
$file = str_replace('<link rel="stylesheet" type="text/css" href="../vendor/vrana/jush/jush.css">' . "\n", "", $file); $file = str_replace('<link rel="stylesheet" type="text/css" href="../vendor/vrana/jush/jush.css">' . "\n", "", $file);
$file = preg_replace_callback("~compile_file\\('([^']+)'(?:, '([^']*)')?\\)~", 'compile_file', $file); // integrate static files $file = preg_replace_callback("~compile_file\\('([^']+)'(?:, '([^']*)')?\\)~", 'compile_file', $file); // integrate static files
$replace = 'preg_replace("~\\\\\\\\?.*~", "", ME) . "?file=\1&version=' . substr(md5(microtime()), 0, 8) . '"'; $replace = 'preg_replace("~\\\\\\\\?.*~", "", ME) . "?file=\1&version=' . substr(md5(microtime()), 0, 8) . '"';
$file = preg_replace('~\.\./adminer/static/(default\.css|favicon\.ico)~', '<?php echo h(' . $replace . '); ?>', $file); $file = preg_replace('~\.\./adminer/static/(favicon\.ico)~', '<?php echo h(' . $replace . '); ?>', $file);
$file = preg_replace('~"\.\./adminer/static/(functions\.js)"~', $replace, $file); $file = preg_replace('~\.\./adminer/static/(default\.css)\?.*default.css"\);\s+\?>~', '<?php echo h(' . $replace . '); ?>', $file);
$file = preg_replace('~"\.\./adminer/static/(functions\.js)\?".*functions.js"\)~', $replace, $file);
$file = preg_replace('~\.\./adminer/static/([^\'"]*)~', '" . h(' . $replace . ') . "', $file); $file = preg_replace('~\.\./adminer/static/([^\'"]*)~', '" . h(' . $replace . ') . "', $file);
$file = preg_replace('~"\.\./vendor/vrana/jush/modules/(jush\.js)"~', $replace, $file); $file = preg_replace('~"\.\./vendor/vrana/jush/modules/(jush\.js)"~', $replace, $file);
$file = preg_replace("~<\\?php\\s*\\?>\n?|\\?>\n?<\\?php~", '', $file); $file = preg_replace("~<\\?php\\s*\\?>\n?|\\?>\n?<\\?php~", '', $file);

View File

@@ -29,7 +29,7 @@
"php": "5.6 - 8.3", "php": "5.6 - 8.3",
"ext-pdo": "*", "ext-pdo": "*",
"ext-json": "*", "ext-json": "*",
"vrana/jush": "@dev", "vrana/jush": "2.0.*",
"vrana/jsshrink": "@dev" "vrana/jsshrink": "@dev"
}, },
"suggest": { "suggest": {
@@ -57,7 +57,7 @@
"repositories": [ "repositories": [
{ {
"type": "vcs", "type": "vcs",
"url": "https://github.com/vrana/jush.git" "url": "https://github.com/pematon/jush.git"
}, },
{ {
"type": "vcs", "type": "vcs",

View File

@@ -11,7 +11,7 @@ if ($adminer->homepage()) {
echo "<table cellspacing='0' class='nowrap checkable'>\n"; echo "<table cellspacing='0' class='nowrap checkable'>\n";
echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"); echo script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");
echo '<thead><tr class="wrap">'; echo '<thead><tr class="wrap">';
echo '<td><input id="check-all" type="checkbox" class="jsonly">' . script("qs('#check-all').onclick = partial(formCheck, /^tables\[/);", ""); echo '<td><input id="check-all" type="checkbox" class="jsonly">' . script("gid('check-all').onclick = partial(formCheck, /^tables\[/);", "");
echo '<th>' . lang('Table'); echo '<th>' . lang('Table');
echo '<td>' . lang('Rows'); echo '<td>' . lang('Rows');
echo "</thead>\n"; echo "</thead>\n";

View File

@@ -77,7 +77,7 @@ class Adminer {
function loginForm() { function loginForm() {
echo "<table cellspacing='0' class='layout'>\n"; echo "<table cellspacing='0' class='layout'>\n";
echo $this->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input type="hidden" name="auth[driver]" value="server"><input name="auth[username]" id="username" value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("focus(qs('#username'));")); echo $this->loginFormField('username', '<tr><th>' . lang('Username') . '<td>', '<input type="hidden" name="auth[driver]" value="server"><input name="auth[username]" id="username" value="' . h($_GET["username"]) . '" autocomplete="username" autocapitalize="off">' . script("focus(gid('username'));"));
echo $this->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">' . "\n"); echo $this->loginFormField('password', '<tr><th>' . lang('Password') . '<td>', '<input type="password" name="auth[password]" autocomplete="current-password">' . "\n");
echo "</table>\n"; echo "</table>\n";
echo "<p><input type='submit' value='" . lang('Login') . "'>\n"; echo "<p><input type='submit' value='" . lang('Login') . "'>\n";
@@ -620,7 +620,7 @@ qsl('div').onclick = whisperClick;", "")
if ($password !== null) { if ($password !== null) {
if ($first) { if ($first) {
echo "<ul id='logins'>"; echo "<ul id='logins'>";
echo script("mixin(qs('#logins'), {onmouseover: menuOver, onmouseout: menuOut});"); echo script("mixin(gid('logins'), {onmouseover: menuOver, onmouseout: menuOut});");
$first = false; $first = false;
} }
echo "<li><a href='" . h(auth_url($vendor, "", $username)) . "'>" . ($username != "" ? h($username) : "<i>" . lang('empty') . "</i>") . "</a>\n"; echo "<li><a href='" . h(auth_url($vendor, "", $username)) . "'>" . ($username != "" ? h($username) : "<i>" . lang('empty') . "</i>") . "</a>\n";
@@ -655,7 +655,7 @@ qsl('div').onclick = whisperClick;", "")
} }
function tablesPrint($tables) { function tablesPrint($tables) {
echo "<ul id='tables'>" . script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});"); echo "<ul id='tables'>" . script("mixin(gid('tables'), {onmouseover: menuOver, onmouseout: menuOut});");
foreach ($tables as $row) { foreach ($tables as $row) {
// Skip views and tables without a name. // Skip views and tables without a name.

View File

@@ -1,11 +1,14 @@
<?php <?php
/** Adminer Editor - Compact database editor /**
* @link https://www.adminer.org/ * Adminer Editor - Compact database editor for end-users
* @author Jakub Vrana, https://www.vrana.cz/ *
* @copyright 2009 Jakub Vrana * @link https://github.com/pematon/adminer
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 * @author Jakub Vrana (https://www.vrana.cz/)
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other) * @author Peter Knut
*/ * @copyright 2009-2021 Jakub Vrana, 2024 Peter Knut
* @license Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
* @license GNU General Public License, version 2 (https://www.gnu.org/licenses/gpl-2.0.html)
*/
include "../adminer/include/bootstrap.inc.php"; include "../adminer/include/bootstrap.inc.php";
$drivers[DRIVER] = lang('Login'); $drivers[DRIVER] = lang('Login');

View File

@@ -6,13 +6,14 @@ function messagesPrint() {
function selectFieldChange() { function selectFieldChange() {
} }
var helpOpen; // Help.
(function() {
window.initHelpPopup = function () {
};
function helpMouseover() { window.initHelpFor = function(element, content, side = false) {
} };
})();
function helpMouseout() {
}
/** Display typeahead /** Display typeahead
* @param string * @param string
@@ -42,7 +43,7 @@ function whisper(url) {
*/ */
function whisperClick(event) { function whisperClick(event) {
var field = this.previousSibling; var field = this.previousSibling;
var el = getTarget(event); var el = event.target;
if (isTag(el, 'a') && !(event.button || event.shiftKey || event.altKey || isCtrl(event))) { if (isTag(el, 'a') && !(event.button || event.shiftKey || event.altKey || isCtrl(event))) {
field.value = el.firstChild.data; field.value = el.firstChild.data;
field.previousSibling.value = decodeURIComponent(el.href.replace(/.*=/, '')); field.previousSibling.value = decodeURIComponent(el.href.replace(/.*=/, ''));

View File

@@ -65,7 +65,7 @@ tinyMCE.init({
return "<textarea$attrs id='fields-" . h($field["field"]) . "' rows='12' cols='50'>" . h($value) . "</textarea>" . script(" return "<textarea$attrs id='fields-" . h($field["field"]) . "' rows='12' cols='50'>" . h($value) . "</textarea>" . script("
tinyMCE.remove(tinyMCE.get('fields-" . js_escape($field["field"]) . "') || { }); tinyMCE.remove(tinyMCE.get('fields-" . js_escape($field["field"]) . "') || { });
tinyMCE.EditorManager.execCommand('mceAddControl', true, 'fields-" . js_escape($field["field"]) . "'); tinyMCE.EditorManager.execCommand('mceAddControl', true, 'fields-" . js_escape($field["field"]) . "');
qs('#form').onsubmit = function () { gid('form').onsubmit = function () {
tinyMCE.each(tinyMCE.editors, function (ed) { tinyMCE.each(tinyMCE.editors, function (ed) {
ed.remove(); ed.remove();
}); });