Thumbnails and image preview.

This commit is contained in:
Lars Jung 2012-05-11 18:54:52 +02:00
parent ce3ef3a195
commit 4cb9967b39
65 changed files with 1510 additions and 806 deletions

View file

@ -1,15 +1,14 @@
# h5ai
Please don't use files from this repository (`src` folder) for installation.
They need to be preprocessed/compiled to work correctly. You'll find a
precompiled package on the [project page](http://larsjung.de/h5ai).
Please don't use files from the `src` folder for installation.
They need to be preprocessed to work correctly. You'll find a preprocessed
package on the [project page](http://larsjung.de/h5ai).
To report a bug or make a feature request
please create [a new issue](http://github.com/lrsjng/h5ai/issues/new).
* 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).
@ -31,7 +30,18 @@ h5ai is provided under the terms of the [MIT License](http://github.com/lrsjng/h
## Changelog
### v0.19 - *2012-04-??*
### v0.20 - *2012-05-11*
* adds image preview
* adds thumbnails for video and pdf
* adds support for lighttpd, nginx and cherokee and maybe other webservers with PHP
* adds folder size in PHP version via shell `du`
* fixes some localization problems
* updates info page at `/_h5ai/`
* switches to JSHint
### v0.19 - *2012-04-19*
* adds lots of config options
* changes in `config.js` and `h5ai.htaccess`
@ -40,7 +50,7 @@ h5ai is provided under the terms of the [MIT License](http://github.com/lrsjng/h
* removes hash changes since they break logical browser history
* fixes thumbnail size for portrait images in icon view
* fixes problems with file type recognition
* adds an info page at `/_h5ai`
* adds an info page at `/_h5ai/`
* sort order is preserved while browsing
* removes PHP error messages on thumbnail generation
* fixes PHP some problems with packed download
@ -54,7 +64,7 @@ h5ai is provided under the terms of the [MIT License](http://github.com/lrsjng/h
* adds ro translation by [Jakob Cosoroabă](http://github.com/midday)
* adds ja translation by [metasta](http://github.com/metasta)
* adds nb translation by [Sindre Sorhus](http://github.com/sindresorhus)
* adds sr translation by [Goran](http://github.com/vBm)
* adds sr translation by [vBm](http://github.com/vBm)
* adds gr translation by [xhmikosr](http://github.com/xhmikosr)

View file

@ -1,22 +1,15 @@
custom = true
# project
project.name = h5ai
project.version = 0.19
project.version = 0.20
# src
src.dir = src
# build
build.dir = build
release.dir = release
# tools
tool.wepp = wepp
tool.jslint = jslint
tool.jshint = jshint

View file

@ -49,14 +49,18 @@
<zip destfile="${release.dir}/${project.name}-${project.version}.zip" basedir="${build.dir}" />
</target>
<target name="lint" depends="build-prepare">
<wepp file="${build.dir}/_h5ai/js/inc/main.js" tofile="${build.dir}/_h5ai/js/inc/main.js" />
<jslint files="${build.dir}/_h5ai/js/inc/main.js" />
</target>
<target name="hint" depends="build-prepare">
<wepp file="${build.dir}/_h5ai/js/inc/main.js" tofile="${build.dir}/_h5ai/js/inc/main.js" />
<jshint files="${build.dir}/_h5ai/js/inc/main.js" />
<target name="jshint" depends="init">
<apply executable="${tool.jshint}" verbose="true" parallel="true">
<srcfile />
<arg line="--config jshint.json" />
<arg line="--show-non-errors" />
<fileset dir="${src.dir}/_h5ai/js/inc">
<patternset>
<include name="**/*.js" />
<exclude name="lib/**/*" />
</patternset>
</fileset>
</apply>
</target>
@ -107,24 +111,4 @@
</sequential>
</macrodef>
<macrodef name="jslint">
<attribute name="files" />
<sequential>
<echo>JSLint @{files}</echo>
<exec executable="${tool.jslint}" failonerror="false">
<arg line="@{files}" />
</exec>
</sequential>
</macrodef>
<macrodef name="jshint">
<attribute name="files" />
<sequential>
<echo>JSHint @{files}</echo>
<exec executable="${tool.jshint}" failonerror="false">
<arg line="@{files}" />
</exec>
</sequential>
</macrodef>
</project>

28
jshint.json Normal file
View file

@ -0,0 +1,28 @@
{
// Enforcing Options
"bitwise": true,
"curly": true,
"eqeqeq": true,
"forin": true,
"latedef": true,
"newcap": true,
"noempty": true,
"plusplus": true,
"trailing": true,
"undef": true,
// Environments
"browser": true,
// Globals
"predef": [
"amplify",
"Base64",
"H5AI_CONFIG",
"jQuery",
"Modernizr",
"module",
"moment",
"_"
]
}

View file

@ -1,37 +1,43 @@
Options -Indexes
Options -Indexes
AddType text/html .php
<IfModule mod_expires.c>
Header set Cache-Control "public"
ExpiresActive on
# Perhaps better to whitelist expires rules? Perhaps.
ExpiresDefault "access plus 1 month"
###########################################
# if php doesn't get interpreted try to
# uncomment one of the following lines
###########################################
# cache.manifest needs re-requests in FF 3.6 (thx Remy ~Introducing HTML5)
ExpiresByType text/cache-manifest "access plus 0 seconds"
#AddHandler application/x-httpd-php .php
#AddHandler application/x-httpd-php5 .php
#AddHandler application/x-httpd-php52 .php
#AddHandler application/x-httpd-php53 .php
#AddHandler php-script .php
#AddHandler php5-script .php
#AddHandler php52-script .php
#AddHandler php53-script .php
# your document html
ExpiresByType text/html "access plus 0 seconds"
# data
ExpiresByType text/xml "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
# cache images, css and js for 7 days
<IfModule headers_module>
<FilesMatch "\.png$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
<FilesMatch "\.css$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
<FilesMatch "\.js$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
<FilesMatch "thumb-.*\.jpg$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
# media: images, video, audio
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# webfonts
ExpiresByType font/truetype "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType font/woff "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
# css and javascript
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
</IfModule>

View file

@ -3,20 +3,13 @@
# customized .htaccess
################################
# Options +Indexes
# Options +FollowSymLinks
Options +Indexes
Options +FollowSymLinks
HeaderName /_h5ai/apache/h5ai-header.html
ReadmeName /_h5ai/apache/h5ai-footer.html
HeaderName /_h5ai/header.html
# pure JavaScript version
ReadmeName /_h5ai/footer.html
# PHP version
# ReadmeName /_h5ai/footer.php
IndexIgnore _h5ai*
IndexIgnore _h5ai*
IndexOptions Charset=UTF-8
IndexOptions FancyIndexing

View file

@ -13,8 +13,7 @@
<link rel="apple-touch-icon" type="image/png" href="/_h5ai/images/h5ai-48x48.png">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="/_h5ai/css/styles.css">
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
<script src="/_h5ai/js/modernizr-2.5.3.min.js"></script>
</head>
<body id="h5ai-main">
<div id="topbar" class="clearfix">
@ -32,9 +31,12 @@
<span class="right"></span>
<span class="center"></span>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/_h5ai/js/jquery-1.7.2.min.js"><\/script>')</script>
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
<div id="data-apache-autoindex" class="hideOnJs">
<!--
The following code was generated by Apache's autoindex module. It is not valid HTML5, but this
section gets removed from the DOM tree as soon as its information is parsed. The actual page
should render as valid HTML5, even if the produced source is not valid HTML5.
The following code was generated by Apache's autoindex module. It is not valid HTML5, but gets
removed from the DOM tree as soon as possible. The actual page should render as valid HTML5.
-->

View file

@ -1,7 +1,7 @@
# Cache
This directory is used for server side caching. To use caching make this
directory writable for Apache.
directory writable for your webserver.
There is no critical data in here. You can savely remove any content. This
will clear the cache.

View file

@ -77,9 +77,9 @@ var H5AI_CONFIG = {
* Supported formats: "tar", "zip".
*/
"download": {
"enabled": false,
"enabled": true,
"execution": "shell",
"format": "zip"
"format": "tar"
},
/*
@ -91,6 +91,15 @@ var H5AI_CONFIG = {
"enabled": true
},
/*
* Requires PHP on the server.
* Calc the size of folders.
* Depends on du.
*/
"foldersize": {
"enabled": false
},
/*
* Associative array of folders and their HTTP status codes to
* avoid HEAD requests to that folders. The key (folder) must start
@ -127,6 +136,26 @@ var H5AI_CONFIG = {
"enabled": true
},
/*
* Shows the server mode in the bottom left corner.
* display values:
* 0: only show mode
* 1: mode and servername
* 2: mode, servername and -version
*/
"mode": {
"enabled": true,
"display": 2
},
/*
* Show an image preview on click.
*/
"preview-img": {
"enabled": true,
"types": ["bmp", "gif", "ico", "image", "jpg", "png", "tiff"]
},
/*
* Show QRCodes on hovering files.
*/
@ -164,10 +193,15 @@ var H5AI_CONFIG = {
* Requires PHP on the server.
* Show thumbnails for image files. Needs the "/_h5ai/cache" folder to be
* writable for the Apache Server.
* - img thumbnails depend on PHP-GD
* - mov thumbnails depend on ffmpeg
* - doc thumbnails depend on convert
*/
"thumbnails": {
"enabled": true,
"types": ["bmp", "gif", "ico", "image", "jpg", "png", "tiff"],
"enabled": false,
"img": ["bmp", "gif", "ico", "image", "jpg", "png", "tiff"],
"mov": ["video"],
"doc": ["pdf", "ps"],
"delay": 1000
},
@ -240,6 +274,7 @@ var H5AI_CONFIG = {
"playlist": [".m3u", ".m3u8", ".pls"],
"png": [".png"],
"pres": [".odp", ".otp", ".pps", ".ppt", ".pptx"],
"ps": [".ps"],
"psd": [".psd"],
"py": [".py"],
"rar": [".rar"],
@ -413,7 +448,8 @@ var H5AI_CONFIG = {
"folders": "mapes",
"files": "faili",
"download": "lejupielādēt",
"noMatch": "nav sakritības"
"noMatch": "nav sakritības",
"dateFormat": "YYYY-MM-DD HH:mm"
},
"nb": {

View file

@ -22,8 +22,13 @@ $H5AI_CONFIG = array(
* http://www.php.net/manual/en/function.preg-match.php
*/
"IGNORE" => array(),
"IGNORE_PATTERNS" => array("/^\\./", "/^_h5ai/")
"IGNORE_PATTERNS" => array("/^\\./", "/^_h5ai/"),
/*
* Folders that contain one of these files will be considered
* as none h5ai folders.
*/
"INDEX_FILES" => array("index.html", "index.htm", "index.php")
);
?>

View file

@ -1,5 +1,17 @@
#extended.details-view {
#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.view-details {
display: none;
ul {
@ -49,7 +61,7 @@
color: #555;
text-decoration: none;
cursor: pointer;
border-bottom: 1px solid #ddd;
border-bottom: 1px solid #e8e8e8;
&:hover, &.hover {
background-color: #f6f6f6;
@ -83,11 +95,6 @@
display: none;
}
}
&.folder {
.size {
display: none;
}
}
}
.icon, .label, .date, .size {
padding: 6px;
@ -98,12 +105,13 @@
left: 0;
top: -2px;
width: 16px;
text-align: center;
img {
width: 16px;
height: 16px;
max-width: 16px;
max-height: 16px;
&.thumb {
// border: 1px solid #ddd;
.box-shadow(0 0 0 1px #ddd);
}
}
}
@ -148,18 +156,124 @@
}
#selection-rect {
#extended.view-list {
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);
ul {
margin: 0;
padding: 0;
list-style: none;
li {
position: relative;
white-space: nowrap;
clear: both;
&.header {
display: none;
}
&.entry {
a, a:active, a:visited {
display: block;
color: #555;
text-decoration: none;
cursor: pointer;
border-bottom: 1px solid #e8e8e8;
height: 56px;
&:hover, &.hover {
background-color: #f6f6f6;
color: #e80;
}
}
&.selected:not(.selecting) a, &.selecting:not(.selected) a {
border-color: rgba(240,100,0,0.2);
background-color: rgba(240,100,0,0.2);
}
&.error {
a, a:active, a:visited {
color: #aaa;
.label {
.hint {
margin-left: 12px;
font-size: 0.9em;
color: #c55;
}
}
&:hover, &.hover {
opacity: 1;
background-color: #f6f6f6;
color: #e80;
}
}
}
&.folder-parent {
.date, .size {
display: none;
}
}
}
.icon, .label, .date, .size {
padding: 6px;
}
.icon {
display: inline-block;
position: absolute;
left: 0;
top: -2px;
width: 100px;
text-align: center;
img {
max-width: 100px;
max-height: 48px;
&.thumb {
.box-shadow(0 0 0 1px #ddd);
}
}
}
.icon.small {
display: none;
}
.label {
display: block;
margin: 0 270px 0 106px;
overflow: hidden;
white-space: nowrap;
text-align: left;
}
.date {
margin: 0 0 0 106px;
text-align: right;
width: 160px;
white-space: nowrap;
}
.size {
text-align: right;
width: 80px;
white-space: nowrap;
}
}
}
.empty, .no-match {
text-align: center;
margin: 50px 0;
color: #ddd;
font-size: 5em;
font-weight: bold;
}
.no-match {
display: none;
}
}
#extended.icons-view {
#extended.view-icons {
display: none;
padding: 3px;
@ -197,13 +311,14 @@
}
.icon {
display: block;
height: 48px;
margin-bottom: 6px;
img {
max-width: 100px;
height: 48px;
margin-bottom: 8px;
max-height: 48px;
&.thumb {
// border: 1px solid #ddd;
.box-shadow(0 0 0 1px #ddd);
}
}
}

View file

@ -11,6 +11,17 @@ body#h5ai-info {
font-family: 'Miltonian Tattoo';
font-weight: normal;
}
.build-version {
display: block;
// font-size: 0.9em;
// color: #aaa;
}
.build-stamp {
display: block;
margin-top: 0.3em;
font-size: 0.6em;
color: #aaa;
}
h1 {
font-size: 3.6em;
margin: 0.9em 0 0 0;
@ -55,6 +66,13 @@ body#h5ai-info {
color: #a55;
}
}
.test-info {
margin: 4px 0 12px 12px;
font-size: 0.7em;
color: #aaa;
width: 300px;
line-height: 1.2em;
}
}
#bottombar {

View file

@ -0,0 +1,125 @@
#preview-overlay {
display: none;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 100;
background-color: #111;
text-align: center;
}
#preview-content {
position: fixed;
#preview-img {
max-width: 100%;
max-height: 100%;
}
#preview-mov {
max-width: 100%;
max-height: 100%;
}
}
#preview-close {
position: fixed;
width: 100%;
height: 100%;
cursor: pointer;
}
#preview-prev {
position: fixed;
cursor: pointer;
}
#preview-next {
position: fixed;
cursor: pointer;
}
#preview-buttons, #preview-topbuttons {
list-style: none;
list-style-image: none;
margin: 0;
padding: 0;
img {
position: relative;
top: -2px;
width: 16px;
height: 16px;
}
img + span, img + input {
margin-left: 6px;
}
input {
background-color: rgba(255,255,255,0.1);
border: none;
color: #ccc;
}
.bar-label {
display: block;
color: #ccc;
height: 30px;
line-height: 30px;
padding: 0 10px;
opacity: 0.7;
.transition(all 0.2s ease-in-out);
}
.bar-highlight {
background-color: rgba(255,255,255,0.1);
opacity: 1.0;
}
@bar-sep-border: 1px solid rgba(255,255,255,0.05);
.bar-button {
.bar-label;
cursor: pointer;
&:hover, &.hover {
.bar-highlight;
}
}
.bar-left {
float: left;
border-right: @bar-sep-border;
}
.bar-right {
float: right;
border-left: @bar-sep-border;
}
}
#preview-topbar {
position: fixed;
z-index: 5;
width: 100%;
left: 0;
top: 0;
.vert-gradient(rgb(37,37,37), rgb(24,24,24));
border-bottom: 1px solid rgb(27,27,27);
}
#preview-bottombar {
position: fixed;
z-index: 5;
width: 100%;
left: 0;
bottom: 0;
.vert-gradient(rgb(27,27,27), rgb(14,14,14));
border-top: 1px solid rgb(45,45,45);
}

View file

@ -5,7 +5,7 @@
display: none;
}
}
#extended.icons-view {
#extended.view-icons {
padding: 0;
border: none;
margin: 0 -14px;
@ -21,7 +21,7 @@
display: block;
}
}
#extended.details-view {
#extended.view-details {
.header .label, .entry .label {
margin-right: 110px;
}

View file

@ -28,6 +28,8 @@ body {
@import "inc/context";
@import "inc/apache-autoindex-table";
@import "inc/preview";
@import "inc/responsive";
@import "inc/h5ai-info";
@ -44,4 +46,3 @@ html.oldie {
display: none !important;
}
}

View file

@ -1,11 +0,0 @@
<!-- generated code ends here -->
</div>
<div id="data-generic-json" class="hidden">
<?php if (stripos($_SERVER["REQUEST_METHOD"], "HEAD") === false) {
require_once(str_replace("\\", "/", __DIR__) . "/php/inc/H5ai.php");
$h5ai = new H5ai();
echo $h5ai->getGenericJson();
} ?>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

View file

@ -6,29 +6,52 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>h5ai %BUILD_VERSION% info page</title>
<meta name="description" content="h5ai · a beautified Apache index">
<title>h5ai %BUILD_VERSION% server details</title>
<meta name="description" content="h5ai server details">
<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" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Miltonian+Tattoo:regular">
<link rel="stylesheet" href="/_h5ai/css/styles.css">
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
<script src="/_h5ai/js/modernizr-2.5.3.min.js"></script>
</head>
<body id="h5ai-info">
<h1><span class="h5ai">h5ai</span></h1>
version %BUILD_VERSION%
<span class="build-version">version %BUILD_VERSION%</span>
<span class="build-stamp">%BUILD_STAMP%</span>
<h2>server supports</h2>
<ul id="tests">
<li class="test" data-id="php"><span class="test-label">php version</span><span class="test-result">?</span></li>
<li class="test" data-id="cache"><span class="test-label">cache</span><span class="test-result">?</span></li>
<li class="test" data-id="thumbs"><span class="test-label">thumbnails</span><span class="test-result">?</span></li>
<li class="test" data-id="temp"><span class="test-label">temp directory</span><span class="test-result">?</span></li>
<li class="test" data-id="archive"><span class="test-label">php tar and zip</span><span class="test-result">?</span></li>
<li class="test" data-id="tar"><span class="test-label">shell tar</span><span class="test-result">?</span></li>
<li class="test" data-id="zip"><span class="test-label">shell zip</span><span class="test-result">?</span></li>
<li class="test" data-id="php"><span class="test-label">php version</span><span class="test-result">?</span>
<div class="test-info">PHP version >= 5.2.1</div>
</li>
<li class="test" data-id="cache"><span class="test-label">cache</span><span class="test-result">?</span>
<div class="test-info">_h5ai/cache writable for the server</div>
</li>
<li class="test" data-id="thumbs"><span class="test-label">image thumbs</span><span class="test-result">?</span>
<div class="test-info">PHP GD extension with JPEG support available</div>
</li>
<li class="test" data-id="ffmpeg"><span class="test-label">movie thumbs</span><span class="test-result">?</span>
<div class="test-info">ffmpeg executable in a shell</div>
</li>
<li class="test" data-id="convert"><span class="test-label">pdf thumbs</span><span class="test-result">?</span>
<div class="test-info">convert executable in a shell</div>
</li>
<li class="test" data-id="temp"><span class="test-label">temp directory</span><span class="test-result">?</span>
<div class="test-info">temporary directory writable for the server</div>
</li>
<li class="test" data-id="archive"><span class="test-label">php tar and zip</span><span class="test-result">?</span>
<div class="test-info">PHP Phar extension available</div>
</li>
<li class="test" data-id="tar"><span class="test-label">shell tar</span><span class="test-result">?</span>
<div class="test-info">tar executable in a shell</div>
</li>
<li class="test" data-id="zip"><span class="test-label">shell zip</span><span class="test-result">?</span>
<div class="test-info">zip executable in a shell</div>
</li>
<li class="test" data-id="du"><span class="test-label">folder size</span><span class="test-result">?</span>
<div class="test-info">du executable in a shell</div>
</li>
</ul>
<div id="bottombar" class="clearfix">
<span class="left">
@ -39,5 +62,9 @@
<span class="right"></span>
<span class="center"></span>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/_h5ai/js/jquery-1.7.2.min.js"><\/script>')</script>
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
</body>
</html>

View file

@ -1,7 +1,7 @@
module.define('core/entry', [jQuery, 'core/parser', 'model/entry'], function ($, parser, Entry) {
var absHref = document.location.pathname;
var absHref = document.location.pathname.replace(/[^\/]*$/, '');
parser.parse(absHref, $('body'));
$('#data-apache-autoindex').remove();

View file

@ -1,9 +1,17 @@
module.define('core/parser', [jQuery], function ($) {
if ($('#data-apache-autoindex').length) {
return module.require('parser/apache-autoindex');
}
if ($('#data-generic-json').length) {
return module.require('parser/generic-json');
}
return module.require('parser/apache-autoindex');
return {
id: 'none',
parse: function () {
return [];
}
};
});

View file

@ -3,7 +3,9 @@ module.define('core/settings', [H5AI_CONFIG], function (config) {
var defaults = {
rootAbsHref: '/',
h5aiAbsHref: '/_h5ai/'
h5aiAbsHref: '/_h5ai/',
server: 'unknown',
mode: 'unknown'
};
return _.extend({}, defaults, config.options);

View file

@ -2,7 +2,7 @@
module.define('ext/crumb', [jQuery, 'core/settings', 'core/resource', 'core/entry'], function ($, allsettings, resource, entry) {
var defaults = {
enabled: true
enabled: false
},
settings = _.extend({}, defaults, allsettings.crumb),

View file

@ -9,7 +9,7 @@ module.define('ext/download', [jQuery, 'core/settings', 'core/resource', 'core/e
settings = _.extend({}, defaults, allsettings.download),
formats = ['tar', 'zip'],
// formats = ['tar', 'zip'],
downloadBtnTemplate = '<li id="download">' +
'<a href="#">' +

View file

@ -2,7 +2,7 @@
module.define('ext/folderstatus', [jQuery, 'core/settings'], function ($, allsettings) {
var defaults = {
enabled: true,
enabled: false,
folders: {}
},

View file

@ -1,5 +1,5 @@
module.define('ext/l10n', [jQuery, 'core/settings', 'core/langs', 'core/format', 'core/store'], function ($, allsettings, langs, format, store) {
module.define('ext/l10n', [jQuery, 'core/settings', 'core/langs', 'core/format', 'core/store', 'core/event'], function ($, allsettings, langs, format, store, event) {
var defaults = {
enabled: true,
@ -21,13 +21,12 @@ module.define('ext/l10n', [jQuery, 'core/settings', 'core/langs', 'core/format',
localize = function (langs, lang, useBrowserLang) {
var storedLang = store.get(storekey),
browserLang, key;
var storedLang = store.get(storekey);
if (langs[storedLang]) {
lang = storedLang;
} else if (useBrowserLang) {
browserLang = navigator.language || navigator.browserLanguage;
var browserLang = navigator.language || navigator.browserLanguage;
if (browserLang) {
if (langs[browserLang]) {
lang = browserLang;
@ -110,7 +109,11 @@ module.define('ext/l10n', [jQuery, 'core/settings', 'core/langs', 'core/format',
}
initLangSelector(langs);
localize(langs, settings.lang, settings.useBrowserLang);
event.sub('ready', function () {
localize(langs, settings.lang, settings.useBrowserLang);
});
};
init();

View file

@ -0,0 +1,35 @@
module.define('ext/mode', [jQuery, 'core/settings', 'core/parser'], function ($, allsettings, parser) {
var defaults = {
enabled: false,
display: 0
},
settings = _.extend({}, defaults, allsettings.mode),
init = function () {
if (!settings.enabled) {
return;
}
var info = '';
if (parser.mode) {
info += parser.mode;
}
if (settings.display > 0 && parser.server.name) {
info += (info ? ' on ' : '') + parser.server.name;
}
if (settings.display > 1 && parser.server.version) {
info += (info ? '-' : '') + parser.server.version;
}
if (info) {
$('#h5ai-reference').append(' (' + info + ')');
}
};
init();
});

View file

@ -0,0 +1,248 @@
module.define('ext/preview-img', [jQuery, 'core/settings', 'core/resource', 'core/store', 'core/entry'], function ($, allsettings, resource, store, entry) {
var defaults = {
enabled: false,
types: ['bmp', 'gif', 'ico', 'image', 'jpg', 'png', 'tiff']
},
settings = _.extend({}, defaults, allsettings['preview-img']),
template = '<div id="preview-overlay" class="noSelection">' +
'<div id="preview-content">' +
'<img id="preview-img" />' +
'</div>' +
'<div id="preview-close" />' +
'<div id="preview-prev" />' +
'<div id="preview-next" />' +
'<div id="preview-bottombar" class="clearfix">' +
'<ul id="preview-buttons">' +
'<li id="preview-bar-size" class="bar-left bar-label"></li>' +
'<li id="preview-bar-percent" class="bar-left bar-label"></li>' +
'<li id="preview-bar-label" class="bar-left bar-label"></li>' +
'<li id="preview-bar-close" class="bar-right bar-button"><img src="' + resource.image('preview/close') + '" /></li>' +
'<li id="preview-bar-original" class="bar-right"><a class="bar-button" target="_blank"><img src="' + resource.image('preview/image') + '" /></a></li>' +
'<li id="preview-bar-fullscreen" class="bar-right bar-button"><img src="' + resource.image('preview/fullscreen') + '" /></li>' +
'<li id="preview-bar-next" class="bar-right bar-button"><img src="' + resource.image('preview/next') + '" /></li>' +
'<li id="preview-bar-idx" class="bar-right bar-label"></li>' +
'<li id="preview-bar-prev" class="bar-right bar-button"><img src="' + resource.image('preview/prev') + '" /></li>' +
'</ul>' +
'</div>' +
'</div>',
storekey = 'h5ai.preview-img.isFullscreen',
currentEntries = [],
currentIdx = 0,
isFullscreen = store.get(storekey) || false,
adjustSize = function () {
var rect = $(window).fracs('viewport'),
$container = $('#preview-content'),
$img = $('#preview-img'),
margin = isFullscreen ? 0 : 20,
barheight = isFullscreen ? 0 : 31;
$container.css({
width: rect.width - 2 * margin,
height: rect.height - 2 * margin - barheight,
left: margin,
top: margin
});
var lr = ($container.width() - $img.width()) / 2,
tb = ($container.height() - $img.height()) / 2;
$img.css({
margin: '' + tb + 'px ' + lr + 'px'
});
rect = $img.fracs('rect');
if (!rect) {
// console.log('RECT FAILED!');
return;
}
rect = rect.relativeTo($('#preview-overlay').fracs('rect'));
$('#preview-prev').css({
'left': rect.left,
'top': rect.top,
'width': rect.width / 2,
'height': rect.height
});
$('#preview-next').css({
'left': rect.left + rect.width / 2,
'top': rect.top,
'width': rect.width / 2,
'height': rect.height
});
},
preload = function (src, callback) {
var $hidden = $('<div><img/></div>')
.css({
position: 'absolute',
overflow: 'hidden',
width: 0,
height: 0
})
.appendTo('body'),
$img = $hidden.find('img')
.one('load', function () {
var width = $img.width(),
height = $img.height();
$hidden.remove();
callback(width, height);
})
.attr('src', src);
},
showImg = function (entries, idx) {
currentEntries = entries;
currentIdx = (idx + currentEntries.length) % currentEntries.length;
var $container = $('#preview-content'),
$img = $('#preview-img'),
src = currentEntries[currentIdx].absHref,
spinnerTimeout = setTimeout(function () {
$container.spin({
length: 12,
width: 4,
radius: 24,
color: '#ccc',
shadow: true
});
}, 200);
$('#preview-overlay').stop(true, true).fadeIn(200);
$('#preview-bar-idx').text('' + (currentIdx + 1) + ' / ' + currentEntries.length);
preload(src, function (width, height) {
clearTimeout(spinnerTimeout);
$container.spin(false);
$img.attr('src', src).show();
adjustSize();
$('#preview-bar-label').text(currentEntries[currentIdx].label);
$('#preview-bar-percent').text('' + (100 * $img.width() / width).toFixed(0) + '%');
$('#preview-bar-size').text('' + width + 'x' + height);
$('#preview-bar-idx').text('' + (currentIdx + 1) + ' / ' + currentEntries.length);
$('#preview-bar-original').find('a').attr('href', currentEntries[currentIdx].absHref);
});
},
checkEntry = function (entry) {
if (entry.$extended && $.inArray(entry.type, settings.types) >= 0) {
var $a = entry.$extended.find('a');
$a.on('click', function (event) {
event.preventDefault();
var entries = _.filter(_.map($('#extended .entry'), function (entry) {
return $(entry).data('entry');
}), function (entry) {
return _.indexOf(settings.types, entry.type) >= 0;
});
showImg(entries, _.indexOf(entries, entry));
});
}
},
init = function (entry) {
if (!settings.enabled) {
return;
}
$(template).appendTo('body');
_.each(entry.content, checkEntry);
$('#preview-bar-prev, #preview-prev').on('click', function (event) {
// event.stopPropagation();
showImg(currentEntries, currentIdx - 1);
});
$('#preview-prev')
.on('mouseenter', function (event) {
// event.stopPropagation();
$('#preview-bar-prev').addClass('hover');
})
.on('mouseleave', function (event) {
// event.stopPropagation();
$('#preview-bar-prev').removeClass('hover');
});
$('#preview-bar-next, #preview-next').on('click', function (event) {
// event.stopPropagation();
showImg(currentEntries, currentIdx + 1);
});
$('#preview-next')
.on('mouseenter', function (event) {
// event.stopPropagation();
$('#preview-bar-next').addClass('hover');
})
.on('mouseleave', function (event) {
// event.stopPropagation();
$('#preview-bar-next').removeClass('hover');
});
$('#preview-bar-close, #preview-close').on('click', function () {
// event.stopPropagation();
$('#preview-overlay').stop(true, true).fadeOut(200);
});
$('#preview-close')
.on('mouseenter', function (event) {
// event.stopPropagation();
$('#preview-bar-close').addClass('hover');
})
.on('mouseleave', function (event) {
// event.stopPropagation();
$('#preview-bar-close').removeClass('hover');
});
$('#preview-bar-fullscreen').on('click', function (event) {
// event.stopPropagation();
isFullscreen = !isFullscreen;
store.put(storekey, isFullscreen);
$('#preview-bar-fullscreen').find('img').attr('src', isFullscreen ? resource.image('preview/no-fullscreen') : resource.image('preview/fullscreen'));
adjustSize();
});
$('#preview-overlay')
.on('mousedown', function (event) {
event.stopPropagation();
})
.on('mousemove', function (event) {
if (isFullscreen) {
var rect = $('#preview-overlay').fracs('rect');
if (rect.bottom - event.pageY < 64) {
$('#preview-bottombar').fadeIn(200);
} else {
$('#preview-bottombar').fadeOut(400);
}
}
});
$(window).on('resize load', adjustSize);
};
init(entry);
});

View file

@ -57,7 +57,19 @@ module.define('ext/select', [jQuery, 'core/settings', 'core/event'], function ($
$selectionRect
.stop(true, true)
.animate({left: l + w * 0.5 * shrink, top: t + h * 0.5 * shrink, width: w * (1 - shrink), height: h * (1 - shrink), opacity: 0}, 300);
.animate(
{
left: l + w * 0.5 * shrink,
top: t + h * 0.5 * shrink,
width: w * (1 - shrink),
height: h * (1 - shrink),
opacity: 0
},
300,
function () {
$selectionRect.hide();
}
);
},
selectionStart = function (event) {

View file

@ -57,9 +57,16 @@ module.define('ext/statusbar', [jQuery, 'core/settings', 'core/format', 'core/ev
event.sub('entry.mouseenter', function (entry) {
var $span = $('<span/>').append(entry.label).append(sepTemplate).append(format.formatDate(entry.time));
if (entry.isParentFolder) {
return;
}
if (!entry.isFolder()) {
var $span = $('<span/>').append(entry.label);
if (_.isNumber(entry.time)) {
$span.append(sepTemplate).append(format.formatDate(entry.time));
}
if (_.isNumber(entry.size)) {
$span.append(sepTemplate).append(format.formatSize(entry.size));
}

View file

@ -3,7 +3,9 @@ module.define('ext/thumbnails', [jQuery, 'core/settings', 'core/resource', 'core
var defaults = {
enabled: false,
types: ["bmp", "gif", "ico", "image", "jpg", "png", "tiff"],
img: ['bmp', 'gif', 'ico', 'image', 'jpg', 'png', 'tiff'],
mov: ['video'],
doc: ['pdf', 'ps'],
delay: 1000
},
@ -21,25 +23,36 @@ module.define('ext/thumbnails', [jQuery, 'core/settings', 'core/resource', 'core
checkEntry = function (entry) {
if (entry.$extended && $.inArray(entry.type, settings.types) >= 0) {
if (entry.$extended) {
var $imgSmall = entry.$extended.find('.icon.small img');
var $imgBig = entry.$extended.find('.icon.big img');
var type = null;
requestThumb($imgSmall, {
action: 'getthumbsrc',
href: entry.absHref,
width: 16,
height: 16,
mode: 'square'
});
requestThumb($imgBig, {
action: 'getthumbsrc',
href: entry.absHref,
width: 100,
height: 48,
mode: 'rational'
});
if ($.inArray(entry.type, settings.img) >= 0) {
type = 'img';
} else if ($.inArray(entry.type, settings.mov) >= 0) {
type = 'mov';
} else if ($.inArray(entry.type, settings.doc) >= 0) {
type = 'doc';
}
if (type) {
requestThumb(entry.$extended.find('.icon.small img'), {
action: 'getthumbsrc',
type: type,
href: entry.absHref,
mode: 'square',
width: 16,
height: 16
});
requestThumb(entry.$extended.find('.icon.big img'), {
action: 'getthumbsrc',
type: type,
href: entry.absHref,
mode: 'rational',
width: 100,
height: 48
});
}
}
},

View file

@ -1,6 +1,5 @@
module.define('h5ai-main', [jQuery, 'core/event'], function ($, event) {
module.define('h5ai-main', [jQuery, 'core/event', 'core/settings'], function ($, event, settings) {
event.pub('beforeView');
@ -8,8 +7,6 @@ module.define('h5ai-main', [jQuery, 'core/event'], function ($, event) {
module.require('view/viewmode');
module.require('view/spacing');
$('#h5ai-reference').append(module.require('core/parser').id === 'apache-autoindex' ? ' (js)' : ' (php)');
event.pub('beforeExt');
_.each(module.getIds(/^ext\/.+/), function (id) {

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,15 @@
(function (global, name) {
'use strict';
var err = function (message) {
throw name + ' exception: ' + message;
};
if (!_) {
err(name + ' depends on underscore');
}
var self = {},
previous = global[name],
@ -18,11 +27,6 @@
return self;
},
err = function (message) {
throw name + ' exception: ' + message;
},
definitions = {},
modules = {},
@ -180,16 +184,14 @@
return obj;
};
if (!_) {
err(name + ' depends on underscore');
}
self.noConflict = noConflict;
self.log = log;
self.define = define;
self.require = require;
self.getIds = getIds;
self.isDefined = isDefined;
_.extend(self, {
noConflict: noConflict,
log: log,
define: define,
require: require,
getIds: getIds,
isDefined: isDefined
});
global[name] = self;

18
src/_h5ai/js/inc/lib/spin-1.2.5.min.js vendored Normal file
View file

@ -0,0 +1,18 @@
//fgnass.github.com/spin.js#v1.2.5
(function(a,b,c){function g(a,c){var d=b.createElement(a||"div"),e;for(e in c)d[e]=c[e];return d}function h(a){for(var b=1,c=arguments.length;b<c;b++)a.appendChild(arguments[b]);return a}function j(a,b,c,d){var g=["opacity",b,~~(a*100),c,d].join("-"),h=.01+c/d*100,j=Math.max(1-(1-a)/b*(100-h),a),k=f.substring(0,f.indexOf("Animation")).toLowerCase(),l=k&&"-"+k+"-"||"";return e[g]||(i.insertRule("@"+l+"keyframes "+g+"{"+"0%{opacity:"+j+"}"+h+"%{opacity:"+a+"}"+(h+.01)+"%{opacity:1}"+(h+b)%100+"%{opacity:"+a+"}"+"100%{opacity:"+j+"}"+"}",0),e[g]=1),g}function k(a,b){var e=a.style,f,g;if(e[b]!==c)return b;b=b.charAt(0).toUpperCase()+b.slice(1);for(g=0;g<d.length;g++){f=d[g]+b;if(e[f]!==c)return f}}function l(a,b){for(var c in b)a.style[k(a,c)||c]=b[c];return a}function m(a){for(var b=1;b<arguments.length;b++){var d=arguments[b];for(var e in d)a[e]===c&&(a[e]=d[e])}return a}function n(a){var b={x:a.offsetLeft,y:a.offsetTop};while(a=a.offsetParent)b.x+=a.offsetLeft,b.y+=a.offsetTop;return b}var d=["webkit","Moz","ms","O"],e={},f,i=function(){var a=g("style");return h(b.getElementsByTagName("head")[0],a),a.sheet||a.styleSheet}(),o={lines:12,length:7,width:5,radius:10,rotate:0,color:"#000",speed:1,trail:100,opacity:.25,fps:20,zIndex:2e9,className:"spinner",top:"auto",left:"auto"},p=function q(a){if(!this.spin)return new q(a);this.opts=m(a||{},q.defaults,o)};p.defaults={},m(p.prototype,{spin:function(a){this.stop();var b=this,c=b.opts,d=b.el=l(g(0,{className:c.className}),{position:"relative",zIndex:c.zIndex}),e=c.radius+c.length+c.width,h,i;a&&(a.insertBefore(d,a.firstChild||null),i=n(a),h=n(d),l(d,{left:(c.left=="auto"?i.x-h.x+(a.offsetWidth>>1):c.left+e)+"px",top:(c.top=="auto"?i.y-h.y+(a.offsetHeight>>1):c.top+e)+"px"})),d.setAttribute("aria-role","progressbar"),b.lines(d,b.opts);if(!f){var j=0,k=c.fps,m=k/c.speed,o=(1-c.opacity)/(m*c.trail/100),p=m/c.lines;!function q(){j++;for(var a=c.lines;a;a--){var e=Math.max(1-(j+a*p)%m*o,c.opacity);b.opacity(d,c.lines-a,e,c)}b.timeout=b.el&&setTimeout(q,~~(1e3/k))}()}return b},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=c),this},lines:function(a,b){function e(a,d){return l(g(),{position:"absolute",width:b.length+b.width+"px",height:b.width+"px",background:a,boxShadow:d,transformOrigin:"left",transform:"rotate("+~~(360/b.lines*c+b.rotate)+"deg) translate("+b.radius+"px"+",0)",borderRadius:(b.width>>1)+"px"})}var c=0,d;for(;c<b.lines;c++)d=l(g(),{position:"absolute",top:1+~(b.width/2)+"px",transform:b.hwaccel?"translate3d(0,0,0)":"",opacity:b.opacity,animation:f&&j(b.opacity,b.trail,c,b.lines)+" "+1/b.speed+"s linear infinite"}),b.shadow&&h(d,l(e("#000","0 0 4px #000"),{top:"2px"})),h(a,h(d,e(b.color,"0 0 1px rgba(0,0,0,.1)")));return a},opacity:function(a,b,c){b<a.childNodes.length&&(a.childNodes[b].style.opacity=c)}}),!function(){function a(a,b){return g("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',b)}var b=l(g("group"),{behavior:"url(#default#VML)"});!k(b,"transform")&&b.adj?(i.addRule(".spin-vml","behavior:url(#default#VML)"),p.prototype.lines=function(b,c){function f(){return l(a("group",{coordsize:e+" "+e,coordorigin:-d+" "+ -d}),{width:e,height:e})}function k(b,e,g){h(i,h(l(f(),{rotation:360/c.lines*b+"deg",left:~~e}),h(l(a("roundrect",{arcsize:1}),{width:d,height:c.width,left:c.radius,top:-c.width>>1,filter:g}),a("fill",{color:c.color,opacity:c.opacity}),a("stroke",{opacity:0}))))}var d=c.length+c.width,e=2*d,g=-(c.width+c.length)*2+"px",i=l(f(),{position:"absolute",top:g,left:g}),j;if(c.shadow)for(j=1;j<=c.lines;j++)k(j,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(j=1;j<=c.lines;j++)k(j);return h(b,i)},p.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d<e.childNodes.length&&(e=e.childNodes[b+d],e=e&&e.firstChild,e=e&&e.firstChild,e&&(e.opacity=c))}):f=k(b,"animation")}(),a.Spinner=p})(window,document);
$.fn.spin = function(opts) {
this.each(function() {
var $this = $(this),
data = $this.data();
if (data.spinner) {
data.spinner.stop();
delete data.spinner;
}
if (opts !== false) {
data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
}
});
return this;
};

View file

@ -1,8 +1,6 @@
(function ($) {
'use strict';
/*jshint browser: true */
/*global _, amplify, Base64, H5AI_CONFIG, jQuery, Modernizr, module, moment */
// @include "core/entry.js"
// @include "core/event.js"
@ -28,6 +26,8 @@
// @include "ext/folderstatus.js"
// @include "ext/l10n.js"
// @include "ext/link-hover-states.js"
// @include "ext/mode.js"
// @include "ext/preview-img.js"
// @include "ext/qrcode.js"
// @include "ext/select.js"
// @include "ext/sort.js"

View file

@ -2,7 +2,7 @@
module.define('model/entry', [jQuery, 'core/types'], function ($, types) {
var domain = document.domain,
location = document.location.pathname,
location = document.location.pathname.replace(/[^\/]*$/, ''),
// utils
@ -104,10 +104,13 @@ module.define('model/entry', [jQuery, 'core/types'], function ($, types) {
if (split.parent) {
this.parent = cache[split.parent] || new Entry(split.parent);
this.parent.content[this.absHref] = this;
if (_.keys(this.parent.content).length > 1) {
this.parent.isContentFetched = true;
}
}
},
get = function (absHref, time, size, status) {
get = function (absHref, time, size, status, isContentFetched) {
absHref = absHref || location;
@ -122,6 +125,9 @@ module.define('model/entry', [jQuery, 'core/types'], function ($, types) {
if (status) {
self.status = status;
}
if (isContentFetched) {
self.isContentFetched = true;
}
return self;
},
@ -146,8 +152,7 @@ module.define('model/entry', [jQuery, 'core/types'], function ($, types) {
var self = cache[absHref] || new Entry(absHref);
if (self.isContentFetched || _.keys(self.content).length > 1) {
self.isContentFetched = true;
if (self.isContentFetched) {
callback(self);
} else {
fetchStatus(absHref, function (self) {
@ -215,7 +220,7 @@ module.define('model/entry', [jQuery, 'core/types'], function ($, types) {
return entry.isFolder();
}), function (entry) {
return entry.absHref;
return entry.label.toLowerCase();
});
},

View file

@ -1,5 +1,5 @@
module.define('parser/apache-autoindex', [jQuery, 'core/format', 'model/entry'], function ($, format, Entry) {
module.define('parser/apache-autoindex', [jQuery, 'core/settings', 'core/format', 'model/entry'], function ($, settings, format, Entry) {
var parseTableRow = function (absHref, tr) {
@ -37,6 +37,11 @@ module.define('parser/apache-autoindex', [jQuery, 'core/format', 'model/entry'],
return {
id: 'apache-autoindex',
mode: 'aai',
server: {
name: 'apache',
version: null
},
parse: parse
};
});

View file

@ -1,14 +1,16 @@
module.define('parser/generic-json', [jQuery, 'core/settings', 'model/entry'], function ($, settings, Entry) {
// expectes an hash of the form
// {
// entries: [
// {absHref: String, time: Number, size: Number, status: Number or "h5ai"}
// ]
// }
var parser = {
id: 'generic-json',
mode: null,
server: {
name: null,
version: null
}
},
var parseJson = function (absHref, json) {
parseJson = function (absHref, json) {
if (json.hasOwnProperty('customHeader')) {
settings.custom.header = json.customHeader;
@ -16,10 +18,16 @@ module.define('parser/generic-json', [jQuery, 'core/settings', 'model/entry'], f
if (json.hasOwnProperty('customFooter')) {
settings.custom.footer = json.customFooter;
}
if (json.hasOwnProperty('mode')) {
parser.mode = json.mode;
}
if (json.hasOwnProperty('server')) {
parser.server = json.server;
}
return _.map(json.entries, function (jsonEntry) {
return Entry.get(jsonEntry.absHref, jsonEntry.time, jsonEntry.size, jsonEntry.status);
return Entry.get(jsonEntry.absHref, jsonEntry.time, jsonEntry.size, jsonEntry.status, jsonEntry.content);
});
},
@ -41,8 +49,7 @@ module.define('parser/generic-json', [jQuery, 'core/settings', 'model/entry'], f
return parseJsonStr(absHref, $id.text());
};
return {
id: 'generic-json',
parse: parse
};
parser.parse = parse;
return parser;
});

View file

@ -28,8 +28,6 @@ module.define('view/extended', [jQuery, 'core/settings', 'core/resource', 'core/
'</ul>',
emptyTemplate = '<div class="empty l10n-empty">empty</div>',
// updates this single entry
update = function (entry) {
@ -43,28 +41,17 @@ module.define('view/extended', [jQuery, 'core/settings', 'core/resource', 'core/
$imgBig = $html.find('.icon.big img'),
$label = $html.find('.label'),
$date = $html.find('.date'),
$size = $html.find('.size'),
icon16 = resource.icon(entry.type),
icon48 = resource.icon(entry.type, true),
escapedHref = entry.absHref.replace(/'/g, "%27").replace(/"/g, "%22");
$size = $html.find('.size');
// escapedHref = entry.absHref.replace(/'/g, "%27").replace(/"/g, "%22");
$html
.addClass(entry.isFolder() ? 'folder' : 'file')
.data('entry', entry)
.data('status', entry.status);
if (entry.isParentFolder) {
icon16 = resource.icon('folder-parent');
icon48 = resource.icon('folder-parent', true);
if (!settings.setParentFolderLabels) {
$label.addClass('l10n-parentDirectory');
}
$html.addClass('folder-parent');
}
$a.attr('href', entry.absHref);
$imgSmall.attr('src', icon16).attr('alt', entry.type);
$imgBig.attr('src', icon48).attr('alt', entry.type);
$imgSmall.attr('src', resource.icon(entry.type)).attr('alt', entry.type);
$imgBig.attr('src', resource.icon(entry.type, true)).attr('alt', entry.type);
$label.text(entry.label);
$date.data('time', entry.time).text(format.formatDate(entry.time));
$size.data('bytes', entry.size).text(format.formatSize(entry.size));
@ -80,6 +67,15 @@ module.define('view/extended', [jQuery, 'core/settings', 'core/resource', 'core/
}
}
if (entry.isParentFolder) {
$imgSmall.attr('src', resource.icon('folder-parent'));
$imgBig.attr('src', resource.icon('folder-parent', true));
if (!settings.setParentFolderLabels) {
$label.addClass('l10n-parentDirectory');
}
$html.addClass('folder-parent');
}
if (entry.$extended) {
entry.$extended.replaceWith($html);
}

View file

@ -2,7 +2,7 @@
module.define('view/viewmode', [jQuery, 'core/settings', 'core/resource', 'core/store'], function ($, allsettings, resource, store) {
var defaults = {
modes: ['details', 'icons'],
modes: ['details', 'list', 'icons'],
setParentFolderLabels: false
},
@ -10,67 +10,51 @@ module.define('view/viewmode', [jQuery, 'core/settings', 'core/resource', 'core/
storekey = 'h5ai.viewmode',
templates = {
details: '<li id="viewdetails" class="view">' +
'<a href="#">' +
'<img src="' + resource.image('view-details') + '" alt="view-details" />' +
'<span class="l10n-details">details</span>' +
'</a>' +
'</li>',
icons: '<li id="viewicons" class="view">' +
'<a href="#">' +
'<img src="' + resource.image('view-icons') + '" alt="view-icons" />' +
'<span class="l10n-icons">icons</span>' +
'</a>' +
'</li>'
},
template = '<li id="view-[MODE]" class="view">' +
'<a href="#">' +
'<img src="' + resource.image('view-[MODE]') + '" alt="view-[MODE]" />' +
'<span class="l10n-[MODE]">[MODE]</span>' +
'</a>' +
'</li>',
update = function (viewmode) {
var $viewDetails = $('#viewdetails'),
$viewIcons = $('#viewicons'),
$extended = $('#extended');
var $extended = $('#extended');
if (viewmode) {
store.put(storekey, viewmode);
} else {
viewmode = store.get(storekey);
}
viewmode = $.inArray(viewmode, settings.modes) >= 0 ? viewmode : settings.modes[0];
viewmode = _.indexOf(settings.modes, viewmode) >= 0 ? viewmode : settings.modes[0];
store.put(storekey, viewmode);
$viewDetails.add($viewIcons).removeClass('current');
if (viewmode === 'details') {
$viewDetails.addClass('current');
$extended.addClass('details-view').removeClass('icons-view').show();
} else if (viewmode === 'icons') {
$viewIcons.addClass('current');
$extended.removeClass('details-view').addClass('icons-view').show();
} else {
$extended.hide();
}
_.each(defaults.modes, function (mode) {
if (mode === viewmode) {
$('#view-' + mode).addClass('current');
$extended.addClass('view-' + mode).show();
} else {
$('#view-' + mode).removeClass('current');
$extended.removeClass('view-' + mode);
}
});
},
init = function () {
var $navbar = $('#navbar'),
$extended = $('#extended');
var $navbar = $('#navbar');
settings.modes = _.intersection(settings.modes, defaults.modes);
if (settings.modes.length > 1) {
_.each(['icons', 'details'], function (view) {
if ($.inArray(view, settings.modes) >= 0) {
$(templates[view])
_.each(defaults.modes.reverse(), function (mode) {
if (_.indexOf(settings.modes, mode) >= 0) {
$(template.replace(/\[MODE\]/g, mode))
.appendTo($navbar)
.on('click', 'a', function (event) {
update(view);
update(mode);
event.preventDefault();
});
}
});
}
update();
update(store.get(storekey));
};
init();

4
src/_h5ai/js/jquery-1.7.2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

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

View file

@ -1,9 +1,9 @@
<?php
require_once(str_replace("\\", "/", __DIR__) . "/inc/H5ai.php");
require_once(str_replace("\\", "/", dirname(__FILE__)) . "/inc/H5ai.php");
$h5ai = new H5ai();
$h5ai = new H5ai(__FILE__);
$options = $h5ai->getOptions();
@ -41,24 +41,20 @@ if ($action === "getthumbsrc") {
json_fail(1, "thumbnails disabled");
}
list($srcAbsHref, $width, $height, $mode) = check_keys(array("href", "width", "height", "mode"));
H5ai::req_once("/php/inc/Thumbnail.php");
H5ai::req_once("/php/inc/Image.php");
$srcAbsPath = $h5ai->getRootAbsPath() . rawurldecode($srcAbsHref);
if (!Thumbnail::isUsable()) {
H5ai::req_once("/php/inc/Thumb.php");
if (!Thumb::is_supported()) {
json_fail(2, "thumbnails not supported");
}
$thumbnail = new Thumbnail($h5ai, $srcAbsHref, $mode, $width, $height);
$thumbnail->create(1);
if (!file_exists($thumbnail->getPath())) {
list($type, $srcAbsHref, $mode, $width, $height) = check_keys(array("type", "href", "mode", "width", "height"));
$thumb = new Thumb($h5ai);
$thumbHref = $thumb->thumb($type, $srcAbsHref, $mode, $width, $height);
if ($thumbHref === null) {
json_fail(3, "thumbnail creation failed");
}
json_exit(array("absHref" => $thumbnail->getHref()));
json_exit(array("absHref" => $thumbHref));
}
@ -102,22 +98,46 @@ else if ($action === "getarchive") {
else if ($action === "getchecks") {
$php = $h5ai->checks["php"];
$cache = $php && $h5ai->checks["cache"];
$temp = $php && $h5ai->checks["temp"];
$php = version_compare(PHP_VERSION, "5.2.1") >= 0;
$archive = class_exists("PharData");
$gd = false;
if (function_exists("gd_info")) {
$gdinfo = gd_info();
$gd = array_key_exists("JPG Support", $gdinfo) && $gdinfo["JPG Support"] || array_key_exists("JPEG Support", $gdinfo) && $gdinfo["JPEG Support"];
}
$cache = is_writable($h5ai->getH5aiAbsPath() . "/cache");
$temp = is_writable(sys_get_temp_dir());
$tar = preg_match("/tar$/", `which tar`) > 0;
$zip = preg_match("/zip$/", `which zip`) > 0;
$convert = preg_match("/convert$/", `which convert`) > 0;
$ffmpeg = preg_match("/ffmpeg$/", `which ffmpeg`) > 0;
$du = preg_match("/du$/", `which du`) > 0;
json_exit(array(
"php" => $php,
"cache" => $cache,
"thumbs" => $cache && $h5ai->checks["gd"],
"thumbs" => $gd,
"temp" => $temp,
"archive" => $temp && $h5ai->checks["archive"],
"tar" => $temp && $h5ai->checks["tar"],
"zip" => $temp && $h5ai->checks["zip"]
"archive" => $archive,
"tar" => $tar,
"zip" => $zip,
"convert" => $convert,
"ffmpeg" => $ffmpeg,
"du" => $du
));
}
else if ($action === "getentries") {
list($href, $content) = check_keys(array("href", "content"));
$content = intval($content, 10);
json_exit(array("entries" => $h5ai->getEntries($href, $content)));
}
else {
json_fail(100, "unsupported action");
}

View file

@ -14,8 +14,7 @@
<link rel="apple-touch-icon" type="image/png" href="/_h5ai/images/h5ai-48x48.png">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ubuntu:regular,italic,bold">
<link rel="stylesheet" href="/_h5ai/css/styles.css">
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
<script src="/_h5ai/js/modernizr-2.5.3.min.js"></script>
</head>
<body id="h5ai-main">
<div id="topbar" class="clearfix">
@ -33,24 +32,37 @@
<span class="right"></span>
<span class="center"></span>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="/_h5ai/js/jquery-1.7.2.min.js"><\/script>')</script>
<script src="/_h5ai/config.js"></script>
<script src="/_h5ai/js/scripts.js"></script>
<div id="data-generic-json" class="hidden">
<?php if (stripos($_SERVER["REQUEST_METHOD"], "HEAD") === false) {
function find_h5ai($path, $h5ai) {
$h5ai_php = str_replace("\\", "/", dirname(__FILE__)) . "/inc/H5ai.php";
if (!file_exists($h5ai_php)) {
function find_h5ai($path, $h5ai) {
if (file_exists($path . $h5ai)) {
return $path . $h5ai;
}
if (file_exists($path . $h5ai)) {
return $path . $h5ai;
} else {
$parent = str_replace("\\", "/", dirname($path));
if ($parent !== $path) {
return find_h5ai($parent, $h5ai);
}
error_log("h5ai not found: " . __FILE__);
}
error_log("h5ai not found from " . __DIR__);
$h5ai_php = find_h5ai(str_replace("\\", "/", dirname(__FILE__)), "/_h5ai/php/inc/H5ai.php");
}
require_once(find_h5ai(str_replace("\\", "/", __DIR__), "/_h5ai/php/inc/H5ai.php"));
$h5ai = new H5ai();
require_once($h5ai_php);
$h5ai = new H5ai(__FILE__);
echo $h5ai->getGenericJson();
} ?>
</div>

View file

@ -21,7 +21,7 @@ class Archive {
$this->files = array();
$this->sc401 = false;
$this->addHrefs($hrefs);
$this->add_hrefs($hrefs);
if ($this->sc401) {
return 401;
@ -55,7 +55,7 @@ class Archive {
$archive->addEmptyDir($archivedDir);
}
foreach ($this->files as $realFile => $archivedFile) {
$archive->addFile($realFile, $archivedFile); // very, very slow :/
$archive->add_file($realFile, $archivedFile); // very, very slow :/
}
}
@ -67,7 +67,7 @@ class Archive {
}
private function addHrefs($hrefs) {
private function add_hrefs($hrefs) {
foreach ($hrefs as $href) {
@ -79,22 +79,22 @@ class Archive {
$this->sc401 = true;
}
if ($code == "h5ai" && !$this->h5ai->ignoreThisFile($n)) {
if ($code == "h5ai" && !$this->h5ai->is_ignored($n)) {
$realFile = $this->h5ai->getAbsPath($href);
$archivedFile = preg_replace("!^" . H5ai::normalize_path($this->h5ai->getRootAbsPath(), true) . "!", "", $realFile);
if (is_dir($realFile)) {
$this->addDir($realFile, $archivedFile);
$this->add_dir($realFile, $archivedFile);
} else {
$this->addFile($realFile, $archivedFile);
$this->add_file($realFile, $archivedFile);
}
}
}
}
private function addFile($realFile, $archivedFile) {
private function add_file($realFile, $archivedFile) {
if (is_readable($realFile)) {
$this->files[$realFile] = $archivedFile;
@ -102,7 +102,7 @@ class Archive {
}
private function addDir($realDir, $archivedDir) {
private function add_dir($realDir, $archivedDir) {
$code = $this->h5ai->getHttpCode($this->h5ai->getAbsHref($realDir));
if ($code == 401) {
@ -112,16 +112,16 @@ class Archive {
if ($code == "h5ai") {
$this->dirs[] = $archivedDir;
$files = $this->h5ai->readDir($realDir);
$files = $this->h5ai->read_dir($realDir);
foreach ($files as $file) {
$realFile = $realDir . "/" . $file;
$archivedFile = $archivedDir . "/" . $file;
if (is_dir($realFile)) {
$this->addDir($realFile, $archivedFile);
$this->add_dir($realFile, $archivedFile);
} else {
$this->addFile($realFile, $archivedFile);
$this->add_file($realFile, $archivedFile);
}
}
}

View file

@ -1,99 +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;
}
}
?>

View file

@ -2,11 +2,13 @@
class Entry {
private static $FOLDER_SIZE_CMD = "du -sb \"[DIR]\"";
private static $cache = array();
public static function getCache() {
public static function get_cache() {
return Entry::$cache;
}
@ -24,16 +26,18 @@ class Entry {
public static function sort() {
uasort(Entry::$cache, function ($entry1, $entry2) {
function cmp($entry1, $entry2) {
return strcasecmp($entry1->absHref, $entry2->absHref);
});
}
uasort(Entry::$cache, "cmp");
}
public $h5ai, $absPath, $absHref, $date, $size, $isFolder, $parent;
public $h5ai, $absPath, $absHref, $date, $size, $isFolder, $parent, $isContentFetched;
private function __construct($h5ai, $absPath, $absHref) {
@ -49,6 +53,11 @@ class Entry {
if ($this->isFolder) {
$this->size = null;
$options = $h5ai->getOptions();
if ($options["foldersize"]["enabled"]) {
$cmd = str_replace("[DIR]", $this->absPath, Entry::$FOLDER_SIZE_CMD);
$this->size = intval(preg_replace("/\s.*$/", "", `$cmd`), 10);
}
} else {
$this->size = filesize($this->absPath);
}
@ -58,6 +67,8 @@ class Entry {
$this->parent = Entry::get($this->h5ai, H5ai::normalize_path(dirname($this->absPath)), H5ai::normalize_path(dirname($this->absHref), true));
}
$this->isContentFetched = false;
Entry::$cache[$this->absHref] = $this;
}
@ -72,6 +83,7 @@ class Entry {
if ($withStatus && $this->isFolder) {
$obj["status"] = $this->h5ai->getHttpCode($this->absHref);
$obj["content"] = $this->isContentFetched;
}
return $obj;
@ -92,12 +104,14 @@ class Entry {
return $content;
}
$files = $this->h5ai->readDir($this->absPath);
$files = $this->h5ai->read_dir($this->absPath);
foreach ($files as $file) {
$entry = Entry::get($this->h5ai, $this->absPath . "/" . $file, $this->absHref . rawurlencode($file));
$content[$entry->absPath] = $entry;
}
$this->isContentFetched = true;
return $content;
}
}

View file

@ -5,7 +5,6 @@ define("H5AI_ABS_PATH", H5ai::normalize_path(dirname(dirname(dirname(__FILE__)))
H5ai::req_once("/config.php");
H5ai::req_once("/php/inc/Cache.php");
H5ai::req_once("/php/inc/Entry.php");
@ -25,18 +24,6 @@ class H5ai {
}
public static final function starts_with($sequence, $start) {
return strcasecmp(substr($sequence, 0, strlen($start)), $start) === 0;
}
public static final function ends_with($sequence, $end) {
return strcasecmp(substr($sequence, -strlen($end)), $end) === 0;
}
private static final function load_config($file) {
$str = file_exists($file) ? file_get_contents($file) : "";
@ -51,26 +38,30 @@ class H5ai {
}
private static $H5AI_CONTENT_TYPE = "Content-Type: text/html;h5ai=";
private $h5aiAbsPath,
$rootAbsPath, $ignore, $ignoreRE,
private $requested_from,
$h5aiAbsPath,
$rootAbsPath, $ignore_names, $ignore_patterns, $index_files,
$config, $options,
$rootAbsHref, $h5aiAbsHref,
$absHref, $absPath,
$cache;
public $checks;
$absHref, $absPath;
public function __construct() {
public function __construct($requested_from) {
$this->requested_from = H5ai::normalize_path($requested_from);
$this->h5aiAbsPath = H5ai::normalize_path(H5AI_ABS_PATH);
global $H5AI_CONFIG;
$this->rootAbsPath = H5ai::normalize_path($H5AI_CONFIG["ROOT_ABS_PATH"]);
$this->ignore = $H5AI_CONFIG["IGNORE"];
$this->ignoreRE = $H5AI_CONFIG["IGNORE_PATTERNS"];
$this->ignore_names = $H5AI_CONFIG["IGNORE"];
$this->ignore_patterns = $H5AI_CONFIG["IGNORE_PATTERNS"];
$this->index_files = $H5AI_CONFIG["INDEX_FILES"];
$this->config = H5ai::load_config($this->h5aiAbsPath . "/config.js");
$this->options = $this->config["options"];
@ -78,20 +69,8 @@ class H5ai {
$this->rootAbsHref = H5ai::normalize_path($this->options["rootAbsHref"], true);
$this->h5aiAbsHref = H5ai::normalize_path($this->options["h5aiAbsHref"], true);
$this->absHref = H5ai::normalize_path(preg_replace('/\\?.*/', '', getenv("REQUEST_URI")), true);
$this->absHref = H5ai::normalize_path(preg_replace('/[^\\/]*$/', '', getenv("REQUEST_URI")), true);
$this->absPath = $this->getAbsPath($this->absHref);
$this->cache = new Cache($this->h5aiAbsPath . "/cache");
$this->checks = array(
"php" => version_compare(PHP_VERSION, "5.2.0") >= 0,
"archive" => class_exists("PharData"),
"gd" => GD_VERSION != "GD_VERSION",
"cache" => is_writable($this->h5aiAbsPath . "/cache"),
"temp" => is_writable(sys_get_temp_dir()),
"tar" => preg_match("/tar$/", `which tar`) > 0,
"zip" => preg_match("/zip$/", `which zip`) > 0
);
}
@ -119,12 +98,6 @@ class H5ai {
}
public function api() {
return $this->h5aiAbsHref . "php/api.php";
}
public function getOptions() {
return $this->options;
@ -161,18 +134,18 @@ class H5ai {
}
public function ignoreThisFile($file) {
public function is_ignored($name) {
// always ignore
if ($file === "." || $file === ".." || H5ai::starts_with($file, '.ht')) {
if ($name === "." || $name === "..") {
return true;
}
if (in_array($file, $this->ignore)) {
if (in_array($name, $this->ignore_names)) {
return true;
}
foreach ($this->ignoreRE as $re) {
if (preg_match($re, $file)) {
foreach ($this->ignore_patterns as $re) {
if (preg_match($re, $name)) {
return true;
}
}
@ -181,13 +154,13 @@ class H5ai {
}
public function readDir($path) {
public function read_dir($path) {
$content = array();
if (is_dir($path)) {
if ($dir = opendir($path)) {
while (($file = readdir($dir)) !== false) {
if (!$this->ignoreThisFile($file)) {
if (!$this->is_ignored($file)) {
$content[] = $file;
}
}
@ -200,25 +173,6 @@ class H5ai {
public function getHttpCode($absHref) {
//return $this->cachedHttpCode($absHref);
return $this->fetchHttpCode($absHref);
}
public function cachedHttpCode($absHref) {
$cached = $this->cache->get($absHref);
if ($cached === false) {
$code = $this->fetchHttpCode($absHref);
$cached = array("href" => $absHref, "code" => $code);
$this->cache->set($absHref, $cached);
}
return $cached["code"];
}
public function fetchHttpCode($absHref) {
if (!is_dir($this->getAbsPath($absHref))) {
return null;
}
@ -230,8 +184,30 @@ class H5ai {
}
}
$contentType = "Content-Type:";
$h5aiContentType = "Content-Type: text/html;h5ai=";
// return $this->fetchHttpCode($absHref);
return $this->guessHttpCode($absHref);
}
public function guessHttpCode($absHref) {
$absPath = $this->getAbsPath($absHref);
foreach ($this->index_files as $if) {
if (file_exists($absPath . "/" . $if)) {
if ($if === "index.php") {
$fileheader = file_get_contents($absPath . "/" . $if, false, null, -1, 50);
return stripos($fileheader, H5ai::$H5AI_CONTENT_TYPE) === false ? 200 : "h5ai";
}
return 200;
}
}
return "h5ai";
}
public function fetchHttpCode($absHref) {
$host = getenv("HTTP_HOST");
$port = getenv("SERVER_PORT");
$msg = "HEAD $absHref HTTP/1.1\r\nHost: $host\r\nConnection: Close\r\n";
@ -251,10 +227,10 @@ class H5ai {
$content = fgets($socket);
$code = intval(trim(substr($content, 9, 4)));
if ($code === 200) {
while (! H5ai::starts_with($content, $contentType)) {
while ($content !== false && stripos($content, "Content-Type") === false) {
$content = fgets($socket);
}
if (H5ai::starts_with($content, $h5aiContentType)) {
if (stripos($content, H5ai::$H5AI_CONTENT_TYPE) !== false) {
$code = "h5ai";
}
}
@ -272,31 +248,49 @@ class H5ai {
public function getGenericJson() {
$entries = $this->getEntries($this->absHref, 1);
$header = $this->options["custom"]["header"];
$footer = $this->options["custom"]["footer"];
$header = $this->fileExists($header ? $this->absPath . "/" . $header : null) ? $header : null;
$footer = $this->fileExists($footer ? $this->absPath . "/" . $footer : null) ? $footer : null;
// collect and sort entries
$folder = Entry::get($this, $this->absPath, $this->absHref);
while ($folder !== null) {
$json = array(
"entries" => $entries,
"customHeader" => $header,
"customFooter" => $footer,
"mode" => $this->requested_from === $this->h5aiAbsPath . "/php/h5ai-index.php" ? "php" : "idx.php",
"server" => array(
"name" => strtolower(preg_replace("/\\/.*$/", "", getenv("SERVER_SOFTWARE"))),
"version" => strtolower(preg_replace("/^.*\\//", "", preg_replace("/\\s.*$/", "", getenv("SERVER_SOFTWARE"))))
)
);
return json_encode($json) . "\n";
}
public function getEntries($absHref, $content) {
$folder = Entry::get($this, $this->getAbsPath($absHref), $absHref);
if ($content > 1 && $folder !== null) {
foreach ($folder->getContent() as $entry) {
$entry->getContent();
}
$folder = $folder->getParent();
}
while ($content > 0 && $folder !== null) {
$folder->getContent();
$folder = $folder->getParent();
}
Entry::sort();
$entries = array();
foreach(Entry::getCache() as $entry) {
foreach (Entry::get_cache() as $entry) {
$entries[] = $entry->toJsonObject(true);
}
$json = array(
"entries" => $entries,
"customHeader" => $header,
"customFooter" => $footer
);
return json_encode($json) . "\n";
return $entries;
}
}

View file

@ -1,225 +0,0 @@
<?php
class Image {
private $sourceFile, $source, $width, $height, $type, $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);
if (!$this->width || !$this->height) {
$this->sourceFile = null;
$this->width = null;
$this->height = null;
$this->type = null;
return;
}
$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($this->source)) {
return;
}
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) {
if (is_null($this->source)) {
return;
}
$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) {
if (is_null($this->source)) {
return;
}
$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) {
if (is_null($this->source)) {
return;
}
$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) {
if (is_null($this->source)) {
return;
}
$w = intval($width);
$h = intval($height);
$this->magic(0, 0, 0, 0, $w, $h, $this->width, $this->height);
}
}
?>

380
src/_h5ai/php/inc/Thumb.php Normal file
View file

@ -0,0 +1,380 @@
<?php
class Thumb {
private static $FFMPEG = "ffmpeg -i \"[SOURCE]\" -an -ss 3 -vframes 1 \"[TARGET]\"";
private static $CONVERT = "convert -strip \"[SOURCE][0]\" \"[TARGET]\"";
public static final function is_supported() {
if (!function_exists("gd_info")) {
return false;
}
$gdinfo = gd_info();
return array_key_exists("JPG Support", $gdinfo) && $gdinfo["JPG Support"] || array_key_exists("JPEG Support", $gdinfo) && $gdinfo["JPEG Support"];
}
private $h5ai;
public function __construct($h5ai) {
$this->h5ai = $h5ai;
}
public function thumb($type, $sourceAbsHref, $mode, $width, $height) {
$sourceAbsPath = $this->h5ai->getAbsPath($sourceAbsHref);
if ($type === "img") {
$captureAbsPath = $sourceAbsPath;
} else if ($type === "mov") {
$captureAbsPath = $this->capture(Thumb::$FFMPEG, $sourceAbsPath);
} else if ($type === "doc") {
$captureAbsPath = $this->capture(Thumb::$CONVERT, $sourceAbsPath);
}
return $this->thumb_href($captureAbsPath, $mode, $width, $height);
}
private function thumb_href($sourceAbsPath, $mode, $width, $height) {
if (!file_exists($sourceAbsPath)) {
return null;
}
$name = "cache/thumb-" . sha1("$sourceAbsPath-$width-$height-$mode") . ".jpg";
// $name = "cache/thumb-" . sha1("$sourceAbsPath-$width-$height-$mode") . ".png";
$thumbAbsHref = $this->h5ai->getH5aiAbsHref() . $name;
$thumbAbsPath = $this->h5ai->getH5aiAbsPath() . "/" . $name;
if (!file_exists($thumbAbsPath) || filemtime($sourceAbsPath) >= filemtime($thumbAbsPath)) {
$image = new Image();
$image->setSource($sourceAbsPath);
$image->thumb($mode, $width, $height);
$image->saveDestJpeg($thumbAbsPath, 80);
// $image->saveDestPng($thumbAbsPath, 9);
// Magic::thumb($mode, $sourceAbsPath, $thumbAbsPath, $width, $height);
}
return file_exists($thumbAbsPath) ? $thumbAbsHref : null;
}
private function capture($cmd, $sourceAbsPath) {
if (!file_exists($sourceAbsPath)) {
return null;
}
$captureAbsPath = $this->h5ai->getH5aiAbsPath() . "/cache/capture-" . sha1($sourceAbsPath) . ".jpg";
if (!file_exists($captureAbsPath) || filemtime($sourceAbsPath) >= filemtime($captureAbsPath)) {
$cmd = str_replace("[SOURCE]", $sourceAbsPath, $cmd);
$cmd = str_replace("[TARGET]", $captureAbsPath, $cmd);
`$cmd`;
}
return file_exists($captureAbsPath) ? $captureAbsPath : null;
}
}
class Magic {
private static $GET_SIZE_CMD = "identify -format \"%w %h\" \"[SOURCE]\"";
private static $RESIZE_CMD = "convert -strip -transparent-color \"#ffffff\" -resize [WIDTH]x[HEIGHT] -quality 80 \"[SOURCE]\" \"[TARGET]\"";
private static $SQUARE_CMD = "convert -strip -transparent-color \"#ffffff\" -crop [CWIDTH]x[CWIDTH]+[CLEFT]+[CTOP] -resize [WIDTH]x[WIDTH] -quality 80 \"[SOURCE]\" \"[TARGET]\"";
private static final function img_size($source) {
$cmd = str_replace("[SOURCE]", str_replace("\"", "\\\"", $source), Magic::$GET_SIZE_CMD);
$size = explode(" ", `$cmd`);
$size[0] = intval($size[0]);
$size[1] = intval($size[1]);
return $size;
}
private static final function rational($source, $target, $width, $height) {
$cmd = str_replace("[SOURCE]", str_replace("\"", "\\\"", $source), Magic::$RESIZE_CMD);
$cmd = str_replace("[TARGET]", str_replace("\"", "\\\"", $target), $cmd);
$cmd = str_replace("[WIDTH]", $width, $cmd);
$cmd = str_replace("[HEIGHT]", $height, $cmd);
`$cmd`;
}
private static final function square($source, $target, $width) {
$size = Magic::img_size($source);
$w = $size[0];
$h = $size[1];
$cwidth = min($w, $h);
$cleft = ($w - $cwidth) / 2;
$ctop = ($h - $cwidth) / 2;
$cmd = str_replace("[SOURCE]", str_replace("\"", "\\\"", $source), Magic::$SQUARE_CMD);
$cmd = str_replace("[TARGET]", str_replace("\"", "\\\"", $target), $cmd);
$cmd = str_replace("[CWIDTH]", $cwidth, $cmd);
$cmd = str_replace("[CLEFT]", $cleft, $cmd);
$cmd = str_replace("[CTOP]", $ctop, $cmd);
$cmd = str_replace("[WIDTH]", $width, $cmd);
`$cmd`;
}
public static final function thumb($mode, $source, $target, $width, $height = null, $color = null) {
if ($height === null) {
$height = $width;
}
if ($mode === "square") {
Magic::square($source, $target, $width);
} elseif ($mode === "rational") {
Magic::rational($source, $target, $width, $height);
}
}
}
class Image {
private $sourceFile, $source, $width, $height, $type, $dest;
public static final function is_supported() {
if (!function_exists("gd_info")) {
return false;
}
$gdinfo = gd_info();
return array_key_exists("JPG Support", $gdinfo) && $gdinfo["JPG Support"] || array_key_exists("JPEG Support", $gdinfo) && $gdinfo["JPEG Support"];
}
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);
if (!$this->width || !$this->height) {
$this->sourceFile = null;
$this->width = null;
$this->height = null;
$this->type = null;
return;
}
$this->source = imagecreatefromstring(file_get_contents($this->sourceFile));
}
public function saveDestJpeg($filename, $quality = 80) {
if (!is_null($this->dest)) {
@imagejpeg($this->dest, $filename, $quality);
@chmod($filename, 0775);
}
}
public function saveDestPng($filename, $quality = 9) {
if (!is_null($this->dest)) {
@imagepng($this->dest, $filename, $quality);
@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($this->source)) {
return;
}
if ($canWidth === 0) {
$canWidth = 1;
}
if ($canHeight === 0) {
$canHeight = 1;
}
if ($destWidth === 0) {
$destWidth = 1;
}
if ($destHeight === 0) {
$destHeight = 1;
}
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) {
if (is_null($this->source)) {
return;
}
$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) {
if (is_null($this->source)) {
return;
}
$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) {
if (is_null($this->source)) {
return;
}
$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) {
if (is_null($this->source)) {
return;
}
$w = intval($width);
$h = intval($height);
$this->magic(0, 0, 0, 0, $w, $h, $this->width, $this->height);
}
}
?>

View file

@ -1,65 +0,0 @@
<?php
H5ai::req_once("/php/inc/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->getRootAbsPath() . 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->getRootAbsPath() . $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;
}
}
?>