Compare commits
54 Commits
Author | SHA1 | Date |
---|---|---|
Lars Jung | aa94de4945 | |
Lars Jung | d99a14eaa9 | |
Lars Jung | 7d914d45fb | |
Lars Jung | 2243f2e6a5 | |
Lars Jung | 4b6c8d796a | |
Lars Jung | d257b79067 | |
Lars Jung | 6b147c7bfb | |
myl7 | 0f2c9d095d | |
myl7 | 184e01c0a6 | |
myl7 | 136c654432 | |
Fox Ears | c122d5e44a | |
Lars Jung | d81d8a9298 | |
Lars Jung | a556a2be50 | |
Lars Jung | 6ef253e44a | |
Lars Jung | 252ffc5cb9 | |
Lars Jung | 70e112a43a | |
Lars Jung | e8434f5d89 | |
Karl G | 984aca0093 | |
Lars Jung | 673ee7ccc7 | |
Lars Jung | 059b2e16ed | |
Lars Jung | 84d84e163b | |
Woet | 6da08269d8 | |
Lars Jung | ce939c3115 | |
Lars Jung | 8889ac4e2e | |
Lars Jung | 2a3f860473 | |
Lars Jung | 5e72d0f6a3 | |
Lars Jung | bc4f964b24 | |
Lars Jung | 3930e8c204 | |
Thomas Mohaupt | b1960a0d15 | |
Lars Jung | a1bb7552dc | |
Lars Jung | 324242a584 | |
Lars Jung | 78d6bf6c27 | |
Lars Jung | 745985bf18 | |
Lars Jung | 50167d3382 | |
Lars Jung | 24d8359a2e | |
Lars Jung | 869f1f5cda | |
Lars Jung | 5ffc5cf6fd | |
Lars Jung | 8966a517c6 | |
Lars Jung | a92c44ef0a | |
Lars Jung | 59a41665b0 | |
Lars Jung | f7333eb51c | |
Lars Jung | 02bf7079dd | |
Lars Jung | 8a15390694 | |
Lars Jung | 6f61a12772 | |
Lars Jung | 566338020a | |
Lars Jung | 52ff7462a9 | |
Daniel Lo Nigro | 946c862dc4 | |
SalGnt | 2d371d112d | |
Azhe-kun | 77d6f4af7f | |
djtm | e2a743bca5 | |
thadius856 | ab4fa886c3 | |
Carlos Cabral | 4b08092c18 | |
Carlos Cabral | 0fd57cad51 | |
Carlos Cabral | db764b6780 |
|
@ -12,7 +12,7 @@ insert_final_newline = true
|
|||
trim_trailing_whitespace = true
|
||||
|
||||
|
||||
[{package.json,.travis.yml,.eslintrc}]
|
||||
[{*.json,*.yml}]
|
||||
indent_size = 2
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
/build/
|
||||
/local/
|
||||
/node_modules/
|
||||
/**/vendor/
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
es6: true
|
||||
node: true
|
||||
|
||||
parserOptions:
|
||||
ecmaVersion: 2020
|
||||
|
||||
rules:
|
||||
array-bracket-spacing: [2, never]
|
||||
arrow-parens: [2, as-needed]
|
|
@ -1,4 +1,6 @@
|
|||
/.nyc_output/
|
||||
/build/
|
||||
/coverage/
|
||||
/local/
|
||||
/node_modules/
|
||||
/npm-debug.log
|
||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,6 +1,27 @@
|
|||
# Changelog
|
||||
|
||||
|
||||
* now require PHP 7.0.0+
|
||||
* fix archive-single-item problem
|
||||
* add header/footer search stop condition
|
||||
* update languages (`id`, `it`, `pt-br`, `pt-pt`)
|
||||
* add EXIF-based image rotation
|
||||
* add `where` to command detection command list
|
||||
* fix #758
|
||||
* fix #760
|
||||
* add `@babel/core` 7.12.10
|
||||
* add `@babel/preset-env` 7.12.11
|
||||
* remove `babel-loader`
|
||||
* update `eslint` to 7.18.0
|
||||
* update `ghu` to 0.26.0
|
||||
* update `jsdom` to 16.4.0
|
||||
* update `kjua` to 0.9.0
|
||||
* update `lolight` to 1.4.0
|
||||
* update `marked` to 1.2.7
|
||||
* update `null-loader` to 4.0.1
|
||||
* update `scar` to 2.3.0
|
||||
|
||||
|
||||
## v0.29.2 - *2019-03-22*
|
||||
|
||||
* update `babel-loader` to 7.1.1
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[![license][license-img]][github] [![web][web-img]][web] [![github][github-img]][github]
|
||||
|
||||
A modern HTTP web server index for Apache httpd, lighttpd, nginx and Cherokee.
|
||||
A modern HTTP web server index for Apache httpd, lighttpd, and nginx.
|
||||
|
||||
|
||||
## Important
|
||||
|
@ -20,7 +20,8 @@ There are installation ready packages for the latest [releases][release] and
|
|||
[dev builds][develop]. But to build **h5ai** yourself either `git clone` or
|
||||
download the repository. From within the root folder run the following
|
||||
commands to find a fresh zipball in folder `build` (tested on linux only,
|
||||
requires [`node 6.0+`][node] to be installed).
|
||||
requires [`node 10.0+`][node] to be installed, might work on other
|
||||
configurations).
|
||||
|
||||
~~~sh
|
||||
> npm install
|
||||
|
@ -32,7 +33,7 @@ requires [`node 6.0+`][node] to be installed).
|
|||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Lars Jung (https://larsjung.de)
|
||||
Copyright (c) 2020 Lars Jung (https://larsjung.de)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
58
ghu.js
58
ghu.js
|
@ -1,7 +1,7 @@
|
|||
const {resolve, join} = require('path');
|
||||
const {
|
||||
ghu, autoprefixer, cssmin, each, ife, includeit, jszip, less, mapfn,
|
||||
newerThan, pug, read, remove, run, uglify, watch, webpack, wrap, write
|
||||
pug, read, remove, run, uglify, watch, webpack, wrap, write
|
||||
} = require('ghu');
|
||||
|
||||
const ROOT = resolve(__dirname);
|
||||
|
@ -10,24 +10,26 @@ const TEST = join(ROOT, 'test');
|
|||
const BUILD = join(ROOT, 'build');
|
||||
|
||||
const mapper = mapfn.p(SRC, BUILD).s('.less', '.css').s('.pug', '');
|
||||
const webpackCfg = include => ({
|
||||
const WEBPACK_CFG = {
|
||||
mode: 'none',
|
||||
module: {
|
||||
loaders: [
|
||||
rules: [
|
||||
{
|
||||
include,
|
||||
loader: 'babel-loader',
|
||||
query: {
|
||||
cacheDirectory: true,
|
||||
presets: ['babel-preset-env']
|
||||
test: /\.js$/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /jsdom/,
|
||||
loader: 'null-loader'
|
||||
use: 'null-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ghu.defaults('release');
|
||||
|
||||
|
@ -45,9 +47,8 @@ ghu.before(runtime => {
|
|||
}
|
||||
|
||||
runtime.comment = `${runtime.pkg.name} v${runtime.pkg.version} - ${runtime.pkg.homepage}`;
|
||||
runtime.commentJs = `/* ${runtime.comment} */\n`;
|
||||
runtime.commentHtml = `<!-- ${runtime.comment} -->`;
|
||||
|
||||
runtime.comment_js = `/* ${runtime.comment} */\n`;
|
||||
runtime.comment_html = `<!-- ${runtime.comment} -->`;
|
||||
console.log(runtime.comment);
|
||||
});
|
||||
|
||||
|
@ -64,45 +65,40 @@ ghu.task('clean', 'delete build folder', () => {
|
|||
|
||||
ghu.task('build:scripts', runtime => {
|
||||
return read(`${SRC}/_h5ai/public/js/scripts.js`)
|
||||
.then(newerThan(mapper, `${SRC}/_h5ai/public/js/**`))
|
||||
.then(webpack(webpackCfg([SRC]), {showStats: false}))
|
||||
.then(webpack(WEBPACK_CFG))
|
||||
.then(wrap('\n\n// @include "pre.js"\n\n'))
|
||||
.then(includeit())
|
||||
.then(ife(() => runtime.args.production, uglify({compressor: {warnings: false}})))
|
||||
.then(wrap(runtime.commentJs))
|
||||
.then(ife(() => runtime.args.production, uglify()))
|
||||
.then(wrap(runtime.comment_js))
|
||||
.then(write(mapper, {overwrite: true}));
|
||||
});
|
||||
|
||||
ghu.task('build:styles', runtime => {
|
||||
return read(`${SRC}/_h5ai/public/css/*.less`)
|
||||
.then(newerThan(mapper, `${SRC}/_h5ai/public/css/**`))
|
||||
.then(includeit())
|
||||
.then(less())
|
||||
.then(autoprefixer())
|
||||
.then(ife(() => runtime.args.production, cssmin()))
|
||||
.then(wrap(runtime.commentJs))
|
||||
.then(wrap(runtime.comment_js))
|
||||
.then(write(mapper, {overwrite: true}));
|
||||
});
|
||||
|
||||
ghu.task('build:pages', runtime => {
|
||||
return read(`${SRC}: **/*.pug, ! **/*.tpl.pug`)
|
||||
.then(newerThan(mapper, `${SRC}/**/*.tpl.pug`))
|
||||
.then(pug({pkg: runtime.pkg}))
|
||||
.then(wrap('', runtime.commentHtml))
|
||||
.then(wrap('', runtime.comment_html))
|
||||
.then(write(mapper, {overwrite: true}));
|
||||
});
|
||||
|
||||
ghu.task('build:copy', runtime => {
|
||||
const mapperRoot = mapfn.p(ROOT, join(BUILD, '_h5ai'));
|
||||
const mapper_root = mapfn.p(ROOT, join(BUILD, '_h5ai'));
|
||||
|
||||
return Promise.all([
|
||||
read(`${SRC}/**/conf/*.json`)
|
||||
.then(newerThan(mapper))
|
||||
.then(wrap(runtime.commentJs))
|
||||
.then(wrap(runtime.comment_js))
|
||||
.then(write(mapper, {overwrite: true, cluster: true})),
|
||||
|
||||
read(`${SRC}: **, ! **/*.js, ! **/*.less, ! **/*.pug, ! **/conf/*.json`)
|
||||
.then(newerThan(mapper))
|
||||
.then(each(obj => {
|
||||
if ((/index\.php$/).test(obj.source)) {
|
||||
obj.content = obj.content.replace('{{VERSION}}', runtime.pkg.version);
|
||||
|
@ -111,23 +107,20 @@ ghu.task('build:copy', runtime => {
|
|||
.then(write(mapper, {overwrite: true, cluster: true})),
|
||||
|
||||
read(`${ROOT}/*.md`)
|
||||
.then(newerThan(mapperRoot))
|
||||
.then(write(mapperRoot, {overwrite: true, cluster: true}))
|
||||
.then(write(mapper_root, {overwrite: true, cluster: true}))
|
||||
]);
|
||||
});
|
||||
|
||||
ghu.task('build:tests', ['build:styles'], 'build the test suite', () => {
|
||||
return Promise.all([
|
||||
read(`${BUILD}/_h5ai/public/css/styles.css`)
|
||||
.then(newerThan(`${BUILD}/test/h5ai-styles.css`))
|
||||
.then(write(`${BUILD}/test/h5ai-styles.css`, {overwrite: true})),
|
||||
|
||||
read(`${TEST}/index.html`)
|
||||
.then(newerThan(`${BUILD}/test/index.html`))
|
||||
.then(write(`${BUILD}/test/index.html`, {overwrite: true})),
|
||||
|
||||
read(`${TEST}: index.js`)
|
||||
.then(webpack(webpackCfg([SRC, TEST]), {showStats: false}))
|
||||
.then(webpack(WEBPACK_CFG))
|
||||
.then(wrap(`\n\n// @include "${SRC}/**/js/pre.js"\n\n`))
|
||||
.then(includeit())
|
||||
.then(write(mapfn.p(TEST, `${BUILD}/test`), {overwrite: true}))
|
||||
|
@ -145,11 +138,10 @@ ghu.task('deploy', ['build'], 'deploy to a specified path with :dest=/some/path'
|
|||
}
|
||||
console.log(`deploy to ${runtime.args.dest}`);
|
||||
|
||||
const mapperDeploy = mapfn.p(BUILD, resolve(runtime.args.dest));
|
||||
const mapper_deploy = mapfn.p(BUILD, resolve(runtime.args.dest));
|
||||
|
||||
return read(`${BUILD}/_h5ai/**`)
|
||||
.then(newerThan(mapperDeploy))
|
||||
.then(write(mapperDeploy, {overwrite: true, cluster: true}));
|
||||
.then(write(mapper_deploy, {overwrite: true, cluster: true}));
|
||||
});
|
||||
|
||||
ghu.task('watch', runtime => {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"name": "h5ai",
|
||||
"version": "0.29.2",
|
||||
"version": "0.30.0",
|
||||
"description": "Modern HTTP web server index.",
|
||||
"homepage": "https://larsjung.de/h5ai/",
|
||||
"bugs": "https://github.com/lrsjng/h5ai/issues",
|
||||
"author": "Lars Jung <lrsjng@gmail.com> (https://larsjung.de)",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
@ -14,22 +13,19 @@
|
|||
"lint": "eslint .",
|
||||
"test": "node test",
|
||||
"build": "node ghu release",
|
||||
"precommit": "npm run lint && npm run test"
|
||||
"precommit": "npm run -s lint && npm run -s test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-loader": "7.1.1",
|
||||
"babel-preset-env": "1.7.0",
|
||||
"eslint": "5.15.3",
|
||||
"ghu": "0.13.0",
|
||||
"jsdom": "14.0.0",
|
||||
"kjua": "0.2.0",
|
||||
"lolight": "1.0.0",
|
||||
"marked": "0.6.1",
|
||||
"@babel/core": "7.12.10",
|
||||
"@babel/preset-env": "7.12.11",
|
||||
"eslint": "7.18.0",
|
||||
"ghu": "0.26.0",
|
||||
"jsdom": "16.4.0",
|
||||
"kjua": "0.9.0",
|
||||
"lolight": "1.4.0",
|
||||
"marked": "1.2.7",
|
||||
"normalize.css": "8.0.1",
|
||||
"null-loader": "0.1.1",
|
||||
"scar": "1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
"null-loader": "4.0.1",
|
||||
"scar": "2.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"lang": "Bahasa Indonesia",
|
||||
|
||||
"dateFormat": "DD-MM-YYYY HH:mm",
|
||||
"details": "rincian",
|
||||
"download": "unduh",
|
||||
"empty": "kosong",
|
||||
"files": "berkas",
|
||||
"filter": "saring",
|
||||
"folders": "pelipat",
|
||||
"grid": "jaring",
|
||||
"icons": "ikon",
|
||||
"language": "Bahasa",
|
||||
"lastModified": "Di modifikasi",
|
||||
"name": "Nama",
|
||||
"noMatch": "tidak cocok",
|
||||
"parentDirectory": "Direktori induk",
|
||||
"search": "cari",
|
||||
"size": "Ukuran",
|
||||
"tree": "Pohon",
|
||||
"view": "Tampil"
|
||||
}
|
|
@ -10,9 +10,13 @@
|
|||
"folders": "cartelle",
|
||||
"grid": "griglia",
|
||||
"icons": "icone",
|
||||
"language": "Linugua",
|
||||
"lastModified": "Ultima modifica",
|
||||
"name": "Nome",
|
||||
"noMatch": "nessun risultato",
|
||||
"parentDirectory": "Cartella Superiore",
|
||||
"size": "Dimensione"
|
||||
"search": "cerca",
|
||||
"size": "Dimensione",
|
||||
"tree": "Albero",
|
||||
"view": "Vista"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"lang": "português do Brasil",
|
||||
|
||||
"dateFormat": "DD-MM-YYYY HH:mm",
|
||||
"details": "detalhes",
|
||||
"download": "download",
|
||||
"empty": "vazio",
|
||||
"files": "arquivos",
|
||||
"filter": "filtro",
|
||||
"folders": "pastas",
|
||||
"grid": "grade",
|
||||
"icons": "ícones",
|
||||
"language": "Idioma",
|
||||
"lastModified": "Última modificação",
|
||||
"name": "Nome",
|
||||
"noMatch": "sem resultados",
|
||||
"parentDirectory": "Diretório acima",
|
||||
"search": "pesquisa",
|
||||
"size": "Tamanho",
|
||||
"tree": "Árvore",
|
||||
"view": "Visualização"
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"lang": "português",
|
||||
"lang": "português de Portugal",
|
||||
|
||||
"dateFormat": "DD-MM-YYYY HH:mm",
|
||||
"details": "detalhes",
|
||||
|
@ -10,9 +10,13 @@
|
|||
"folders": "pastas",
|
||||
"grid": "grelha",
|
||||
"icons": "ícones",
|
||||
"language": "Idioma",
|
||||
"lastModified": "última modificação",
|
||||
"name": "Nome",
|
||||
"noMatch": "sem resultados",
|
||||
"parentDirectory": "diretório acima",
|
||||
"size": "Tamanho"
|
||||
"parentDirectory": "Diretório acima",
|
||||
"search": "pesquisa",
|
||||
"size": "Tamanho",
|
||||
"tree": "Árvore",
|
||||
"view": "Visualização"
|
||||
}
|
|
@ -54,7 +54,7 @@
|
|||
is given the view size is fixed and the selector buttons are hidden.
|
||||
The user selected view size is also stored local in modern browsers
|
||||
so that it will be persistent.
|
||||
- theme: string, name of one of the folders in "_h5ai/images/themes", defaults to "default"
|
||||
- theme: string, name of one of the folders in "_h5ai/public/images/themes", defaults to "default"
|
||||
- unmanaged: array of strings, don't manage folders containing one of those files
|
||||
- unmanagedInNewWindow: boolean, open unmanaged links in new window/tab
|
||||
*/
|
||||
|
@ -108,9 +108,14 @@
|
|||
Note the different filenames: "header" (only current) - "headers" (current and sub directories)!
|
||||
The file's content will be placed inside a <div/> tag above/below the main content.
|
||||
If a file's extension is ".md" instead of ".html" its content will be interpreted as markdown.
|
||||
|
||||
- stopSearchingAtRoot: boolean, only search for header and footer files until the web root
|
||||
directory. if `false`, will search for header/footer up the entire directory structure,
|
||||
even above the web root
|
||||
*/
|
||||
"custom": {
|
||||
"enabled": true
|
||||
"enabled": true,
|
||||
"stopSearchingAtRoot": true
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -343,7 +348,7 @@
|
|||
},
|
||||
|
||||
/*
|
||||
Show thumbnails for image files. Needs the "/_h5ai/cache" folder to be
|
||||
Show thumbnails for image files. Needs the "/_h5ai/public/cache" folder to be
|
||||
writable for the web Server.
|
||||
|
||||
- img: array of strings
|
||||
|
|
|
@ -31,6 +31,7 @@ class Api {
|
|||
$archive = new Archive($this->context);
|
||||
|
||||
set_time_limit(0);
|
||||
session_write_close();
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="' . $as . '"');
|
||||
header('Connection: close');
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
class Context {
|
||||
private static $DEFAULT_PASSHASH = 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e';
|
||||
private static $AS_ADMIN_SESSION_KEY = 'AS_ADMIN';
|
||||
private static $L10N_ISO_CODES = array(
|
||||
'af', 'bg', 'cs', 'da', 'de', 'el', 'en', 'es', 'et', 'fi', 'fr', 'he',
|
||||
'hi', 'hr', 'hu', 'id', 'it', 'ja','ko', 'lv', 'nb', 'nl', 'pl',
|
||||
'pt-br', 'pt-pt', 'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'tr', 'uk',
|
||||
'zh-cn', 'zh-tw'
|
||||
);
|
||||
|
||||
private $session;
|
||||
private $request;
|
||||
|
@ -227,6 +233,10 @@ class Context {
|
|||
$results = [];
|
||||
|
||||
foreach ($iso_codes as $iso_code) {
|
||||
if (!in_array($iso_code, Context::$L10N_ISO_CODES)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = $this->setup->get('CONF_PATH') . '/l10n/' . $iso_code . '.json';
|
||||
$results[$iso_code] = Json::load($file);
|
||||
$results[$iso_code]['isoCode'] = $iso_code;
|
||||
|
|
|
@ -122,12 +122,15 @@ class Setup {
|
|||
if (sizeof($cmds) === 0 || $this->refresh) {
|
||||
$cmds['command'] = Util::exec_0('command -v command');
|
||||
$cmds['which'] = Util::exec_0('which which') || Util::exec_0('which which.exe');
|
||||
$cmds['where'] = Util::exec_0('where where.exe');
|
||||
|
||||
$cmd = false;
|
||||
if ($cmds['command']) {
|
||||
$cmd = 'command -v';
|
||||
} elseif ($cmds['which']) {
|
||||
$cmd = 'which';
|
||||
} elseif ($cmds['where']) {
|
||||
$cmd = 'where';
|
||||
}
|
||||
|
||||
foreach (['avconv', 'convert', 'du', 'ffmpeg', 'gm', 'tar', 'zip'] as $c) {
|
||||
|
|
|
@ -135,12 +135,17 @@ class Archive {
|
|||
}
|
||||
|
||||
private function add_hrefs($hrefs) {
|
||||
if (!is_array($hrefs)) {
|
||||
$hrefs = array($hrefs);
|
||||
}
|
||||
|
||||
foreach ($hrefs as $href) {
|
||||
if (trim($href) === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$d = Util::normalize_path(dirname($href), true);
|
||||
$href = Util::normalize_path($href, false);
|
||||
$d = dirname($href);
|
||||
$n = basename($href);
|
||||
|
||||
if ($this->context->is_managed_href($d) && !$this->context->is_hidden($n)) {
|
||||
|
|
|
@ -54,6 +54,14 @@ class Custom {
|
|||
if ($parent_path === $path) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Stop once we reach the root
|
||||
if (
|
||||
$this->context->query_option('custom.stopSearchingAtRoot', true) &&
|
||||
$path === $this->context->get_setup()->get('ROOT_PATH')
|
||||
) {
|
||||
break;
|
||||
}
|
||||
$path = $parent_path;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
position: absolute;
|
||||
|
||||
image-orientation: from-image;
|
||||
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
|
||||
<g transform="translate(0,-8)">
|
||||
<path d="m15.8 22.3h-0.8l-0.3-0.3c1-1.1 1.6-2.6 1.6-4.2 0-3.6-2.9-6.5-6.5-6.5-3.6 0-6.5 2.9-6.5 6.5 0 3.6 2.9 6.5 6.5 6.5 1.6 0 3.1-0.6 4.2-1.6l0.3 0.3v0.8l5 5 1.5-1.5-5-5zm-6 0c-2.5 0-4.5-2-4.5-4.5 0-2.5 2-4.5 4.5-4.5 2.5 0 4.5 2 4.5 4.5 0 2.5-2 4.5-4.5 4.5z" fill="#555"/>
|
||||
</g>
|
||||
</svg>
|
||||
<path d="M20 4H4l6 9v5l4 2v-7z" fill="#555"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 172 B |
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
define('H5AI_VERSION', '{{VERSION}}');
|
||||
define('MIN_PHP_VERSION', '5.5.0');
|
||||
define('MIN_PHP_VERSION', '7.0.0');
|
||||
|
||||
if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<')) {
|
||||
header('Content-type: text/plain;charset=utf-8');
|
||||
|
|
|
@ -24,6 +24,7 @@ const updateGui = () => {
|
|||
|
||||
const addUnloadFn = el => {
|
||||
el.unload = () => {
|
||||
el.pause();
|
||||
el.src = '';
|
||||
el.load();
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@ const updateGui = () => {
|
|||
|
||||
const addUnloadFn = el => {
|
||||
el.unload = () => {
|
||||
el.pause();
|
||||
el.src = '';
|
||||
el.load();
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ const XHR = global.window.XMLHttpRequest;
|
|||
const request = data => {
|
||||
return new Promise(resolve => {
|
||||
const xhr = new XHR();
|
||||
const onReadyStateChange = () => {
|
||||
const on_ready_state_change = () => {
|
||||
if (xhr.readyState === XHR.DONE) {
|
||||
try {
|
||||
resolve(JSON.parse(xhr.responseText));
|
||||
|
@ -15,7 +15,7 @@ const request = data => {
|
|||
};
|
||||
|
||||
xhr.open('POST', '?', true);
|
||||
xhr.onreadystatechange = onReadyStateChange;
|
||||
xhr.onreadystatechange = on_ready_state_change;
|
||||
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8');
|
||||
xhr.send(JSON.stringify(data));
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ const {each, filter, hasLength, is, isStr, map, isInstanceOf, toArray} = require
|
|||
const win = global.window;
|
||||
const doc = win.document;
|
||||
|
||||
const parseHtml = (() => {
|
||||
const parse_html = (() => {
|
||||
const create = name => doc.createElement(name);
|
||||
const rules = [
|
||||
[/^<t(head|body|foot)|^<c(ap|olg)/i, create('table')],
|
||||
|
@ -32,7 +32,7 @@ const parseHtml = (() => {
|
|||
};
|
||||
})();
|
||||
|
||||
const queryAll = (selector, context = doc) => {
|
||||
const query_all = (selector, context = doc) => {
|
||||
try {
|
||||
return toArray(context.querySelectorAll(selector));
|
||||
} catch (err) {
|
||||
|
@ -40,27 +40,27 @@ const queryAll = (selector, context = doc) => {
|
|||
}
|
||||
};
|
||||
|
||||
const isElement = x => isInstanceOf(x, win.Element);
|
||||
const isDocument = x => isInstanceOf(x, win.Document);
|
||||
const isWindow = x => is(x) && x.window === x && isDocument(x.document);
|
||||
const isElDocWin = x => isElement(x) || isDocument(x) || isWindow(x);
|
||||
const is_el = x => isInstanceOf(x, win.Element);
|
||||
const is_doc = x => isInstanceOf(x, win.Document);
|
||||
const is_win = x => is(x) && x.window === x && is_doc(x.document);
|
||||
const is_el_doc_win = x => is_el(x) || is_doc(x) || is_win(x);
|
||||
|
||||
const addListener = (el, type, fn) => el.addEventListener(type, fn);
|
||||
const removeListener = (el, type, fn) => el.removeEventListener(type, fn);
|
||||
const add_listener = (el, type, fn) => el.addEventListener(type, fn);
|
||||
const remove_listener = (el, type, fn) => el.removeEventListener(type, fn);
|
||||
|
||||
const readyPromise = new Promise(resolve => {
|
||||
const ready_promise = new Promise(resolve => {
|
||||
if ((/^(i|c|loade)/).test(doc.readyState)) {
|
||||
resolve();
|
||||
} else {
|
||||
addListener(doc, 'DOMContentLoaded', () => resolve());
|
||||
add_listener(doc, 'DOMContentLoaded', () => resolve());
|
||||
}
|
||||
});
|
||||
const awaitReady = () => readyPromise;
|
||||
const await_ready = () => ready_promise;
|
||||
|
||||
const loadPromise = new Promise(resolve => {
|
||||
addListener(win, 'load', () => resolve());
|
||||
const load_promise = new Promise(resolve => {
|
||||
add_listener(win, 'load', () => resolve());
|
||||
});
|
||||
const awaitLoad = () => loadPromise;
|
||||
const await_load = () => load_promise;
|
||||
|
||||
const dom = arg => {
|
||||
if (isInstanceOf(arg, dom)) {
|
||||
|
@ -70,13 +70,13 @@ const dom = arg => {
|
|||
let els;
|
||||
if (isStr(arg)) {
|
||||
arg = arg.trim();
|
||||
els = arg[0] === '<' ? parseHtml(arg) : queryAll(arg);
|
||||
} else if (isElDocWin(arg)) {
|
||||
els = arg[0] === '<' ? parse_html(arg) : query_all(arg);
|
||||
} else if (is_el_doc_win(arg)) {
|
||||
els = [arg];
|
||||
} else {
|
||||
els = hasLength(arg) ? arg : [arg];
|
||||
}
|
||||
els = filter(els, isElDocWin);
|
||||
els = filter(els, is_el_doc_win);
|
||||
|
||||
return Object.assign(Object.create(dom.prototype), els, {length: els.length});
|
||||
};
|
||||
|
@ -94,15 +94,15 @@ dom.prototype = {
|
|||
},
|
||||
|
||||
find(selector) {
|
||||
return dom([].concat(...this.map(el => queryAll(selector, el))));
|
||||
return dom([].concat(...this.map(el => query_all(selector, el))));
|
||||
},
|
||||
|
||||
on(type, fn) {
|
||||
return this.each(el => addListener(el, type, fn));
|
||||
return this.each(el => add_listener(el, type, fn));
|
||||
},
|
||||
|
||||
off(type, fn) {
|
||||
return this.each(el => removeListener(el, type, fn));
|
||||
return this.each(el => remove_listener(el, type, fn));
|
||||
},
|
||||
|
||||
attr(key, value) {
|
||||
|
@ -271,7 +271,7 @@ dom.prototype = {
|
|||
};
|
||||
|
||||
module.exports = {
|
||||
awaitReady,
|
||||
awaitLoad,
|
||||
awaitReady: await_ready,
|
||||
awaitLoad: await_load,
|
||||
dom
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = Object.assign({},
|
||||
require('./lo'),
|
||||
require('./dom'),
|
||||
require('./naturalCmp'),
|
||||
require('./natural_cmp'),
|
||||
require('./misc')
|
||||
);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
const escapePattern = sequence => {
|
||||
const esc_pattern = sequence => {
|
||||
return sequence.replace(/[\-\[\]{}()*+?.,\\$\^|#\s]/g, '\\$&');
|
||||
};
|
||||
|
||||
const parsePattern = (sequence, advanced) => {
|
||||
const parse_pattern = (sequence, advanced) => {
|
||||
if (!advanced) {
|
||||
return escapePattern(sequence);
|
||||
return esc_pattern(sequence);
|
||||
}
|
||||
|
||||
if (sequence.substr(0, 3) === 're:') {
|
||||
|
@ -12,10 +12,10 @@ const parsePattern = (sequence, advanced) => {
|
|||
}
|
||||
|
||||
return sequence.trim().split(/\s+/).map(part => {
|
||||
return part.split('').map(char => escapePattern(char)).join('.*?');
|
||||
return part.split('').map(char => esc_pattern(char)).join('.*?');
|
||||
}).join('|');
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
parsePattern
|
||||
parsePattern: parse_pattern
|
||||
};
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
// Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
|
||||
// Author: Jim Palmer (based on chunking idea from Dave Koelle)
|
||||
|
||||
// Modified to make it work with h5ai
|
||||
|
||||
const reToken = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi;
|
||||
const reDate = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
|
||||
const reHex = /^0x[0-9a-f]+$/i;
|
||||
const reLeadingZero = /^0/;
|
||||
|
||||
/* eslint-disable complexity */
|
||||
const naturalCmp = (a, b) => {
|
||||
// convert all to strings strip whitespace
|
||||
const x = String(a).trim();
|
||||
const y = String(b).trim();
|
||||
|
||||
// chunk/tokenize
|
||||
const xTokens = x.replace(reToken, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
|
||||
const yTokens = y.replace(reToken, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
|
||||
|
||||
// first try and sort Hex codes or Dates
|
||||
const xDate = parseInt(x.match(reHex), 16) || xTokens.length !== 1 && x.match(reDate) && Date.parse(x);
|
||||
const yDate = parseInt(y.match(reHex), 16) || xDate && y.match(reDate) && Date.parse(y) || null;
|
||||
if (yDate) {
|
||||
if (xDate < yDate) {
|
||||
return -1;
|
||||
}
|
||||
if (xDate > yDate) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// natural sorting through split numeric strings and default strings
|
||||
for (let idx = 0, len = Math.max(xTokens.length, yTokens.length); idx < len; idx += 1) {
|
||||
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
|
||||
let xToken = !(xTokens[idx] || '').match(reLeadingZero) && parseFloat(xTokens[idx]) || xTokens[idx] || 0;
|
||||
let yToken = !(yTokens[idx] || '').match(reLeadingZero) && parseFloat(yTokens[idx]) || yTokens[idx] || 0;
|
||||
|
||||
// handle numeric vs string comparison - number < string - (Kyle Adams)
|
||||
if (isNaN(xToken) !== isNaN(yToken)) {
|
||||
return isNaN(xToken) ? 1 : -1;
|
||||
}
|
||||
|
||||
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
|
||||
if (typeof xToken !== typeof yToken) {
|
||||
xToken = String(xToken);
|
||||
yToken = String(yToken);
|
||||
}
|
||||
|
||||
if (xToken < yToken) {
|
||||
return -1;
|
||||
}
|
||||
if (xToken > yToken) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
module.exports = {
|
||||
naturalCmp
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
// Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
|
||||
// Author: Jim Palmer (based on chunking idea from Dave Koelle)
|
||||
|
||||
// Modified to make it work with h5ai
|
||||
|
||||
const RE_TOKEN = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi;
|
||||
const RE_DATE = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
|
||||
const RE_HEX = /^0x[0-9a-f]+$/i;
|
||||
const RE_LEADING_ZERO = /^0/;
|
||||
|
||||
/* eslint-disable complexity */
|
||||
const natural_cmp = (a, b) => {
|
||||
// convert all to strings strip whitespace
|
||||
const x = String(a).trim();
|
||||
const y = String(b).trim();
|
||||
|
||||
// chunk/tokenize
|
||||
const x_toks = x.replace(RE_TOKEN, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
|
||||
const y_toks = y.replace(RE_TOKEN, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
|
||||
|
||||
// first try and sort Hex codes or Dates
|
||||
const x_date = parseInt(x.match(RE_HEX), 16) || x_toks.length !== 1 && x.match(RE_DATE) && Date.parse(x);
|
||||
const y_date = parseInt(y.match(RE_HEX), 16) || x_date && y.match(RE_DATE) && Date.parse(y) || null;
|
||||
if (y_date) {
|
||||
if (x_date < y_date) {
|
||||
return -1;
|
||||
}
|
||||
if (x_date > y_date) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// natural sorting through split numeric strings and default strings
|
||||
for (let idx = 0, len = Math.max(x_toks.length, y_toks.length); idx < len; idx += 1) {
|
||||
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
|
||||
let x_tok = !(x_toks[idx] || '').match(RE_LEADING_ZERO) && parseFloat(x_toks[idx]) || x_toks[idx] || 0;
|
||||
let y_tok = !(y_toks[idx] || '').match(RE_LEADING_ZERO) && parseFloat(y_toks[idx]) || y_toks[idx] || 0;
|
||||
|
||||
// handle numeric vs string comparison - number < string - (Kyle Adams)
|
||||
if (isNaN(x_tok) !== isNaN(y_tok)) {
|
||||
return isNaN(x_tok) ? 1 : -1;
|
||||
}
|
||||
|
||||
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
|
||||
if (typeof x_tok !== typeof y_tok) {
|
||||
x_tok = String(x_tok);
|
||||
y_tok = String(y_tok);
|
||||
}
|
||||
|
||||
if (x_tok < y_tok) {
|
||||
return -1;
|
||||
}
|
||||
if (x_tok > y_tok) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
module.exports = {
|
||||
naturalCmp: natural_cmp
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
const {dom} = require('../util');
|
||||
|
||||
const rootSelector = 'body';
|
||||
const topbarTpl =
|
||||
const SEL_ROOT = 'body';
|
||||
const TPL_TOPBAR =
|
||||
`<div id="topbar">
|
||||
<div id="toolbar"></div>
|
||||
<div id="flowbar"></div>
|
||||
|
@ -10,17 +10,17 @@ const topbarTpl =
|
|||
<div>by h5ai</div>
|
||||
</a>
|
||||
</div>`;
|
||||
const mainrowTpl =
|
||||
const TPL_MAINROW =
|
||||
`<div id="mainrow">
|
||||
<div id="content"></div>
|
||||
</div>`;
|
||||
|
||||
const init = () => {
|
||||
const $root = dom(rootSelector)
|
||||
const $root = dom(SEL_ROOT)
|
||||
.attr('id', 'root')
|
||||
.clr()
|
||||
.app(topbarTpl)
|
||||
.app(mainrowTpl);
|
||||
.app(TPL_TOPBAR)
|
||||
.app(TPL_MAINROW);
|
||||
|
||||
return {
|
||||
$root,
|
||||
|
|
|
@ -4,25 +4,25 @@
|
|||
throw new Error('no-window');
|
||||
}
|
||||
|
||||
var noBrowser = 'no-browser';
|
||||
var docEl = win.document.documentElement;
|
||||
docEl.className = '';
|
||||
var no_browser = 'no-browser';
|
||||
var doc_el = win.document.documentElement;
|
||||
doc_el.className = '';
|
||||
|
||||
function assert(msg, expr) {
|
||||
if (!expr) {
|
||||
docEl.className = noBrowser;
|
||||
throw new Error(noBrowser + ': ' + msg);
|
||||
doc_el.className = no_browser;
|
||||
throw new Error(no_browser + ': ' + msg);
|
||||
}
|
||||
}
|
||||
|
||||
function isFn(x) {
|
||||
function is_fn(x) {
|
||||
return typeof x === 'function';
|
||||
}
|
||||
|
||||
assert('console', win.console && isFn(win.console.log));
|
||||
assert('assign', win.Object && isFn(win.Object.assign));
|
||||
assert('promise', isFn(win.Promise));
|
||||
// assert('xhr', isFn(win.XMLHttpRequest)); // is object in safari
|
||||
assert('console', win.console && is_fn(win.console.log));
|
||||
assert('assign', win.Object && is_fn(win.Object.assign));
|
||||
assert('promise', is_fn(win.Promise));
|
||||
// assert('xhr', is_fn(win.XMLHttpRequest)); // is object in safari
|
||||
assert('xhr', win.XMLHttpRequest);
|
||||
}(this));
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -4,7 +4,7 @@ if (!global.window) {
|
|||
}
|
||||
|
||||
const {test} = require('scar');
|
||||
const {pinHtml} = require('./util/pin');
|
||||
const {pin_html} = require('./util/pin');
|
||||
|
||||
require('./tests/premisses');
|
||||
require('./tests/unit/core/event');
|
||||
|
@ -12,6 +12,6 @@ require('./tests/unit/core/format');
|
|||
require('./tests/unit/util/naturalCmp');
|
||||
require('./tests/unit/util/parsePatten');
|
||||
|
||||
pinHtml();
|
||||
pin_html();
|
||||
|
||||
test.cli({sync: true});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const {test, assert} = require('scar');
|
||||
const event = require('../../../../src/_h5ai/public/js/lib/core/event');
|
||||
const reqlib = require('../../../util/reqlib');
|
||||
const event = reqlib('core/event');
|
||||
|
||||
test('core.event', () => {
|
||||
assert.equal(typeof event, 'object', 'is object');
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const {test, assert} = require('scar');
|
||||
const format = require('../../../../src/_h5ai/public/js/lib/core/format');
|
||||
const reqlib = require('../../../util/reqlib');
|
||||
const format = reqlib('core/format');
|
||||
|
||||
test('core.format', () => {
|
||||
assert.equal(typeof format, 'object');
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const {test, assert, insp} = require('scar');
|
||||
const {naturalCmp} = require('../../../../src/_h5ai/public/js/lib/util');
|
||||
const reqlib = require('../../../util/reqlib');
|
||||
const {naturalCmp} = reqlib('util');
|
||||
|
||||
test('util.naturalCmp()', () => {
|
||||
assert.equal(typeof naturalCmp, 'function', 'is function');
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const {test, assert, insp} = require('scar');
|
||||
const {parsePattern} = require('../../../../src/_h5ai/public/js/lib/util');
|
||||
const reqlib = require('../../../util/reqlib');
|
||||
const {parsePattern} = reqlib('util');
|
||||
|
||||
test('util.parsePattern()', () => {
|
||||
assert.equal(typeof parsePattern, 'function', 'is function');
|
||||
|
|
|
@ -16,30 +16,30 @@ const attr = (el, name, value) => {
|
|||
return el.setAttribute(name, value);
|
||||
};
|
||||
|
||||
const rootChildren = () => {
|
||||
const root_children = () => {
|
||||
return [
|
||||
...doc.querySelector('head').childNodes,
|
||||
...doc.querySelector('body').childNodes
|
||||
];
|
||||
};
|
||||
|
||||
const pinHtml = () => {
|
||||
const pin_html = () => {
|
||||
pinned.title = doc.title;
|
||||
pinned.htmlId = attr('html', 'id');
|
||||
pinned.htmlClasses = attr('html', 'class');
|
||||
pinned.bodyId = attr('body', 'id');
|
||||
pinned.bodyClasses = attr('body', 'class');
|
||||
pinned.els = rootChildren();
|
||||
pinned.els = root_children();
|
||||
// console.log('pinned', pinned);
|
||||
};
|
||||
|
||||
const restoreHtml = () => {
|
||||
const restore_html = () => {
|
||||
doc.title = pinned.title;
|
||||
attr('html', 'id', pinned.htmlId);
|
||||
attr('html', 'class', pinned.htmlClasses);
|
||||
attr('body', 'id', pinned.bodyId);
|
||||
attr('body', 'class', pinned.bodyClasses);
|
||||
rootChildren().forEach(el => {
|
||||
root_children().forEach(el => {
|
||||
if (pinned.els.indexOf(el) < 0) {
|
||||
el.remove();
|
||||
}
|
||||
|
@ -48,6 +48,6 @@ const restoreHtml = () => {
|
|||
};
|
||||
|
||||
module.exports = {
|
||||
pinHtml,
|
||||
restoreHtml
|
||||
pin_html,
|
||||
restore_html
|
||||
};
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
const reqlib = x => require(`../../src/_h5ai/public/js/lib/${x}`);
|
||||
|
||||
module.exports = reqlib;
|
Loading…
Reference in New Issue