1
0
mirror of https://github.com/lrsjng/h5ai.git synced 2025-09-06 04:02:52 +02:00

Compare commits

..

21 Commits

Author SHA1 Message Date
Lars Jung
74d967a27a Merge branch 'develop' 2011-11-02 00:57:18 +01:00
Lars Jung
6a5c35ad9a Last changes before release. 2011-11-02 00:56:55 +01:00
Lars Jung
4901034721 A lot of small fixes. 2011-11-01 22:33:00 +01:00
Lars Jung
8b1d6b9ef7 Updates readme. 2011-10-30 22:05:25 +01:00
Lars Jung
0fcd4e9709 Merge branch 'redesign-js' into develop 2011-10-30 21:55:05 +01:00
Lars Jung
4a65ce61df Improves file selection for zipped download. Zipped download is now disabled by default. 2011-10-30 21:50:25 +01:00
Lars Jung
3c23c7e956 Changes default language from to . 2011-10-07 16:38:30 +02:00
Lars Jung
caced60eaa More refactorings. 2011-10-06 05:12:29 +02:00
Lars Jung
4c1ed3d1e8 Redesigned js. 2011-09-29 13:43:22 +02:00
Lars Jung
8670b248a9 Refactorings. 2011-09-27 17:45:09 +02:00
Lars Jung
7f46dceba0 Minor refactorings. 2011-09-24 03:26:03 +02:00
Lars Jung
a95aef17be Removed release folder. 2011-09-23 17:09:05 +02:00
Lars Jung
e8dcb83d92 Improved HTML head sections. 2011-09-23 17:05:40 +02:00
Lars Jung
03de1d9472 Sorted translations in options.js. 2011-09-18 22:45:30 +02:00
Lars Jung
5e486e0aa2 Moved to version 0.16. 2011-09-18 19:54:18 +02:00
Lars Jung
a8f62f187c Added it translation. Switched to wepp. 2011-09-18 19:49:26 +02:00
Lars Jung
8e21a3c038 Fixed security issues. 2011-09-06 20:57:49 +02:00
Lars Jung
df30c67ade Added selection and zipped download. 2011-09-04 21:18:55 +02:00
Lars Jung
2d3b0eed4f Added selection and zipped download. 2011-09-04 21:18:19 +02:00
Lars Jung
a371ee2afd Minor additions. 2011-09-01 14:27:58 +02:00
Lars Jung
480c4aab79 Added thumbs and slide option. 2011-08-16 21:03:09 +02:00
67 changed files with 4037 additions and 3223 deletions

4
.gitignore vendored
View File

@@ -1,10 +1,8 @@
# Build folders to ignore
bin
build
target
build.local.*
release
# Eclipse

View File

@@ -2,13 +2,69 @@
* Website with download, docs and demo: <http://larsjung.de/h5ai>
* Sources: <http://github.com/lrsjng/h5ai>
* Q&A group: <http://groups.google.com/group/h5ai>
h5ai is provided under the terms of the [MIT License](http://github.com/lrsjng/h5ai/blob/master/LICENSE.txt).
It uses the [Faenza icon set](http://tiheum.deviantart.com/art/Faenza-Icons-173323228) (GPL license).
## h5ai profits from these great projects
* [AmplifyJS](http://amplifyjs.com) (MIT/GPL)
* [Datejs](http://www.datejs.com) (MIT)
* [Faenza icon set](http://tiheum.deviantart.com/art/Faenza-Icons-173323228) (GPL)
* [HTML5 ★ Boilerplate](http://html5boilerplate.com)
* [jQuery](http://jquery.com) (MIT/GPL)
* [jQuery.mousewheel](http://github.com/brandonaaron/jquery-mousewheel) (MIT)
* [modernizr](http://www.modernizr.com) (MIT/BSD)
## Changelog
### v0.16 · *2011-11-02*
* sorts translations in `options.js`
* improves HTML head sections
* refactors JavaScript and PHP a lot
* improves/fixes file selection for zipped download
* fixes scrollbar and header/footer link issues (didn't work when zipped download enabled)
* adds support for ctrl-select
* `dateFormat` in `options.js` changed, now affecting JS and PHP version
* `dateFormat` is localizable by adding it to a translation in `options.js`
* PHP version is now configurable via `php/config.php` (set custom doc root and other PHP related things)
* image thumbs and zipped download is disabled by default now, but works fine if PHP is configured
### v0.15.2 · *2011-09-18*
* added it translation by [Salvo Gentile](http://github.com/SalvoGentile) and [Marco Patriarca](http://github.com/Fexys)
* switched build process from scripp to wepp
### v0.15.1 · *2011-09-06*
* fixed security issues with the zipped download feature
* made zipped download optional (but enabled by default)
### v0.15 · *2011-09-04*
* added zipped download for selected files
* cleaned and refactored
### v0.14.1 · *2011-09-01*
* display meta information in bottom bar (icon view)
* added zh-cn translation by [Dongsheng Cai](http://github.com/dongsheng)
* added pl translation by Radosław Zając
* added ru translation by Богдан Илюхин
### v0.14 · *2011-08-16*
* added image thumbnails for PHP version
* new option `slideTree` to turn off auto slide in
### v0.13.2 · *2011-08-12*

View File

@@ -3,7 +3,7 @@ custom = true
# project
project.name = h5ai
project.version = 0.13.2
project.version = 0.16
# src
@@ -15,6 +15,6 @@ build.dir = build
release.dir = release
# libs
lib.scripp.jar = tools/scripp.jar
# tools
tool.wepp = tools/wepp

View File

@@ -3,8 +3,10 @@
name="h5ai"
basedir="."
default="release"
xmlns:scripp="antlib:de.larsjung.scripp.ant"
>
<import file="tools/wepp.ant.xml" />
<target name="init">
<property file="build.properties" />
<tstamp>
@@ -12,20 +14,13 @@
</tstamp>
<property name="build.label" value="${project.name} ${project.version} b${build.stamp}" />
<echo>Build: ${build.label}</echo>
<taskdef
resource="de/larsjung/scripp/ant/antlib.xml"
uri="antlib:de.larsjung.scripp.ant"
classpath="${lib.scripp.jar}"
/>
</target>
<target name="clean" depends="init">
<delete dir="${build.dir}" />
<delete dir="${release.dir}" />
</target>
<target name="build-prepare" depends="clean">
<mkdir dir="${build.dir}" />
<copy todir="${build.dir}">
@@ -41,19 +36,16 @@
</replace>
</target>
<target name="build" depends="build-prepare">
<scripp.dir dir="${build.dir}/h5ai/css" />
<scripp.dir dir="${build.dir}/h5ai/js" />
<wepp.dir dir="${build.dir}/h5ai/css" />
<wepp.dir dir="${build.dir}/h5ai/js" />
</target>
<target name="build-uncompressed" depends="build-prepare">
<scripp.dir dir="${build.dir}/h5ai/css" compress="false" />
<scripp.dir dir="${build.dir}/h5ai/js" compress="false" />
<wepp.dir dir="${build.dir}/h5ai/css" args="--nc" />
<wepp.dir dir="${build.dir}/h5ai/js" args="--nc" />
</target>
<target name="release" depends="build">
<mkdir dir="${release.dir}" />
<zip destfile="${release.dir}/${project.name}-${project.version}.zip" basedir="${build.dir}" />
@@ -62,20 +54,4 @@
<delete file="${release.dir}/${project.name}-${project.version}.tar" />
</target>
<macrodef name="scripp.dir">
<attribute name="dir" />
<attribute name="compress" default="true" />
<sequential>
<scripp:process compress="@{compress}">
<fileset dir="@{dir}" includes="**/*.less,**/*.css,**/*.js" excludes="inc/**/*,**/*.min.css,**/*.min.js" />
<globmapper from="*.less" to="*.css" />
<globmapper from="*.css" to="*.css" />
<globmapper from="*.js" to="*.js" />
</scripp:process>
<delete dir="@{dir}/inc">
<fileset dir="@{dir}" includes="**/*.less" />
</delete>
</sequential>
</macrodef>
</project>

View File

@@ -28,5 +28,8 @@ AddType text/html .php
<FilesMatch "\.js$">
Header set Cache-Control "max-age=31449600, public"
</FilesMatch>
<FilesMatch "thumb-.*\.jpg$">
Header set Cache-Control "max-age=31449600, public"
</FilesMatch>
</IfModule>

View File

@@ -33,18 +33,22 @@
}
}
&.entry {
border-bottom: 1px solid #ddd;
a, a:active, a:visited {
display: block;
color: #555;
text-decoration: none;
cursor: pointer;
border-bottom: 1px solid #ddd;
&:hover, &.hover {
background-color: #f6f6f6;
color: #e80;
}
&.selected:not(.selecting), &.selecting:not(.selected) {
border-color: rgba(240,100,0,0.2);
background-color: rgba(240,100,0,0.2);
}
}
&.error {
a, a:active, a:visited {
@@ -116,6 +120,18 @@
width: 80px;
white-space: nowrap;
}
&.entry.thumb .icon.small {
overflow: hidden;
padding: 5px;
img {
background-color: #eee;
width: 16px;
height: 16px;
border: 1px solid #ddd;
overflow: hidden;
}
}
}
}
.empty {
@@ -128,6 +144,17 @@
}
#selection-rect {
display: none;
position: absolute;
left: 0;
top: 0;
z-index: 2;
border: 1px dashed rgba(240,100,0,0.5);
background-color: rgba(240,100,0,0.2);
}
#extended.icons-view {
display: none;
padding: 3px;
@@ -166,12 +193,16 @@
border-color: #eee;
background-color: #f6f6f6;
}
&.selected:not(.selecting), &.selecting:not(.selected) {
border-color: rgba(240,100,0,0.2);
background-color: rgba(240,100,0,0.2);
}
.icon {
display: block;
img {
width: 48px;
height: 48px;
min-width: 48px;
min-height: 48px;
margin-bottom: 8px;
}
}
@@ -206,6 +237,21 @@
}
}
}
&.entry.thumb .icon.big {
width: 100px;
height: 58px;
overflow: hidden;
img {
background-color: #eee;
min-width: 46px;
min-height: 46px;
min-width: 12px;
min-height: 12px;
border: 1px solid #ddd;
overflow: hidden;
}
}
}
}
.empty {

View File

@@ -87,6 +87,11 @@ body > nav {
float: right;
border-left: 1px solid rgb(231,231,231);
}
#download {
display: none;
float: right;
border-left: 1px solid rgb(231,231,231);
}
}
@@ -153,6 +158,17 @@ body > footer {
padding: 0 8px;
float: right
}
.status {
.sep {
display: inline-block;
padding: 0 6px;
}
&.default {
}
&.dynamic {
display: none;
}
}
#langSelector {
position: relative;
cursor: pointer;

View File

@@ -5,9 +5,11 @@
#table {
display: none;
}
#tree, #content > header, #content > footer {
display: block;
}
html.no-js {
#extended.details-view, #extended.icons-view {
display: block;

View File

@@ -1,6 +1,6 @@
<!-- generated code ends here -->
</section>
<section id="extended"></section>
<section id="extended" class="clearfix"></section>
<footer></footer>
</section>
<section id="tree"></section>
@@ -17,13 +17,17 @@
</span>
<span class="center">
<span class="hideOnNoJs">
<span class="status default">
<span class="folderCount"></span> <span class="l10n-folders">folders</span>
·
<span class='sep'>·</span>
<span class="fileCount"></span> <span class="l10n-files">files</span>
</span>
<span class="status dynamic">
</span>
</span>
</span>
</footer>
<script src="/h5ai/js/lib/jquery.min.js"></script>
<script src="/h5ai/js/libs.js"></script>
<script src="/h5ai/options.js"></script>
<script src="/h5ai/js/main-js.js"></script>
</body>

View File

@@ -1,18 +1,23 @@
<!DOCTYPE html>
<html class="no-js">
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Directory index · styled with h5ai</title>
<meta name="h5ai-version" content="h5ai %BUILD_VERSION% (js)">
<meta name="description" content="Directory index styled with h5ai (http://larsjung.de/h5ai)">
<meta name="keywords" content="directory, index, autoindex, h5ai">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/png" href="/h5ai/images/h5ai-16x16.png">
<link rel="apple-touch-icon" type="image/png" href="/h5ai/images/h5ai-48x48.png">
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" type="text/css" href="/h5ai/css/main-js.css">
<script src="/h5ai/js/lib/modernizr.min.js"></script>
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="/h5ai/css/main-js.css">
<script src="/h5ai/js/modernizr.min.js"></script>
</head>
<body>
<div id="selection-rect"></div>
<nav class="clearfix">
<ul>
<li id="viewicons" class="view hideOnNoJs">
@@ -21,6 +26,9 @@
<li id="viewdetails" class="view hideOnNoJs" >
<a href="#"><img src="/h5ai/images/view-details.png" alt="view-details" /><span class="l10n-details">details</span></a>
</li>
<li id="download" class="hideOnNoJs">
<a href="#"><img src="/h5ai/images/download.png" alt="download" /><span class="l10n-download">download</span></a>
</li>
</ul>
</nav>
<section id="content">

View File

@@ -1,19 +1,24 @@
<!DOCTYPE html>
<html class="no-js">
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<?php include "php/main.php"; ?>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title><?php echo $h5ai->getTitle(); ?></title>
<meta name="h5ai-version" content="h5ai %BUILD_VERSION% (php)">
<meta name="description" content="Directory index styled with h5ai (http://larsjung.de/h5ai)">
<meta name="keywords" content="directory, index, autoindex, h5ai">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/png" href="/h5ai/images/h5ai-16x16.png">
<link rel="apple-touch-icon" type="image/png" href="/h5ai/images/h5ai-48x48.png">
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" type="text/css" href="/h5ai/css/main-php.css">
<script src="/h5ai/js/lib/modernizr.min.js"></script>
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="/h5ai/css/main-php.css">
<script src="/h5ai/js/modernizr.min.js"></script>
</head>
<body>
<div id="selection-rect"></div>
<nav class="clearfix">
<ul>
<?php echo $crumb->toHtml(); ?>
@@ -23,6 +28,9 @@
<li id="viewdetails" class="view hideOnNoJs" >
<a href="#"><img src="/h5ai/images/view-details.png" alt="view-details" /><span class="l10n-details">details</span></a>
</li>
<li id="download" class="hideOnNoJs">
<a href="#"><img src="/h5ai/images/download.png" alt="download" /><span class="l10n-download">download</span></a>
</li>
</ul>
</nav>
<?php echo $tree->toHtml(); ?>
@@ -43,12 +51,16 @@
</span>
</span>
<span class="center">
<span class="status default">
<?php echo $extended->getFolderCount(); ?> <span class="l10n-folders">folders</span>
·
<span class='sep'>·</span>
<?php echo $extended->getFileCount(); ?> <span class="l10n-files">files</span>
</span>
<span class="status dynamic">
</span>
</span>
</footer>
<script src="/h5ai/js/lib/jquery.min.js"></script>
<script src="/h5ai/js/libs.js"></script>
<script src="/h5ai/options.js"></script>
<script src="/h5ai/js/main-php.js"></script>
<section id="table">

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

View File

@@ -0,0 +1,143 @@
/*global $, H5aiJs */
H5aiJs.factory.Connector = function () {
var contentTypeRegEx = /^text\/html;h5ai=/,
pathnameStatusCache = {},
fetchStatus = function (pathname, callback) {
if (H5aiJs.h5ai.settings.folderStatus[pathname]) {
callback(H5aiJs.h5ai.settings.folderStatus[pathname]);
return;
} else if (pathnameStatusCache[pathname]) {
callback(pathnameStatusCache[pathname]);
return;
}
$.ajax({
url: pathname,
type: "HEAD",
complete: function (xhr) {
var status = xhr.status;
if (status === 200 && contentTypeRegEx.test(xhr.getResponseHeader("Content-Type"))) {
status = "h5ai";
}
pathnameStatusCache[pathname] = status;
callback(status);
}
});
},
updatePath = function (path) {
if (path.isFolder && !path.isParentFolder && path.status === undefined) {
fetchStatus(path.absHref, function (status) {
if (status !== "h5ai") {
path.status = status;
}
H5aiJs.html.updateHtml(path);
H5aiJs.h5ai.linkHoverStates();
});
}
},
updatePaths = function () {
$.each(H5aiJs.pathCache.cache, function (ref, cached) {
updatePath(cached);
});
},
fetchStatusAndContent = function (pathname, includeParent, callback) {
fetchStatus(pathname, function (status) {
if (status !== "h5ai") {
callback(status, {});
return;
}
$.ajax({
url: pathname,
type: "GET",
dataType: "html",
error: function (xhr) {
callback(xhr.status, {}); // since it was checked before this should never happen
},
success: function (html, status, xhr) {
var content = {};
if (!contentTypeRegEx.test(xhr.getResponseHeader("Content-Type"))) {
callback(xhr.status, {}); // since it was checked before this should never happen
return;
}
$(html).find("#table td").closest("tr").each(function () {
var path = H5aiJs.pathCache.getPath(pathname, this);
if (path.isFolder && (!path.isParentFolder || includeParent)) {
content[path.absHref] = path;
updatePath(path);
}
});
callback("h5ai", content);
}
});
});
},
fetchPath = function (pathname, callback) {
fetchStatusAndContent(pathname, false, function (status, content) {
var path = H5aiJs.pathCache.getPath(pathname);
path.status = status;
path.content = content;
callback(path);
});
},
fetchTree = function (pathname, callback, childPath) {
fetchPath(pathname, function (path) {
var parent = H5aiJs.pathCache.splitPathname(pathname)[0];
path.treeOpen = true;
if (childPath) {
path.content[childPath.absHref] = childPath;
}
if (parent === "") {
callback(path);
} else {
fetchTree(parent, callback, path);
}
});
},
populateTree = function () {
fetchTree(document.location.pathname, function (path) {
$("#tree")
.append(H5aiJs.html.updateTreeHtml(path))
.scrollpanel()
.show();
H5aiJs.h5ai.shiftTree(false, true);
H5aiJs.h5ai.linkHoverStates();
setTimeout(function () { $("#tree").get(0).updateScrollbar(); }, 1);
});
},
init = function () {
if (H5aiJs.h5ai.settings.showTree) {
updatePaths();
populateTree();
}
};
return {
fetchStatusAndContent: fetchStatusAndContent,
init: init
};
};

112
src/h5ai/js/inc/Extended.js Normal file
View File

@@ -0,0 +1,112 @@
/*global $, H5aiJs */
H5aiJs.factory.Extended = function () {
var settings = {
customHeader: "h5ai.header.html",
customFooter: "h5ai.footer.html"
},
initTitle = function () {
document.title = document.domain + document.location.pathname;
try {
document.title = decodeURI(document.title);
} catch (err) {}
},
initBreadcrumb = function () {
var $ul = $("body > nav ul"),
pathname = "/",
path = H5aiJs.pathCache.getPath(pathname),
pathnameParts = document.location.pathname.split("/");
$ul.append(H5aiJs.html.updateCrumbHtml(path));
$.each(pathnameParts, function (idx, part) {
if (part !== "") {
pathname += part + "/";
$ul.append(H5aiJs.html.updateCrumbHtml(H5aiJs.pathCache.getPath(pathname)));
}
});
},
initExtendedView = function () {
var $ths = $("#table th"),
$label = $ths.eq(1).find("a"),
$date = $ths.eq(2).find("a"),
$size = $ths.eq(3).find("a"),
sortquery = document.location.search,
order = {
column: (sortquery.indexOf("C=N") >= 0) ? "name" : (sortquery.indexOf("C=M") >= 0) ? "date" : (sortquery.indexOf("C=S") >= 0) ? "size" : H5aiJs.h5ai.settings.sortorder.column,
ascending: (sortquery.indexOf("O=A") >= 0) ? true : (sortquery.indexOf("O=D") >= 0) ? false : H5aiJs.h5ai.settings.sortorder.ascending
},
$icon, $ul, $li;
$ul = $("<ul/>");
$li = $("<li class='header' />")
.appendTo($ul)
.append($("<a class='icon'></a>"))
.append($("<a class='label' href='" + $label.attr("href") + "'><span class='l10n-name'>" + $label.text() + "</span></a>"))
.append($("<a class='date' href='" + $date.attr("href") + "'><span class='l10n-lastModified'>" + $date.text() + "</span></a>"))
.append($("<a class='size' href='" + $size.attr("href") + "'><span class='l10n-size'>" + $size.text() + "</span></a>"));
// header sort icons
if (order.ascending) {
$icon = $("<img src='" + H5aiJs.h5ai.image("ascending") + "' class='sort' alt='ascending' />");
} else {
$icon = $("<img src='" + H5aiJs.h5ai.image("descending") + "' class='sort' alt='descending' />");
}
if (order.column === "date") {
$li.find("a.date").prepend($icon);
} else if (order.column === "size") {
$li.find("a.size").prepend($icon);
} else {
$li.find("a.label").append($icon);
}
// entries
$("#table td").closest("tr").each(function () {
var path = H5aiJs.pathCache.getPath(document.location.pathname, this);
$ul.append(H5aiJs.html.updateExtendedHtml(path));
});
$("#extended").append($ul);
// empty
if ($ul.children(".entry:not(.parentfolder)").size() === 0) {
$("#extended").append($("<div class='empty l10n-empty'>empty</div>"));
}
},
customize = function () {
$.ajax({
url: settings.customHeader,
dataType: "html",
success: function (data) {
$("#content > header").append($(data)).show();
}
});
$.ajax({
url: settings.customFooter,
dataType: "html",
success: function (data) {
$("#content > footer").prepend($(data)).show();
}
});
},
initTotals = function () {
$(".folderCount").text($("#extended .entry.folder:not(.parentfolder)").size());
$(".fileCount").text($("#extended .entry.file").size());
};
this.init = function () {
initTitle();
initBreadcrumb();
initExtendedView();
customize();
initTotals();
};
};

461
src/h5ai/js/inc/H5ai.js Normal file
View File

@@ -0,0 +1,461 @@
H5aiJs.factory.H5ai = function (options, langs) {
/*global window, $, amplify*/
var $window = $(window),
$document = $(document),
defaults = {
store: {
viewmode: "h5ai.pref.viewmode",
lang: "h5ai.pref.lang"
},
callbacks: {
pathClick: []
},
h5aiAbsHref: "/h5ai",
viewmodes: ["details", "icons"],
sortorder: {
column: "name",
ascending: true
},
showTree: true,
slideTree: true,
folderStatus: {},
lang: null,
useBrowserLang: true,
setParentFolderLabels: true,
linkHoverStates: true,
dateFormat: "yyyy-MM-dd HH:mm",
showThumbs: true,
zippedDownload: true
},
settings = $.extend({}, defaults, options),
api = function () {
return settings.h5aiAbsHref + "/php/api.php";
},
image = function (id) {
return settings.h5aiAbsHref + "/images/" + id + ".png";
},
icon = function (id, big) {
return settings.h5aiAbsHref + "/icons/" + (big ? "48x48" : "16x16") + "/" + id + ".png";
},
pathClick = function (fn) {
if ($.isFunction(fn)) {
settings.callbacks.pathClick.push(fn);
}
},
triggerPathClick = function (path, context) {
$.each(settings.callbacks.pathClick, function (idx, callback) {
callback(path, context);
});
},
getViewmode = function () {
var viewmode = amplify.store(settings.store.viewmode);
return $.inArray(viewmode, settings.viewmodes) >= 0 ? viewmode : settings.viewmodes[0];
},
applyViewmode = function (viewmode) {
if (viewmode) {
amplify.store(settings.store.viewmode, viewmode);
}
viewmode = getViewmode();
$("#viewdetails,#viewicons").hide().removeClass("current");
if (settings.viewmodes.length > 1) {
if ($.inArray("details", settings.viewmodes) >= 0) {
$("#viewdetails").show();
}
if ($.inArray("icons", settings.viewmodes) >= 0) {
$("#viewicons").show();
}
}
if (viewmode === "details") {
$("#viewdetails").closest("li").addClass("current");
$("#extended").addClass("details-view").removeClass("icons-view").show();
} else if (viewmode === "icons") {
$("#viewicons").closest("li").addClass("current");
$("#extended").removeClass("details-view").addClass("icons-view").show();
} else {
$("#extended").hide();
}
},
initTopSpace = function () {
var adjustTopSpace = function () {
var winHeight = $window.height(),
navHeight = $("body > nav").outerHeight(),
footerHeight = $("body > footer").outerHeight(),
contentSpacing = 50,
treeSpacing = 50;
$("body").css({
"margin-top": navHeight + contentSpacing,
"margin-bottom": footerHeight + contentSpacing
});
$("#tree").css({
top: navHeight + treeSpacing,
height: winHeight - navHeight - footerHeight - 36 - 2 * treeSpacing
});
try {
$("#tree").get(0).updateScrollbar();
} catch (err) {}
};
$window.resize(function () {
adjustTopSpace();
});
adjustTopSpace();
},
initViews = function () {
$("#table").remove();
$("#viewdetails").closest("li")
.click(function () { applyViewmode("details"); });
$("#viewicons").closest("li")
.click(function () { applyViewmode("icons"); });
// status update
$("#extended .entry a").hover(
function () {
if ($("#extended").hasClass("icons-view")) {
var $this = $(this);
$(".status.default").hide();
$(".status.dynamic")
.empty()
.append($this.find(".label").clone())
.append($("<span class='sep'>·</span>"))
.append($this.find(".date").clone())
.show();
if (!$this.closest(".entry").hasClass("folder")) {
$(".status.dynamic")
.append($("<span class='sep'>·</span>"))
.append($this.find(".size").clone());
}
}
},
function () {
$(".status.default").show();
$(".status.dynamic").empty().hide();
}
);
},
shiftTree = function (forceVisible, dontAnimate) {
var $tree = $("#tree"),
$extended = $("#extended");
if ((settings.slideTree && $tree.outerWidth() < $extended.offset().left) || forceVisible) {
if (dontAnimate) {
$tree.stop().css({ left: 0 });
} else {
$tree.stop().animate({ left: 0 });
}
} else {
if (dontAnimate) {
$tree.stop().css({ left: 18 - $tree.outerWidth() });
} else {
$tree.stop().animate({ left: 18 - $tree.outerWidth() });
}
}
},
initTree = function () {
$("#tree").hover(
function () { shiftTree(true); },
function () { shiftTree(); }
);
$window.resize(function () { shiftTree(); });
shiftTree(false, true);
},
selectLinks = function (href) {
var elements = [];
$("a[href^='/']").each(function () {
if ($(this).attr("href") === href) {
elements.push(this);
}
});
return $(elements);
},
linkHoverStates = function () {
if (settings.linkHoverStates) {
$("a[href^='/']:not(.linkedHoverStates)").each(function () {
var $a = $(this).addClass("linkedHoverStates"),
href = $a.attr("href");
$a.hover(
function () { selectLinks(href).addClass("hover"); },
function () { selectLinks(href).removeClass("hover"); }
);
});
}
},
localize = function (langs, lang, useBrowserLang) {
var storedLang = amplify.store(settings.store.lang),
dateFormat = settings.dateFormat,
browserLang, selected, key;
if (langs[storedLang]) {
lang = storedLang;
} else if (useBrowserLang) {
browserLang = navigator.language;
if (langs[browserLang]) {
lang = browserLang;
} else if (browserLang.length > 2 && langs[browserLang.substr(0, 2)]) {
lang = browserLang.substr(0, 2);
}
}
if (!langs[lang]) {
lang = "en";
}
selected = langs[lang];
if (selected) {
$.each(selected, function (key, value) {
$(".l10n-" + key).text(value);
});
$(".lang").text(lang);
$(".langOption").removeClass("current");
$(".langOption." + lang).addClass("current");
}
dateFormat = selected.dateFormat || dateFormat;
$("#extended .entry .date").each(function () {
var $this = $(this),
time = $this.data("time"),
formattedDate = time ? new Date(time).toString(dateFormat) : "";
$this.text(formattedDate);
});
},
initLangSelector = function (langs) {
var $langOptions = $(".langOptions"),
sortedLangsKeys = [],
$ul;
$.each(langs, function (lang) {
sortedLangsKeys.push(lang);
});
sortedLangsKeys.sort();
$ul = $("<ul />");
$.each(sortedLangsKeys, function (idx, lang) {
$("<li class='langOption' />")
.addClass(lang)
.text(lang + " - " + langs[lang].lang)
.appendTo($ul)
.click(function () {
amplify.store(settings.store.lang, lang);
localize(langs, lang, false);
});
});
$("#langSelector .langOptions").append($ul);
$("#langSelector").hover(
function () {
$langOptions
.css("top", "-" + $langOptions.outerHeight() + "px")
.stop(true, true)
.fadeIn();
},
function () {
$langOptions
.stop(true, true)
.fadeOut();
}
);
},
onIndicatorClick = function (event) {
var $indicator = $(this),
$entry = $indicator.closest(".entry");
if ($indicator.hasClass("unknown")) {
$.get(api(), { "action": "tree", "href": $entry.find("> a").attr("href") }, function (html) {
var $content = $(html);
$indicator.removeClass("unknown");
if ($content.find("> li").size() === 0) {
$indicator.replaceWith($("<span class='blank' />"));
} else {
$indicator.addClass("open");
$entry.find("> .content").replaceWith($content);
$("#tree").get(0).updateScrollbar();
$content.find(".indicator:not(.initiated)")
.click(onIndicatorClick)
.addClass("initiated");
}
});
} else if ($indicator.hasClass("open")) {
$indicator.removeClass("open");
$("#tree").get(0).updateScrollbar(true);
$entry.find("> .content").slideUp(function () {
$("#tree").get(0).updateScrollbar();
});
} else {
$indicator.addClass("open");
$("#tree").get(0).updateScrollbar(true);
$entry.find("> .content").slideDown(function () {
$("#tree").get(0).updateScrollbar();
});
}
},
initIndicators = function () {
$("#tree .entry.folder .indicator:not(.initiated)")
.click(onIndicatorClick)
.addClass("initiated");
},
initZippedDownload = function () {
var x = 0,
y = 0,
ctrl = false,
updateDownloadBtn = function () {
var query,
href,
$selected = $("#extended a.selected");
if ($selected.size() > 0) {
$selected.each(function () {
href = $(this).attr("href");
query = query ? query + ":" + href : href;
});
query = api() + "?action=zip&hrefs=" + query;
$("#download").show().find("a").attr("href", query);
} else {
$("#download").hide().find("a").attr("href", "#");
}
},
selectionUpdate = function (event) {
var l = Math.min(x, event.pageX),
t = Math.min(y, event.pageY),
w = Math.abs(x - event.pageX),
h = Math.abs(y - event.pageY),
sel;
event.preventDefault();
$("#selection-rect").css({left: l, top: t, width: w, height: h});
sel = $("#selection-rect").fracs("rect");
$("#extended a").removeClass("selecting").each(function () {
var $a = $(this),
rect = $a.fracs("rect"),
inter = sel.intersection(rect);
if (inter && !$a.closest(".entry").hasClass("folder-parent")) {
$a.addClass("selecting");
}
});
},
selectionEnd = function (event) {
event.preventDefault();
$document.unbind("mousemove", selectionUpdate);
$("#selection-rect").hide().css({left: 0, top: 0, width: 0, height: 0});
$("#extended a.selecting.selected").removeClass("selecting").removeClass("selected");
$("#extended a.selecting").removeClass("selecting").addClass("selected");
updateDownloadBtn();
},
selectionStart = function (event) {
var view = $.fracs.viewport();
x = event.pageX;
y = event.pageY;
// only on left button and don't block the scrollbars
if (event.button !== 0 || x >= view.right || y >= view.bottom) {
return;
}
event.preventDefault();
if (!ctrl) {
$("#extended a").removeClass("selected");
updateDownloadBtn();
}
$("#selection-rect").show().css({left: x, top: y, width: 0, height: 0});
selectionUpdate(event);
$document
.bind("mousemove", selectionUpdate)
.one("mouseup", selectionEnd);
},
noSelection = function (event) {
event.stopPropagation();
return false;
},
noSelectionUnlessCtrl = function (event) {
if (!ctrl) {
noSelection(event);
}
};
if (settings.zippedDownload) {
$("body>nav,body>footer,#tree").bind("mousedown", noSelection);
$("#extended .entry a").bind("mousedown", noSelectionUnlessCtrl).live("mousedown", noSelectionUnlessCtrl);
$document
.bind("mousedown", selectionStart)
.keydown(function (event) {
if (event.keyCode === 17) {
ctrl = true;
}
})
.keyup(function (event) {
if (event.keyCode === 17) {
ctrl = false;
}
});
}
},
init = function () {
applyViewmode();
initTopSpace();
initViews();
initTree();
linkHoverStates();
initLangSelector(langs);
localize(langs, settings.lang, settings.useBrowserLang);
initIndicators();
initZippedDownload();
};
return {
settings: settings,
api: api,
image: image,
icon: icon,
shiftTree: shiftTree,
linkHoverStates: linkHoverStates,
pathClick: pathClick,
triggerPathClick: triggerPathClick,
initIndicators: initIndicators,
init: init
};
};

260
src/h5ai/js/inc/Html.js Normal file
View File

@@ -0,0 +1,260 @@
/*global $, H5aiJs */
H5aiJs.factory.Html = function () {
var thumbExts = ["bmp", "gif", "ico", "image", "jpg", "jpeg", "png", "tiff"],
onClick = function (path, context) {
H5aiJs.h5ai.triggerPathClick(path, context);
},
updateCrumbHtml = function (path) {
var $html, $a;
if (path.html.$crumb && path.html.$crumb.data("status") === path.status) {
return path.html.$crumb;
}
$html = $("<li class='crumb' />")
.data("path", path)
.addClass(path.isFolder ? "folder" : "file");
if (path.status) {
$html.data("status", path.status);
}
$a = $("<a><img src='" + H5aiJs.h5ai.image("crumb") + "' alt='>' />" + path.label + "</a>")
.appendTo($html)
.attr("href", path.absHref)
.click(function() { onClick(path, "crumb"); });
if (path.isDomain) {
$html.addClass("domain");
$a.find("img").attr("src", H5aiJs.h5ai.image("home"));
}
if (path.isCurrentFolder) {
$html.addClass("current");
}
if (!isNaN(path.status)) {
if (path.status === 200) {
$("<img class='hint' src='" + H5aiJs.h5ai.image("page") + "' alt='not listable' />").appendTo($a);
} else {
$("<span class='hint'>(" + path.status + ")</span>").appendTo($a);
}
}
if (path.html.$crumb) {
path.html.$crumb.replaceWith($html);
}
path.html.$crumb = $html;
return $html;
},
updateExtendedHtml = function (path) {
var $html, $a, $label,
formattedDate = path.date ? path.date.toString(H5aiJs.h5ai.settings.dateFormat) : "",
ext = path.absHref.substr((path.absHref.lastIndexOf('.') + 1)),
icon16 = path.icon16,
icon48 = path.icon48;
if (path.html.$extended && path.html.$extended.data("status") === path.status) {
return path.html.$extended;
}
$html = $("<li class='entry' />")
.data("path", path)
.addClass(path.isFolder ? "folder" : "file");
if (path.status) {
$html.data("status", path.status);
}
if (H5aiJs.h5ai.settings.showThumbs && $.inArray(ext.toLowerCase(), thumbExts) >= 0) {
icon16 = H5aiJs.h5ai.api() + "?action=thumb&href=" + path.absHref + "&width=16&height=16&mode=square";
icon48 = H5aiJs.h5ai.api() + "?action=thumb&href=" + path.absHref + "&width=96&height=46&mode=rational";
}
$label = $("<span class='label'>" + path.label + "</span>");
$a = $("<a />")
.attr("href", path.absHref)
.click(function() { onClick(path, "extended"); })
.appendTo($html)
.append($("<span class='icon small'><img src='" + icon16 + "' alt='" + path.alt + "' /></span>"))
.append($("<span class='icon big'><img src='" + icon48 + "' alt='" + path.alt + "' /></span>"))
.append($label)
.append($("<span class='date' data-time='" + path.time + "'></span>"))
.append($("<span class='size'>" + path.size + "</span>"));
$a.hover(
function () {
if ($("#extended").hasClass("icons-view")) {
var $this = $(this);
$(".status.default").hide();
$(".status.dynamic")
.empty()
.append($this.find(".label").clone())
.append($("<span class='sep'>·</span>"))
.append($this.find(".date").clone())
.show();
if (!$this.closest(".entry").hasClass("folder")) {
$(".status.dynamic")
.append($("<span class='sep'>·</span>"))
.append($this.find(".size").clone());
}
}
},
function () {
$(".status.default").show();
$(".status.dynamic").empty().hide();
}
);
if (path.isParentFolder) {
if (!H5aiJs.h5ai.settings.setParentFolderLabels) {
$label.addClass("l10n-parentDirectory");
}
$html.addClass("folder-parent");
}
if (!isNaN(path.status)) {
if (path.status === 200) {
$html.addClass("page");
$a.find(".icon.small img").attr("src", H5aiJs.h5ai.icon("folder-page"));
$a.find(".icon.big img").attr("src", H5aiJs.h5ai.icon("folder-page", true));
} else {
$html.addClass("error");
$label.append($("<span class='hint'> " + path.status + " </span>"));
}
}
if (path.html.$extended) {
path.html.$extended.replaceWith($html);
}
path.html.$extended = $html;
return $html;
},
updateTreeHtml = function (path) {
var $html, $blank, $a, $indicator, $ul, idx;
$html = $("<div class='entry' />")
.data("path", path)
.addClass(path.isFolder ? "folder" : "file");
$blank = $("<span class='blank' />").appendTo($html);
$a = $("<a />")
.attr("href", path.absHref)
.click(function() { path.onClick(path, "tree"); })
.appendTo($html)
.append($("<span class='icon'><img src='" + path.icon16 + "' /></span>"))
.append($("<span class='label'>" + path.label + "</span>"));
if (path.isFolder) {
// indicator
if (path.status === undefined || !path.isEmpty()) {
$indicator = $("<span class='indicator initiated'><img src='" + H5aiJs.h5ai.image("tree") + "' /></span>")
.click(function (event) {
var $entry = $indicator.closest(".entry"); // $html
if ($indicator.hasClass("unknown")) {
H5aiJs.connector.fetchStatusAndContent(path.absHref, false, function (status, content) {
path.status = status;
path.content = content;
path.treeOpen = true;
$("#tree").get(0).updateScrollbar(true);
updateTreeHtml(path);
$("#tree").get(0).updateScrollbar();
});
} else if ($indicator.hasClass("open")) {
path.treeOpen = false;
$indicator.removeClass("open");
$("#tree").get(0).updateScrollbar(true);
$entry.find("> ul.content").slideUp(function() {
$("#tree").get(0).updateScrollbar();
});
} else {
path.treeOpen = true;
$indicator.addClass("open");
$("#tree").get(0).updateScrollbar(true);
$entry.find("> ul.content").slideDown(function() {
$("#tree").get(0).updateScrollbar();
});
}
});
if (path.status === undefined) {
$indicator.addClass("unknown");
} else if (path.treeOpen) {
$indicator.addClass("open");
}
$blank.replaceWith($indicator);
}
// is path the domain?
if (path.isDomain) {
$html.addClass("domain");
$a.find(".icon img").attr("src", H5aiJs.h5ai.icon("folder-home"));
}
// is path the current folder?
if (path.isCurrentFolder) {
$html.addClass("current");
$a.find(".icon img").attr("src", H5aiJs.h5ai.icon("folder-open"));
}
// does it have subfolders?
if (!path.isEmpty()) {
$ul = $("<ul class='content' />").appendTo($html);
$.each(path.content, function (idx, entry) {
$("<li />").append(updateTreeHtml(entry)).appendTo($ul);
});
if (path.status === undefined || !path.treeOpen) {
$ul.hide();
}
}
// reflect folder status
if (!isNaN(path.status)) {
if (path.status === 200) {
$a.find(".icon img").attr("src", H5aiJs.h5ai.icon("folder-page"));
$a.append($("<span class='hint'><img src='" + H5aiJs.h5ai.image("page") + "' /></span>"));
} else {
$html.addClass("error");
$a.append($("<span class='hint'>" + path.status + "</span>"));
}
}
}
if (path.html.$tree) {
path.html.$tree.replaceWith($html);
}
path.html.$tree = $html;
return $html;
},
updateHtml = function (path) {
updateCrumbHtml(path);
updateExtendedHtml(path);
updateTreeHtml(path);
};
return {
updateCrumbHtml: updateCrumbHtml,
updateExtendedHtml: updateExtendedHtml,
updateTreeHtml: updateTreeHtml,
updateHtml: updateHtml
};
};

102
src/h5ai/js/inc/Path.js Normal file
View File

@@ -0,0 +1,102 @@
/*global $, H5aiJs */
H5aiJs.factory.Path = function (folder, tableRow) {
var checkedDecodeUri = function (uri) {
try { return decodeURI(uri); } catch (err) {}
return uri;
},
$tds, $img, $a, splits;
// parentFolder: undefined,
// label: undefined,
// dateOrgStr: undefined,
// date: undefined,
// time: undefined,
// size: undefined,
// href: undefined,
// absHref: undefined,
// alt: undefined,
// icon16: undefined,
// icon48: undefined,
// isFolder: undefined,
// isParentFolder: undefined,
// isCurrentFolder: undefined,
// isDomain: undefined,
this.status = undefined; // undefined, "h5ai" or HTTP response code
this.content = undefined; // associative array path.absHref -> path
this.html = {
$crumb: undefined,
$extended: undefined,
$tree: undefined
};
this.treeOpen = false;
if (!H5aiJs.pathCache.pathEndsWithSlash(folder)) {
folder += "/";
}
if (tableRow) {
$tds = $(tableRow).find("td");
$img = $tds.eq(0).find("img");
$a = $tds.eq(1).find("a");
this.parentFolder = folder;
this.label = $a.text();
this.dateOrgStr = $tds.eq(2).text();
this.date = Date.parse(this.dateOrgStr);
this.time = this.date ? this.date.getTime() : 0;
this.size = $tds.eq(3).text();
this.href = $a.attr("href");
this.alt = $img.attr("alt");
this.icon16 = $img.attr("src");
} else {
splits = H5aiJs.pathCache.splitPathname(folder);
this.parentFolder = splits[0];
this.label = checkedDecodeUri(splits[1]);
if (this.label === "/") {
this.label = checkedDecodeUri(document.domain) + "/";
}
this.dateOrgStr = "";
this.date = null;
this.time = 0;
this.size = "";
this.href = splits[1];
this.alt = "[DIR]";
this.icon16 = H5aiJs.h5ai.icon("folder");
}
if (H5aiJs.pathCache.pathEndsWithSlash(this.label)) {
this.label = this.label.slice(0, -1);
}
this.icon48 = this.icon16.replace("16x16", "48x48");
this.isFolder = (this.alt === "[DIR]");
this.isParentFolder = (this.isFolder && this.label === "Parent Directory");
this.absHref = this.isParentFolder ? this.href : this.parentFolder + this.href;
this.isCurrentFolder = (this.absHref === document.location.pathname);
this.isDomain = (this.absHref === "/");
if (this.isParentFolder && H5aiJs.h5ai.settings.setParentFolderLabels) {
if (this.isDomain) {
this.label = checkedDecodeUri(document.domain);
} else {
this.label = checkedDecodeUri(H5aiJs.pathCache.splitPathname(H5aiJs.pathCache.splitPathname(this.parentFolder)[0])[1].slice(0, -1));
}
}
};
H5aiJs.factory.Path.prototype = {
isEmpty: function () {
return !this.content || $.isEmptyObject(this.content);
},
onClick: function (context) {
H5aiJs.h5ai.triggerPathClick(this, context);
}
};

View File

@@ -0,0 +1,56 @@
/*global $, H5aiJs */
H5aiJs.factory.PathCache = function () {
var cache = {},
rePathnameSplit = /^(\/(.*\/)*)([^\/]+\/?)$/,
rePathEndsWithSlash = /\/$/,
splitPathname = function (pathname) {
var match;
if (pathname === "/") {
return ["", "/"];
}
match = rePathnameSplit.exec(pathname);
return [match[1], match[3]];
},
pathEndsWithSlash = function (pathname) {
return rePathEndsWithSlash.test(pathname);
},
getAbsHref = function (folder, tableRow) {
var $a, isParentFolder, href;
if (!pathEndsWithSlash(folder)) {
folder += "/";
}
if (!tableRow) {
return folder;
}
$a = $(tableRow).find("td").eq(1).find("a");
isParentFolder = ($a.text() === "Parent Directory");
href = $a.attr("href");
return isParentFolder ? undefined : folder + href;
},
getPath = function (folder, tableRow) {
var absHref = getAbsHref(folder, tableRow),
path = cache[absHref];
if (!path) {
path = new H5aiJs.factory.Path(folder, tableRow);
if (!path.isParentFolder) {
cache[path.absHref] = path;
}
}
return path;
};
this.splitPathname = splitPathname;
this.pathEndsWithSlash = pathEndsWithSlash;
this.getPath = getPath;
this.cache = cache;
};

View File

@@ -1,153 +0,0 @@
var Extended = function ( pathCache, h5ai ) {
/*******************************
* config
*******************************/
this.config = {
customHeader: "h5ai.header.html",
customFooter: "h5ai.footer.html"
};
/*******************************
* init
*******************************/
this.init = function () {
try {
document.title = decodeURI( document.domain + document.location.pathname );
} catch ( err ) {
document.title = document.domain + document.location.pathname;
};
this.initBreadcrumb();
this.initExtendedView();
this.customize();
this.initCounts();
};
/*******************************
* breadcrumb
*******************************/
this.initBreadcrumb = function () {
var $ul = $( "body > nav ul" );
var pathname = "/";
var path = pathCache.getPathForFolder( pathname );
$ul.append( path.updateCrumbHtml() );
var pathnameParts = document.location.pathname.split( "/" );
for ( idx in pathnameParts ) {
var part = pathnameParts[idx];
if ( part !== "" ) {
pathname += part + "/";
var path = pathCache.getPathForFolder( pathname );
$ul.append( path.updateCrumbHtml() );
};
};
};
/*******************************
* extended view
*******************************/
this.initExtendedView = function () {
var $ul = $( "<ul/>" );
// headers
var $ths = $( "#table th" );
var $label = $ths.eq( 1 ).find( "a" );
var $date = $ths.eq( 2 ).find( "a" );
var $size = $ths.eq( 3 ).find( "a" );
var $li = $( "<li class='header' />" ).appendTo( $ul );
$( "<a class='icon'></a>" ).appendTo( $li );
$( "<a class='label' href='" + $label.attr( "href" ) + "'><span class='l10n-name'>" + $label.text() + "</span></a>" ).appendTo( $li );
$( "<a class='date' href='" + $date.attr( "href" ) + "'><span class='l10n-lastModified'>" + $date.text() + "</span></a>" ).appendTo( $li );
$( "<a class='size' href='" + $size.attr( "href" ) + "'><span class='l10n-size'>" + $size.text() + "</span></a>" ).appendTo( $li );
// header sort icons
var sortquery = document.location.search;
var order = {
column: ( sortquery.indexOf( "C=N" ) >= 0 ) ? "name" : ( sortquery.indexOf( "C=M" ) >= 0 ) ? "date" : ( sortquery.indexOf( "C=S" ) >= 0 ) ? "size" : h5ai.config.sortorder.column,
ascending: ( sortquery.indexOf( "O=A" ) >= 0 ) ? true : ( sortquery.indexOf( "O=D" ) >= 0 ) ? false : h5ai.config.sortorder.ascending
};
var $icon;
if ( order.ascending ) {
$icon = $( "<img src='/h5ai/images/ascending.png' class='sort' alt='ascending' />" );
} else {
$icon = $( "<img src='/h5ai/images/descending.png' class='sort' alt='descending' />" );
};
if ( order.column === "date" ) {
$li.find( "a.date" ).prepend( $icon );
} else if ( order.column === "size" ) {
$li.find( "a.size" ).prepend( $icon );
} else {
$li.find( "a.label" ).append( $icon );
};
// entries
$( "#table td" ).closest( "tr" ).each( function () {
var path = pathCache.getPathForTableRow( document.location.pathname, this );
$ul.append( path.updateExtendedHtml() );
} );
$( "#extended" ).append( $ul );
$.log( document.location.pathname, "folders:", $( "#extended .folder" ).size() , "files:", $( "#extended .file" ).size() );
// empty
if ( $ul.children( ".entry:not(.parentfolder)" ).size() === 0 ) {
$( "#extended" ).append( $( "<div class='empty l10n-empty'>empty</div>" ) );
};
// in case of floats
$( "#extended" ).addClass( "clearfix" );
};
/*******************************
* customize
*******************************/
this.customize = function () {
$.ajax( {
url: this.config.customHeader,
dataType: "html",
success: function ( data ) {
$( "#content > header" ).append( $( data ) ).show();
}
} );
$.ajax( {
url: this.config.customFooter,
dataType: "html",
success: function ( data ) {
$( "#content > footer" ).prepend( $( data ) ).show();
}
} );
};
/*******************************
* init counts
*******************************/
this.initCounts = function () {
$( ".folderCount" ).text( $( "#extended .entry.folder:not(.parentfolder)" ).size() );
$( ".fileCount" ).text( $( "#extended .entry.file" ).size() );
};
};

View File

@@ -1,351 +0,0 @@
var H5ai = function ( options, langs ) {
/*******************************
* config
*******************************/
var defaults = {
store: {
viewmode: "h5ai.viewmode",
lang: "h5ai.lang"
},
callbacks: {
pathClick: []
},
viewmodes: [ "details", "icons" ],
sortorder: {
column: "name",
ascending: true
},
showTree: true,
folderStatus: {
},
lang: null,
useBrowserLang: true,
setParentFolderLabels: true,
linkHoverStates: true
};
this.config = $.extend( {}, defaults, options );
/*******************************
* public api
*******************************/
this.pathClick = function ( fn ) {
if ( $.isFunction( fn ) ) {
this.config.callbacks.pathClick.push( fn );
};
return this;
};
/*******************************
* init
*******************************/
this.init = function () {
this.applyViewmode();
this.initTopSpace();
this.initViews();
this.initTree();
this.linkHoverStates();
this.initLangSelector( langs );
this.localize( langs, this.config.lang, this.config.useBrowserLang );
this.initIndicators();
};
/*******************************
* callback triggers
*******************************/
this.triggerPathClick = function ( path, context ) {
for ( idx in this.config.callbacks.pathClick ) {
this.config.callbacks.pathClick[idx].call( window, path, context );
};
};
/*******************************
* local stored viewmode
*******************************/
this.getViewmode = function () {
var viewmode = localStorage.getItem( this.config.store.viewmode );
return $.inArray( viewmode, this.config.viewmodes ) >= 0 ? viewmode : this.config.viewmodes[0];
};
this.applyViewmode = function ( viewmode ) {
if ( viewmode !== undefined ) {
localStorage.setItem( this.config.store.viewmode, viewmode );
};
viewmode = this.getViewmode();
$( "#viewdetails,#viewicons" ).hide().removeClass( "current" );
if ( this.config.viewmodes.length > 1 ) {
if ( $.inArray( "details", this.config.viewmodes ) >= 0 ) {
$( "#viewdetails" ).show();
};
if ( $.inArray( "icons", this.config.viewmodes ) >= 0 ) {
$( "#viewicons" ).show();
};
};
if ( viewmode === "details" ) {
$( "#viewdetails" ).closest( "li" ).addClass( "current" );
$( "#extended" ).addClass( "details-view" ).removeClass( "icons-view" ).show();
} else if ( viewmode === "icons" ) {
$( "#viewicons" ).closest( "li" ).addClass( "current" );
$( "#extended" ).removeClass( "details-view" ).addClass( "icons-view" ).show();
} else {
$( "#extended" ).hide();
};
};
/*******************************
* top space, depending on nav height
*******************************/
this.initTopSpace = function () {
function adjustTopSpace() {
var winHeight = $( window ).height();
var navHeight = $( "body > nav" ).outerHeight();
var footerHeight = $( "body > footer" ).outerHeight();
var contentSpacing = 50;
var treeSpacing = 50;
$( "body" )
.css( "margin-top", "" + ( navHeight + contentSpacing ) + "px" )
.css( "margin-bottom", "" + ( footerHeight + contentSpacing ) + "px" );
$( "#tree" )
.css( "top", "" + ( navHeight + treeSpacing ) + "px" )
.css( "height", "" + ( winHeight - navHeight - footerHeight - 36 - 2 * treeSpacing ) + "px" );
try {
$( "#tree" ).get( 0 ).updateScrollbar();
} catch ( err ) {};
};
$( window ).resize( function () {
adjustTopSpace();
} );
adjustTopSpace();
};
/*******************************
* init views
*******************************/
this.initViews = function () {
$( "#table" ).remove();
$( "#viewdetails" ).closest( "li" )
.click( $.proxy( function () {
this.applyViewmode( "details" );
}, this ) );
$( "#viewicons" ).closest( "li" )
.click( $.proxy( function () {
this.applyViewmode( "icons" );
}, this ) );
};
/*******************************
* init tree
*******************************/
this.shiftTree = function ( forceVisible, dontAnimate ) {
var $tree = $( "#tree" );
var $extended = $( "#extended" );
if ( $tree.outerWidth() < $extended.offset().left || forceVisible === true ) {
if ( dontAnimate === true ) {
$tree.stop().css( { left: 0 } );
} else {
$tree.stop().animate( { left: 0 } );
};
} else {
if ( dontAnimate === true ) {
$tree.stop().css( { left: 18 - $tree.outerWidth() } );
} else {
$tree.stop().animate( { left: 18 - $tree.outerWidth() } );
};
};
};
this.initTree = function () {
$( "#tree" ).hover(
$.proxy( function () { this.shiftTree( true ); }, this ),
$.proxy( function () { this.shiftTree(); }, this )
);
$( window ).resize( $.proxy( function () { this.shiftTree(); }, this ) );
this.shiftTree( false, true );
};
/*******************************
* link hover states
*******************************/
this.linkHoverStates = function () {
if ( !this.config.linkHoverStates ) {
return;
};
$( "a[href^='/']:not(.linkedHoverStates)" ).each( function () {
var $a = $( this ).addClass( "linkedHoverStates" );
var href = $a.attr( "href" );
$a.hover(
function () {
$( "a[href='" + href + "']" ).addClass( "hover" );
},
function () {
$( "a[href='" + href + "']" ).removeClass( "hover" );
}
);
} );
};
/*******************************
* localization
*******************************/
this.initLangSelector = function ( langs ) {
var sortedLangsKeys = [];
for ( lang in langs ) {
sortedLangsKeys.push( lang );
};
sortedLangsKeys.sort();
var THIS = this;
var $ul = $( "<ul />" );
for ( idx in sortedLangsKeys ) {
( function ( lang ) {
$( "<li class='langOption' />" )
.addClass( lang )
.text( lang + " - " + langs[lang]["lang"] )
.appendTo( $ul )
.click( function () {
localStorage.setItem( THIS.config.store.lang, lang );
THIS.localize( langs, lang, false );
} );
} )( sortedLangsKeys[idx] );
};
$( "#langSelector .langOptions" )
.append( $ul );
$( "#langSelector" ).hover(
function () {
var $ele = $( ".langOptions" );
$ele.css( "top", "-" + $ele.outerHeight() + "px" ).stop( true, true ).fadeIn();
},
function () {
$( ".langOptions" ).stop( true, true ).fadeOut();
}
);
};
this.localize = function ( langs, lang, useBrowserLang ) {
if ( useBrowserLang === true ) {
var browserLang = navigator.language;
if ( langs[ browserLang ] !== undefined ) {
lang = browserLang;
} else if ( browserLang.length > 2 && langs[ browserLang.substr( 0, 2 ) ] !== undefined ) {
lang = browserLang.substr( 0, 2 );
};
};
if ( langs[ lang ] === undefined ) {
lang = "en";
};
var storedLang = localStorage.getItem( this.config.store.lang );
if ( langs[ storedLang ] !== undefined ) {
lang = storedLang;
};
var selected = langs[ lang ];
for ( key in selected ) {
$( ".l10n-" + key ).text( selected[key] );
};
$( ".lang" ).text( lang );
$( ".langOption" ).removeClass( "current" );
$( ".langOption." + lang ).addClass( "current" );
};
/*******************************
* initiate tree indicators
*******************************/
this.initIndicators = function () {
var THIS = this;
$( "#tree .entry.folder:not(.initiatedIndicator)" ).each( function () {
var $entry = $( this ).addClass( "initiatedIndicator" );
var $indicator = $entry.find( "> .indicator" );
$indicator.click( function( event ) {
if ( $indicator.hasClass( "unknown" ) ) {
$.get( "/h5ai/php/treecontent.php", { "href": $entry.find( "> a" ).attr( "href" ) }, function ( html ) {
$content = $( html );
$indicator.removeClass( "unknown" );
if ( $content.find( "> li" ).size() === 0 ) {
$indicator.replaceWith( $( "<span class='blank' />" ) );
} else {
$indicator.addClass( "open" );
$entry.find( "> .content" ).replaceWith( $content );
$( "#tree" ).get( 0 ).updateScrollbar();
THIS.initIndicators();
};
} );
} else if ( $indicator.hasClass( "open" ) ) {
$indicator.removeClass( "open" );
$( "#tree" ).get( 0 ).updateScrollbar( true );
$entry.find( "> .content" ).slideUp( function() {
$( "#tree" ).get( 0 ).updateScrollbar();
} );
} else {
$indicator.addClass( "open" );
$( "#tree" ).get( 0 ).updateScrollbar( true );
$entry.find( "> .content" ).slideDown( function() {
$( "#tree" ).get( 0 ).updateScrollbar();
} );
};
} );
} );
};
};

View File

@@ -1,31 +0,0 @@
// v2.2
(function($){$.toJSON=function(o)
{if(typeof(JSON)=='object'&&JSON.stringify)
return JSON.stringify(o);var type=typeof(o);if(o===null)
return"null";if(type=="undefined")
return undefined;if(type=="number"||type=="boolean")
return o+"";if(type=="string")
return $.quoteString(o);if(type=='object')
{if(typeof o.toJSON=="function")
return $.toJSON(o.toJSON());if(o.constructor===Date)
{var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+
hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
if(o.constructor===Array)
{var ret=[];for(var i=0;i<o.length;i++)
ret.push($.toJSON(o[i])||"null");return"["+ret.join(",")+"]";}
var pairs=[];for(var k in o){var name;var type=typeof k;if(type=="number")
name='"'+k+'"';else if(type=="string")
name=$.quoteString(k);else
continue;if(typeof o[k]=="function")
continue;var val=$.toJSON(o[k]);pairs.push(name+":"+val);}
return"{"+pairs.join(", ")+"}";}};$.evalJSON=function(src)
{if(typeof(JSON)=='object'&&JSON.parse)
return JSON.parse(src);return eval("("+src+")");};$.secureEvalJSON=function(src)
{if(typeof(JSON)=='object'&&JSON.parse)
return JSON.parse(src);var filtered=src;filtered=filtered.replace(/\\["\\\/bfnrtu]/g,'@');filtered=filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']');filtered=filtered.replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered))
return eval("("+src+")");else
throw new SyntaxError("Error parsing JSON, source is not valid.");};$.quoteString=function(string)
{if(string.match(_escapeable))
{return'"'+string.replace(_escapeable,function(a)
{var c=_meta[a];if(typeof c==='string')return c;c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
return'"'+string+'"';};var _escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var _meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};})(jQuery);

View File

@@ -1,133 +0,0 @@
( function ( $ ) {
var init = function ( htmlElement ) {
var $element = $( htmlElement );
if ( $element.css( "position" ) === undefined || $element.css( "position" ) === "static" ) {
$element.css( "position", "relative" );
};
var $scrollbar = $( "<div class='scrollbar' />" );
var $drag = $( "<div class='drag' />" ).appendTo( $scrollbar );
$element
.wrapInner( "<div class='wrapper'><div class='content' /></div>" )
.append( $scrollbar );
var $wrapper = $element.find( "> .wrapper" );
var $content = $wrapper.find( "> .content" );
var mouseOffsetY = 0;
var updateId = undefined;
var update = function ( repeat ) {
if ( updateId !== undefined && !repeat ) {
clearInterval( updateId );
updateId = undefined;
} else if ( updateId === undefined && repeat ) {
updateId = setInterval( function() { update( true ); }, 50 );
};
$wrapper.css( "height", $element.height() );
var visibleHeight = $element.height();
var contentHeight = $content.outerHeight();
var scrollTop = $wrapper.scrollTop();
var scrollTopFrac = scrollTop / contentHeight;
var visVertFrac = Math.min( visibleHeight / contentHeight, 1 );
if ( visVertFrac < 1 ) {
$scrollbar
.fadeIn( 50 )
.css( {
height: $element.innerHeight() + $scrollbar.height() - $scrollbar.outerHeight( true )
} );
$drag
.css( {
top: $scrollbar.height() * scrollTopFrac,
height: $scrollbar.height() * visVertFrac
} );
} else {
$scrollbar.fadeOut( 50 );
};
};
var scroll = function ( event ) {
event.preventDefault();
var clickFrac = ( event.pageY - $scrollbar.offset().top - mouseOffsetY ) / $scrollbar.height();
$wrapper.scrollTop( $content.outerHeight() * clickFrac );
update();
};
$element
.mousewheel( function ( event, delta) {
$wrapper.scrollTop( $wrapper.scrollTop() - 50 * delta );
update();
event.stopPropagation();
event.preventDefault();
} )
.scroll( update );
$element.get( 0 ).updateScrollbar = update;
$wrapper
.css( {
"padding-right": $scrollbar.outerWidth( true ),
height: $element.height(),
overflow: "hidden"
} );
$scrollbar
.css( {
position: "absolute",
top: 0,
right: 0,
overflow: "hidden",
cursor: "pointer"
} )
.mousedown( function ( event ) {
mouseOffsetY = $drag.outerHeight() / 2;
scroll( event );
$scrollbar.addClass( "dragOn" );
$( window )
.bind( "mousemove", scroll )
.one( "mouseup", function ( event ) {
$scrollbar.removeClass( "dragOn" );
$( window ).unbind( "mousemove", scroll );
scroll( event );
event.stopPropagation();
} );
event.preventDefault();
} )
.each( function () {
this.onselectstart = function () {
return false;
};
} );
$drag
.css( {
position: "absolute",
left: 0,
width: "100%"
} )
.mousedown( function ( event ) {
mouseOffsetY = event.pageY - $drag.offset().top;
scroll( event );
$scrollbar.addClass( "dragOn" );
$( window )
.bind( "mousemove", scroll )
.one( "mouseup", function ( event ) {
$scrollbar.removeClass( "dragOn" );
$( window ).unbind( "mousemove", scroll );
scroll( event );
event.stopPropagation();
} );
event.stopPropagation();
} );
update();
};
$.fn.scrollpanel = function () {
return this.each( function () {
init( this );
} );
};
} )( jQuery );

View File

@@ -1,24 +0,0 @@
( function( $ ) {
// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
// modified
$.log = function () {
$.log.history = $.log.history || [];
$.log.history.push( arguments );
if ( window.console ) {
window.console.log( Array.prototype.slice.call( arguments ) );
};
};
var Timer = function () {
this.start = new Date().getTime();;
this.last = this.start;
this.log = function ( label ) {
var now = new Date().getTime();
$.log( "timer", label, "+" + (now - this.last), "=" + (now - this.start) );
this.last = now;
};
};
$.timer = new Timer();
} )( jQuery );

10
src/h5ai/js/inc/lib/amplify.min.js vendored Normal file

File diff suppressed because one or more lines are too long

104
src/h5ai/js/inc/lib/date.js Normal file
View File

@@ -0,0 +1,104 @@
/**
* Version: 1.0 Alpha-1
* Build Date: 13-Nov-2007
* Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.
* License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
* Website: http://www.datejs.com/ or http://www.coolite.com/datejs/
*/
Date.CultureInfo={name:"en-US",englishName:"English (United States)",nativeName:"English (United States)",dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbreviatedDayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],shortestDayNames:["Su","Mo","Tu","We","Th","Fr","Sa"],firstLetterDayNames:["S","M","T","W","T","F","S"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],abbreviatedMonthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],amDesignator:"AM",pmDesignator:"PM",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:"mdy",formatPatterns:{shortDate:"M/d/yyyy",longDate:"dddd, MMMM dd, yyyy",shortTime:"h:mm tt",longTime:"h:mm:ss tt",fullDateTime:"dddd, MMMM dd, yyyy h:mm:ss tt",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss GMT",monthDay:"MMMM dd",yearMonth:"MMMM, yyyy"},regexPatterns:{jan:/^jan(uary)?/i,feb:/^feb(ruary)?/i,mar:/^mar(ch)?/i,apr:/^apr(il)?/i,may:/^may/i,jun:/^jun(e)?/i,jul:/^jul(y)?/i,aug:/^aug(ust)?/i,sep:/^sep(t(ember)?)?/i,oct:/^oct(ober)?/i,nov:/^nov(ember)?/i,dec:/^dec(ember)?/i,sun:/^su(n(day)?)?/i,mon:/^mo(n(day)?)?/i,tue:/^tu(e(s(day)?)?)?/i,wed:/^we(d(nesday)?)?/i,thu:/^th(u(r(s(day)?)?)?)?/i,fri:/^fr(i(day)?)?/i,sat:/^sa(t(urday)?)?/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\+|after|from)/i,subtract:/^(\-|before|ago)/i,yesterday:/^yesterday/i,today:/^t(oday)?/i,tomorrow:/^tomorrow/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^min(ute)?s?/i,hour:/^h(ou)?rs?/i,week:/^w(ee)?k/i,month:/^m(o(nth)?s?)?/i,day:/^d(ays?)?/i,year:/^y((ea)?rs?)?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\.?m?\.?|p\.?m?\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt)/i,ordinalSuffix:/^\s*(st|nd|rd|th)/i,timeContext:/^\s*(\:|a|p)/i},abbreviatedTimeZoneStandard:{GMT:"-000",EST:"-0400",CST:"-0500",MST:"-0600",PST:"-0700"},abbreviatedTimeZoneDST:{GMT:"-000",EDT:"-0500",CDT:"-0600",MDT:"-0700",PDT:"-0800"}};
Date.getMonthNumberFromName=function(name){var n=Date.CultureInfo.monthNames,m=Date.CultureInfo.abbreviatedMonthNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
return-1;};Date.getDayNumberFromName=function(name){var n=Date.CultureInfo.dayNames,m=Date.CultureInfo.abbreviatedDayNames,o=Date.CultureInfo.shortestDayNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
return-1;};Date.isLeapYear=function(year){return(((year%4===0)&&(year%100!==0))||(year%400===0));};Date.getDaysInMonth=function(year,month){return[31,(Date.isLeapYear(year)?29:28),31,30,31,30,31,31,30,31,30,31][month];};Date.getTimezoneOffset=function(s,dst){return(dst||false)?Date.CultureInfo.abbreviatedTimeZoneDST[s.toUpperCase()]:Date.CultureInfo.abbreviatedTimeZoneStandard[s.toUpperCase()];};Date.getTimezoneAbbreviation=function(offset,dst){var n=(dst||false)?Date.CultureInfo.abbreviatedTimeZoneDST:Date.CultureInfo.abbreviatedTimeZoneStandard,p;for(p in n){if(n[p]===offset){return p;}}
return null;};Date.prototype.clone=function(){return new Date(this.getTime());};Date.prototype.compareTo=function(date){if(isNaN(this)){throw new Error(this);}
if(date instanceof Date&&!isNaN(date)){return(this>date)?1:(this<date)?-1:0;}else{throw new TypeError(date);}};Date.prototype.equals=function(date){return(this.compareTo(date)===0);};Date.prototype.between=function(start,end){var t=this.getTime();return t>=start.getTime()&&t<=end.getTime();};Date.prototype.addMilliseconds=function(value){this.setMilliseconds(this.getMilliseconds()+value);return this;};Date.prototype.addSeconds=function(value){return this.addMilliseconds(value*1000);};Date.prototype.addMinutes=function(value){return this.addMilliseconds(value*60000);};Date.prototype.addHours=function(value){return this.addMilliseconds(value*3600000);};Date.prototype.addDays=function(value){return this.addMilliseconds(value*86400000);};Date.prototype.addWeeks=function(value){return this.addMilliseconds(value*604800000);};Date.prototype.addMonths=function(value){var n=this.getDate();this.setDate(1);this.setMonth(this.getMonth()+value);this.setDate(Math.min(n,this.getDaysInMonth()));return this;};Date.prototype.addYears=function(value){return this.addMonths(value*12);};Date.prototype.add=function(config){if(typeof config=="number"){this._orient=config;return this;}
var x=config;if(x.millisecond||x.milliseconds){this.addMilliseconds(x.millisecond||x.milliseconds);}
if(x.second||x.seconds){this.addSeconds(x.second||x.seconds);}
if(x.minute||x.minutes){this.addMinutes(x.minute||x.minutes);}
if(x.hour||x.hours){this.addHours(x.hour||x.hours);}
if(x.month||x.months){this.addMonths(x.month||x.months);}
if(x.year||x.years){this.addYears(x.year||x.years);}
if(x.day||x.days){this.addDays(x.day||x.days);}
return this;};Date._validate=function(value,min,max,name){if(typeof value!="number"){throw new TypeError(value+" is not a Number.");}else if(value<min||value>max){throw new RangeError(value+" is not a valid value for "+name+".");}
return true;};Date.validateMillisecond=function(n){return Date._validate(n,0,999,"milliseconds");};Date.validateSecond=function(n){return Date._validate(n,0,59,"seconds");};Date.validateMinute=function(n){return Date._validate(n,0,59,"minutes");};Date.validateHour=function(n){return Date._validate(n,0,23,"hours");};Date.validateDay=function(n,year,month){return Date._validate(n,1,Date.getDaysInMonth(year,month),"days");};Date.validateMonth=function(n){return Date._validate(n,0,11,"months");};Date.validateYear=function(n){return Date._validate(n,1,9999,"seconds");};Date.prototype.set=function(config){var x=config;if(!x.millisecond&&x.millisecond!==0){x.millisecond=-1;}
if(!x.second&&x.second!==0){x.second=-1;}
if(!x.minute&&x.minute!==0){x.minute=-1;}
if(!x.hour&&x.hour!==0){x.hour=-1;}
if(!x.day&&x.day!==0){x.day=-1;}
if(!x.month&&x.month!==0){x.month=-1;}
if(!x.year&&x.year!==0){x.year=-1;}
if(x.millisecond!=-1&&Date.validateMillisecond(x.millisecond)){this.addMilliseconds(x.millisecond-this.getMilliseconds());}
if(x.second!=-1&&Date.validateSecond(x.second)){this.addSeconds(x.second-this.getSeconds());}
if(x.minute!=-1&&Date.validateMinute(x.minute)){this.addMinutes(x.minute-this.getMinutes());}
if(x.hour!=-1&&Date.validateHour(x.hour)){this.addHours(x.hour-this.getHours());}
if(x.month!==-1&&Date.validateMonth(x.month)){this.addMonths(x.month-this.getMonth());}
if(x.year!=-1&&Date.validateYear(x.year)){this.addYears(x.year-this.getFullYear());}
if(x.day!=-1&&Date.validateDay(x.day,this.getFullYear(),this.getMonth())){this.addDays(x.day-this.getDate());}
if(x.timezone){this.setTimezone(x.timezone);}
if(x.timezoneOffset){this.setTimezoneOffset(x.timezoneOffset);}
return this;};Date.prototype.clearTime=function(){this.setHours(0);this.setMinutes(0);this.setSeconds(0);this.setMilliseconds(0);return this;};Date.prototype.isLeapYear=function(){var y=this.getFullYear();return(((y%4===0)&&(y%100!==0))||(y%400===0));};Date.prototype.isWeekday=function(){return!(this.is().sat()||this.is().sun());};Date.prototype.getDaysInMonth=function(){return Date.getDaysInMonth(this.getFullYear(),this.getMonth());};Date.prototype.moveToFirstDayOfMonth=function(){return this.set({day:1});};Date.prototype.moveToLastDayOfMonth=function(){return this.set({day:this.getDaysInMonth()});};Date.prototype.moveToDayOfWeek=function(day,orient){var diff=(day-this.getDay()+7*(orient||+1))%7;return this.addDays((diff===0)?diff+=7*(orient||+1):diff);};Date.prototype.moveToMonth=function(month,orient){var diff=(month-this.getMonth()+12*(orient||+1))%12;return this.addMonths((diff===0)?diff+=12*(orient||+1):diff);};Date.prototype.getDayOfYear=function(){return Math.floor((this-new Date(this.getFullYear(),0,1))/86400000);};Date.prototype.getWeekOfYear=function(firstDayOfWeek){var y=this.getFullYear(),m=this.getMonth(),d=this.getDate();var dow=firstDayOfWeek||Date.CultureInfo.firstDayOfWeek;var offset=7+1-new Date(y,0,1).getDay();if(offset==8){offset=1;}
var daynum=((Date.UTC(y,m,d,0,0,0)-Date.UTC(y,0,1,0,0,0))/86400000)+1;var w=Math.floor((daynum-offset+7)/7);if(w===dow){y--;var prevOffset=7+1-new Date(y,0,1).getDay();if(prevOffset==2||prevOffset==8){w=53;}else{w=52;}}
return w;};Date.prototype.isDST=function(){console.log('isDST');return this.toString().match(/(E|C|M|P)(S|D)T/)[2]=="D";};Date.prototype.getTimezone=function(){return Date.getTimezoneAbbreviation(this.getUTCOffset,this.isDST());};Date.prototype.setTimezoneOffset=function(s){var here=this.getTimezoneOffset(),there=Number(s)*-6/10;this.addMinutes(there-here);return this;};Date.prototype.setTimezone=function(s){return this.setTimezoneOffset(Date.getTimezoneOffset(s));};Date.prototype.getUTCOffset=function(){var n=this.getTimezoneOffset()*-10/6,r;if(n<0){r=(n-10000).toString();return r[0]+r.substr(2);}else{r=(n+10000).toString();return"+"+r.substr(1);}};Date.prototype.getDayName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedDayNames[this.getDay()]:Date.CultureInfo.dayNames[this.getDay()];};Date.prototype.getMonthName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedMonthNames[this.getMonth()]:Date.CultureInfo.monthNames[this.getMonth()];};Date.prototype._toString=Date.prototype.toString;Date.prototype.toString=function(format){var self=this;var p=function p(s){return(s.toString().length==1)?"0"+s:s;};return format?format.replace(/dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?/g,function(format){switch(format){case"hh":return p(self.getHours()<13?self.getHours():(self.getHours()-12));case"h":return self.getHours()<13?self.getHours():(self.getHours()-12);case"HH":return p(self.getHours());case"H":return self.getHours();case"mm":return p(self.getMinutes());case"m":return self.getMinutes();case"ss":return p(self.getSeconds());case"s":return self.getSeconds();case"yyyy":return self.getFullYear();case"yy":return self.getFullYear().toString().substring(2,4);case"dddd":return self.getDayName();case"ddd":return self.getDayName(true);case"dd":return p(self.getDate());case"d":return self.getDate().toString();case"MMMM":return self.getMonthName();case"MMM":return self.getMonthName(true);case"MM":return p((self.getMonth()+1));case"M":return self.getMonth()+1;case"t":return self.getHours()<12?Date.CultureInfo.amDesignator.substring(0,1):Date.CultureInfo.pmDesignator.substring(0,1);case"tt":return self.getHours()<12?Date.CultureInfo.amDesignator:Date.CultureInfo.pmDesignator;case"zzz":case"zz":case"z":return"";}}):this._toString();};
Date.now=function(){return new Date();};Date.today=function(){return Date.now().clearTime();};Date.prototype._orient=+1;Date.prototype.next=function(){this._orient=+1;return this;};Date.prototype.last=Date.prototype.prev=Date.prototype.previous=function(){this._orient=-1;return this;};Date.prototype._is=false;Date.prototype.is=function(){this._is=true;return this;};Number.prototype._dateElement="day";Number.prototype.fromNow=function(){var c={};c[this._dateElement]=this;return Date.now().add(c);};Number.prototype.ago=function(){var c={};c[this._dateElement]=this*-1;return Date.now().add(c);};(function(){var $D=Date.prototype,$N=Number.prototype;var dx=("sunday monday tuesday wednesday thursday friday saturday").split(/\s/),mx=("january february march april may june july august september october november december").split(/\s/),px=("Millisecond Second Minute Hour Day Week Month Year").split(/\s/),de;var df=function(n){return function(){if(this._is){this._is=false;return this.getDay()==n;}
return this.moveToDayOfWeek(n,this._orient);};};for(var i=0;i<dx.length;i++){$D[dx[i]]=$D[dx[i].substring(0,3)]=df(i);}
var mf=function(n){return function(){if(this._is){this._is=false;return this.getMonth()===n;}
return this.moveToMonth(n,this._orient);};};for(var j=0;j<mx.length;j++){$D[mx[j]]=$D[mx[j].substring(0,3)]=mf(j);}
var ef=function(j){return function(){if(j.substring(j.length-1)!="s"){j+="s";}
return this["add"+j](this._orient);};};var nf=function(n){return function(){this._dateElement=n;return this;};};for(var k=0;k<px.length;k++){de=px[k].toLowerCase();$D[de]=$D[de+"s"]=ef(px[k]);$N[de]=$N[de+"s"]=nf(de);}}());Date.prototype.toJSONString=function(){return this.toString("yyyy-MM-ddThh:mm:ssZ");};Date.prototype.toShortDateString=function(){return this.toString(Date.CultureInfo.formatPatterns.shortDatePattern);};Date.prototype.toLongDateString=function(){return this.toString(Date.CultureInfo.formatPatterns.longDatePattern);};Date.prototype.toShortTimeString=function(){return this.toString(Date.CultureInfo.formatPatterns.shortTimePattern);};Date.prototype.toLongTimeString=function(){return this.toString(Date.CultureInfo.formatPatterns.longTimePattern);};Date.prototype.getOrdinal=function(){switch(this.getDate()){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th";}};
(function(){Date.Parsing={Exception:function(s){this.message="Parse error at '"+s.substring(0,10)+" ...'";}};var $P=Date.Parsing;var _=$P.Operators={rtoken:function(r){return function(s){var mx=s.match(r);if(mx){return([mx[0],s.substring(mx[0].length)]);}else{throw new $P.Exception(s);}};},token:function(s){return function(s){return _.rtoken(new RegExp("^\s*"+s+"\s*"))(s);};},stoken:function(s){return _.rtoken(new RegExp("^"+s));},until:function(p){return function(s){var qx=[],rx=null;while(s.length){try{rx=p.call(this,s);}catch(e){qx.push(rx[0]);s=rx[1];continue;}
break;}
return[qx,s];};},many:function(p){return function(s){var rx=[],r=null;while(s.length){try{r=p.call(this,s);}catch(e){return[rx,s];}
rx.push(r[0]);s=r[1];}
return[rx,s];};},optional:function(p){return function(s){var r=null;try{r=p.call(this,s);}catch(e){return[null,s];}
return[r[0],r[1]];};},not:function(p){return function(s){try{p.call(this,s);}catch(e){return[null,s];}
throw new $P.Exception(s);};},ignore:function(p){return p?function(s){var r=null;r=p.call(this,s);return[null,r[1]];}:null;},product:function(){var px=arguments[0],qx=Array.prototype.slice.call(arguments,1),rx=[];for(var i=0;i<px.length;i++){rx.push(_.each(px[i],qx));}
return rx;},cache:function(rule){var cache={},r=null;return function(s){try{r=cache[s]=(cache[s]||rule.call(this,s));}catch(e){r=cache[s]=e;}
if(r instanceof $P.Exception){throw r;}else{return r;}};},any:function(){var px=arguments;return function(s){var r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
try{r=(px[i].call(this,s));}catch(e){r=null;}
if(r){return r;}}
throw new $P.Exception(s);};},each:function(){var px=arguments;return function(s){var rx=[],r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
try{r=(px[i].call(this,s));}catch(e){throw new $P.Exception(s);}
rx.push(r[0]);s=r[1];}
return[rx,s];};},all:function(){var px=arguments,_=_;return _.each(_.optional(px));},sequence:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;if(px.length==1){return px[0];}
return function(s){var r=null,q=null;var rx=[];for(var i=0;i<px.length;i++){try{r=px[i].call(this,s);}catch(e){break;}
rx.push(r[0]);try{q=d.call(this,r[1]);}catch(ex){q=null;break;}
s=q[1];}
if(!r){throw new $P.Exception(s);}
if(q){throw new $P.Exception(q[1]);}
if(c){try{r=c.call(this,r[1]);}catch(ey){throw new $P.Exception(r[1]);}}
return[rx,(r?r[1]:s)];};},between:function(d1,p,d2){d2=d2||d1;var _fn=_.each(_.ignore(d1),p,_.ignore(d2));return function(s){var rx=_fn.call(this,s);return[[rx[0][0],r[0][2]],rx[1]];};},list:function(p,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return(p instanceof Array?_.each(_.product(p.slice(0,-1),_.ignore(d)),p.slice(-1),_.ignore(c)):_.each(_.many(_.each(p,_.ignore(d))),px,_.ignore(c)));},set:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return function(s){var r=null,p=null,q=null,rx=null,best=[[],s],last=false;for(var i=0;i<px.length;i++){q=null;p=null;r=null;last=(px.length==1);try{r=px[i].call(this,s);}catch(e){continue;}
rx=[[r[0]],r[1]];if(r[1].length>0&&!last){try{q=d.call(this,r[1]);}catch(ex){last=true;}}else{last=true;}
if(!last&&q[1].length===0){last=true;}
if(!last){var qx=[];for(var j=0;j<px.length;j++){if(i!=j){qx.push(px[j]);}}
p=_.set(qx,d).call(this,q[1]);if(p[0].length>0){rx[0]=rx[0].concat(p[0]);rx[1]=p[1];}}
if(rx[1].length<best[1].length){best=rx;}
if(best[1].length===0){break;}}
if(best[0].length===0){return best;}
if(c){try{q=c.call(this,best[1]);}catch(ey){throw new $P.Exception(best[1]);}
best[1]=q[1];}
return best;};},forward:function(gr,fname){return function(s){return gr[fname].call(this,s);};},replace:function(rule,repl){return function(s){var r=rule.call(this,s);return[repl,r[1]];};},process:function(rule,fn){return function(s){var r=rule.call(this,s);return[fn.call(this,r[0]),r[1]];};},min:function(min,rule){return function(s){var rx=rule.call(this,s);if(rx[0].length<min){throw new $P.Exception(s);}
return rx;};}};var _generator=function(op){return function(){var args=null,rx=[];if(arguments.length>1){args=Array.prototype.slice.call(arguments);}else if(arguments[0]instanceof Array){args=arguments[0];}
if(args){for(var i=0,px=args.shift();i<px.length;i++){args.unshift(px[i]);rx.push(op.apply(null,args));args.shift();return rx;}}else{return op.apply(null,arguments);}};};var gx="optional not ignore cache".split(/\s/);for(var i=0;i<gx.length;i++){_[gx[i]]=_generator(_[gx[i]]);}
var _vector=function(op){return function(){if(arguments[0]instanceof Array){return op.apply(null,arguments[0]);}else{return op.apply(null,arguments);}};};var vx="each any all".split(/\s/);for(var j=0;j<vx.length;j++){_[vx[j]]=_vector(_[vx[j]]);}}());(function(){var flattenAndCompact=function(ax){var rx=[];for(var i=0;i<ax.length;i++){if(ax[i]instanceof Array){rx=rx.concat(flattenAndCompact(ax[i]));}else{if(ax[i]){rx.push(ax[i]);}}}
return rx;};Date.Grammar={};Date.Translator={hour:function(s){return function(){this.hour=Number(s);};},minute:function(s){return function(){this.minute=Number(s);};},second:function(s){return function(){this.second=Number(s);};},meridian:function(s){return function(){this.meridian=s.slice(0,1).toLowerCase();};},timezone:function(s){return function(){var n=s.replace(/[^\d\+\-]/g,"");if(n.length){this.timezoneOffset=Number(n);}else{this.timezone=s.toLowerCase();}};},day:function(x){var s=x[0];return function(){this.day=Number(s.match(/\d+/)[0]);};},month:function(s){return function(){this.month=((s.length==3)?Date.getMonthNumberFromName(s):(Number(s)-1));};},year:function(s){return function(){var n=Number(s);this.year=((s.length>2)?n:(n+(((n+2000)<Date.CultureInfo.twoDigitYearMax)?2000:1900)));};},rday:function(s){return function(){switch(s){case"yesterday":this.days=-1;break;case"tomorrow":this.days=1;break;case"today":this.days=0;break;case"now":this.days=0;this.now=true;break;}};},finishExact:function(x){x=(x instanceof Array)?x:[x];var now=new Date();this.year=now.getFullYear();this.month=now.getMonth();this.day=1;this.hour=0;this.minute=0;this.second=0;for(var i=0;i<x.length;i++){if(x[i]){x[i].call(this);}}
this.hour=(this.meridian=="p"&&this.hour<13)?this.hour+12:this.hour;if(this.day>Date.getDaysInMonth(this.year,this.month)){throw new RangeError(this.day+" is not a valid value for days.");}
var r=new Date(this.year,this.month,this.day,this.hour,this.minute,this.second);if(this.timezone){r.set({timezone:this.timezone});}else if(this.timezoneOffset){r.set({timezoneOffset:this.timezoneOffset});}
return r;},finish:function(x){x=(x instanceof Array)?flattenAndCompact(x):[x];if(x.length===0){return null;}
for(var i=0;i<x.length;i++){if(typeof x[i]=="function"){x[i].call(this);}}
if(this.now){return new Date();}
var today=Date.today();var method=null;var expression=!!(this.days!=null||this.orient||this.operator);if(expression){var gap,mod,orient;orient=((this.orient=="past"||this.operator=="subtract")?-1:1);if(this.weekday){this.unit="day";gap=(Date.getDayNumberFromName(this.weekday)-today.getDay());mod=7;this.days=gap?((gap+(orient*mod))%mod):(orient*mod);}
if(this.month){this.unit="month";gap=(this.month-today.getMonth());mod=12;this.months=gap?((gap+(orient*mod))%mod):(orient*mod);this.month=null;}
if(!this.unit){this.unit="day";}
if(this[this.unit+"s"]==null||this.operator!=null){if(!this.value){this.value=1;}
if(this.unit=="week"){this.unit="day";this.value=this.value*7;}
this[this.unit+"s"]=this.value*orient;}
return today.add(this);}else{if(this.meridian&&this.hour){this.hour=(this.hour<13&&this.meridian=="p")?this.hour+12:this.hour;}
if(this.weekday&&!this.day){this.day=(today.addDays((Date.getDayNumberFromName(this.weekday)-today.getDay()))).getDate();}
if(this.month&&!this.day){this.day=1;}
return today.set(this);}}};var _=Date.Parsing.Operators,g=Date.Grammar,t=Date.Translator,_fn;g.datePartDelimiter=_.rtoken(/^([\s\-\.\,\/\x27]+)/);g.timePartDelimiter=_.stoken(":");g.whiteSpace=_.rtoken(/^\s*/);g.generalDelimiter=_.rtoken(/^(([\s\,]|at|on)+)/);var _C={};g.ctoken=function(keys){var fn=_C[keys];if(!fn){var c=Date.CultureInfo.regexPatterns;var kx=keys.split(/\s+/),px=[];for(var i=0;i<kx.length;i++){px.push(_.replace(_.rtoken(c[kx[i]]),kx[i]));}
fn=_C[keys]=_.any.apply(null,px);}
return fn;};g.ctoken2=function(key){return _.rtoken(Date.CultureInfo.regexPatterns[key]);};g.h=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2]|[1-9])/),t.hour));g.hh=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2])/),t.hour));g.H=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3]|[0-9])/),t.hour));g.HH=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3])/),t.hour));g.m=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.minute));g.mm=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.minute));g.s=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.second));g.ss=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.second));g.hms=_.cache(_.sequence([g.H,g.mm,g.ss],g.timePartDelimiter));g.t=_.cache(_.process(g.ctoken2("shortMeridian"),t.meridian));g.tt=_.cache(_.process(g.ctoken2("longMeridian"),t.meridian));g.z=_.cache(_.process(_.rtoken(/^(\+|\-)?\s*\d\d\d\d?/),t.timezone));g.zz=_.cache(_.process(_.rtoken(/^(\+|\-)\s*\d\d\d\d/),t.timezone));g.zzz=_.cache(_.process(g.ctoken2("timezone"),t.timezone));g.timeSuffix=_.each(_.ignore(g.whiteSpace),_.set([g.tt,g.zzz]));g.time=_.each(_.optional(_.ignore(_.stoken("T"))),g.hms,g.timeSuffix);g.d=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1]|\d)/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.dd=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1])/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.ddd=g.dddd=_.cache(_.process(g.ctoken("sun mon tue wed thu fri sat"),function(s){return function(){this.weekday=s;};}));g.M=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d|\d)/),t.month));g.MM=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d)/),t.month));g.MMM=g.MMMM=_.cache(_.process(g.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"),t.month));g.y=_.cache(_.process(_.rtoken(/^(\d\d?)/),t.year));g.yy=_.cache(_.process(_.rtoken(/^(\d\d)/),t.year));g.yyy=_.cache(_.process(_.rtoken(/^(\d\d?\d?\d?)/),t.year));g.yyyy=_.cache(_.process(_.rtoken(/^(\d\d\d\d)/),t.year));_fn=function(){return _.each(_.any.apply(null,arguments),_.not(g.ctoken2("timeContext")));};g.day=_fn(g.d,g.dd);g.month=_fn(g.M,g.MMM);g.year=_fn(g.yyyy,g.yy);g.orientation=_.process(g.ctoken("past future"),function(s){return function(){this.orient=s;};});g.operator=_.process(g.ctoken("add subtract"),function(s){return function(){this.operator=s;};});g.rday=_.process(g.ctoken("yesterday tomorrow today now"),t.rday);g.unit=_.process(g.ctoken("minute hour day week month year"),function(s){return function(){this.unit=s;};});g.value=_.process(_.rtoken(/^\d\d?(st|nd|rd|th)?/),function(s){return function(){this.value=s.replace(/\D/g,"");};});g.expression=_.set([g.rday,g.operator,g.value,g.unit,g.orientation,g.ddd,g.MMM]);_fn=function(){return _.set(arguments,g.datePartDelimiter);};g.mdy=_fn(g.ddd,g.month,g.day,g.year);g.ymd=_fn(g.ddd,g.year,g.month,g.day);g.dmy=_fn(g.ddd,g.day,g.month,g.year);g.date=function(s){return((g[Date.CultureInfo.dateElementOrder]||g.mdy).call(this,s));};g.format=_.process(_.many(_.any(_.process(_.rtoken(/^(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/),function(fmt){if(g[fmt]){return g[fmt];}else{throw Date.Parsing.Exception(fmt);}}),_.process(_.rtoken(/^[^dMyhHmstz]+/),function(s){return _.ignore(_.stoken(s));}))),function(rules){return _.process(_.each.apply(null,rules),t.finishExact);});var _F={};var _get=function(f){return _F[f]=(_F[f]||g.format(f)[0]);};g.formats=function(fx){if(fx instanceof Array){var rx=[];for(var i=0;i<fx.length;i++){rx.push(_get(fx[i]));}
return _.any.apply(null,rx);}else{return _get(fx);}};g._formats=g.formats(["yyyy-MM-ddTHH:mm:ss","ddd, MMM dd, yyyy H:mm:ss tt","ddd MMM d yyyy HH:mm:ss zzz","d"]);g._start=_.process(_.set([g.date,g.time,g.expression],g.generalDelimiter,g.whiteSpace),t.finish);g.start=function(s){try{var r=g._formats.call({},s);if(r[1].length===0){return r;}}catch(e){}
return g._start.call({},s);};}());Date._parse=Date.parse;Date.parse=function(s){var r=null;if(!s){return null;}
try{r=Date.Grammar.start.call({},s);}catch(e){return null;}
return((r[1].length===0)?r[0]:null);};Date.getParseFunction=function(fx){var fn=Date.Grammar.formats(fx);return function(s){var r=null;try{r=fn.call({},s);}catch(e){return null;}
return((r[1].length===0)?r[0]:null);};};Date.parseExact=function(s,fx){return Date.getParseFunction(fx)(s);};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,150 @@
(function ($) {
"use strict";
/*global window, jQuery */
var init = function (htmlElement) {
var $element = $(htmlElement),
$scrollbar, $drag, $wrapper, $content, mouseOffsetY, updateId,
update, scroll;
if (!$element.css("position") || $element.css("position") === "static") {
$element.css("position", "relative");
}
$scrollbar = $("<div class='scrollbar' />");
$drag = $("<div class='drag' />").appendTo($scrollbar);
$element
.wrapInner("<div class='wrapper'><div class='content' /></div>")
.append($scrollbar);
$wrapper = $element.find("> .wrapper");
$content = $wrapper.find("> .content");
mouseOffsetY = 0;
update = function (repeat) {
var visibleHeight, contentHeight, scrollTop, scrollTopFrac, visVertFrac;
if (updateId && !repeat) {
clearInterval(updateId);
updateId = undefined;
} else if (!updateId && repeat) {
updateId = setInterval(function() { update(true); }, 50);
}
$wrapper.css("height", $element.height());
visibleHeight = $element.height();
contentHeight = $content.outerHeight();
scrollTop = $wrapper.scrollTop();
scrollTopFrac = scrollTop / contentHeight;
visVertFrac = Math.min(visibleHeight / contentHeight, 1);
if (visVertFrac < 1) {
$scrollbar
.fadeIn(50)
.css({
height: $element.innerHeight() + $scrollbar.height() - $scrollbar.outerHeight(true)
});
$drag
.css({
top: $scrollbar.height() * scrollTopFrac,
height: $scrollbar.height() * visVertFrac
});
} else {
$scrollbar.fadeOut(50);
}
};
scroll = function (event) {
var clickFrac = (event.pageY - $scrollbar.offset().top - mouseOffsetY) / $scrollbar.height();
$wrapper.scrollTop($content.outerHeight() * clickFrac);
update();
event.preventDefault();
};
$element
.mousewheel(function (event, delta) {
$wrapper.scrollTop($wrapper.scrollTop() - 50 * delta);
update();
event.stopPropagation();
event.preventDefault();
})
.scroll(update);
$element.get(0).updateScrollbar = update;
$wrapper
.css({
"padding-right": $scrollbar.outerWidth(true),
height: $element.height(),
overflow: "hidden"
});
$scrollbar
.css({
position: "absolute",
top: 0,
right: 0,
overflow: "hidden",
cursor: "pointer"
})
.mousedown(function (event) {
mouseOffsetY = $drag.outerHeight() / 2;
scroll(event);
$scrollbar.addClass("dragOn");
$(window)
.bind("mousemove", scroll)
.one("mouseup", function (event) {
$scrollbar.removeClass("dragOn");
$(window).unbind("mousemove", scroll);
scroll(event);
event.stopPropagation();
});
event.preventDefault();
})
.each(function () {
this.onselectstart = function () {
return false;
};
});
$drag
.css({
position: "absolute",
left: 0,
width: "100%"
})
.mousedown(function (event) {
mouseOffsetY = event.pageY - $drag.offset().top;
scroll(event);
$scrollbar.addClass("dragOn");
$(window)
.bind("mousemove", scroll)
.one("mouseup", function (event) {
$scrollbar.removeClass("dragOn");
$(window).unbind("mousemove", scroll);
scroll(event);
event.stopPropagation();
});
event.stopPropagation();
});
update();
};
$.fn.scrollpanel = function () {
return this.each(function () {
init(this);
});
};
}(jQuery));

View File

@@ -1,465 +0,0 @@
var pathnameSplitRegEx = /^(\/(.*\/)*)([^\/]+\/?)$/;
var pathEndsWithSlashRegEx = /\/$/;
var PathCache = function () {
this.cache = {};
this.objectCache = {};
this.splitPathname = function ( pathname ) {
if ( pathname === "/" ) {
return [ "", "/" ];
};
var match = pathnameSplitRegEx.exec( pathname );
return [ match[1], match[3] ];
};
this.loadCache = function () {
var json = localStorage.getItem( "h5ai.cache" );
var objs = $.evalJSON( json );
var objectCache = {};
for ( idx in objs ) {
var obj = objs[idx];
objectCache[obj.r] = obj;
};
return objectCache;
};
this.storeCache = function () {
var objs = [];
for ( ref in this.objectCache ) {
objs.push( this.objectCache[ref] );
};
var json = $.toJSON( objs );
localStorage.setItem( "h5ai.cache", json );
};
this.pathToObject = function ( path ) {
var object = {
r: path.absHref,
s: path.status,
c: [],
o: path.treeOpen
};
if ( path.content !== undefined ) {
for ( ref in path.content ) {
object.c.push( ref );
};
};
return object;
};
this.objectToPath = function ( obj ) {
var path = this.getPathForFolder( obj.r );
path.status = obj.s;
path.content = {};
path.treeOpen = obj.o;
for ( idx in obj.c ) {
var href = obj.c[idx];
path.content[href] = this.getPathForFolder( href );
};
return path;
};
this.getAbsHref = function ( folder, tableRow ) {
if ( ! pathEndsWithSlashRegEx.test( folder ) ) {
folder += "/";
};
if ( tableRow === undefined ) {
return folder;
};
var $a = $( tableRow ).find( "td" ).eq( 1 ).find( "a" );
var isParentFolder = ( $a.text() === "Parent Directory" );
var href = $a.attr( "href" );
return isParentFolder ? undefined : folder + href;
};
this.getPathForFolder = function ( folder ) {
var absHref = this.getAbsHref( folder );
var cachedPath = this.cache[absHref];
if ( cachedPath !== undefined ) {
return cachedPath;
};
var path = new Path( this, folder );
this.cache[path.absHref] = path;
var obj = this.objectCache[absHref];
if ( obj !== undefined ) {
path.status = obj.s;
path.content = {};
path.treeOpen = obj.o;
for ( idx in obj.c ) {
var href = obj.c[idx];
path.content[href] = this.getPathForFolder( href );
};
} else {
var obj = this.pathToObject( path );
this.objectCache[obj.r] = obj;
this.storeCache();
};
return path;
};
this.getPathForTableRow = function ( parentFolder, tableRow ) {
var absHref = this.getAbsHref( parentFolder, tableRow );
var cachedPath = this.cache[absHref];
if ( cachedPath !== undefined ) {
return cachedPath;
};
var path = new Path( this, parentFolder, tableRow );
if ( ! path.isParentFolder ) {
this.cache[path.absHref] = path;
var obj = this.objectCache[absHref];
if ( obj !== undefined ) {
path.status = obj.s;
path.content = {};
path.treeOpen = obj.o;
for ( idx in obj.c ) {
var href = obj.c[idx];
path.content[href] = this.getPathForFolder( href );
};
} else {
if ( path.isFolder && path.status !== undefined ) {
var obj = this.pathToObject( path );
this.objectCache[obj.r] = obj;
this.storeCache();
};
};
};
return path;
};
this.objectCache = this.loadCache();
};
var Path = function ( pathCache, folder, tableRow ) {
this.checkedDecodeUri = function ( uri ) {
try {
return decodeURI( uri );
} catch ( err ) {
};
return uri;
}
if ( ! pathEndsWithSlashRegEx.test( folder ) ) {
folder += "/";
};
if ( tableRow !== undefined ) {
var $tds = $( tableRow ).find( "td" );
var $img = $tds.eq( 0 ).find( "img" );
var $a= $tds.eq( 1 ).find( "a" );
this.parentFolder = folder;
this.icon16 = $img.attr( "src" );
this.alt = $img.attr( "alt" );
this.label = $a.text();
this.href = $a.attr("href");
this.date = $tds.eq( 2 ).text();
this.size = $tds.eq( 3 ).text();
} else {
var splits = pathCache.splitPathname( folder );
this.parentFolder = splits[0];
this.href = splits[1];
this.label = this.checkedDecodeUri( splits[1] );
this.icon16 = "/h5ai/icons/16x16/folder.png";
this.alt = "[DIR]";
this.date = "";
this.size = "";
if ( this.label === "/" ) {
this.label = this.checkedDecodeUri( document.domain ) + "/";
};
};
if ( pathEndsWithSlashRegEx.test( this.label ) ) {
this.label = this.label.slice( 0, -1 );
};
this.icon48 = this.icon16.replace( "16x16", "48x48" );
this.isFolder = ( this.alt === "[DIR]" );
this.isParentFolder = ( this.isFolder && this.label === "Parent Directory" );
this.absHref = this.isParentFolder ? this.href : this.parentFolder + this.href;
this.isCurrentFolder = ( this.absHref === document.location.pathname );
this.isDomain = ( this.absHref === "/" );
if ( this.isParentFolder && h5ai.config.setParentFolderLabels ) {
if ( this.isDomain ) {
this.label = this.checkedDecodeUri( document.domain );
} else {
this.label = this.checkedDecodeUri( pathCache.splitPathname( pathCache.splitPathname( this.parentFolder )[0] )[1].slice( 0, -1 ) );
};
};
this.status = undefined; // undefined, "h5ai" or HTTP response code
this.content = undefined; // associative array path.absHref -> path
this.html = {
$crumb: undefined,
$extended: undefined,
$tree: undefined
};
this.treeOpen = false;
this.isEmpty = function() {
return this.content === undefined || $.isEmptyObject( this.content );
};
this.onClick = function ( context ) {
h5ai.triggerPathClick( this, context );
};
this.updateHtml = function () {
this.updateCrumbHtml();
this.updateExtendedHtml();
this.updateTreeHtml();
};
this.updateCrumbHtml = function () {
if ( this.html.$crumb !== undefined && this.html.$crumb.data( "status" ) === this.status ) {
return this.html.$crumb;
};
var $html = $( "<li class='crumb' />" ).data( "path", this );
if ( this.status !== undefined ) {
$html.data( "status", this.status );
};
try {
$html.addClass( this.isFolder ? "folder" : "file" );
var $a = $( "<a><img src='/h5ai/images/crumb.png' alt='>' />" + this.label + "</a>" );
$a.attr( "href", this.absHref );
$a.click( $.proxy( function() { this.onClick( "crumb" ); }, this ) );
$html.append( $a );
if ( this.isDomain ) {
$html.addClass( "domain" );
$a.find( "img" ).attr( "src", "/h5ai/images/home.png" );
};
if ( this.isCurrentFolder ) {
$html.addClass( "current" );
};
if ( !isNaN( this.status ) ) {
if ( this.status === 200 ) {
$( "<img class='hint' src='/h5ai/images/page.png' alt='not listable' />" ).appendTo( $a );
} else {
$( "<span class='hint'>(" + this.status + ")</span>" ).appendTo( $a );
};
};
} catch( err ) {
$( "<span class='fail'>failed</span>" ).appendTo( $html );
};
if ( this.html.$crumb !== undefined ) {
this.html.$crumb.replaceWith( $html );
};
this.html.$crumb = $html;
return $html;
};
this.updateExtendedHtml = function () {
if ( this.html.$extended !== undefined && this.html.$extended.data( "status" ) === this.status ) {
return this.html.$extended;
};
var $html = $( "<li class='entry' />" ).data( "path", this );
if ( this.status !== undefined ) {
$html.data( "status", this.status );
};
try {
$html.addClass( this.isFolder ? "folder" : "file" );
var $a = $( "<a />" ).appendTo( $html );
$a.attr( "href", this.absHref );
$a.click( $.proxy( function() { this.onClick( "extended" ); }, this ) );
$( "<span class='icon small'><img src='" + this.icon16 + "' alt='" + this.alt + "' /></span>" ).appendTo( $a );
$( "<span class='icon big'><img src='" + this.icon48 + "' alt='" + this.alt + "' /></span>" ).appendTo( $a );
var $label = $( "<span class='label'>" + this.label + "</span>" ).appendTo( $a );
$( "<span class='date'>" + this.date + "</span>" ).appendTo( $a );
$( "<span class='size'>" + this.size + "</span>" ).appendTo( $a );
if ( this.isParentFolder ) {
if ( !h5ai.config.setParentFolderLabels ) {
$label.addClass( "l10n-parentDirectory" );
};
$html.addClass( "parentfolder" );
};
if ( !isNaN( this.status ) ) {
if ( this.status === 200 ) {
$html.addClass( "page" );
$a.find( ".icon.small img" ).attr( "src", "/h5ai/icons/16x16/folder-page.png" );
$a.find( ".icon.big img" ).attr( "src", "/h5ai/icons/48x48/folder-page.png" );
} else {
$html.addClass( "error" );
$label.append( $( "<span class='hint'> " + this.status + " </span>" ) );
};
};
} catch( err ) {
$( "<span class='fail'>failed</span>" ).appendTo( $html );
};
if ( this.html.$extended !== undefined ) {
this.html.$extended.replaceWith( $html );
};
this.html.$extended = $html;
return $html;
};
this.updateTreeHtml = function () {
var $html = $( "<div class='entry' />" ).data( "path", this );
var $blank = $( "<span class='blank' />" ).appendTo( $html );
try {
$html.addClass( this.isFolder ? "folder" : "file" );
var $a = $( "<a />" )
.appendTo( $html )
.append( $( "<span class='icon'><img src='" + this.icon16 + "' /></span>" ) )
.append( $( "<span class='label'>" + this.label + "</span>" ) );
$a.attr( "href", this.absHref );
$a.click( $.proxy( function() { this.onClick( "tree" ); }, this ) );
if ( this.isFolder ) {
// indicator
if ( this.status === undefined || !this.isEmpty() ) {
var $indicator = $( "<span class='indicator'><img src='/h5ai/images/tree.png' /></span>" );
if ( this.status === undefined ) {
$indicator.addClass( "unknown" );
} else if ( this.treeOpen ) {
$indicator.addClass( "open" );
};
$indicator.click( $.proxy( function( event ) {
if ( $indicator.hasClass( "unknown" ) ) {
tree.fetchStatusAndContent( this.absHref, false, $.proxy( function ( status, content ) {
this.status = status;
this.content = content;
this.treeOpen = true;
pathCache.objectCache[this.absHref] = pathCache.pathToObject( this );
pathCache.storeCache();
$( "#tree" ).get( 0 ).updateScrollbar( true );
this.updateTreeHtml( function() {
$( "#tree" ).get( 0 ).updateScrollbar();
} );
}, this ) );
} else if ( $indicator.hasClass( "open" ) ) {
this.treeOpen = false;
pathCache.objectCache[this.absHref] = pathCache.pathToObject( this );
pathCache.storeCache();
$indicator.removeClass( "open" );
$( "#tree" ).get( 0 ).updateScrollbar( true );
$html.find( "> ul.content" ).slideUp( function() {
$( "#tree" ).get( 0 ).updateScrollbar();
} );
} else {
this.treeOpen = true;
pathCache.objectCache[this.absHref] = pathCache.pathToObject( this );
pathCache.storeCache();
$indicator.addClass( "open" );
$( "#tree" ).get( 0 ).updateScrollbar( true );
$html.find( "> ul.content" ).slideDown( function() {
$( "#tree" ).get( 0 ).updateScrollbar();
} );
};
}, this ) );
$html.addClass( "initiatedIndicator" );
$blank.replaceWith( $indicator );
};
// is this the domain?
if ( this.isDomain ) {
$html.addClass( "domain" );
$a.find( ".icon img" ).attr( "src", "/h5ai/icons/16x16/folder-home.png" );
};
// is this the current folder?
if ( this.isCurrentFolder ) {
$html.addClass( "current" );
$a.find( ".icon img" ).attr( "src", "/h5ai/icons/16x16/folder-open.png" );
};
// does it have subfolders?
if ( !this.isEmpty() ) {
var $ul = $( "<ul class='content' />" ).appendTo( $html );
for ( idx in this.content ) {
$( "<li />" ).append( this.content[idx].updateTreeHtml() ).appendTo( $ul );
};
if ( this.status === undefined || !this.treeOpen ) {
$ul.hide();
};
};
// reflect folder status
if ( !isNaN( this.status ) ) {
if ( this.status === 200 ) {
$a.find( ".icon img" ).attr( "src", "/h5ai/icons/16x16/folder-page.png" );
$a.append( $( "<span class='hint'><img src='/h5ai/images/page.png' /></span>" ) );
} else {
$html.addClass( "error" );
$a.append( $( "<span class='hint'>" + this.status + "</span>" ) );
};
};
};
} catch( err ) {
$( "<span class='fail'>failed</span>" ).appendTo( $html );
};
if ( this.html.$tree !== undefined ) {
this.html.$tree.replaceWith( $html );
};
this.html.$tree = $html;
return $html;
};
};

View File

@@ -1,149 +0,0 @@
var Tree = function ( pathCache, h5ai ) {
this.init = function () {
if ( h5ai.config.showTree ) {
this.updatePaths();
this.populateTree();
};
};
this.updatePath = function ( path ) {
if ( path.isFolder && !path.isParentFolder && path.status === undefined ) {
this.fetchStatus( path.absHref, function ( status ) {
if ( status !== "h5ai" ) {
path.status = status;
};
path.updateHtml();
h5ai.linkHoverStates();
} );
};
};
this.updatePaths = function () {
for ( var ref in pathCache.cache ) {
this.updatePath( pathCache.cache[ref] );
};
};
this.populateTree = function () {
this.fetchTree( document.location.pathname, function( path ) {
$( "#tree" )
.append( path.updateTreeHtml() )
.scrollpanel()
.show();
h5ai.shiftTree( false, true );
h5ai.linkHoverStates();
pathCache.storeCache();
setTimeout( function () {
$( "#tree" ).get( 0 ).updateScrollbar();
}, 1 );
} );
};
this.fetchTree = function ( pathname, callback, childPath ) {
this.fetchPath( pathname, $.proxy( function ( path ) {
path.treeOpen = true;
if ( childPath !== undefined ) {
path.content[ childPath.absHref ] = childPath;
};
var parent = pathCache.splitPathname( pathname )[0];
if ( parent === "" ) {
callback( path );
} else {
this.fetchTree( parent, callback, path );
};
}, this ) );
};
this.fetchPath = function ( pathname, callback ) {
this.fetchStatusAndContent( pathname, false, function ( status, content ) {
var path = pathCache.getPathForFolder( pathname );
path.status = status;
path.content = content;
callback( path );
} );
};
var THIS = this;
var contentTypeRegEx = /^text\/html;h5ai=/;
var pathnameStatusCache = {};
this.fetchStatusAndContent = function ( pathname, includeParent, callback ) {
this.fetchStatus( pathname, function ( status ) {
if ( status !== "h5ai" ) {
callback( status, {} );
return;
};
$.ajax( {
url: pathname,
type: "GET",
dataType: "html",
error: function ( xhr ) {
callback( xhr.status, {} ); // since it was checked before this should never happen
},
success: function ( html, status, xhr ) {
if ( !contentTypeRegEx.test( xhr.getResponseHeader( "Content-Type" ) ) ) {
callback( xhr.status, {} ); // since it was checked before this should never happen
return;
};
var content = {};
$( html ).find( "#table td" ).closest( "tr" ).each( function () {
var path = pathCache.getPathForTableRow( pathname, this );
if ( path.isFolder && ( !path.isParentFolder || includeParent ) ) {
content[path.absHref] = path;
THIS.updatePath( path );
};
} );
callback( "h5ai", content );
}
} );
} );
};
this.fetchStatus = function ( pathname, callback ) {
if ( h5ai.config.folderStatus[ pathname ] !== undefined ) {
callback( h5ai.config.folderStatus[ pathname ] );
return;
} else if ( pathnameStatusCache[ pathname ] !== undefined ) {
callback( pathnameStatusCache[ pathname ] );
return;
};
$.ajax( {
url: pathname,
type: "HEAD",
complete: function ( xhr ) {
var status = xhr.status;
if ( status === 200 && contentTypeRegEx.test( xhr.getResponseHeader( "Content-Type" ) ) ) {
status = "h5ai";
};
pathnameStatusCache[ pathname ] = status;
callback( status );
}
} );
};
};

File diff suppressed because one or more lines are too long

8
src/h5ai/js/libs.js Normal file
View File

@@ -0,0 +1,8 @@
// @include "inc/lib/jquery.min.js"
// @include "inc/lib/jquery.mousewheel.min.js"
// @include "inc/lib/jquery.fracs-core.min.js"
// @include "inc/lib/jquery.scrollpanel.js"
// @include "inc/lib/amplify.min.js"
// @include "inc/lib/date.js"

View File

@@ -1,42 +1,37 @@
(function($) {
"use strict";
// @include "inc/jquery.json.min.js"
// @include "inc/jquery.mousewheel.min.js"
// @include "inc/jquery.scrollpanel.js"
// @include "inc/jquery.utils.js"
// @include "inc/path.js"
// @include "inc/extended.js"
// @include "inc/h5ai.js"
// @include "inc/tree.js"
var H5aiJs = {
factory: {},
init: function () {
this.h5ai = new this.factory.H5ai(h5aiOptions, h5aiLangs);
this.pathCache = new this.factory.PathCache();
this.connector = new this.factory.Connector();
this.html = new this.factory.Html();
this.extended = new this.factory.Extended();
/*******************************
* create
*******************************/
var pathCache = new PathCache();
var h5ai = new H5ai( h5aiOptions, h5aiLangs );
var extended = new Extended( pathCache, h5ai );
var tree = new Tree( pathCache, h5ai );
/*******************************
* register public api
*******************************/
this.extended.init();
this.connector.init();
this.h5ai.init();
$.h5ai = {
click: $.proxy( h5ai.pathClick, h5ai )
click: $.proxy(this.h5ai.pathClick, this.h5ai)
};
}
};
/*******************************
* init after dom load
*******************************/
// @include "inc/H5ai.js"
// @include "inc/Path.js"
// @include "inc/PathCache.js"
// @include "inc/Connector.js"
// @include "inc/Html.js"
// @include "inc/Extended.js"
$(function () {
extended.init();
tree.init();
h5ai.init();
H5aiJs.init();
});
} )( jQuery );
}(jQuery));

View File

@@ -1,37 +1,28 @@
(function($) {
"use strict";
// @include "inc/jquery.json.min.js"
// @include "inc/jquery.mousewheel.min.js"
// @include "inc/jquery.scrollpanel.js"
// @include "inc/jquery.utils.js"
// @include "inc/h5ai.js"
var H5aiJs = {
factory: {},
init: function () {
this.h5ai = new this.factory.H5ai(h5aiOptions, h5aiLangs);
/*******************************
* create
*******************************/
var h5ai = new H5ai( h5aiOptions, h5aiLangs );
/*******************************
* register public api
*******************************/
this.h5ai.init();
$("#tree").scrollpanel();
this.h5ai.shiftTree(false, true);
$.h5ai = {
click: $.proxy( h5ai.pathClick, h5ai )
click: $.proxy(this.h5ai.pathClick, this.h5ai)
};
}
};
/*******************************
* init after dom load
*******************************/
// @include "inc/H5ai.js"
$(function () {
h5ai.init();
$( "#tree" ).scrollpanel();
h5ai.shiftTree( false, true );
H5aiJs.init();
});
} )( jQuery );
}(jQuery));

4
src/h5ai/js/modernizr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,12 @@
h5aiOptions = {
/*
* The absolute link to h5ai.
* Don't change this unless you know what you are doing.
*/
"h5aiAbsHref": "/h5ai",
/*
* An array of view modes the user may choose from. Currently there
* are two possible values: "details" and "icons". The first value
@@ -35,6 +41,11 @@ h5aiOptions = {
*/
"showTree": true,
/*
* Slide tree bar into viewport if there is enough space, boolean.
*/
"slideTree": true,
/*
* Associative array of folders and their HTTP status codes to
* avoid HEAD requests to that folders. The key (folder) must start
@@ -45,19 +56,14 @@ h5aiOptions = {
* as a non auto indexed folder, that means a folder containing an
* appropriate default index file.
*/
"folderStatus": {
/*
* for example:
* "/some/folder/": 200
*/
},
"folderStatus": {},
/*
* Localization, for example "en", "de" etc. - see h5aiLangs below for
* possible values. Adjust it to your needs. If lang is not found in
* h5aiLangs it defaults to "en".
*/
"lang": null,
"lang": "en",
/*
* Try to use browser language, falls back to previous specified lang.
@@ -75,23 +81,23 @@ h5aiOptions = {
"linkHoverStates": true,
/*
* Only used in PHP implementation.
* Date format in PHP syntax, for example: "Y-m-d H:i:s"
* http://www.php.net/manual/en/function.date.php
* Date format in detailed view, for example: "yyyy-MM-dd HH:mm:ss"
* Syntax as specified by date.js
* http://code.google.com/p/datejs/wiki/FormatSpecifiers
*/
"dateFormat": "Y-m-d H:i",
"dateFormat": "yyyy-MM-dd HH:mm",
/*
* IMPORTANT: PHP implementation doesn't care about Apache's
* ignores, so you have to specify this here.
*
* Only used in PHP implementation.
* Files/folders that should never be listed. Specified
* by the complete filename or by a regular expression.
* http://www.php.net/manual/en/function.preg-match.php
* Requieres PHP on the server.
* Show thumbnails in icons view.
*/
"ignore": [ "h5ai", "h5ai.header.html", "h5ai.footer.html" ],
"ignoreRE": [ "/^\\./" ]
"showThumbs": false,
/*
* Requieres PHP on the server.
* Enable zipped download of selected entries.
*/
"zippedDownload": false
};
@@ -111,124 +117,8 @@ h5aiLangs = {
"parentDirectory": "Parent Directory",
"empty": "empty",
"folders": "folders",
"files": "files"
},
"de": {
"lang": "deutsch",
"details": "Details",
"icons": "Icons",
"name": "Name",
"lastModified": "Geändert",
"size": "Größe",
"parentDirectory": "Übergeordnetes Verzeichnis",
"empty": "leer",
"folders": "Ordner",
"files": "Dateien"
},
"fr": {
"lang": "français",
"details": "détails",
"icons": "icônes",
"name": "Nom",
"lastModified": "Dernière modification",
"size": "Taille",
"parentDirectory": "Dossier parent",
"empty": "vide",
"folders": "Répertoires",
"files": "Fichiers"
},
"nl": {
"lang": "nederlands",
"details": "details",
"icons": "iconen",
"name": "Naam",
"lastModified": "Laatste wijziging",
"size": "Grootte",
"parentDirectory": "Bovenliggende map",
"empty": "lege",
"folders": "[?folders?]",
"files": "[?files?]"
},
"sv": {
"lang": "svenska",
"details": "detaljerad",
"icons": "ikoner",
"name": "Filnamn",
"lastModified": "Senast ändrad",
"size": "Filstorlek",
"parentDirectory": "Till överordnad mapp",
"empty": "tom",
"folders": "[?folders?]",
"files": "[?files?]"
},
"cs": {
"lang": "čeština",
"details": "podrobnosti",
"icons": "ikony",
"name": "Název",
"lastModified": "Upraveno",
"size": "Velikost",
"parentDirectory": "Nadřazený adresář",
"empty": "prázdný",
"folders": "složek",
"files": "souborů"
},
"sk": {
"lang": "slovenčina",
"details": "podrobnosti",
"icons": "ikony",
"name": "Názov",
"lastModified": "Upravené",
"size": "Velkosť",
"parentDirectory": "Nadriadený priečinok",
"empty": "prázdny",
"folders": "priečinkov",
"files": "súborov"
},
"es": {
"lang": "español",
"details": "Detalles",
"icons": "Íconos",
"name": "Nombre",
"lastModified": "Última modificación",
"size": "Tamaño",
"parentDirectory": "Directorio superior",
"empty": "vacío",
"folders": "[?folders?]",
"files": "[?files?]"
},
"tr": {
"lang": "türkçe",
"details": "detaylar",
"icons": "ikonlar",
"name": "İsim",
"lastModified": "Son Düzenleme",
"size": "Boyut",
"parentDirectory": "Üst Dizin",
"empty": "boş",
"folders": "[?folders?]",
"files": "[?files?]"
},
"pt": {
"lang": "português",
"details": "detalhes",
"icons": "ícones",
"name": "Nome",
"lastModified": "Última modificação",
"size": "Tamanho",
"parentDirectory": "Diretório superior",
"empty": "vazio",
"folders": "pastas",
"files": "arquivos"
"files": "files",
"download": "download"
},
"bg": {
@@ -241,7 +131,78 @@ h5aiLangs = {
"parentDirectory": "Предходна директория",
"empty": "празно",
"folders": "папки",
"files": "файлове"
"files": "файлове",
"download": "download"
},
"cs": {
"lang": "čeština",
"details": "podrobnosti",
"icons": "ikony",
"name": "Název",
"lastModified": "Upraveno",
"size": "Velikost",
"parentDirectory": "Nadřazený adresář",
"empty": "prázdný",
"folders": "složek",
"files": "souborů",
"download": "download"
},
"de": {
"lang": "deutsch",
"details": "Details",
"icons": "Icons",
"name": "Name",
"lastModified": "Geändert",
"size": "Größe",
"parentDirectory": "Übergeordnetes Verzeichnis",
"empty": "leer",
"folders": "Ordner",
"files": "Dateien",
"download": "Download"
},
"es": {
"lang": "español",
"details": "Detalles",
"icons": "Íconos",
"name": "Nombre",
"lastModified": "Última modificación",
"size": "Tamaño",
"parentDirectory": "Directorio superior",
"empty": "vacío",
"folders": "folders",
"files": "files",
"download": "download"
},
"fr": {
"lang": "français",
"details": "détails",
"icons": "icônes",
"name": "Nom",
"lastModified": "Dernière modification",
"size": "Taille",
"parentDirectory": "Dossier parent",
"empty": "vide",
"folders": "Répertoires",
"files": "Fichiers",
"download": "télécharger"
},
"it": {
"lang": "italiano",
"details": "dettagli",
"icons": "icone",
"name": "Nome",
"lastModified": "Ultima modifica",
"size": "Dimensione",
"parentDirectory": "Cartella Superiore",
"empty": "vuota",
"folders": "cartelle",
"files": "file",
"download": "download"
},
"lv": {
@@ -254,6 +215,119 @@ h5aiLangs = {
"parentDirectory": "Vecākdirektorijs",
"empty": "tukšs",
"folders": "mapes",
"files": "faili"
"files": "faili",
"download": "lejupielādēt"
},
"nl": {
"lang": "nederlands",
"details": "details",
"icons": "iconen",
"name": "Naam",
"lastModified": "Laatste wijziging",
"size": "Grootte",
"parentDirectory": "Bovenliggende map",
"empty": "lege",
"folders": "folders",
"files": "files",
"download": "download"
},
"pl": {
"lang": "polski",
"details": "szczegóły",
"icons": "ikony",
"name": "Nazwa",
"lastModified": "Ostatnia modyfikacja",
"size": "Rozmiar",
"parentDirectory": "Katalog nadrzędny",
"empty": "pusty",
"folders": "foldery",
"files": "pliki",
"download": "download"
},
"pt": {
"lang": "português",
"details": "detalhes",
"icons": "ícones",
"name": "Nome",
"lastModified": "Última modificação",
"size": "Tamanho",
"parentDirectory": "Diretório superior",
"empty": "vazio",
"folders": "pastas",
"files": "arquivos",
"download": "download"
},
"ru": {
"lang": "русский",
"details": "детали",
"icons": "иконки",
"name": "Имя",
"lastModified": "Последние изменения",
"size": "Размер",
"parentDirectory": "Главная директория",
"empty": "пусто",
"folders": "папки",
"files": "файлы",
"download": "download"
},
"sk": {
"lang": "slovenčina",
"details": "podrobnosti",
"icons": "ikony",
"name": "Názov",
"lastModified": "Upravené",
"size": "Velkosť",
"parentDirectory": "Nadriadený priečinok",
"empty": "prázdny",
"folders": "priečinkov",
"files": "súborov",
"download": "download"
},
"sv": {
"lang": "svenska",
"details": "detaljerad",
"icons": "ikoner",
"name": "Filnamn",
"lastModified": "Senast ändrad",
"size": "Filstorlek",
"parentDirectory": "Till överordnad mapp",
"empty": "tom",
"folders": "folders",
"files": "files",
"download": "download"
},
"tr": {
"lang": "türkçe",
"details": "detaylar",
"icons": "ikonlar",
"name": "İsim",
"lastModified": "Son Düzenleme",
"size": "Boyut",
"parentDirectory": "Üst Dizin",
"empty": "boş",
"folders": "klasörler",
"files": "dosyalar",
"download": "indir"
},
"zh-cn": {
"lang": "简体中文",
"details": "详情",
"icons": "图标",
"name": "文件名",
"lastModified": "上次修改",
"size": "大小",
"parentDirectory": "上层文件夹",
"empty": "空文件夹",
"folders": "文件夹",
"files": "文件",
"download": "download"
}
};

126
src/h5ai/php/api.php Normal file
View File

@@ -0,0 +1,126 @@
<?php
function fail($code, $msg, $cond = true) {
if ($cond) {
echo "$code: $msg";
exit;
}
}
function checkKeys($keys) {
$values = array();
foreach ($keys as $key) {
fail(1, "parameter '$key' is missing", !array_key_exists($key, $_REQUEST));
$values[] = $_REQUEST[$key];
}
return $values;
}
list($action) = checkKeys(array("action"));
require_once "config.php";
require_once "inc/H5ai.php";
$h5ai = new H5ai();
$options = $h5ai->getOptions();
if ($action === "httpcodes") {
list($hrefs) = checkKeys(array("hrefs"));
function getHttpCodes($h5ai, $hrefs) {
$codes = array();
foreach ($hrefs as $href) {
$href = trim($href);
if (strlen($href) > 0) {
$codes[$href] = $h5ai->getHttpCode($href);
}
}
return $codes;
}
$hrefs = preg_split("/;/", $hrefs);
$codes = getHttpCodes($h5ai, $hrefs);
echo count($codes) === 0 ? "{}" : json_encode($codes);
}
else if ($action === "thumb") {
fail(0, "thumbs are disabled", !$options["showThumbs"]);
list($srcAbsHref, $width, $height, $mode) = checkKeys(array("href", "width", "height", "mode"));
require_once "inc/Thumbnail.php";
require_once "inc/Image.php";
$srcAbsPath = $h5ai->getDocRoot() . rawurldecode($srcAbsHref);
if (!Thumbnail::isUsable()) {
Image::showImage($srcAbsPath);
exit;
}
$thumbnail = new Thumbnail($h5ai, $srcAbsHref, $mode, $width, $height);
$thumbnail->create(1);
if (file_exists($thumbnail->getPath())) {
Image::showImage($thumbnail->getPath());
} else {
$image = new Image();
$image->setSource($srcAbsPath);
$image->thumb($mode, $width, $height);
$image->showDest();
}
}
else if ($action === "tree") {
list($href) = checkKeys(array("href"));
require_once "inc/Tree.php";
$absHref = trim($href);
$absPath = $h5ai->getAbsPath($absHref);
$tree = new TreeEntry($h5ai, $absPath, $absHref);
$tree->loadContent();
echo $tree->contentToHtml();
}
else if ($action === "zip") {
fail(0, "zipped download is disabled", !$options["zippedDownload"]);
list($hrefs) = checkKeys(array("hrefs"));
require_once "inc/ZipIt.php";
$zipit = new ZipIt($h5ai);
$hrefs = explode(":", trim($hrefs));
$zipFile = $zipit->zip($hrefs);
if ($zipFile === false) {
fail(2, "something went wrong while building the zip");
}
header("Content-Disposition: attachment; filename=\"h5ai-selection.zip\"");
header("Content-Type: application/force-download");
header("Content-Length: " . filesize($zipFile));
header("Connection: close");
readfile($zipFile);
}
else {
fail(1, "unsupported 'action' specified");
}
?>

View File

@@ -1,93 +0,0 @@
<?php
##############################################
# taken from here:
# http://www.jongales.com/blog/2009/02/18/simple-file-based-php-cache-class/
# with minor modifications
##############################################
class Cache {
private $dir;
function __construct( $dir ) {
$this->dir = $dir;
}
private function _name( $key ) {
return $this->dir . "/" . sha1( $key );
}
public function get( $key, $expiration = 3600 ) {
if ( !is_dir( $this->dir ) || !is_writable( $this->dir ) ) {
return false;
}
$cache_path = $this->_name( $key );
if ( !@file_exists( $cache_path ) ) {
return false;
}
if ( filemtime( $cache_path ) < ( time() - $expiration ) ) {
$this->clear( $key );
return false;
}
if ( !$fp = @fopen( $cache_path, "rb" ) ) {
return false;
}
flock( $fp, LOCK_SH );
$cache = "";
if ( filesize( $cache_path ) > 0 ) {
$cache = unserialize( fread( $fp, filesize( $cache_path ) ) );
} else {
$cache = null;
}
flock( $fp, LOCK_UN );
fclose( $fp );
return $cache;
}
public function set( $key, $data ) {
if ( !is_dir( $this->dir ) || !is_writable( $this->dir ) ) {
return false;
}
$cache_path = $this->_name( $key );
if ( ! $fp = fopen( $cache_path, "wb" ) ) {
return false;
}
if ( flock( $fp, LOCK_EX ) ) {
fwrite( $fp, serialize( $data ) );
flock( $fp, LOCK_UN );
} else {
return false;
}
fclose( $fp );
@chmod( $cache_path, 0777 );
return true;
}
public function clear( $key ) {
$cache_path = $this->_name( $key );
if ( file_exists( $cache_path ) ) {
unlink( $cache_path );
return true;
}
return false;
}
}
?>

15
src/h5ai/php/config.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
global $H5AI_CONFIG;
$H5AI_CONFIG = array();
$H5AI_CONFIG["DOCUMENT_ROOT"] = getenv("DOCUMENT_ROOT");
$H5AI_CONFIG["H5AI_ROOT"] = getenv("DOCUMENT_ROOT") . "/h5ai";
# Files/folders that should never be listed. Specified
# by the complete filename or by a regular expression.
# http://www.php.net/manual/en/function.preg-match.php
$H5AI_CONFIG["IGNORE"] = array("h5ai", "h5ai.header.html", "h5ai.footer.html");
$H5AI_CONFIG["IGNORE_PATTERNS"] = array("/^\\./");
?>

View File

@@ -1,52 +0,0 @@
<?php
class Crumb {
private $h5ai, $parts;
public function __construct( $h5ai ) {
$this->h5ai = $h5ai;
$this->parts = array();
$href = $h5ai->getAbsHref();
while ( $href !== "/" && $href !== "//" ) {
$this->parts[] = $href;
$href = dirname( $href ) . "/";
}
$this->parts[] = "/";
$this->parts = array_reverse( $this->parts );
}
public function toHtml() {
$html = "";
$idx = 0;
foreach( $this->parts as $href ) {
$idx++;
$classes = "crumb folder" . ( $idx === 1 ? " domain" : "" ) . ( $idx === count( $this->parts ) ? " current" : "" );
$image = "/h5ai/images/" . ( $idx === 1 ? "home.png" : "crumb.png" );
$label = $this->h5ai->getLabel( $href );
$hint = "";
$code = $this->h5ai->getHttpCode( $href );
$classes .= " checkedHttpCode";
if ( $code !== "h5ai" ) {
if ( $code === 200 ) {
$hint = "<img class='hint' src='/h5ai/images/page.png' alt='page' />";
} else {
$hint = "<span class='hint'>(" . $code . ")</span>";
}
}
$html .= "<li class='$classes'>\n";
$html .= "\t<a href='$href'>\n";
$html .= "\t\t<img src='$image' alt='>' />" . $label . $hint . "\n";
$html .= "\t</a>\n";
$html .= "</li>\n";
}
return $html;
}
}
?>

View File

@@ -1,27 +0,0 @@
<?php
class Customize {
private $h5ai;
public function __construct( $h5ai ) {
$this->h5ai = $h5ai;
}
public function getHeader() {
return $this->getContent( $this->h5ai->getAbsPath() . "/" . "h5ai.header.html", "header" );
}
public function getFooter() {
return $this->getContent( $this->h5ai->getAbsPath() . "/" . "h5ai.footer.html", "footer" );
}
private function getContent( $file, $tag ) {
return file_exists( $file ) ? ( "<" . $tag . ">" . file_get_contents( $file ) . "</" . $tag . ">" ) : "";
}
}
?>

View File

@@ -1,205 +0,0 @@
<?php
class Entry {
private $h5ai, $label, $absPath, $absHref, $date, $isFolder, $type, $size;
public function __construct( $h5ai, $absPath, $absHref, $type = null, $label = null ) {
$this->h5ai = $h5ai;
$this->label = $label !== null ? $label : $this->h5ai->getLabel( $absHref );
$this->absPath = $this->h5ai->normalizePath( $absPath, false );
$this->isFolder = is_dir( $this->absPath );
$this->absHref = $this->h5ai->normalizePath( $absHref, $this->isFolder );
$this->date = filemtime( $this->absPath );
if ( $this->isFolder ) {
$this->type = $type !== null ? $type : "folder";
$this->size = "";
} else {
$this->type = $type !== null ? $type : $this->h5ai->getType( $this->absPath );
$this->size = filesize( $this->absPath );
}
}
public function isFolder() {
return $this->isFolder;
}
function compare( $that, $sortOrder ) {
if ( $this->isFolder && !$that->isFolder ) {
return -1;
}
if ( !$this->isFolder && $that->isFolder ) {
return 1;
}
$res = 0;
if ( $sortOrder[ "column" ] === "name" ) {
$res = strcasecmp( $this->absPath, $that->absPath );
} else if ( $sortOrder[ "column" ] === "date" ) {
$res = $this->date - $that->date;
} else if ( $sortOrder[ "column" ] === "size" ) {
$res = $this->size - $that->size;
}
if ( ! $sortOrder[ "ascending" ] ) {
$res = -$res;
}
return $res;
}
public function toHtml( $dateFormat ) {
$classes = "entry " . $this->type;
$img = $this->type;
$hint = "";
$dateLabel = date( $dateFormat, $this->date );
if ( $this->isFolder && $this->type !== "folder-parent" ) {
$code = $this->h5ai->getHttpCode( $this->absHref );
$classes .= " checkedHttpCode";
if ( $code !== "h5ai" ) {
if ( $code === 200 ) {
$img = "folder-page";
} else {
$classes .= " error";
$hint = "<span class='hint'> " . $code . " </span>";
}
}
}
$html = "\t<li class='" . $classes . "'>\n";
$html .= "\t\t<a href='" . $this->absHref . "'>\n";
$html .= "\t\t\t<span class='icon small'><img src='/h5ai/icons/16x16/" . $img . ".png' alt='" . $img . "' /></span>\n";
$html .= "\t\t\t<span class='icon big'><img src='/h5ai/icons/48x48/" . $img . ".png' alt='" . $img . "' /></span>\n";
$html .= "\t\t\t<span class='label'>" . $this->label . $hint . "</span>\n";
$html .= "\t\t\t<span class='date'>" . $dateLabel . "</span>\n";
$html .= "\t\t\t<span class='size'>" . $this->formatSize( $this->size ) . "</span>\n";
$html .= "\t\t</a>\n";
$html .= "\t</li>\n";
return $html;
}
private function formatSize( $size ) {
$units = array( 'B', 'KB', 'MB', 'GB', 'TB' );
for ( $i = 0; $size >= 1024 && $i < 4; $i++ ) {
$size /= 1024;
}
return round( $size, 0 ) . " " . $units[$i];
}
}
class Extended {
private $h5ai, $parent, $content;
public function __construct( $h5ai ) {
$this->h5ai = $h5ai;
$this->parent = null;
$this->content = array();
$this->loadContent();
}
private function loadContent() {
if ( $this->h5ai->getAbsHref() !== "/" ) {
$options = $this->h5ai->getOptions();
$parentPath = dirname( $this->h5ai->getAbsPath() );
$parentHref = dirname( $this->h5ai->getAbsHref() );
$label = $options["setParentFolderLabels"] === true ? $this->h5ai->getLabel( $parentHref ) : "<span class='l10n-parentDirectory'>Parent Directory</span>";
$this->parent = new Entry( $this->h5ai, $parentPath, $parentHref, "folder-parent", $label );
}
$this->content = array();
$files = $this->h5ai->readDir( $this->h5ai->getAbsPath() );
foreach ( $files as $file ) {
$absPath = $this->h5ai->getAbsPath() . "/" . $file;
$absHref = $this->h5ai->getAbsHref() . rawurlencode( $file );
$this->content[$absPath] = new Entry( $this->h5ai, $absPath, $absHref );
}
$this->sort();
}
public function cmpEntries( $p1, $p2 ) {
return $p1->compare( $p2, $this->h5ai->getSortOrder() );
}
public function sort() {
uasort( $this->content, array( $this, "cmpEntries" ) );
}
public function getFolderCount() {
$count = 0;
foreach( $this->content as $entry ) {
if ( $entry->isFolder() ) {
$count++;
}
}
return $count;
}
public function getFileCount() {
$count = 0;
foreach( $this->content as $entry ) {
if ( !$entry->isFolder() ) {
$count++;
}
}
return $count;
}
public function toHtml() {
$html = "<section id='extended' class='" . $this->h5ai->getView() . "-view clearfix'>\n";
$html .= "<ul>\n";
$html .= $this->generateHeaders();
if ( $this->parent !== null ) {
$html .= $this->parent->toHtml( $this->h5ai->getDateFormat() );
}
foreach( $this->content as $entry ) {
$html .= $entry->toHtml( $this->h5ai->getDateFormat() );
}
$html .= "</ul>\n";
if ( count( $this->content ) === 0 ) {
$html .= "<div class='empty l10n-empty'>empty</div>";
}
$html .="</section>";
return $html;
}
public function generateHeaders() {
$asc = "<img src='/h5ai/images/ascending.png' class='sort' alt='ascending' />";
$desc = "<img src='/h5ai/images/descending.png' class='sort' alt='descending' />";
$so = $this->h5ai->getSortOrder();
$order = $so["ascending"] ? $asc : $desc;
$nameHref = "?col=name" . ( $so["column"] === "name" && $so["ascending"] ? "&asc=false" : "&asc=true" );
$dateHref = "?col=date" . ( $so["column"] === "date" && $so["ascending"] ? "&asc=false" : "&asc=true" );
$sizeHref = "?col=size" . ( $so["column"] === "size" && $so["ascending"] ? "&asc=false" : "&asc=true" );
$nameSort = $so["column"] === "name" ? $order : "";
$dateSort = $so["column"] === "date" ? $order : "";
$sizeSort = $so["column"] === "size" ? $order : "";
$html = "\t<li class='header'>\n";
$html .= "\t\t<a class='icon'></a>\n";
$html .= "\t\t<a class='label' href='$nameHref'><span class='l10n-name'>Name</span>$nameSort</a>\n";
$html .= "\t\t<a class='date' href='$dateHref'>$dateSort<span class='l10n-lastModified'>Last modified</span></a>\n";
$html .= "\t\t<a class='size' href='$sizeHref'>$sizeSort<span class='l10n-size'>Size</span></a>\n";
$html .= "\t</li>\n";
return $html;
}
}
?>

View File

@@ -1,265 +0,0 @@
<?php
require_once "cache.php";
class H5ai {
private static $SORT_ORDER = array( "column" => "name", "ascending" => true );
private static $VIEWMODES = array( "details", "icons" );
private $docRoot, $domain, $options, $types, $cache, $absHref, $absPath, $ignore, $ignoreRE, $sortOrder, $dateFormat, $view;
public function __construct() {
$this->docRoot = getenv( "DOCUMENT_ROOT" );
$this->domain = getenv( "HTTP_HOST" );
$this->options = $this->loadOptions( $this->docRoot . "/h5ai/options.js" );
$this->types = $this->loadTypes( $this->docRoot . "/h5ai/types.txt" );
$this->cache = new Cache( $this->docRoot . "/h5ai/cache" );
$this->absHref = $this->normalizePath( preg_replace( '/\\?.*/', '', getenv( "REQUEST_URI" ) ), true );
$this->absPath = $this->normalizePath( $this->docRoot . rawurldecode( $this->absHref ), false );
$this->ignore = $this->options["options"]["ignore"];
$this->ignoreRE = $this->options["options"]["ignoreRE"];
$defaultSortOrder = $this->options["options"]["sortorder"];
$this->sortOrder = array(
"column" => array_key_exists( "col", $_REQUEST ) ? $_REQUEST["col"] : $defaultSortOrder["column"],
"ascending" => array_key_exists( "asc", $_REQUEST ) ? $_REQUEST["asc"] !== "false" : $defaultSortOrder["ascending"]
);
$this->dateFormat = $this->options["options"]["dateFormat"];
$this->view = array_key_exists( "view", $_REQUEST ) ? $_REQUEST["view"] : $this->options["options"]["viewmodes"][0];
if ( !in_array( $this->view, H5ai::$VIEWMODES ) ) {
$this->view = H5ai::$VIEWMODES[0];
}
}
private function loadOptions( $file ) {
$str = file_exists( $file ) ? file_get_contents( $file ) : "";
$str = preg_replace( "/\/\*.*?\*\//s", "", $str );
$optstr = preg_replace( "/^.*h5aiOptions\s*=\s*/s", "", $str );
$optstr = preg_replace( "/;.*/s", "", $optstr );
$options = json_decode( $optstr, true );
$langstr = preg_replace( "/^.*h5aiLangs\s*=\s*/s", "", $str );
$langstr = preg_replace( "/;.*/s", "", $langstr );
$langs = json_decode( $langstr, true );
return array( "options" => $options, "langs" => $langs );
}
private function loadTypes( $file ) {
$lines = file( $file );
$types = array();
foreach( $lines as $line ) {
if ( substr( $line, 0, 1 ) === '#' ) {
continue;
}
$types[] = preg_split( "/\s+/", $line );
}
return $types;
}
public function getDocRoot() {
return $this->docRoot;
}
public function getDomain() {
return $this->domain;
}
public function getView() {
return $this->view;
}
public function getOptions() {
return $this->options["options"] ;
}
public function getLangs() {
return $this->options["langs"];
}
public function getAbsHref( $absPath = null, $endWithSlash = true ) {
if ( $absPath === null ) {
return $this->absHref;
}
return $this->normalizePath( rawurlencode( preg_replace( "!^" . $this->docRoot . "!", "", $absPath ) ), $endWithSlash );
}
public function getAbsPath( $absHref = null ) {
if ( $absHref === null ) {
return $this->absPath;
}
return $this->normalizePath( $this->docRoot . rawurldecode( $absHref ), false );
}
public function getSortOrder() {
return $this->sortOrder;
}
public function getDateFormat() {
return $this->dateFormat;
}
public function getTitle() {
$title = $this->domain . rawurldecode( $this->absHref );
$title = preg_replace( "/\/$/", "", $title );
$title = preg_replace( "/\//", " > ", $title );
if ( $this->absHref !== "/" ) {
$title = basename( $this->absPath ) . " - " . $title;
}
return $title;
}
public function getType( $absPath ) {
foreach( $this->types as $type ) {
foreach( array_slice( $type, 1 ) as $pattern ) {
if ( $this->endsWith( $absPath, $pattern ) ) {
return $type[0];
}
}
}
return "unknown";
}
public function ignoreThisFile( $file ) {
# always ignore
if ( $file === "." || $file === ".." || $this->startsWith( $file, '.ht' ) ) {
return true;
}
if ( in_array( $file, $this->ignore ) ) {
return true;
}
foreach ( $this->ignoreRE as $re ) {
if ( preg_match( $re, $file ) ) {
return true;
}
}
return false;
}
public function readDir( $path ) {
$content = array();
if ( is_dir( $path ) ) {
if ( $dir = opendir( $path ) ) {
while ( ( $file = readdir( $dir ) ) !== false ) {
if ( ! $this->ignoreThisFile( $file ) ) {
$content[] = $file;
}
}
closedir( $dir );
}
}
return $content;
}
public function getLabel( $absHref ) {
return $absHref === "/" ? $this->domain : rawurldecode( basename( $absHref ) );
}
public function normalizePath( $path, $endWithSlash ) {
return ( $path === "/" ) ? "/" : ( preg_replace( '#/$#', '', $path ) . ( $endWithSlash ? "/" : "" ) );
}
public function startsWith( $sequence, $start ) {
return substr( $sequence, 0, strlen( $start ) ) === $start;
}
public function endsWith( $sequence, $end ) {
return substr( $sequence, -strlen( $end ) ) === $end;
}
public function getHttpCode( $absHref ) {
return $this->cachedHttpCode( $absHref );
#return $this->fetchHttpCode( $absHref );
#return $this->guessHttpCode( $absHref );
}
public function cachedHttpCode( $absHref ) {
$cached = $this->cache->get( $absHref );
if ( $cached === false ) {
$folderStatus = $this->options["options"]["folderStatus"];
if ( array_key_exists( $absHref, $folderStatus ) ) {
$code = $folderStatus[$absHref];
} else {
$code = $this->fetchHttpCode( $absHref );
}
$cached = array( "href" => $absHref, "code" => $code );
$this->cache->set( $absHref, $cached );
}
return $cached["code"];
}
public function fetchHttpCode( $absHref ) {
$contentType = "Content-Type:";
$h5aiContentType = "Content-Type: text/html;h5ai=";
$host = getenv( "HTTP_HOST" );
$port = getenv( "SERVER_PORT" );
$msg = "HEAD $absHref HTTP/1.1\r\nHost: $host\r\nConnection: Close\r\n\r\n";
$errno = "";
$errstr = "";
$socket = fsockopen( $host, $port, $errno, $errstr, 30 );
if( $socket === 0 ) {
return null;
}
fwrite( $socket, $msg );
$content = fgets( $socket );
$code = intval( trim( substr( $content, 9, 4 ) ) );
if ( $code === 200 ) {
while ( ! $this->startsWith( $content, $contentType ) ) {
$content = fgets( $socket );
}
if ( $this->startsWith( $content, $h5aiContentType ) ) {
$code = "h5ai";
}
}
fclose( $socket );
return $code;
}
public function guessHttpCode( $absHref ) {
$indexFiles = array( "index.html", "index.cgi", "index.pl", "index.php", "index.xhtml", "index.htm" );
$absPath = $this->getAbsPath( $absHref );
$files = $this->readDir( $absPath );
foreach ( $files as $file ) {
if ( in_array( $file, $indexFiles ) ) {
return 200;
}
}
return "h5ai";
}
}
?>

View File

@@ -1,23 +0,0 @@
<?php
require_once "h5ai.php";
function getHttpCodes( $h5ai, $hrefs ) {
$codes = array();
foreach ( $hrefs as $href ) {
$href = trim( $href );
if ( strlen( $href ) > 0 ) {
$codes[$href] = $h5ai->getHttpCode( $href );
}
}
return $codes;
}
$h5ai = new H5ai();
$hrefs = preg_split( "/;/", array_key_exists( "hrefs", $_REQUEST ) ? $_REQUEST[ "hrefs" ] : "" );
$codes = getHttpCodes( $h5ai, $hrefs );
echo count( $codes ) === 0 ? "{}" : json_encode( $codes );
?>

View File

@@ -0,0 +1,93 @@
<?php
##############################################
# taken from here:
# http://www.jongales.com/blog/2009/02/18/simple-file-based-php-cache-class/
# with minor modifications
##############################################
class Cache {
private $dir;
function __construct($dir) {
$this->dir = $dir;
}
private function _name($key) {
return $this->dir . "/" . sha1($key);
}
public function get($key, $expiration = 3600) {
if (!is_dir($this->dir) || !is_writable($this->dir)) {
return false;
}
$cache_path = $this->_name($key);
if (!@file_exists($cache_path)) {
return false;
}
if (filemtime($cache_path) < (time() - $expiration)) {
$this->clear($key);
return false;
}
if (!$fp = @fopen($cache_path, "rb")) {
return false;
}
flock($fp, LOCK_SH);
$cache = "";
if (filesize($cache_path) > 0) {
$cache = unserialize(fread($fp, filesize($cache_path)));
} else {
$cache = null;
}
flock($fp, LOCK_UN);
fclose($fp);
return $cache;
}
public function set($key, $data) {
if (!is_dir($this->dir) || !is_writable($this->dir)) {
return false;
}
$cache_path = $this->_name($key);
if (! $fp = fopen($cache_path, "wb")) {
return false;
}
if (flock($fp, LOCK_EX)) {
fwrite($fp, serialize($data));
flock($fp, LOCK_UN);
} else {
return false;
}
fclose($fp);
@chmod($cache_path, 0777);
return true;
}
public function clear($key) {
$cache_path = $this->_name($key);
if (file_exists($cache_path)) {
unlink($cache_path);
return true;
}
return false;
}
}
?>

View File

@@ -0,0 +1,52 @@
<?php
class Crumb {
private $h5ai, $parts;
public function __construct($h5ai) {
$this->h5ai = $h5ai;
$this->parts = array();
$href = $h5ai->getAbsHref();
while ($href !== "/" && $href !== "//") {
$this->parts[] = $href;
$href = dirname($href) . "/";
}
$this->parts[] = "/";
$this->parts = array_reverse($this->parts);
}
public function toHtml() {
$html = "";
$idx = 0;
foreach($this->parts as $href) {
$idx++;
$classes = "crumb folder" . ($idx === 1 ? " domain" : "") . ($idx === count($this->parts) ? " current" : "");
$image = $this->h5ai->image($idx === 1 ? "home" : "crumb");
$label = $this->h5ai->getLabel($href);
$hint = "";
$code = $this->h5ai->getHttpCode($href);
$classes .= " checkedHttpCode";
if ($code !== "h5ai") {
if ($code === 200) {
$hint = "<img class='hint' src='" . $this->h5ai->image("page") . "' alt='page' />";
} else {
$hint = "<span class='hint'>(" . $code . ")</span>";
}
}
$html .= "<li class='$classes'>\n";
$html .= "\t<a href='$href'>\n";
$html .= "\t\t<img src='$image' alt='>' />" . $label . $hint . "\n";
$html .= "\t</a>\n";
$html .= "</li>\n";
}
return $html;
}
}
?>

View File

@@ -0,0 +1,27 @@
<?php
class Customize {
private $h5ai;
public function __construct($h5ai) {
$this->h5ai = $h5ai;
}
public function getHeader() {
return $this->getContent($this->h5ai->getAbsPath() . "/h5ai.header.html", "header");
}
public function getFooter() {
return $this->getContent($this->h5ai->getAbsPath() . "/h5ai.footer.html", "footer");
}
private function getContent($file, $tag) {
return file_exists($file) ? ("<" . $tag . ">" . file_get_contents($file) . "</" . $tag . ">") : "";
}
}
?>

View File

@@ -0,0 +1,222 @@
<?php
require_once "Thumbnail.php";
class Entry {
private $h5ai, $label, $absPath, $absHref, $date, $isFolder, $type, $size, $thumbTypes;
public function __construct($h5ai, $absPath, $absHref, $type = null, $label = null) {
$this->h5ai = $h5ai;
$this->label = $label !== null ? $label : $this->h5ai->getLabel($absHref);
$this->absPath = $this->h5ai->normalizePath($absPath, false);
$this->isFolder = is_dir($this->absPath);
$this->absHref = $this->h5ai->normalizePath($absHref, $this->isFolder);
$this->date = filemtime($this->absPath);
if ($this->isFolder) {
$this->type = $type !== null ? $type : "folder";
$this->size = "";
} else {
$this->type = $type !== null ? $type : $this->h5ai->getType($this->absPath);
$this->size = filesize($this->absPath);
}
$this->thumbTypes = array("bmp", "gif", "ico", "image", "jpg", "png", "tiff");
}
public function isFolder() {
return $this->isFolder;
}
function compare($that, $sortOrder) {
if ($this->isFolder && !$that->isFolder) {
return -1;
}
if (!$this->isFolder && $that->isFolder) {
return 1;
}
$res = 0;
if ($sortOrder[ "column" ] === "name") {
$res = strcasecmp($this->absPath, $that->absPath);
} else if ($sortOrder[ "column" ] === "date") {
$res = $this->date - $that->date;
} else if ($sortOrder[ "column" ] === "size") {
$res = $this->size - $that->size;
}
if (! $sortOrder[ "ascending" ]) {
$res = -$res;
}
return $res;
}
public function toHtml($dateFormat) {
$classes = "entry " . $this->type;
$img = $this->type;
$smallImg = $this->h5ai->icon($this->type);
$bigImg = $this->h5ai->icon($this->type, true);
$hint = "";
if ($this->isFolder && $this->type !== "folder-parent") {
$code = $this->h5ai->getHttpCode($this->absHref);
$classes .= " checkedHttpCode";
if ($code !== "h5ai") {
if ($code === 200) {
$img = "folder-page";
$smallImg = $this->h5ai->icon("folder-page");
$bigImg = $this->h5ai->icon("folder-page", true);
} else {
$classes .= " error";
$hint = "<span class='hint'> " . $code . " </span>";
}
}
}
if ($this->h5ai->showThumbs() && in_array($this->type, $this->thumbTypes)) {
$classes .= " thumb";
$thumbnail = new Thumbnail($this->h5ai, $this->absHref, "square", 16, 16);
$thumbnail->create();
$smallImg = file_exists($thumbnail->getPath()) ? $thumbnail->getHref() : $thumbnail->getLiveHref();
$thumbnail = new Thumbnail($this->h5ai,$this->absHref, "rational", 96, 46);
$thumbnail->create();
$bigImg = file_exists($thumbnail->getPath()) ? $thumbnail->getHref() : $thumbnail->getLiveHref();
}
$html = "\t<li class='" . $classes . "'>\n";
$html .= "\t\t<a href='" . $this->absHref . "'>\n";
$html .= "\t\t\t<span class='icon small'><img src='" . $smallImg . "' alt='" . $img . "' /></span>\n";
$html .= "\t\t\t<span class='icon big'><img src='" . $bigImg . "' alt='" . $img . "' /></span>\n";
$html .= "\t\t\t<span class='label'>" . $this->label . $hint . "</span>\n";
$html .= "\t\t\t<span class='date' data-time='" . $this->date . "000'></span>\n";
$html .= "\t\t\t<span class='size'>" . $this->formatSize($this->size) . "</span>\n";
$html .= "\t\t</a>\n";
$html .= "\t</li>\n";
return $html;
}
private function formatSize($size) {
$units = array('B', 'KB', 'MB', 'GB', 'TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
$size /= 1024;
}
return round($size, 0) . " " . $units[$i];
}
}
class Extended {
private $h5ai, $parent, $content;
public function __construct($h5ai) {
$this->h5ai = $h5ai;
$this->parent = null;
$this->content = array();
$this->loadContent();
}
private function loadContent() {
if ($this->h5ai->getAbsHref() !== "/") {
$options = $this->h5ai->getOptions();
$parentPath = dirname($this->h5ai->getAbsPath());
$parentHref = dirname($this->h5ai->getAbsHref());
$label = $options["setParentFolderLabels"] === true ? $this->h5ai->getLabel($parentHref) : "<span class='l10n-parentDirectory'>Parent Directory</span>";
$this->parent = new Entry($this->h5ai, $parentPath, $parentHref, "folder-parent", $label);
}
$this->content = array();
$files = $this->h5ai->readDir($this->h5ai->getAbsPath());
foreach ($files as $file) {
$absPath = $this->h5ai->getAbsPath() . "/" . $file;
$absHref = $this->h5ai->getAbsHref() . rawurlencode($file);
$this->content[$absPath] = new Entry($this->h5ai, $absPath, $absHref);
}
$this->sort();
}
public function cmpEntries($p1, $p2) {
return $p1->compare($p2, $this->h5ai->getSortOrder());
}
public function sort() {
uasort($this->content, array($this, "cmpEntries"));
}
public function getFolderCount() {
$count = 0;
foreach($this->content as $entry) {
if ($entry->isFolder()) {
$count++;
}
}
return $count;
}
public function getFileCount() {
$count = 0;
foreach($this->content as $entry) {
if (!$entry->isFolder()) {
$count++;
}
}
return $count;
}
public function toHtml() {
$html = "<section id='extended' class='" . $this->h5ai->getView() . "-view clearfix'>\n";
$html .= "<ul>\n";
$html .= $this->generateHeaders();
if ($this->parent !== null) {
$html .= $this->parent->toHtml($this->h5ai->getDateFormat());
}
foreach($this->content as $entry) {
$html .= $entry->toHtml($this->h5ai->getDateFormat());
}
$html .= "</ul>\n";
if (count($this->content) === 0) {
$html .= "<div class='empty l10n-empty'>empty</div>";
}
$html .="</section>";
return $html;
}
public function generateHeaders() {
$asc = "<img src='" . $this->h5ai->image("ascending") . "' class='sort' alt='ascending' />";
$desc = "<img src='" . $this->h5ai->image("descending") . "' class='sort' alt='descending' />";
$so = $this->h5ai->getSortOrder();
$order = $so["ascending"] ? $asc : $desc;
$nameHref = "?col=name" . ($so["column"] === "name" && $so["ascending"] ? "&asc=false" : "&asc=true");
$dateHref = "?col=date" . ($so["column"] === "date" && $so["ascending"] ? "&asc=false" : "&asc=true");
$sizeHref = "?col=size" . ($so["column"] === "size" && $so["ascending"] ? "&asc=false" : "&asc=true");
$nameSort = $so["column"] === "name" ? $order : "";
$dateSort = $so["column"] === "date" ? $order : "";
$sizeSort = $so["column"] === "size" ? $order : "";
$html = "\t<li class='header'>\n";
$html .= "\t\t<a class='icon'></a>\n";
$html .= "\t\t<a class='label' href='$nameHref'><span class='l10n-name'>Name</span>$nameSort</a>\n";
$html .= "\t\t<a class='date' href='$dateHref'>$dateSort<span class='l10n-lastModified'>Last modified</span></a>\n";
$html .= "\t\t<a class='size' href='$sizeHref'>$sizeSort<span class='l10n-size'>Size</span></a>\n";
$html .= "\t</li>\n";
return $html;
}
}
?>

305
src/h5ai/php/inc/H5ai.php Normal file
View File

@@ -0,0 +1,305 @@
<?php
require_once "Cache.php";
class H5ai {
private static $SORT_ORDER = array("column" => "name", "ascending" => true);
private static $VIEWMODES = array("details", "icons");
private $docRoot, $h5aiRoot, $h5aiAbsHref, $domain, $options, $types, $cache, $absHref, $absPath, $ignore, $ignoreRE, $sortOrder, $dateFormat, $view;
public function __construct() {
global $H5AI_CONFIG;
$this->docRoot = $H5AI_CONFIG["DOCUMENT_ROOT"];
$this->h5aiRoot = $H5AI_CONFIG["H5AI_ROOT"];
$this->ignore = $H5AI_CONFIG["IGNORE"];
$this->ignoreRE = $H5AI_CONFIG["IGNORE_PATTERNS"];
$this->options = $this->loadOptions($this->h5aiRoot . "/options.js");
$this->types = $this->loadTypes($this->h5aiRoot . "/types.txt");
$this->cache = new Cache($this->h5aiRoot . "/cache");
$this->h5aiAbsHref = $this->options["options"]["h5aiAbsHref"];
$this->domain = getenv("HTTP_HOST");
$this->absHref = $this->normalizePath(preg_replace('/\\?.*/', '', getenv("REQUEST_URI")), true);
$this->absPath = $this->normalizePath($this->docRoot . rawurldecode($this->absHref), false);
$defaultSortOrder = $this->options["options"]["sortorder"];
$this->sortOrder = array(
"column" => array_key_exists("col", $_REQUEST) ? $_REQUEST["col"] : $defaultSortOrder["column"],
"ascending" => array_key_exists("asc", $_REQUEST) ? $_REQUEST["asc"] !== "false" : $defaultSortOrder["ascending"]
);
$this->view = array_key_exists("view", $_REQUEST) ? $_REQUEST["view"] : $this->options["options"]["viewmodes"][0];
if (!in_array($this->view, H5ai::$VIEWMODES)) {
$this->view = H5ai::$VIEWMODES[0];
}
}
private function loadOptions($file) {
$str = file_exists($file) ? file_get_contents($file) : "";
$str = preg_replace("/\/\*.*?\*\//s", "", $str);
$optstr = preg_replace("/^.*h5aiOptions\s*=\s*/s", "", $str);
$optstr = preg_replace("/;.*/s", "", $optstr);
$options = json_decode($optstr, true);
$langstr = preg_replace("/^.*h5aiLangs\s*=\s*/s", "", $str);
$langstr = preg_replace("/;.*/s", "", $langstr);
$langs = json_decode($langstr, true);
return array("options" => $options, "langs" => $langs);
}
private function loadTypes($file) {
$lines = file($file);
$types = array();
foreach($lines as $line) {
if (substr($line, 0, 1) === '#') {
continue;
}
$types[] = preg_split("/\s+/", $line);
}
return $types;
}
public function getDocRoot() {
return $this->docRoot;
}
public function getH5aiRoot() {
return $this->h5aiRoot;
}
public function getH5aiAbsHref() {
return $this->h5aiAbsHref;
}
public function getDomain() {
return $this->domain;
}
public function getView() {
return $this->view;
}
public function api() {
return $this->h5aiAbsHref . "/php/api.php";
}
public function image($id) {
return $this->h5aiAbsHref . "/images/" . $id . ".png";
}
public function icon($id, $big = false) {
return $this->h5aiAbsHref . "/icons/" . ($big ? "48x48" : "16x16") . "/" . $id . ".png";
}
public function getOptions() {
return $this->options["options"];
}
public function getLangs() {
return $this->options["langs"];
}
public function getAbsHref($absPath = null, $endWithSlash = true) {
if ($absPath === null) {
return $this->absHref;
}
$absHref = preg_replace("!^" . $this->docRoot . "!", "", $absPath);
$parts = explode("/", $absHref);
$encodedParts = array();
foreach ($parts as $part) {
$encodedParts[] = rawurlencode($part);
}
$endodedAbsHref = implode("/", $encodedParts);
return $this->normalizePath($endodedAbsHref, $endWithSlash);
}
public function getAbsPath($absHref = null) {
if ($absHref === null) {
return $this->absPath;
}
return $this->normalizePath($this->docRoot . rawurldecode($absHref), false);
}
public function getSortOrder() {
return $this->sortOrder;
}
public function getDateFormat() {
return $this->dateFormat;
}
public function showThumbs() {
return $this->options["options"]["showThumbs"] === true;
}
public function getTitle() {
$title = $this->domain . rawurldecode($this->absHref);
$title = preg_replace("/\/$/", "", $title);
$title = preg_replace("/\//", " > ", $title);
if ($this->absHref !== "/") {
$title = basename($this->absPath) . " - " . $title;
}
return $title;
}
public function getType($absPath) {
foreach($this->types as $type) {
foreach(array_slice($type, 1) as $pattern) {
if ($this->endsWith($absPath, $pattern)) {
return $type[0];
}
}
}
return "unknown";
}
public function ignoreThisFile($file) {
# always ignore
if ($file === "." || $file === ".." || $this->startsWith($file, '.ht')) {
return true;
}
if (in_array($file, $this->ignore)) {
return true;
}
foreach ($this->ignoreRE as $re) {
if (preg_match($re, $file)) {
return true;
}
}
return false;
}
public function readDir($path) {
$content = array();
if (is_dir($path)) {
if ($dir = opendir($path)) {
while (($file = readdir($dir)) !== false) {
if (!$this->ignoreThisFile($file)) {
$content[] = $file;
}
}
closedir($dir);
}
}
return $content;
}
public function getLabel($absHref) {
return $absHref === "/" ? $this->domain : rawurldecode(basename($absHref));
}
public function normalizePath($path, $endWithSlash) {
return ($path === "/") ? "/" : (preg_replace('#/$#', '', $path) . ($endWithSlash ? "/" : ""));
}
public function startsWith($sequence, $start) {
return strcasecmp(substr($sequence, 0, strlen($start)), $start) === 0;
}
public function endsWith($sequence, $end) {
return strcasecmp(substr($sequence, -strlen($end)), $end) === 0;
}
public function getHttpCode($absHref) {
return $this->cachedHttpCode($absHref);
#return $this->fetchHttpCode($absHref);
#return $this->guessHttpCode($absHref);
}
public function cachedHttpCode($absHref) {
$cached = $this->cache->get($absHref);
if ($cached === false) {
$folderStatus = $this->options["options"]["folderStatus"];
if (array_key_exists($absHref, $folderStatus)) {
$code = $folderStatus[$absHref];
} else {
$code = $this->fetchHttpCode($absHref);
}
$cached = array("href" => $absHref, "code" => $code);
$this->cache->set($absHref, $cached);
}
return $cached["code"];
}
public function fetchHttpCode($absHref) {
$contentType = "Content-Type:";
$h5aiContentType = "Content-Type: text/html;h5ai=";
$host = getenv("HTTP_HOST");
$port = getenv("SERVER_PORT");
$msg = "HEAD $absHref HTTP/1.1\r\nHost: $host\r\nConnection: Close\r\n\r\n";
$errno = "";
$errstr = "";
$socket = fsockopen($host, $port, $errno, $errstr, 30);
if($socket === 0) {
return null;
}
fwrite($socket, $msg);
$content = fgets($socket);
$code = intval(trim(substr($content, 9, 4)));
if ($code === 200) {
while (! $this->startsWith($content, $contentType)) {
$content = fgets($socket);
}
if ($this->startsWith($content, $h5aiContentType)) {
$code = "h5ai";
}
}
fclose($socket);
return $code;
}
public function guessHttpCode($absHref) {
$indexFiles = array("index.html", "index.cgi", "index.pl", "index.php", "index.xhtml", "index.htm");
$absPath = $this->getAbsPath($absHref);
$files = $this->readDir($absPath);
foreach ($files as $file) {
if (in_array($file, $indexFiles)) {
return 200;
}
}
return "h5ai";
}
}
?>

203
src/h5ai/php/inc/Image.php Normal file
View File

@@ -0,0 +1,203 @@
<?php
class Image {
private $sourceFile;
private $source;
private $width;
private $height;
private $type;
private $dest;
public static function isUsable() {
return GD_VERSION != "GD_VERSION";
}
public static function showImage($filename) {
$image = file_get_contents($filename);
header("content-type: image");
echo $image;
}
public function __construct($filename = null) {
$this->sourceFile = null;
$this->source = null;
$this->width = null;
$this->height = null;
$this->type = null;
$this->dest = null;
$this->setSource($filename);
}
public function __destruct() {
$this->releaseSource();
$this->releaseDest();
}
public function setSource($filename) {
$this->releaseSource();
$this->releaseDest();
if (is_null($filename)) {
return;
}
$this->sourceFile = $filename;
list($this->width, $this->height, $this->type) = getimagesize($this->sourceFile);
$this->source = imagecreatefromstring(file_get_contents($this->sourceFile));
}
public function showDest() {
if (!is_null($this->dest)) {
header("Content-type: image/jpeg");
imagejpeg($this->dest, null, 100);
}
}
public function saveDest($filename) {
if (!is_null($this->dest)) {
@imagejpeg($this->dest, $filename, 90);
@chmod($filename, 0775);
}
}
public function releaseDest() {
if (!is_null($this->dest)) {
imagedestroy($this->dest);
$this->dest = null;
}
}
public function releaseSource() {
if (!is_null($this->source)) {
imagedestroy($this->source);
$this->sourceFile = null;
$this->source = null;
$this->width = null;
$this->height = null;
$this->type = null;
}
}
private function magic($destX, $destY, $srcX, $srcY, $destWidth, $destHeight, $srcWidth, $srcHeight, $canWidth = null, $canHeight = null, $color = null) {
if (!is_null($canWidth) && !is_null($canHeight)) {
$this->dest = imagecreatetruecolor($canWidth, $canHeight);
} else {
$this->dest = imagecreatetruecolor($destWidth, $destHeight);
}
if (is_null($color)) {
$color = array(255, 255, 255);
}
$icol = imagecolorallocate($this->dest, $color[0], $color[1], $color[2]);
imagefill($this->dest, 0, 0, $icol);
imagecopyresampled($this->dest, $this->source, $destX, $destY, $srcX, $srcY, $destWidth, $destHeight, $srcWidth, $srcHeight);
}
public function thumb($mode, $width, $height = null, $color = null) {
if ($height === null) {
$height = $width;
}
if ($mode === "square") {
$this->squareThumb($width);
} elseif ($mode === "rational") {
$this->rationalThumb($width, $height);
} elseif ($mode === "center") {
$this->centerThumb($width, $height, $color);
} else {
$this->freeThumb($width, $height);
}
}
public function squareThumb($width) {
$a = min($this->width, $this->height);
$x = intval(($this->width - $a) / 2);
$y = intval(($this->height - $a) / 2);
$this->magic(0, 0, $x, $y, $width, $width, $a, $a);
}
public function rationalThumb($width, $height) {
$r = 1.0 * $this->width / $this->height;
$h = $height;
$w = $r * $h;
if ($w > $width) {
$w = $width;
$h = 1.0 / $r * $w;
}
$w = intval($w);
$h = intval($h);
$this->magic(0, 0, 0, 0, $w, $h, $this->width, $this->height);
}
public function centerThumb($width, $height, $color = null) {
$r = 1.0 * $this->width / $this->height;
$h = $height;
$w = $r * $h;
if ($w > $width) {
$w = $width;
$h = 1.0 / $r * $w;
}
$w = intval($w);
$h = intval($h);
$x = intval(($width - $w) / 2);
$y = intval(($height - $h) / 2);
$this->magic($x, $y, 0, 0, $w, $h, $this->width, $this->height, $width, $height, $color);
}
public function freeThumb($width, $height) {
$w = intval($width);
$h = intval($height);
$this->magic(0, 0, 0, 0, $w, $h, $this->width, $this->height);
}
}
?>

View File

@@ -0,0 +1,59 @@
<?php
require_once "Image.php";
class Thumbnail {
private $srcAbsHref, $srcAbsPath, $width, $height, $name, $href, $path;
public static function isUsable() {
return Image::isUsable();
}
public function __construct($h5ai, $absHref, $mode, $width, $height) {
$this->h5ai = $h5ai;
$this->srcAbsHref = $absHref;
$this->srcAbsPath = $this->h5ai->getDocRoot() . urldecode($absHref);
$this->width = $width;
$this->height = $height;
$this->mode = $mode;
$this->name = sha1("$this->srcAbsPath-$this->width-$this->height-$this->mode");
$this->href = $this->h5ai->getH5aiAbsHref() . "/cache/thumb-" . $this->name . ".jpg";
$this->path = $this->h5ai->getDocRoot() . $this->href;
$this->liveHref = $this->h5ai->api() . "?action=thumb&href=" . $this->srcAbsHref . "&width=" . $this->width . "&height=" . $this->height . "&mode=" . $this->mode;
}
public function create($force = 0) {
if (
$force === 2
|| ($force === 1 && !file_exists($this->path))
|| (file_exists($this->path) && filemtime($this->srcAbsPath) >= filemtime($this->path))
) {
$image = new Image();
$image->setSource($this->srcAbsPath);
$image->thumb($this->mode, $this->width, $this->height);
$image->saveDest($this->path);
}
}
public function getHref() {
return $this->href;
}
public function getPath() {
return $this->path;
}
public function getLiveHref() {
return $this->liveHref;
}
}
?>

147
src/h5ai/php/inc/Tree.php Normal file
View File

@@ -0,0 +1,147 @@
<?php
class TreeEntry {
private $h5ai, $label, $absPath, $absHref, $isFolder, $type, $content;
public function __construct($h5ai, $absPath, $absHref, $type = null) {
$this->h5ai = $h5ai;
$this->label = $this->h5ai->getLabel($absHref);
$this->absPath = $this->h5ai->normalizePath($absPath, false);
$this->isFolder = is_dir($this->absPath);
$this->absHref = $this->h5ai->normalizePath($absHref, $this->isFolder);
$this->type = $type !== null ? $type : ($this->isFolder ? "folder" : $this->h5ai->getType($this->absPath));
$this->content = null;
}
public function loadContent() {
$this->content = array();
if ($this->h5ai->getHttpCode($this->absHref) !== "h5ai") {
return;
}
$files = $this->h5ai->readDir($this->absPath);
foreach ($files as $file) {
$tree = new TreeEntry($this->h5ai, $this->absPath . "/" . $file, $this->absHref . rawurlencode($file));
if ($tree->isFolder) {
$this->content[$tree->absPath] = $tree;
}
}
$this->sort();
}
public function cmpTrees($t1, $t2) {
if ($t1->isFolder && !$t2->isFolder) {
return -1;
}
if (!$t1->isFolder && $t2->isFolder) {
return 1;
}
return strcasecmp($t1->absPath, $t2->absPath);
}
public function sort() {
if ($this->content !== null) {
uasort($this->content, array($this, "cmpTrees"));
}
}
public function toHtml() {
$classes = "entry " . $this->type . ($this->absHref === $this->h5ai->getAbsHref() ? " current" : "");
$icon = $this->type;
if ($this->absHref === "/") {
$icon = "folder-home";
}
$hint = "";
$code = "h5ai";
if ($this->isFolder) {
$code = $this->h5ai->getHttpCode($this->absHref);
$classes .= " checkedHttpCode";
if ($code !== "h5ai") {
if ($code === 200) {
$icon = "folder-page";
$hint = "<span class='hint'><img src='" . $this->h5ai->image("page") . "' alt='page' /></span>";
} else {
$classes .= " error";
$hint = "<span class='hint'> " . $code . " </span>";
}
}
}
$html = "<div class='" . $classes ."'>\n";
if ($this->content !== null && count($this->content) === 0 || $code !== "h5ai") {
$html .= "<span class='blank'></span>\n";
} else {
$indicatorState = $this->content === null ? " unknown" : " open";
$html .= "<span class='indicator" . $indicatorState . "'><img src='" . $this->h5ai->image("tree") . "' alt='>' /></span>\n";
}
$html .= "<a href='" . $this->absHref . "'>\n";
$html .= "<span class='icon'><img src='" . $this->h5ai->icon($icon) . "' alt='" . $icon . "' /></span>\n";
$html .= "<span class='label'>" . $this->label . "</span>" . $hint . "\n";
$html .= "</a>\n";
$html .= $this->contentToHtml();
$html .= "</div>\n";
return $html;
}
public function contentToHtml() {
$html = "<ul class='content'>\n";
if ($this->content !== null) {
foreach($this->content as $tree) {
$html .= "<li>" . $tree->toHtml() . "</li>";
}
}
$html .= "</ul>\n";
return $html;
}
public function getRoot() {
if ($this->absHref === "/") {
return $this;
};
$tree = new TreeEntry($this->h5ai, dirname($this->absPath), dirname($this->absHref));
$tree->loadContent();
$tree->content[ $this->absPath ] = $this;
return $tree->getRoot();
}
}
class Tree {
private $h5ai;
public function __construct($h5ai) {
$this->h5ai = $h5ai;
}
public function toHtml() {
$options = $this->h5ai->getOptions();
if ($options["showTree"] === false) {
return "";
}
$tree = new TreeEntry($this->h5ai, $this->h5ai->getAbsPath(), $this->h5ai->getAbsHref());
$tree->loadContent();
$root = $tree->getRoot();
return "<section id='tree'>\n" . $root->toHtml() . "</section>\n";
}
}
?>

View File

@@ -0,0 +1,63 @@
<?php
class ZipIt {
private $h5ai;
public function __construct($h5ai) {
$this->h5ai = $h5ai;
}
public function zip($hrefs) {
$zipFile = tempnam("/tmp", "h5ai-download");
$zip = new ZipArchive();
if (!$zip->open($zipFile, ZIPARCHIVE::CREATE)) {
return false;
}
foreach ($hrefs as $href) {
$d = dirname($href);
$n = basename($href);
if ($this->h5ai->getHttpCode($this->h5ai->getAbsHref($d)) === "h5ai" && !$this->h5ai->ignoreThisFile($n)) {
$localFile = $this->h5ai->getAbsPath($href);
$file = preg_replace("!^" . $this->h5ai->getDocRoot() . "!", "", $localFile);
if (is_dir($localFile)) {
$this->zipDir($zip, $localFile, $file);
} else {
$this->zipFile($zip, $localFile, $file);
}
}
}
$zip->close();
return $zipFile;
}
private function zipFile($zip, $localFile, $file) {
if (is_readable($localFile)) {
$zip->addFile($localFile, $file);
}
}
private function zipDir($zip, $localDir, $dir) {
if ($this->h5ai->getHttpCode($this->h5ai->getAbsHref($localDir)) === "h5ai") {
$zip->addEmptyDir($dir);
$files = $this->h5ai->readDir($localDir);
foreach ($files as $file) {
$localFile = $localDir . "/" . $file;
$file = $dir . "/" . $file;
if (is_dir($localFile)) {
$this->zipDir($zip, $localFile, $file);
} else {
$this->zipFile($zip, $localFile, $file);
}
}
}
}
}
?>

View File

@@ -1,10 +1,11 @@
<?php
require_once "h5ai.php";
require_once "crumb.php";
require_once "customize.php";
require_once "extended.php";
require_once "tree.php";
require_once "config.php";
require_once "inc/H5ai.php";
require_once "inc/Crumb.php";
require_once "inc/Customize.php";
require_once "inc/Extended.php";
require_once "inc/Tree.php";
$h5ai = new H5ai();
$crumb = new Crumb($h5ai);

View File

@@ -1,147 +0,0 @@
<?php
class TreeEntry {
private $h5ai, $label, $absPath, $absHref, $isFolder, $type, $content;
public function __construct( $h5ai, $absPath, $absHref, $type = null ) {
$this->h5ai = $h5ai;
$this->label = $this->h5ai->getLabel( $absHref );
$this->absPath = $this->h5ai->normalizePath( $absPath, false );
$this->isFolder = is_dir( $this->absPath );
$this->absHref = $this->h5ai->normalizePath( $absHref, $this->isFolder );
$this->type = $type !== null ? $type : ( $this->isFolder ? "folder" : $this->h5ai->getType( $this->absPath ) );
$this->content = null;
}
public function loadContent() {
$this->content = array();
if ( $this->h5ai->getHttpCode( $this->absHref ) !== "h5ai" ) {
return;
}
$files = $this->h5ai->readDir( $this->absPath );
foreach ( $files as $file ) {
$tree = new TreeEntry( $this->h5ai, $this->absPath . "/" . $file, $this->absHref . rawurlencode( $file ) );
if ( $tree->isFolder ) {
$this->content[$tree->absPath] = $tree;
}
}
$this->sort();
}
public function cmpTrees( $t1, $t2 ) {
if ( $t1->isFolder && !$t2->isFolder ) {
return -1;
}
if ( !$t1->isFolder && $t2->isFolder ) {
return 1;
}
return strcasecmp( $t1->absPath, $t2->absPath );
}
public function sort() {
if ( $this->content !== null ) {
uasort( $this->content, array( $this, "cmpTrees" ) );
}
}
public function toHtml() {
$classes = "entry " . $this->type . ( $this->absHref === $this->h5ai->getAbsHref() ? " current" : "" );
$img = $this->type;
if ( $this->absHref === "/" ) {
$img = "folder-home";
}
$hint = "";
$code = "h5ai";
if ( $this->isFolder ) {
$code = $this->h5ai->getHttpCode( $this->absHref );
$classes .= " checkedHttpCode";
if ( $code !== "h5ai" ) {
if ( $code === 200 ) {
$img = "folder-page";
$hint = "<span class='hint'><img src='/h5ai/images/page.png' alt='page' /></span>";
} else {
$classes .= " error";
$hint = "<span class='hint'> " . $code . " </span>";
}
}
}
$html = "<div class='" . $classes ."'>\n";
if ( $this->content !== null && count( $this->content ) === 0 || $code !== "h5ai" ) {
$html .= "<span class='blank'></span>\n";
} else {
$indicatorState = $this->content === null ? " unknown" : " open";
$html .= "<span class='indicator" . $indicatorState . "'><img src='/h5ai/images/tree.png' alt='>' /></span>\n";
}
$html .= "<a href='" . $this->absHref . "'>\n";
$html .= "<span class='icon'><img src='/h5ai/icons/16x16/" . $img . ".png' alt='" . $img . "' /></span>\n";
$html .= "<span class='label'>" . $this->label . "</span>" . $hint . "\n";
$html .= "</a>\n";
$html .= $this->contentToHtml();
$html .= "</div>\n";
return $html;
}
public function contentToHtml() {
$html = "<ul class='content'>\n";
if ( $this->content !== null ) {
foreach( $this->content as $tree ) {
$html .= "<li>" . $tree->toHtml() . "</li>";
}
}
$html .= "</ul>\n";
return $html;
}
public function getRoot() {
if ( $this->absHref === "/" ) {
return $this;
};
$tree = new TreeEntry( $this->h5ai, dirname( $this->absPath ), dirname( $this->absHref ) );
$tree->loadContent();
$tree->content[ $this->absPath ] = $this;
return $tree->getRoot();
}
}
class Tree {
private $h5ai;
public function __construct( $h5ai ) {
$this->h5ai = $h5ai;
}
public function toHtml() {
$options = $this->h5ai->getOptions();
if ( $options["showTree"] === false ) {
return "";
}
$tree = new TreeEntry( $this->h5ai, $this->h5ai->getAbsPath(), $this->h5ai->getAbsHref() );
$tree->loadContent();
$root = $tree->getRoot();
return "<section id='tree'>\n" . $root->toHtml() . "</section>\n";
}
}
?>

View File

@@ -1,20 +0,0 @@
<?php
if ( ! array_key_exists( "href", $_REQUEST ) ) {
exit;
}
require_once "h5ai.php";
require_once "tree.php";
$h5ai = new H5ai();
$absHref = trim( $_REQUEST[ "href" ] );
$absPath = $h5ai->getAbsPath( $absHref );
$tree = new TreeEntry( $h5ai, $absPath, $absHref );
$tree->loadContent();
echo $tree->contentToHtml();
?>

View File

@@ -1,5 +1,5 @@
################################
# %BUILD_NAME% %BUILD_VERSION% (js)
# h5ai %BUILD_VERSION% (js)
# customized .htaccess
################################
@@ -27,7 +27,8 @@ IndexIgnore h5ai h5ai.header.html h5ai.footer.html
# table options
################################
# syntax for default sort order is: IndexOrderDefault Ascending|Descending Name|Date|Size
# syntax for default sort order is:
# IndexOrderDefault Ascending|Descending Name|Date|Size
IndexOrderDefault Ascending Name
IndexOptions Type=text/html;h5ai=%BUILD_VERSION%

View File

@@ -1,5 +1,5 @@
################################
# %BUILD_NAME% %BUILD_VERSION% (php)
# h5ai %BUILD_VERSION% (php)
# customized .htaccess
################################

Binary file not shown.

4
tools/wepp Executable file
View File

@@ -0,0 +1,4 @@
#! /usr/bin/env node
// -*- js -*-
require("wepp").processArgs(process.argv);

40
tools/wepp.ant.xml Normal file
View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<project name="wepp.macros" basedir=".">
<macrodef name="wepp">
<attribute name="file" />
<attribute name="toFile" />
<attribute name="args" default="" />
<sequential>
<exec executable="${tool.wepp}">
<arg line="@{args}" />
<arg value="--in" />
<arg value="@{file}" />
<arg value="--out" />
<arg value="@{toFile}" />
</exec>
</sequential>
</macrodef>
<macrodef name="wepp.dir">
<attribute name="dir" />
<attribute name="args" default="" />
<sequential>
<exec executable="${tool.wepp}">
<arg line="@{args}" />
<arg value="--inDir" />
<arg value="@{dir}" />
<arg value="--outDir" />
<arg value="@{dir}" />
</exec>
<delete dir="@{dir}/inc">
<fileset
dir="."
includes="@{dir}/**/*.less"
excludes="@{dir}/inc/**,@{dir}/lib/**"
/>
</delete>
</sequential>
</macrodef>
</project>