Adds support for tarred downloads.

This commit is contained in:
Lars Jung 2012-04-17 23:38:15 +02:00
parent e67c854cb5
commit de92767e5a
22 changed files with 160 additions and 162 deletions

View file

@ -39,11 +39,13 @@ h5ai is provided under the terms of the [MIT License](http://github.com/lrsjng/h
* hides broken tree view in IE < 9, adds a message to the footer * hides broken tree view in IE < 9, adds a message to the footer
* removes hash changes since they break logical browser history * removes hash changes since they break logical browser history
* fixes thumbnail size for portrait images in icon view * fixes thumbnail size for portrait images in icon view
* fixes problems with file type recognition
* adds an info page at `/_h5ai` * adds an info page at `/_h5ai`
* sort order is preserved while browsing * sort order is preserved while browsing
* removes PHP error messages on thumbnail generation * removes PHP error messages on thumbnail generation
* fixes PHP problems with zipped download * fixes PHP problems with zipped download
* changes crumb image for folders with an index file * changes crumb image for folders with an index file
* adds support for tarred downloads
* adds `index.php` to use h5ai in non-Apache environments * adds `index.php` to use h5ai in non-Apache environments
* switches from [Datejs](http://www.datejs.com) to [Moment.js](http://momentjs.com) * switches from [Datejs](http://www.datejs.com) to [Moment.js](http://momentjs.com)
* adds [underscore.js](http://underscorejs.org) * adds [underscore.js](http://underscorejs.org)

View file

@ -68,6 +68,15 @@ var H5AI_CONFIG = {
"footer": "_h5ai.footer.html" "footer": "_h5ai.footer.html"
}, },
/*
* Requires PHP on the server.
* Enable packaged download of selected entries. Supported formats: "tar", "zip".
*/
"download": {
"enabled": true,
"format": "zip"
},
/* /*
* Allow filtering the displayed files and folders. * Allow filtering the displayed files and folders.
* Note: filters will be treated as JavaScript regular expressions * Note: filters will be treated as JavaScript regular expressions
@ -181,14 +190,6 @@ var H5AI_CONFIG = {
"tree": { "tree": {
"enabled": true, "enabled": true,
"slide": true "slide": true
},
/*
* Requires PHP on the server.
* Enable zipped download of selected entries.
*/
"zipped-download": {
"enabled": true
} }
}, },
@ -355,7 +356,7 @@ var H5AI_CONFIG = {
"folders": "Répertoires", "folders": "Répertoires",
"files": "Fichiers", "files": "Fichiers",
"download": "télécharger", "download": "télécharger",
"noMatch": "no match" "noMatch": "rien trouvé"
}, },
"gr": { "gr": {

View file

@ -13,7 +13,8 @@
<link rel="apple-touch-icon" type="image/png" href="/_h5ai/images/h5ai-48x48.png"> <link rel="apple-touch-icon" type="image/png" href="/_h5ai/images/h5ai-48x48.png">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="/_h5ai/css/styles.css"> <link rel="stylesheet" href="/_h5ai/css/styles.css">
<script src="/_h5ai/js/modernizr-2.5.3.min.js"></script> <script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
</head> </head>
<body id="h5ai-main"> <body id="h5ai-main">
<div id="topbar" class="clearfix"> <div id="topbar" class="clearfix">
@ -31,8 +32,6 @@
<span class="right"></span> <span class="right"></span>
<span class="center"></span> <span class="center"></span>
</div> </div>
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
<div id="data-apache-autoindex" class="hideOnJs"> <div id="data-apache-autoindex" class="hideOnJs">
<!-- <!--
The following code was generated by Apache's autoindex module. It is not valid HTML5, but this The following code was generated by Apache's autoindex module. It is not valid HTML5, but this

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 395 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

@ -14,7 +14,8 @@
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Miltonian+Tattoo:regular"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Miltonian+Tattoo:regular">
<link rel="stylesheet" href="/_h5ai/css/styles.css"> <link rel="stylesheet" href="/_h5ai/css/styles.css">
<script src="/_h5ai/js/modernizr-2.5.3.min.js"></script> <script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
</head> </head>
<body id="h5ai-info"> <body id="h5ai-info">
<h1><span class="h5ai">h5ai</span></h1> <h1><span class="h5ai">h5ai</span></h1>
@ -25,7 +26,7 @@
<li id="test-cache"><span class="test-label">cache</span><span class="test-result">?</span></li> <li id="test-cache"><span class="test-label">cache</span><span class="test-result">?</span></li>
<li id="test-thumbs"><span class="test-label">thumbnails</span><span class="test-result">?</span></li> <li id="test-thumbs"><span class="test-label">thumbnails</span><span class="test-result">?</span></li>
<li id="test-temp"><span class="test-label">temp directory</span><span class="test-result">?</span></li> <li id="test-temp"><span class="test-label">temp directory</span><span class="test-result">?</span></li>
<li id="test-zips"><span class="test-label">zipped downloads</span><span class="test-result">?</span></li> <li id="test-download"><span class="test-label">packaged downloads</span><span class="test-result">?</span></li>
</ul> </ul>
<div id="bottombar" class="clearfix"> <div id="bottombar" class="clearfix">
<span class="left"> <span class="left">
@ -36,7 +37,5 @@
<span class="right"></span> <span class="right"></span>
<span class="center"></span> <span class="center"></span>
</div> </div>
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
</body> </body>
</html> </html>

View file

@ -1,11 +1,14 @@
module.define('ext/zipped-download', [jQuery, 'core/settings', 'core/resource', 'core/event'], function ($, allsettings, resource, event) { module.define('ext/download', [jQuery, 'core/settings', 'core/resource', 'core/event'], function ($, allsettings, resource, event) {
var defaults = { var defaults = {
enabled: false enabled: false,
format: 'tar'
}, },
settings = _.extend({}, defaults, allsettings['zipped-download']), settings = _.extend({}, defaults, allsettings['download']),
formats = ['tar', 'zip'],
downloadBtnTemplate = '<li id="download">' + downloadBtnTemplate = '<li id="download">' +
'<a href="#">' + '<a href="#">' +
@ -38,7 +41,7 @@ module.define('ext/zipped-download', [jQuery, 'core/settings', 'core/resource',
if (response.status === 'ok') { if (response.status === 'ok') {
setTimeout(function () { // wait here so the img above can be updated in time setTimeout(function () { // wait here so the img above can be updated in time
window.location = resource.api() + '?action=getzip&id=' + response.id; window.location = resource.api() + '?action=getarchive&id=' + response.id + '&as=h5ai-selection.' + settings.format;
}, 200); }, 200);
} else { } else {
if (response.code === 401) { if (response.code === 401) {
@ -57,14 +60,15 @@ module.define('ext/zipped-download', [jQuery, 'core/settings', 'core/resource',
} }
}, },
requestZipping = function (hrefsStr) { requestArchive = function (hrefsStr) {
$download.addClass('current'); $download.addClass('current');
$img.attr('src', resource.image('loading.gif', true)); $img.attr('src', resource.image('loading.gif', true));
$.ajax({ $.ajax({
url: resource.api(), url: resource.api(),
data: { data: {
action: 'zip', action: 'archive',
format: settings.format,
hrefs: hrefsStr hrefs: hrefsStr
}, },
type: 'POST', type: 'POST',
@ -91,7 +95,7 @@ module.define('ext/zipped-download', [jQuery, 'core/settings', 'core/resource',
onSelection = function (entries) { onSelection = function (entries) {
var $downloadBtn = $('#download'); var $download = $('#download').appendTo('#navbar');
selectedHrefsStr = ''; selectedHrefsStr = '';
if (entries.length) { if (entries.length) {
@ -99,9 +103,9 @@ module.define('ext/zipped-download', [jQuery, 'core/settings', 'core/resource',
return entry.absHref; return entry.absHref;
}).join(':'); }).join(':');
$downloadBtn.show(); $download.show();
} else { } else {
$downloadBtn.hide(); $download.hide();
$downloadAuth.hide(); $downloadAuth.hide();
} }
}, },
@ -113,16 +117,16 @@ module.define('ext/zipped-download', [jQuery, 'core/settings', 'core/resource',
} }
$download = $(downloadBtnTemplate) $download = $(downloadBtnTemplate)
.appendTo($('#navbar')) .appendTo('#navbar')
.find('a').on('click', function (event) { .find('a').on('click', function (event) {
event.preventDefault(); event.preventDefault();
$downloadAuth.hide(); $downloadAuth.hide();
requestZipping(selectedHrefsStr); requestArchive(selectedHrefsStr);
}); });
$img = $download.find('img'); $img = $download.find('img');
$downloadAuth = $(authTemplate).appendTo($('body')); $downloadAuth = $(authTemplate).appendTo('body');
$downloadUser = $downloadAuth.find('#download-auth-user'); $downloadUser = $downloadAuth.find('#download-auth-user');
$downloadPassword = $downloadAuth.find('#download-auth-password'); $downloadPassword = $downloadAuth.find('#download-auth-password');

View file

@ -98,14 +98,14 @@ module.define('ext/filter', [jQuery, 'core/settings', 'core/resource'], function
$filter = $(template); $filter = $(template);
$input = $filter.find('input'); $input = $filter.find('input');
$noMatch = $(noMatchTemplate).appendTo($('#extended')); $noMatch = $(noMatchTemplate).appendTo('#extended');
$filter $filter
.on('click', function () { .on('click', function () {
$input.focus(); $input.focus();
}) })
.appendTo($('#navbar')); .appendTo('#navbar');
$input $input
.on('focus', function () { .on('focus', function () {

View file

@ -3,7 +3,7 @@ module.define('ext/l10n', [jQuery, 'core/settings', 'core/langs', 'core/format',
var defaults = { var defaults = {
enabled: true, enabled: true,
lang: "en", lang: 'en',
useBrowserLang: true, useBrowserLang: true,
defaultDateFormat: 'YYYY-MM-DD HH:mm' defaultDateFormat: 'YYYY-MM-DD HH:mm'
}, },

View file

@ -98,7 +98,7 @@ module.define('ext/select', [jQuery, 'core/settings', 'core/event'], function ($
return; return;
} }
$selectionRect.hide().appendTo($('body')); $selectionRect.hide().appendTo('body');
$document $document
.on('mousedown', '.noSelection', noSelection) .on('mousedown', '.noSelection', noSelection)

View file

@ -67,7 +67,7 @@ module.define('ext/sort', [jQuery, 'core/settings', 'core/resource', 'core/store
$all.removeClass('ascending').removeClass('descending'); $all.removeClass('ascending').removeClass('descending');
order.head.addClass(order.clas); order.head.addClass(order.clas);
$('#extended .entry').detach().sort(order.fn).appendTo($('#extended > ul')); $('#extended .entry').detach().sort(order.fn).appendTo('#extended > ul');
}, },
init = function () { init = function () {

View file

@ -197,7 +197,7 @@ module.define('ext/tree', [jQuery, 'core/settings', 'core/resource', 'core/event
return; return;
} }
var $tree = $('<div id="tree" />').appendTo($('body')); var $tree = $('<div id="tree" />').appendTo('body');
fetchTree(entry, parser, function (root) { fetchTree(entry, parser, function (root) {

View file

@ -14,7 +14,7 @@ module.define('h5ai-info', [jQuery, 'core/resource'], function ($, resource) {
handleChecksResponse = function (response) { handleChecksResponse = function (response) {
_.each(['php', 'cache', 'thumbs', 'temp', 'zips'], function (test) { _.each(['php', 'cache', 'thumbs', 'temp', 'download'], function (test) {
setCheckResult('#test-' + test, response && response[test]); setCheckResult('#test-' + test, response && response[test]);
}) })

View file

@ -23,6 +23,7 @@
// @include "ext/crumb.js" // @include "ext/crumb.js"
// @include "ext/custom.js" // @include "ext/custom.js"
// @include "ext/download.js"
// @include "ext/filter.js" // @include "ext/filter.js"
// @include "ext/folderstatus.js" // @include "ext/folderstatus.js"
// @include "ext/l10n.js" // @include "ext/l10n.js"
@ -34,7 +35,6 @@
// @include "ext/thumbnails.js" // @include "ext/thumbnails.js"
// @include "ext/title.js" // @include "ext/title.js"
// @include "ext/tree.js" // @include "ext/tree.js"
// @include "ext/zipped-download.js"
// @include "h5ai-info.js" // @include "h5ai-info.js"
// @include "h5ai-main.js" // @include "h5ai-main.js"

View file

@ -10,17 +10,17 @@ module.define('parser/generic-json', [jQuery, 'core/settings', 'model/entry'], f
var parseJson = function (absHref, json) { var parseJson = function (absHref, json) {
_.each(json.entries, function (jsonEntry) {
Entry.get(jsonEntry.absHref, jsonEntry.time, jsonEntry.size, jsonEntry.status);
});
if (json.hasOwnProperty('customHeader')) { if (json.hasOwnProperty('customHeader')) {
settings.custom.header = json.customHeader; settings.custom.header = json.customHeader;
} }
if (json.hasOwnProperty('customFooter')) { if (json.hasOwnProperty('customFooter')) {
settings.custom.footer = json.customFooter; settings.custom.footer = json.customFooter;
} }
return _.map(json.entries, function (jsonEntry) {
return Entry.get(jsonEntry.absHref, jsonEntry.time, jsonEntry.size, jsonEntry.status);
});
}, },
parseJsonStr = function (absHref, jsonStr) { parseJsonStr = function (absHref, jsonStr) {

View file

@ -1,20 +1,24 @@
// jQuery and plugins // libs
// ------------------ // ----
// @include "inc/lib/modernizr-2.5.3.min.js"
// @include "inc/lib/moment-1.5.0.min.js"
// @include "inc/lib/json2.js"
// @include "inc/lib/base64.js"
// underscore libs
// ---------------
// @include "inc/lib/underscore-1.3.1.min.js"
// @include "inc/lib/module.js"
// jQuery libs
// -----------
// @include "inc/lib/jquery-1.7.1.min.js" // @include "inc/lib/jquery-1.7.1.min.js"
// @include "inc/lib/jquery.fracs-0.11.min.js" // @include "inc/lib/jquery.fracs-0.11.min.js"
// @include "inc/lib/jquery.mousewheel-3.0.6.js" // @include "inc/lib/jquery.mousewheel-3.0.6.js"
// @include "inc/lib/jquery.qrcode.js" // @include "inc/lib/jquery.qrcode.js"
// @include "inc/lib/jquery.scrollpanel.js" // @include "inc/lib/jquery.scrollpanel.js"
// non jQuery libs
// ---------------
// @include "inc/lib/amplify-1.1.0.min.js" // @include "inc/lib/amplify-1.1.0.min.js"
// @include "inc/lib/base64.js"
// @include "inc/lib/json2.js"
// @include "inc/lib/moment-1.5.0.min.js"
// @include "inc/lib/underscore-1.3.1.min.js"
// @include "inc/lib/module.js"
// h5ai // h5ai
// ---- // ----

View file

@ -108,40 +108,42 @@ else if ($action === "thumbsrc") {
} }
else if ($action === "zip") { else if ($action === "archive") {
fail(0, "zipped download is disabled", !$options["zipped-download"]["enabled"]); fail(0, "download is disabled", !$options["download"]["enabled"]);
list($hrefs) = checkKeys(array("hrefs"));
H5ai::req_once("/php/inc/ZipIt.php"); list($format, $hrefs) = checkKeys(array("format", "hrefs"));
$zipit = new ZipIt($h5ai); H5ai::req_once("/php/inc/Archive.php");
$archive = new Archive($h5ai);
$hrefs = explode(":", trim($hrefs)); $hrefs = explode(":", trim($hrefs));
$zipFile = $zipit->zip($hrefs); $target = $archive->create($format, $hrefs);
if (is_string($zipFile)) { if (is_string($target)) {
$response = array('status' => 'ok', 'id' => basename($zipFile), 'size' => filesize($zipFile)); $response = array('status' => 'ok', 'id' => basename($target), 'size' => filesize($target));
} else { } else {
$response = array('status' => 'failed', 'code' => $zipFile); $response = array('status' => 'failed', 'code' => $target);
} }
echo json_encode($response); echo json_encode($response);
} }
else if ($action === "getzip") { else if ($action === "getarchive") {
list($id) = checkKeys(array("id")); fail(0, "download is disabled", !$options["download"]["enabled"]);
fail(1, "zipped file not found: " . $id, !preg_match("/^h5ai-zip-/", $id));
$zipFile = str_replace("\\", "/", sys_get_temp_dir()) . "/" . $id; list($id,$as) = checkKeys(array("id", "as"));
fail(2, "zipped file not found: " . $id, !file_exists($zipFile)); fail(1, "file not found: " . $id, !preg_match("/^h5ai-selection-/", $id));
header("Content-Disposition: attachment; filename=\"h5ai-selection.zip\""); $target = H5ai::normalize_path(sys_get_temp_dir(), true) . $id;
fail(2, "file not found: " . $id, !file_exists($target));
header("Content-Disposition: attachment; filename=\"$as\"");
header("Content-Type: application/octet-stream"); header("Content-Type: application/octet-stream");
header("Content-Length: " . filesize($zipFile)); header("Content-Length: " . filesize($target));
header("Connection: close"); header("Connection: close");
readfile($zipFile); readfile($target);
} }
@ -152,7 +154,7 @@ else if ($action === "checks") {
'cache' => $h5ai->checks["php"] && $h5ai->checks["cache"], 'cache' => $h5ai->checks["php"] && $h5ai->checks["cache"],
'thumbs' => $h5ai->checks["php"] && $h5ai->checks["cache"] && $h5ai->checks["gd"], 'thumbs' => $h5ai->checks["php"] && $h5ai->checks["cache"] && $h5ai->checks["gd"],
'temp' => $h5ai->checks["php"] && $h5ai->checks["temp"], 'temp' => $h5ai->checks["php"] && $h5ai->checks["temp"],
'zips' => $h5ai->checks["php"] && $h5ai->checks["temp"] && $h5ai->checks["zip"] 'download' => $h5ai->checks["php"] && $h5ai->checks["temp"] && $h5ai->checks["archive"]
); );
echo json_encode($response); echo json_encode($response);
} }

View file

@ -0,0 +1,77 @@
<?php
class Archive {
private $h5ai;
public function __construct($h5ai) {
$this->h5ai = $h5ai;
}
public function create($format, $hrefs) {
$target = H5ai::normalize_path(sys_get_temp_dir(), true) . "h5ai-selection-" . microtime(true) . "-" . rand() . "." . $format;
$archive = new PharData($target);
foreach ($hrefs as $href) {
$d = H5ai::normalize_path(dirname($href), true);
$n = basename($href);
$code = $this->h5ai->getHttpCode($d);
if ($code == 401) {
return $code;
}
if ($code == "h5ai" && !$this->h5ai->ignoreThisFile($n)) {
$realFile = $this->h5ai->getAbsPath($href);
$archivedFile = preg_replace("!^" . $this->h5ai->getRootAbsPath() . "!", "", $realFile);
if (is_dir($realFile)) {
$rcode = $this->addDir($archive, $realFile, $archivedFile);
if ($rcode == 401) {
return $rcode;
}
} else {
$this->addFile($archive, $realFile, $archivedFile);
}
}
}
return filesize($target) ? $target : null;
}
private function addFile($archive, $realFile, $archivedFile) {
if (is_readable($realFile)) {
$archive->addFile($realFile, $archivedFile);
}
}
private function addDir($archive, $realDir, $archivedDir) {
$code = $this->h5ai->getHttpCode($this->h5ai->getAbsHref($realDir));
if ($code == "h5ai") {
$archive->addEmptyDir($archivedDir);
$files = $this->h5ai->readDir($realDir);
foreach ($files as $file) {
$realFile = $realDir . "/" . $file;
$archivedFile = $archivedDir . "/" . $file;
if (is_dir($realFile)) {
$rcode = $this->addDir($archive, $realFile, $archivedFile);
if ($rcode == 401) {
return $rcode;
}
} else {
$this->addFile($archive, $realFile, $archivedFile);
}
}
}
return $code;
}
}
?>

View file

@ -83,7 +83,7 @@ class H5ai {
$this->checks = array( $this->checks = array(
"php" => version_compare(PHP_VERSION, "5.2.0") >= 0, "php" => version_compare(PHP_VERSION, "5.2.0") >= 0,
"zip" => class_exists("ZipArchive"), "archive" => class_exists("PharData"),
"gd" => GD_VERSION != "GD_VERSION", "gd" => GD_VERSION != "GD_VERSION",
"cache" => is_writable($this->h5aiAbsPath . "/cache"), "cache" => is_writable($this->h5aiAbsPath . "/cache"),
"temp" => is_writable(sys_get_temp_dir()) "temp" => is_writable(sys_get_temp_dir())

View file

@ -1,89 +0,0 @@
<?php
class ZipIt {
private $h5ai;
public static function isUsable() {
return class_exists("ZipArchive");
}
public function __construct($h5ai) {
$this->h5ai = $h5ai;
}
public function zip($hrefs) {
$zipFile = tempnam(sys_get_temp_dir(), "h5ai-zip-");
$zip = new ZipArchive();
if (!$zip->open($zipFile, ZIPARCHIVE::CREATE)) {
return null;
}
$zip->addEmptyDir("/");
foreach ($hrefs as $href) {
$d = H5ai::normalize_path(dirname($href), true);
$n = basename($href);
$code = $this->h5ai->getHttpCode($d);
if ($code == 401) {
return $code;
}
if ($code == "h5ai" && !$this->h5ai->ignoreThisFile($n)) {
$localFile = $this->h5ai->getAbsPath($href);
$file = preg_replace("!^" . $this->h5ai->getRootAbsPath() . "!", "", $localFile);
if (is_dir($localFile)) {
$rcode = $this->zipDir($zip, $localFile, $file);
if ($rcode == 401) {
return $rcode;
}
} else {
$this->zipFile($zip, $localFile, $file);
}
}
}
$zip->close();
return filesize($zipFile) ? $zipFile : null;
}
private function zipFile($zip, $localFile, $file) {
if (is_readable($localFile)) {
$zip->addFile($localFile, $file);
}
}
private function zipDir($zip, $localDir, $dir) {
$code = $this->h5ai->getHttpCode($this->h5ai->getAbsHref($localDir));
if ($code == 'h5ai') {
$zip->addEmptyDir($dir);
$files = $this->h5ai->readDir($localDir);
foreach ($files as $file) {
$localFile = $localDir . "/" . $file;
$file = $dir . "/" . $file;
if (is_dir($localFile)) {
$rcode = $this->zipDir($zip, $localFile, $file);
if ($rcode == 401) {
return $rcode;
}
} else {
$this->zipFile($zip, $localFile, $file);
}
}
}
return $code;
}
}
?>

View file

@ -14,7 +14,8 @@
<link rel="apple-touch-icon" type="image/png" href="/_h5ai/images/h5ai-48x48.png"> <link rel="apple-touch-icon" type="image/png" href="/_h5ai/images/h5ai-48x48.png">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="/_h5ai/css/styles.css"> <link rel="stylesheet" href="/_h5ai/css/styles.css">
<script src="/_h5ai/js/modernizr-2.5.3.min.js"></script> <script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
</head> </head>
<body id="h5ai-main"> <body id="h5ai-main">
<div id="topbar" class="clearfix"> <div id="topbar" class="clearfix">
@ -32,8 +33,6 @@
<span class="right"></span> <span class="right"></span>
<span class="center"></span> <span class="center"></span>
</div> </div>
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
<div id="data-generic-json" class="hidden"> <div id="data-generic-json" class="hidden">
<?php if (stripos($_SERVER["REQUEST_METHOD"], "HEAD") === false) { <?php if (stripos($_SERVER["REQUEST_METHOD"], "HEAD") === false) {