├── .eslintignore
├── src
├── _h5ai
│ ├── public
│ │ ├── js
│ │ │ ├── scripts.js
│ │ │ ├── lib
│ │ │ │ ├── core
│ │ │ │ │ ├── langs.js
│ │ │ │ │ ├── settings.js
│ │ │ │ │ ├── store.js
│ │ │ │ │ ├── event.js
│ │ │ │ │ ├── resource.js
│ │ │ │ │ ├── types.js
│ │ │ │ │ └── format.js
│ │ │ │ ├── ext
│ │ │ │ │ ├── preview
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── preview-aud.js
│ │ │ │ │ │ ├── preview-vid.js
│ │ │ │ │ │ ├── preview-img.js
│ │ │ │ │ │ └── preview-txt.js
│ │ │ │ │ ├── title.js
│ │ │ │ │ ├── autorefresh.js
│ │ │ │ │ ├── google-analytics.js
│ │ │ │ │ ├── custom.js
│ │ │ │ │ ├── piwik-analytics.js
│ │ │ │ │ ├── crumb.js
│ │ │ │ │ ├── download.js
│ │ │ │ │ ├── filter.js
│ │ │ │ │ ├── search.js
│ │ │ │ │ ├── sort.js
│ │ │ │ │ ├── thumbnails.js
│ │ │ │ │ ├── l10n.js
│ │ │ │ │ └── info.js
│ │ │ │ ├── util
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── misc.js
│ │ │ │ │ ├── lo.js
│ │ │ │ │ └── naturalCmp.js
│ │ │ │ ├── config.js
│ │ │ │ ├── view
│ │ │ │ │ ├── notification.js
│ │ │ │ │ ├── base.js
│ │ │ │ │ ├── sidebar.js
│ │ │ │ │ └── viewmode.js
│ │ │ │ ├── init.js
│ │ │ │ ├── main
│ │ │ │ │ └── index.js
│ │ │ │ └── server.js
│ │ │ └── pre.js
│ │ ├── images
│ │ │ ├── fallback
│ │ │ │ ├── file.png
│ │ │ │ ├── folder.png
│ │ │ │ └── folder-parent.png
│ │ │ ├── favicon
│ │ │ │ ├── favicon-152.png
│ │ │ │ ├── favicon-16.png
│ │ │ │ ├── favicon-32.png
│ │ │ │ ├── favicon-16-32.ico
│ │ │ │ └── favicon.svg
│ │ │ ├── themes
│ │ │ │ ├── default
│ │ │ │ │ ├── aud.svg
│ │ │ │ │ ├── vid.svg
│ │ │ │ │ ├── folder.svg
│ │ │ │ │ ├── x.svg
│ │ │ │ │ ├── file.svg
│ │ │ │ │ ├── ar.svg
│ │ │ │ │ ├── folder-page.svg
│ │ │ │ │ ├── txt.svg
│ │ │ │ │ ├── folder-parent.svg
│ │ │ │ │ ├── img.svg
│ │ │ │ │ └── bin.svg
│ │ │ │ ├── comity
│ │ │ │ │ ├── txt-md.svg
│ │ │ │ │ ├── txt-rb.svg
│ │ │ │ │ ├── ar-apk.svg
│ │ │ │ │ ├── txt-script.svg
│ │ │ │ │ ├── txt-php.svg
│ │ │ │ │ ├── txt-html.svg
│ │ │ │ │ ├── txt-css.svg
│ │ │ │ │ ├── txt-py.svg
│ │ │ │ │ ├── txt-js.svg
│ │ │ │ │ ├── x-pdf.svg
│ │ │ │ │ ├── txt-less.svg
│ │ │ │ │ ├── txt-rust.svg
│ │ │ │ │ ├── ar-rpm.svg
│ │ │ │ │ └── ar-deb.svg
│ │ │ │ └── README.md
│ │ │ └── ui
│ │ │ │ ├── crumb.svg
│ │ │ │ ├── sort.svg
│ │ │ │ ├── download.svg
│ │ │ │ ├── preview-next.svg
│ │ │ │ ├── preview-prev.svg
│ │ │ │ ├── preview-raw.svg
│ │ │ │ ├── selected.svg
│ │ │ │ ├── sidebar.svg
│ │ │ │ ├── tree-indicator.svg
│ │ │ │ ├── view-details.svg
│ │ │ │ ├── back.svg
│ │ │ │ ├── view-icons.svg
│ │ │ │ ├── preview-fullscreen.svg
│ │ │ │ ├── preview-no-fullscreen.svg
│ │ │ │ ├── preview-close.svg
│ │ │ │ ├── view-grid.svg
│ │ │ │ ├── tree-toggle.svg
│ │ │ │ ├── info-toggle.svg
│ │ │ │ ├── spinner.svg
│ │ │ │ ├── filter.svg
│ │ │ │ ├── search.svg
│ │ │ │ └── paypal.svg
│ │ ├── css
│ │ │ ├── lib
│ │ │ │ ├── view
│ │ │ │ │ ├── content.less
│ │ │ │ │ ├── mainrow.less
│ │ │ │ │ ├── notification.less
│ │ │ │ │ ├── root.less
│ │ │ │ │ ├── view-grid.less
│ │ │ │ │ ├── view-icons.less
│ │ │ │ │ ├── topbar.less
│ │ │ │ │ ├── view.less
│ │ │ │ │ ├── fallback.less
│ │ │ │ │ ├── view-details.less
│ │ │ │ │ └── sidebar.less
│ │ │ │ ├── ext
│ │ │ │ │ ├── preview-aud.less
│ │ │ │ │ ├── preview-vid.less
│ │ │ │ │ ├── custom.less
│ │ │ │ │ ├── filter.less
│ │ │ │ │ ├── search.less
│ │ │ │ │ ├── preview-txt.less
│ │ │ │ │ ├── preview-img.less
│ │ │ │ │ ├── info.less
│ │ │ │ │ ├── crumb.less
│ │ │ │ │ ├── contextmenu.less
│ │ │ │ │ ├── select.less
│ │ │ │ │ ├── tree.less
│ │ │ │ │ └── preview.less
│ │ │ │ ├── fonts.less
│ │ │ │ ├── misc.less
│ │ │ │ ├── responsive.less
│ │ │ │ ├── colors.less
│ │ │ │ ├── mixins.less
│ │ │ │ └── main
│ │ │ │ │ └── info.less
│ │ │ └── styles.less
│ │ ├── ext
│ │ │ └── README.md
│ │ ├── cache
│ │ │ └── README.md
│ │ ├── .htaccess
│ │ └── index.php
│ └── private
│ │ ├── php
│ │ ├── pages
│ │ │ ├── index.php.pug
│ │ │ ├── info.php.pug
│ │ │ └── page.tpl.pug
│ │ ├── core
│ │ │ ├── class-session.php
│ │ │ ├── class-logger.php
│ │ │ ├── class-theme.php
│ │ │ ├── class-request.php
│ │ │ ├── class-fallback.php
│ │ │ ├── class-json.php
│ │ │ ├── class-filesize.php
│ │ │ ├── class-item.php
│ │ │ ├── class-util.php
│ │ │ └── class-api.php
│ │ ├── ext
│ │ │ ├── class-search.php
│ │ │ └── class-custom.php
│ │ └── class-bootstrap.php
│ │ ├── cache
│ │ └── README.md
│ │ └── conf
│ │ ├── l10n
│ │ ├── sk.json
│ │ ├── tr.json
│ │ ├── nb.json
│ │ ├── ro.json
│ │ ├── sr.json
│ │ ├── hu.json
│ │ ├── sv.json
│ │ ├── he.json
│ │ ├── da.json
│ │ ├── hi.json
│ │ ├── af.json
│ │ ├── fi.json
│ │ ├── hr.json
│ │ ├── sl.json
│ │ ├── bg.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── pt.json
│ │ ├── uk.json
│ │ ├── cs.json
│ │ ├── el.json
│ │ ├── ko.json
│ │ ├── ru.json
│ │ ├── zh-cn.json
│ │ ├── zh-tw.json
│ │ ├── de.json
│ │ ├── et.json
│ │ ├── nl.json
│ │ ├── pl.json
│ │ ├── lv.json
│ │ ├── es.json
│ │ ├── fr.json
│ │ └── en.json
│ │ └── types.json
└── .eslintrc
├── .gitignore
├── test
├── tests
│ ├── premisses.js
│ └── unit
│ │ ├── core
│ │ ├── event.js
│ │ └── format.js
│ │ └── util
│ │ ├── naturalCmp.js
│ │ └── parsePatten.js
├── index.html
├── index.js
└── util
│ └── pin.js
├── .editorconfig
├── package.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | build
2 | node_modules
3 | **/vendor
4 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/scripts.js:
--------------------------------------------------------------------------------
1 | require('./lib/init');
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | local
3 | node_modules
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/src/.eslintrc:
--------------------------------------------------------------------------------
1 | ---
2 | rules:
3 | no-console: 1
4 | prefer-reflect: 0
5 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/core/langs.js:
--------------------------------------------------------------------------------
1 | const {langs} = require('../config');
2 | module.exports = Object.assign({}, langs);
3 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/fallback/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0oVicero0/h5ai/master/src/_h5ai/public/images/fallback/file.png
--------------------------------------------------------------------------------
/src/_h5ai/public/images/fallback/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0oVicero0/h5ai/master/src/_h5ai/public/images/fallback/folder.png
--------------------------------------------------------------------------------
/src/_h5ai/public/images/favicon/favicon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0oVicero0/h5ai/master/src/_h5ai/public/images/favicon/favicon-152.png
--------------------------------------------------------------------------------
/src/_h5ai/public/images/favicon/favicon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0oVicero0/h5ai/master/src/_h5ai/public/images/favicon/favicon-16.png
--------------------------------------------------------------------------------
/src/_h5ai/public/images/favicon/favicon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0oVicero0/h5ai/master/src/_h5ai/public/images/favicon/favicon-32.png
--------------------------------------------------------------------------------
/src/_h5ai/public/images/fallback/folder-parent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0oVicero0/h5ai/master/src/_h5ai/public/images/fallback/folder-parent.png
--------------------------------------------------------------------------------
/src/_h5ai/public/images/favicon/favicon-16-32.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0oVicero0/h5ai/master/src/_h5ai/public/images/favicon/favicon-16-32.ico
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/content.less:
--------------------------------------------------------------------------------
1 | #content {
2 | overflow: auto;
3 | flex: 1 1 auto;
4 | order: 50;
5 | position: relative;
6 | }
7 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/mainrow.less:
--------------------------------------------------------------------------------
1 | #mainrow {
2 | .flex-row;
3 | flex: 1 1 auto;
4 | order: 50;
5 |
6 | height: 0; // non-webkit fix;
7 | }
8 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/preview-aud.less:
--------------------------------------------------------------------------------
1 | #pv-content-aud {
2 | .raised;
3 |
4 | position: absolute;
5 |
6 | max-width: 100%;
7 | max-height: 100%;
8 | }
9 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/aud.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/vid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/preview/index.js:
--------------------------------------------------------------------------------
1 | require('./preview');
2 | require('./preview-aud');
3 | require('./preview-img');
4 | require('./preview-txt');
5 | require('./preview-vid');
6 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/util/index.js:
--------------------------------------------------------------------------------
1 | module.exports = Object.assign({},
2 | require('./lo'),
3 | require('./dom'),
4 | require('./naturalCmp'),
5 | require('./misc')
6 | );
7 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/folder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/favicon/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/x.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/ar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/config.js:
--------------------------------------------------------------------------------
1 | const {request} = require('./server');
2 |
3 | const config = module.exports = {
4 | _update: query => request(query).then(resp => Object.assign(config, resp))
5 | };
6 |
--------------------------------------------------------------------------------
/src/_h5ai/public/ext/README.md:
--------------------------------------------------------------------------------
1 | # Extensions
2 |
3 | This directory is used for additional script and style files. You have to add
4 | them manualy to the `resources` section in `_h5ai/private/conf/options.json`.
5 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/folder-page.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/txt.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/folder-parent.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/img.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/core/settings.js:
--------------------------------------------------------------------------------
1 | const config = require('../config');
2 |
3 | module.exports = Object.assign({}, config.options, {
4 | publicHref: config.setup.PUBLIC_HREF,
5 | rootHref: config.setup.ROOT_HREF
6 | });
7 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/crumb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/sort.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/tests/premisses.js:
--------------------------------------------------------------------------------
1 | const {test, assert} = require('scar');
2 |
3 | test('window is global object', () => {
4 | assert.ok(global.window);
5 | assert.equal(global.window, global.window.window);
6 | assert.ok(global.window.document);
7 | });
8 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/pages/index.php.pug:
--------------------------------------------------------------------------------
1 | extends ./page.tpl.pug
2 |
3 | block init
4 | - var title = `index - powered by h5ai v${pkg.version} (${pkg.homepage})`
5 | - var module = 'index'
6 |
7 | block body
8 | div#fallback = $fallback_html; ?>
9 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/preview-next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/preview-prev.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/preview-raw.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/selected.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/sidebar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/tree-indicator.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/fonts.less:
--------------------------------------------------------------------------------
1 | @font-family: "Ubuntu", "Roboto", "Helvetica", "Arial", "sans-serif";
2 | @font-weight: normal;
3 | @font-size: 13px;
4 | @font-family-mono: "Ubuntu Mono", "Monaco", "Lucida Sans Typewriter", "monospace";
5 | @font-size-mono: 15px;
6 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/view-details.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/back.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/view-icons.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/pages/info.php.pug:
--------------------------------------------------------------------------------
1 | extends ./page.tpl.pug
2 |
3 | block init
4 | - var title = `h5ai info page - v${pkg.version}`
5 | - var module = 'info'
6 |
7 | block body
8 | div#content
9 | h1#header
10 | a(href=pkg.homepage) h5ai
11 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | h5ai test suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/preview-vid.less:
--------------------------------------------------------------------------------
1 | #pv-content-vid {
2 | .raised;
3 |
4 | position: absolute;
5 |
6 | max-width: 100%;
7 | max-height: 100%;
8 | }
9 |
10 | #pv-content-vid:-webkit-full-screen {
11 | top: auto !important;
12 | left: auto !important;
13 | }
14 |
--------------------------------------------------------------------------------
/src/_h5ai/public/cache/README.md:
--------------------------------------------------------------------------------
1 | # Cache
2 |
3 | Public cache.
4 |
5 | This directory is used for server side caching. To use caching make this
6 | directory writable for your webserver.
7 |
8 | There is no critical data in here. You can savely remove any content. This
9 | will clear the cache.
10 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/preview-fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/preview-no-fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/private/cache/README.md:
--------------------------------------------------------------------------------
1 | # Cache
2 |
3 | Private cache.
4 |
5 | This directory is used for server side caching. To use caching make this
6 | directory writable for your webserver.
7 |
8 | There is no critical data in here. You can savely remove any content. This
9 | will clear the cache.
10 |
--------------------------------------------------------------------------------
/src/_h5ai/public/.htaccess:
--------------------------------------------------------------------------------
1 | ## make this folder accessible
2 |
3 | # Apache < 2.3
4 |
5 | Order allow,deny
6 | Allow from all
7 | Satisfy All
8 |
9 |
10 | # Apache ≥ 2.3
11 |
12 | Require all granted
13 |
14 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-md.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/preview-close.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/view-grid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/styles.less:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | // @include "../../../../node_modules/normalize.css/normalize.css"
4 |
5 | // @include "lib/colors.less"
6 | // @include "lib/fonts.less"
7 | // @include "lib/misc.less"
8 | // @include "lib/mixins.less"
9 |
10 | // @include "lib/*/*.less"
11 |
12 | // @include "lib/responsive.less"
13 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/tree-toggle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/sk.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "slovenčina",
3 |
4 | "details": "podrobnosti",
5 | "empty": "prázdny",
6 | "files": "súborov",
7 | "folders": "priečinkov",
8 | "icons": "ikony",
9 | "lastModified": "Upravené",
10 | "name": "Názov",
11 | "parentDirectory": "Nadriadený priečinok",
12 | "size": "Velkosť"
13 | }
14 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/info-toggle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "türkçe",
3 |
4 | "details": "detaylar",
5 | "download": "indir",
6 | "empty": "boş",
7 | "files": "dosyalar",
8 | "folders": "klasörler",
9 | "icons": "ikonlar",
10 | "lastModified": "Son Düzenleme",
11 | "name": "İsim",
12 | "parentDirectory": "Üst Dizin",
13 | "size": "Boyut"
14 | }
15 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/notification.less:
--------------------------------------------------------------------------------
1 | #notification {
2 | position: fixed;
3 | left: 50%;
4 | width: 200px;
5 | margin-left: -100px;
6 | z-index: 100;
7 | padding: 3px 6px 6px 6px;
8 | color: @col-text-primary-white;
9 | background: rgba(0,0,0,0.2);
10 | border-radius: 0 0 4px 4px;
11 | text-align: center;
12 | overflow: hidden;
13 | }
14 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/nb.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "norwegian",
3 |
4 | "details": "detaljer",
5 | "download": "last ned",
6 | "empty": "tom",
7 | "files": "filer",
8 | "folders": "mapper",
9 | "icons": "ikoner",
10 | "lastModified": "Sist endret",
11 | "name": "Navn",
12 | "noMatch": "ingen treff",
13 | "parentDirectory": "Overordnet mappe",
14 | "size": "Størrelse"
15 | }
16 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/ro.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "română",
3 |
4 | "details": "detalii",
5 | "download": "descarcă",
6 | "empty": "gol",
7 | "files": "fişiere",
8 | "folders": "dosar",
9 | "icons": "pictograme",
10 | "lastModified": "ultima modificare",
11 | "name": "nume",
12 | "noMatch": "0 rezultate",
13 | "parentDirectory": "dosar părinte",
14 | "size": "mărime"
15 | }
16 |
--------------------------------------------------------------------------------
/test/tests/unit/core/event.js:
--------------------------------------------------------------------------------
1 | const {test, assert} = require('scar');
2 | const event = require('../../../../src/_h5ai/public/js/lib/core/event');
3 |
4 | test('core.event', () => {
5 | assert.equal(typeof event, 'object', 'is object');
6 | assert.deepEqual(Object.keys(event).sort(), ['sub', 'pub'].sort());
7 | assert.equal(typeof event.sub, 'function');
8 | assert.equal(typeof event.pub, 'function');
9 | });
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | root = true
4 |
5 |
6 | [*]
7 | charset = utf-8
8 | indent_style = space
9 | indent_size = 4
10 | end_of_line = lf
11 | insert_final_newline = true
12 | trim_trailing_whitespace = true
13 |
14 |
15 | [{package.json,.travis.yml,.eslintrc}]
16 | indent_size = 2
17 |
18 |
19 | [{*.md,*.pug}]
20 | trim_trailing_whitespace = false
21 |
22 |
23 | [*.svg]
24 | insert_final_newline = false
25 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/root.less:
--------------------------------------------------------------------------------
1 | #root {
2 | .flex-column;
3 | position: absolute;
4 | left: 0;
5 | top: 0;
6 | right: 0;
7 | bottom: 0;
8 | overflow: hidden;
9 | background: @col-back;
10 | line-height: 1.4;
11 | }
12 |
13 | #root, input, select {
14 | font-family: @font-family;
15 | font-size: @font-size;
16 | font-weight: @font-weight;
17 | color: @col-text;
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/sr.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "srpski",
3 |
4 | "details": "detalji",
5 | "download": "download",
6 | "empty": "prazno",
7 | "files": "fajlovi",
8 | "folders": "direktorijum",
9 | "icons": "ikone",
10 | "lastModified": "Poslednja modifikacija",
11 | "name": "Ime",
12 | "noMatch": "bez poklapanja",
13 | "parentDirectory": "Roditeljski direktorijum",
14 | "size": "Veličina"
15 | }
16 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/README.md:
--------------------------------------------------------------------------------
1 | # Themes
2 |
3 | This directory will contain any themes you add. At the moment there are only
4 | icon themes supported. The folder structure is: `/.`,
5 | with `` one of `svg`, `png` or `jpg`.
6 |
7 | To select a theme use the option `view > theme` in file `conf/options.json`.
8 |
9 | You will find the previously included icon themes [here](https://github.com/lrsjng/h5ai-themes).
10 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/custom.less:
--------------------------------------------------------------------------------
1 | #content-header, #content-footer {
2 | margin: 16px;
3 | padding: 8px;
4 | color: @col-text;
5 |
6 | a, a:active, a:visited {
7 | color: @col-link;
8 | text-decoration: none;
9 | cursor: pointer;
10 |
11 | &:hover {
12 | color: @col-link-hover;
13 | }
14 | }
15 |
16 | h1, h2, h3, h4, h5, h6, p {
17 | margin: 0.1em 0;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | if (!global.window) {
2 | global.window = require('jsdom').jsdom().defaultView;
3 | }
4 |
5 | const {test} = require('scar');
6 | const {pinHtml} = require('./util/pin');
7 |
8 | require('./tests/premisses');
9 | require('./tests/unit/core/event');
10 | require('./tests/unit/core/format');
11 | require('./tests/unit/util/naturalCmp');
12 | require('./tests/unit/util/parsePatten');
13 |
14 | pinHtml();
15 |
16 | test.cli({sync: true});
17 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "magyar",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "részletek",
6 | "download": "letöltés",
7 | "empty": "üres",
8 | "files": "fájlok",
9 | "folders": "mappák",
10 | "icons": "ikonok",
11 | "lastModified": "Utoljára módosítva",
12 | "name": "Név",
13 | "noMatch": "nincs találat",
14 | "parentDirectory": "Szülő könyvtár",
15 | "size": "Méret"
16 | }
17 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "svenska",
3 |
4 | "details": "detaljerad",
5 | "download": "ladda ner",
6 | "empty": "tom",
7 | "files": "filer",
8 | "folders": "kataloger",
9 | "grid": "rutnät",
10 | "icons": "ikoner",
11 | "lastModified": "Senast ändrad",
12 | "name": "Filnamn",
13 | "noMatch": "ingen matchning",
14 | "parentDirectory": "Till överordnad mapp",
15 | "size": "Filstorlek"
16 | }
17 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/he.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "עברית",
3 |
4 | "dateFormat": "DD.MM.YYYY HH:mm",
5 | "details": "פרטים",
6 | "download": "הורדה",
7 | "empty": "ריק",
8 | "files": "קבצים",
9 | "filter": "סינון",
10 | "folders": "תיקיות",
11 | "icons": "צלמיות",
12 | "lastModified": "שינוי אחרון",
13 | "name": "שם",
14 | "noMatch": "אין תוצאות",
15 | "parentDirectory": "תיקיית הורה",
16 | "size": "גודל"
17 | }
18 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/spinner.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/filter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/view/notification.js:
--------------------------------------------------------------------------------
1 | const {dom} = require('../util');
2 | const base = require('./base');
3 |
4 | const init = () => {
5 | const $el = dom('').hide().appTo(base.$root);
6 |
7 | const set = content => {
8 | if (content) {
9 | $el.html(content).show();
10 | } else {
11 | $el.hide();
12 | }
13 | };
14 |
15 | return {
16 | set
17 | };
18 | };
19 |
20 | module.exports = init();
21 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/da.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "dansk",
3 |
4 | "dateFormat": "DD-MM-YYYY HH:mm",
5 | "details": "detaljer",
6 | "download": "download",
7 | "empty": "tom",
8 | "files": "filer",
9 | "filter": "filter",
10 | "folders": "mapper",
11 | "grid": "grid",
12 | "icons": "ikoner",
13 | "lastModified": "Sidst ændret",
14 | "name": "Navn",
15 | "noMatch": "ingen match",
16 | "parentDirectory": "Overordnet mappe",
17 | "size": "Størrelse"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/hi.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "हिंदी",
3 |
4 | "dateFormat": "DD.MM.YYYY HH:mm",
5 | "details": "विस्तार",
6 | "download": "डाउनलोड",
7 | "empty": "खाली",
8 | "files": "फ़ाइलें",
9 | "filter": "फ़िल्टर",
10 | "folders": "फोल्डर",
11 | "grid": "ग्रिड",
12 | "icons": "आइकॉन",
13 | "lastModified": "पिछला परिवर्तन",
14 | "name": "नाम",
15 | "noMatch": "कोई समानता नहीं",
16 | "parentDirectory": "मूल डायरेक्टरी",
17 | "size": "माप"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/af.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "afrikaans",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "besonderhede",
6 | "download": "aflaai",
7 | "empty": "leeg",
8 | "files": "lêers",
9 | "filter": "filter",
10 | "folders": "gidse",
11 | "grid": "rooster",
12 | "icons": "ikone",
13 | "lastModified": "Laas verander",
14 | "name": "Naam",
15 | "noMatch": "geen resultaat",
16 | "parentDirectory": "Hoër Vlak",
17 | "size": "Grootte"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/fi.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "finnish",
3 |
4 | "dateFormat": "DD.MM.YYYY HH:mm",
5 | "details": "tiedot",
6 | "download": "lataa",
7 | "empty": "tyhjä",
8 | "files": "tiedostoa",
9 | "filter": "suodata",
10 | "folders": "hakemistoa",
11 | "grid": "ruudukko",
12 | "icons": "ikonit",
13 | "lastModified": "Viimeksi muokattu",
14 | "name": "Nimi",
15 | "noMatch": "ei osumia",
16 | "parentDirectory": "Ylähakemisto",
17 | "size": "Koko"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/hr.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "hrvatski",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "detalji",
6 | "download": "preuzmi",
7 | "empty": "prazno",
8 | "files": "datoteka",
9 | "filter": "filter",
10 | "folders": "direktorij(a)",
11 | "grid": "mreža",
12 | "icons": "ikone",
13 | "lastModified": "Posljednja izmjena",
14 | "name": "Naziv",
15 | "noMatch": "nema rezultata",
16 | "parentDirectory": "Natrag",
17 | "size": "Veličina"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/sl.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "slovenščina",
3 |
4 | "dateFormat": "DD. MM. YYYY HH:mm",
5 | "details": "podrobnosti",
6 | "download": "prenesi",
7 | "empty": "prazno",
8 | "files": "datoteke",
9 | "filter": "filter",
10 | "folders": "mape",
11 | "grid": "mreža",
12 | "icons": "ikone",
13 | "lastModified": "Zadnja sprememba",
14 | "name": "Ime",
15 | "noMatch": "ni zadetkov",
16 | "parentDirectory": "Nadrejena mapa",
17 | "size": "Velikost"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/bg.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "български",
3 |
4 | "dateFormat": "DD-MM-YYYY HH:mm",
5 | "details": "детайли",
6 | "download": "изтегляне",
7 | "empty": "празна",
8 | "files": "файлове",
9 | "filter": "филтър",
10 | "folders": "директории",
11 | "grid": "мрежа",
12 | "icons": "икони",
13 | "lastModified": "Последна промяна",
14 | "name": "Име",
15 | "noMatch": "няма съвпадение",
16 | "parentDirectory": "Предходна директория",
17 | "size": "Размер"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "italiano",
3 |
4 | "dateFormat": "DD-MM-YYYY HH:mm",
5 | "details": "dettagli",
6 | "download": "download",
7 | "empty": "vuota",
8 | "files": "file",
9 | "filter": "filtra",
10 | "folders": "cartelle",
11 | "grid": "griglia",
12 | "icons": "icone",
13 | "lastModified": "Ultima modifica",
14 | "name": "Nome",
15 | "noMatch": "nessun risultato",
16 | "parentDirectory": "Cartella Superiore",
17 | "size": "Dimensione"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "日本語",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "詳細",
6 | "download": "ダウンロード",
7 | "empty": "(空)",
8 | "files": "ファイル",
9 | "filter": "フィルター",
10 | "folders": "フォルダー",
11 | "grid": "グリッド",
12 | "icons": "アイコン",
13 | "language": "言語",
14 | "lastModified": "最終変更日時",
15 | "name": "名前",
16 | "noMatch": "一致する項目が見つかりません",
17 | "parentDirectory": "親ディレクトリへ",
18 | "size": "サイズ",
19 | "view": "ビュー"
20 | }
21 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/pt.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "português",
3 |
4 | "dateFormat": "DD-MM-YYYY HH:mm",
5 | "details": "detalhes",
6 | "download": "descarregar",
7 | "empty": "vazio",
8 | "files": "arquivos",
9 | "filter": "filtro",
10 | "folders": "pastas",
11 | "grid": "grelha",
12 | "icons": "ícones",
13 | "lastModified": "última modificação",
14 | "name": "Nome",
15 | "noMatch": "sem resultados",
16 | "parentDirectory": "diretório acima",
17 | "size": "Tamanho"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/uk.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "українська",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "Деталі",
6 | "download": "Завантажити",
7 | "empty": "Порожньо",
8 | "files": "Файли(ів)",
9 | "filter": "Фільтр",
10 | "folders": "Тек(и)",
11 | "grid": "Гратка",
12 | "icons": "Піктограми",
13 | "lastModified": "Останні зміни",
14 | "name": "Ім'я",
15 | "noMatch": "Немає співпадінь",
16 | "parentDirectory": "Головна тека",
17 | "size": "Розмір"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "čeština",
3 |
4 | "dateFormat": "DD.MM.YYYY HH:mm",
5 | "details": "Podrobnosti",
6 | "download": "Stáhnout",
7 | "empty": "Prázdná složka",
8 | "files": "souborů",
9 | "filter": "Filtr",
10 | "folders": "složek",
11 | "grid": "Seznam",
12 | "icons": "Velké ikony",
13 | "lastModified": "Datum změny",
14 | "name": "Název",
15 | "noMatch": "Žádná shoda",
16 | "parentDirectory": "Nadřazený adresář",
17 | "size": "Velikost"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "ελληνικά",
3 |
4 | "dateFormat": "DD/MM/YYYY HH:mm",
5 | "details": "λεπτομέρειες",
6 | "download": "μεταμόρφωση",
7 | "empty": "κενό",
8 | "files": "αρχεία",
9 | "filter": "φίλτρο",
10 | "folders": "φάκελοι",
11 | "grid": "πλέγμα",
12 | "icons": "εικονίδια",
13 | "lastModified": "Τελευταία τροποποίηση",
14 | "name": "Όνομα",
15 | "noMatch": "κανένα αποτέλεσμα",
16 | "parentDirectory": "Προηγούμενος Κατάλογος",
17 | "size": "Μέγεθος"
18 | }
19 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/ko.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "한국어",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "자세히",
6 | "download": "다운로드",
7 | "empty": "빈 폴더",
8 | "files": "파일",
9 | "filter": "필터",
10 | "folders": "폴더",
11 | "grid": "그리드",
12 | "icons": "아이콘",
13 | "language": "언어",
14 | "lastModified": "최근 수정일",
15 | "name": "파일명",
16 | "noMatch": "해당파일이 없습니다.",
17 | "parentDirectory": "상위폴더",
18 | "search": "검색",
19 | "size": "크기",
20 | "tree": "트리",
21 | "view": "보기"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "русский",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "Детали",
6 | "download": "Скачать",
7 | "empty": "Пусто",
8 | "files": "Файлы",
9 | "filter": "Фильтр",
10 | "folders": "Папки",
11 | "grid": "Сетка",
12 | "icons": "Иконки",
13 | "language": "Язык",
14 | "lastModified": "Последние изменения",
15 | "name": "Имя",
16 | "noMatch": "Нет совпадений",
17 | "parentDirectory": "Главная директория",
18 | "size": "Размер",
19 | "view": "Вид"
20 | }
21 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/zh-cn.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "简体中文",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "详情",
6 | "download": "下载",
7 | "empty": "空文件夹",
8 | "files": "文件",
9 | "filter": "过滤",
10 | "folders": "文件夹",
11 | "grid": "网格",
12 | "icons": "图标",
13 | "language": "语言",
14 | "lastModified": "修改时间",
15 | "name": "文件名",
16 | "noMatch": "无匹配项",
17 | "parentDirectory": "父文件夹",
18 | "search": "搜索",
19 | "size": "大小",
20 | "tree": "树形目录",
21 | "view": "视图",
22 | "info": "信息"
23 | }
24 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/zh-tw.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "正體中文",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "詳細資料",
6 | "download": "下載",
7 | "empty": "空資料夾",
8 | "files": "檔案",
9 | "filter": "過濾",
10 | "folders": "資料夾",
11 | "grid": "網格",
12 | "icons": "圖示",
13 | "language": "語言",
14 | "lastModified": "上次修改",
15 | "name": "檔名",
16 | "noMatch": "沒有符合的檔案",
17 | "parentDirectory": "上層目錄",
18 | "search": "搜尋",
19 | "size": "大小",
20 | "tree": "樹形目錄",
21 | "view": "檢視",
22 | "info": "資訊"
23 | }
24 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/core/store.js:
--------------------------------------------------------------------------------
1 | const store = global.window.localStorage;
2 | const storekey = '_h5ai';
3 |
4 |
5 | const load = () => {
6 | try {
7 | return JSON.parse(store[storekey]);
8 | } catch (e) {/* skip */}
9 | return {};
10 | };
11 |
12 | const save = obj => {
13 | store[storekey] = JSON.stringify(obj);
14 | };
15 |
16 | const put = (key, value) => {
17 | const obj = load();
18 | obj[key] = value;
19 | save(obj);
20 | };
21 |
22 | const get = key => load()[key];
23 |
24 |
25 | module.exports = {
26 | put,
27 | get
28 | };
29 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-session.php:
--------------------------------------------------------------------------------
1 | store = &$store;
9 | }
10 |
11 | public function set($key, $value) {
12 | $key = Session::$KEY_PREFIX . $key;
13 | $this->store[$key] = $value;
14 | }
15 |
16 | public function get($key, $default = null) {
17 | $key = Session::$KEY_PREFIX . $key;
18 | return array_key_exists($key, $this->store) ? $this->store[$key] : $default;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/filter.less:
--------------------------------------------------------------------------------
1 | #filter {
2 |
3 | input {
4 | display: none;
5 | border: none;
6 | font-size: 16px;
7 | color: @col-text;
8 | background: transparent;
9 | outline: 0;
10 | width: 160px;
11 | padding: 0 12px 0 4px;
12 | line-height: 48px;
13 | vertical-align: top;
14 | }
15 |
16 | &.active {
17 | input {
18 | display: inline-block;
19 | }
20 | }
21 |
22 | &.pending {
23 | input {
24 | color: @col-text-disabled-black;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/search.less:
--------------------------------------------------------------------------------
1 | #search {
2 |
3 | input {
4 | display: none;
5 | border: none;
6 | font-size: 16px;
7 | color: @col-text;
8 | background: transparent;
9 | outline: 0;
10 | width: 160px;
11 | padding: 0 12px 0 4px;
12 | line-height: 48px;
13 | vertical-align: top;
14 | }
15 |
16 | &.active {
17 | input {
18 | display: inline-block;
19 | }
20 | }
21 |
22 | &.pending {
23 | input {
24 | color: @col-text-disabled-black;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/util/misc.js:
--------------------------------------------------------------------------------
1 | const escapePattern = sequence => {
2 | return sequence.replace(/[\-\[\]{}()*+?.,\\$\^|#\s]/g, '\\$&');
3 | };
4 |
5 | const parsePattern = (sequence, advanced) => {
6 | if (!advanced) {
7 | return escapePattern(sequence);
8 | }
9 |
10 | if (sequence.substr(0, 3) === 're:') {
11 | return sequence.substr(3);
12 | }
13 |
14 | return sequence.trim().split(/\s+/).map(part => {
15 | return part.split('').map(char => escapePattern(char)).join('.*?');
16 | }).join('|');
17 | };
18 |
19 | module.exports = {
20 | parsePattern
21 | };
22 |
--------------------------------------------------------------------------------
/test/tests/unit/core/format.js:
--------------------------------------------------------------------------------
1 | const {test, assert} = require('scar');
2 | const format = require('../../../../src/_h5ai/public/js/lib/core/format');
3 |
4 | test('core.format', () => {
5 | assert.equal(typeof format, 'object');
6 | assert.deepEqual(Object.keys(format).sort(), ['setDefaultMetric', 'formatSize', 'setDefaultDateFormat', 'formatDate'].sort());
7 | assert.equal(typeof format.setDefaultMetric, 'function');
8 | assert.equal(typeof format.formatSize, 'function');
9 | assert.equal(typeof format.setDefaultDateFormat, 'function');
10 | assert.equal(typeof format.formatDate, 'function');
11 | });
12 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "deutsch",
3 |
4 | "dateFormat": "DD.MM.YYYY HH:mm",
5 | "details": "Details",
6 | "download": "Download",
7 | "empty": "leer",
8 | "files": "Dateien",
9 | "filter": "filtern",
10 | "folders": "Ordner",
11 | "grid": "Gitter",
12 | "icons": "Icons",
13 | "language": "Sprache",
14 | "lastModified": "Geändert",
15 | "name": "Name",
16 | "noMatch": "keine Treffer",
17 | "parentDirectory": "Übergeordnetes Verzeichnis",
18 | "search": "suchen",
19 | "size": "Größe",
20 | "tree": "Baum",
21 | "view": "Ansicht"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/et.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "estonian",
3 |
4 | "dateFormat": "DD-MM-YYYY HH.mm",
5 | "details": "täpsem info",
6 | "download": "laadi alla",
7 | "empty": "tühi",
8 | "files": "failid",
9 | "filter": "filter",
10 | "folders": "kataloogid",
11 | "grid": "võre",
12 | "icons": "ikoonid",
13 | "language": "Keel",
14 | "lastModified": "Viimati muudetud",
15 | "name": "Nimi",
16 | "noMatch": "ei leitud sobivat",
17 | "parentDirectory": "Emakataloog",
18 | "search": "otsi",
19 | "size": "Suurus",
20 | "tree": "Puu",
21 | "view": "Vaade"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "nederlands",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "details",
6 | "download": "download",
7 | "empty": "leeg",
8 | "files": "bestanden",
9 | "filter": "filter",
10 | "folders": "mappen",
11 | "grid": "grid",
12 | "icons": "iconen",
13 | "language": "Taal",
14 | "lastModified": "Laatste wijziging",
15 | "name": "Naam",
16 | "noMatch": "geen overeenkomst",
17 | "parentDirectory": "Bovenliggende map",
18 | "search": "zoeken",
19 | "size": "Grootte",
20 | "tree": "Boom",
21 | "view": "Bekijk"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "polski",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "szczegóły",
6 | "download": "pobierz",
7 | "empty": "pusty",
8 | "files": "plików",
9 | "filter": "filtr",
10 | "folders": "folderów",
11 | "grid": "kafelki",
12 | "icons": "ikony",
13 | "language": "Język",
14 | "lastModified": "Ostatnia modyfikacja",
15 | "name": "Nazwa",
16 | "noMatch": "nie znaleziono",
17 | "parentDirectory": "Katalog nadrzędny",
18 | "search": "szukaj",
19 | "size": "Rozmiar",
20 | "tree": "Drzewo",
21 | "view": "Układ"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/lv.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "latviešu",
3 |
4 | "dateFormat": "YYYY-MM-DD HH:mm",
5 | "details": "detaļas",
6 | "download": "lejupielādēt",
7 | "empty": "tukšs",
8 | "files": "faili",
9 | "filter": "filtrēt",
10 | "folders": "mapes",
11 | "grid": "režģis",
12 | "icons": "ikonas",
13 | "language": "Valoda",
14 | "lastModified": "Pēdējoreiz modificēts",
15 | "name": "Nosaukums",
16 | "noMatch": "nav sakritības",
17 | "parentDirectory": "Vecākdirektorijs",
18 | "search": "meklēt",
19 | "size": "Izmērs",
20 | "tree": "Koks",
21 | "view": "Skats"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/init.js:
--------------------------------------------------------------------------------
1 | const {dom, awaitReady} = require('./util');
2 | const config = require('./config');
3 |
4 | const name = dom('script[data-module]').attr('data-module');
5 | const query = {
6 | action: 'get',
7 | setup: true,
8 | options: true,
9 | types: true
10 | };
11 |
12 | if (name === 'index') {
13 | query.theme = true;
14 | query.langs = true;
15 | } else if (name === 'info') {
16 | query.refresh = true;
17 | } else {
18 | throw new Error(`no-main-module: '${name}'`);
19 | }
20 |
21 | config._update(query)
22 | .then(() => awaitReady())
23 | .then(() => require(`./main/${name}`));
24 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "español",
3 |
4 | "dateFormat": "DD/MM/YYYY HH:mm",
5 | "details": "Detalles",
6 | "download": "Descargar",
7 | "empty": "vacío",
8 | "files": "Archivos",
9 | "filter": "Filtrar",
10 | "folders": "Directorios",
11 | "grid": "Cuadrícula",
12 | "icons": "Íconos",
13 | "language": "Idioma",
14 | "lastModified": "Última modificación",
15 | "name": "Nombre",
16 | "noMatch": "Sin coincidencias",
17 | "parentDirectory": "Directorio superior",
18 | "search": "buscar",
19 | "size": "Tamaño",
20 | "tree": "Arbol",
21 | "view": "Vista"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "lang": "français",
3 |
4 | "dateFormat": "DD/MM/YYYY HH:mm",
5 | "details": "détails",
6 | "download": "télécharger",
7 | "empty": "vide",
8 | "files": "Fichiers",
9 | "filter": "filtrer",
10 | "folders": "Répertoires",
11 | "grid": "grille",
12 | "icons": "icônes",
13 | "language": "Langue",
14 | "lastModified": "Dernière modification",
15 | "name": "Nom",
16 | "noMatch": "rien trouvé",
17 | "parentDirectory": "Dossier parent",
18 | "search": "rechercher",
19 | "size": "Taille",
20 | "tree": "Arborescence",
21 | "view": "Disposition"
22 | }
23 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-rb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/l10n/en.json:
--------------------------------------------------------------------------------
1 | /* only here as a reference, these values are the hardcoded defaults */
2 | {
3 | "lang": "english",
4 |
5 | "dateFormat": "YYYY-MM-DD HH:mm",
6 | "details": "details",
7 | "download": "download",
8 | "empty": "empty",
9 | "files": "files",
10 | "filter": "filter",
11 | "folders": "folders",
12 | "grid": "grid",
13 | "icons": "icons",
14 | "language": "Language",
15 | "lastModified": "Last modified",
16 | "name": "Name",
17 | "noMatch": "no match",
18 | "parentDirectory": "Parent Directory",
19 | "search": "search",
20 | "size": "Size",
21 | "tree": "Tree",
22 | "view": "View"
23 | }
24 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/default/bin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/main/index.js:
--------------------------------------------------------------------------------
1 | require('../view/viewmode');
2 |
3 | require('../ext/autorefresh');
4 | require('../ext/contextmenu');
5 | require('../ext/crumb');
6 | require('../ext/custom');
7 | require('../ext/download');
8 | require('../ext/filter');
9 | require('../ext/google-analytics');
10 | require('../ext/info');
11 | require('../ext/l10n');
12 | require('../ext/piwik-analytics');
13 | require('../ext/preview');
14 | require('../ext/search');
15 | require('../ext/select');
16 | require('../ext/sort');
17 | require('../ext/thumbnails');
18 | require('../ext/title');
19 | require('../ext/tree');
20 |
21 | const href = global.window.document.location.href;
22 | require('../core/location').setLocation(href, true);
23 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-script.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/title.js:
--------------------------------------------------------------------------------
1 | const event = require('../core/event');
2 | const allsettings = require('../core/settings');
3 |
4 | const doc = global.window.document;
5 | const settings = Object.assign({
6 | enabled: false
7 | }, allsettings.title);
8 |
9 | const onLocationChanged = item => {
10 | const labels = item.getCrumb().map(i => i.label);
11 | let title = labels.join(' > ');
12 |
13 | if (labels.length > 1) {
14 | title = labels[labels.length - 1] + ' - ' + title;
15 | }
16 |
17 | doc.title = title;
18 | };
19 |
20 | const init = () => {
21 | if (!settings.enabled) {
22 | return;
23 | }
24 |
25 | event.sub('location.changed', onLocationChanged);
26 | };
27 |
28 | init();
29 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/preview-txt.less:
--------------------------------------------------------------------------------
1 | #pv-content-txt {
2 | .raised;
3 |
4 | box-sizing: border-box;
5 | max-width: 960px;
6 | text-align: left;
7 | background: @col-back-paper;
8 | margin: 0 auto;
9 | padding: 8px;
10 | overflow: auto;
11 |
12 | a, a:active, a:visited {
13 | color: #2080FF;
14 | text-decoration: none;
15 | cursor: pointer;
16 |
17 | &:hover {
18 | color: #68A9FF;
19 | }
20 | }
21 | }
22 |
23 | pre#pv-content-txt {
24 | code {
25 | line-height: 1.2em;
26 | }
27 | }
28 |
29 | div#pv-content-txt {
30 | font-size: 1.1em;
31 | padding: 8px 24px;
32 |
33 | code {
34 | color: #008200;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/core/event.js:
--------------------------------------------------------------------------------
1 | const {isStr, isFn, dom} = require('../util');
2 |
3 | const subscriptions = {};
4 |
5 | const sub = (topic, listener) => {
6 | if (isStr(topic) && isFn(listener)) {
7 | if (!subscriptions[topic]) {
8 | subscriptions[topic] = [];
9 | }
10 | subscriptions[topic].push(listener);
11 | }
12 | };
13 |
14 | const pub = (topic, ...args) => {
15 | // console.log(topic, args);
16 | if (isStr(topic) && subscriptions[topic]) {
17 | subscriptions[topic].forEach(listener => {
18 | listener.apply(topic, args);
19 | });
20 | }
21 | };
22 |
23 | dom(global.window).on('resize', () => pub('resize'));
24 |
25 | module.exports = {
26 | sub,
27 | pub
28 | };
29 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/preview-img.less:
--------------------------------------------------------------------------------
1 | #pv-content-img {
2 | .raised;
3 |
4 | @check-white: #f8f8f8;
5 | @check-black: #e8e8e8;
6 |
7 | position: absolute;
8 |
9 | max-width: 100%;
10 | max-height: 100%;
11 |
12 | background-color: @check-white;
13 | background-image:
14 | -webkit-linear-gradient(45deg, @check-black 25%, transparent 25%, transparent 75%, @check-black 75%, @check-black),
15 | -webkit-linear-gradient(45deg, @check-black 25%, transparent 25%, transparent 75%, @check-black 75%, @check-black);
16 | background-size: 60px 60px;
17 | background-position: 0 0, 30px 30px;
18 |
19 | &.loading {
20 | opacity: 0.5;
21 | margin-top: 32px;
22 | width: 240px;
23 | height: 240px;
24 | border-radius: 1000px;
25 | overflow: hidden;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "h5ai",
3 | "version": "0.29.0",
4 | "description": "Modern HTTP web server index.",
5 | "homepage": "https://larsjung.de/h5ai/",
6 | "bugs": "https://github.com/lrsjng/h5ai/issues",
7 | "author": "Lars Jung (https://larsjung.de)",
8 | "license": "MIT",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/lrsjng/h5ai.git"
12 | },
13 | "scripts": {
14 | "build": "node ghu release"
15 | },
16 | "devDependencies": {
17 | "babel-loader": "6.2.4",
18 | "babel-preset-es2015": "6.13.2",
19 | "eslint": "3.2.2",
20 | "ghu": "0.11.0",
21 | "jsdom": "9.4.2",
22 | "kjua": "0.1.1",
23 | "lolight": "0.3.0",
24 | "marked": "0.3.6",
25 | "normalize.css": "4.2.0",
26 | "null-loader": "0.1.1",
27 | "scar": "0.17.0"
28 | },
29 | "engines": {
30 | "node": ">=6.0.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-logger.php:
--------------------------------------------------------------------------------
1 | {
5 | assert.equal(typeof naturalCmp, 'function', 'is function');
6 |
7 | [
8 | '-1',
9 | '0',
10 | '00',
11 | '000',
12 | '001',
13 | '01',
14 | '02',
15 | '1',
16 | '3',
17 | 'a0',
18 | 'a00',
19 | 'a1',
20 | 'a2',
21 | 'a 0',
22 | 'a 00',
23 | 'a 000',
24 | 'a 01',
25 | 'a 1',
26 | 'a 2',
27 | 'a 3',
28 | 'a.1',
29 | 'a.1.0',
30 | 'a.1.1',
31 | 'a.1.1.0',
32 | 'a.1.10',
33 | 'z'
34 | ].forEach((b, idx, arr) => {
35 | if (idx === 0) {
36 | return;
37 | }
38 | const a = arr[idx - 1];
39 | assert.equal(naturalCmp(a, b), -1, `fix#${idx} - ${insp(a)} < ${insp(b)}`);
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/autorefresh.js:
--------------------------------------------------------------------------------
1 | const event = require('../core/event');
2 | const location = require('../core/location');
3 | const allsettings = require('../core/settings');
4 |
5 | const win = global.window;
6 | const settings = Object.assign({
7 | enabled: false,
8 | interval: 5000
9 | }, allsettings.autorefresh);
10 | let timeoutId = null;
11 |
12 |
13 | const heartbeat = () => {
14 | location.refresh();
15 | };
16 |
17 | const before = () => {
18 | win.clearTimeout(timeoutId);
19 | };
20 |
21 | const after = () => {
22 | win.clearTimeout(timeoutId);
23 | timeoutId = win.setTimeout(heartbeat, settings.interval);
24 | };
25 |
26 | const init = () => {
27 | if (!settings.enabled) {
28 | return;
29 | }
30 |
31 | settings.interval = Math.max(1000, settings.interval);
32 |
33 | event.sub('location.beforeChange', before);
34 | event.sub('location.beforeRefresh', before);
35 | event.sub('location.changed', after);
36 | event.sub('location.refreshed', after);
37 | };
38 |
39 |
40 | init();
41 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/view-icons.less:
--------------------------------------------------------------------------------
1 | #view.view-icons {
2 |
3 | .view-icons-sized(@size) {
4 |
5 | .item {
6 | width: @size * 4/3;
7 | }
8 |
9 | .landscape {
10 | width: @size * 4/3;
11 | height: @size;
12 |
13 | img {
14 | width: @size;
15 | height: @size;
16 | }
17 |
18 | .thumb {
19 | width: @size * 4/3;
20 | }
21 | }
22 | }
23 |
24 | margin: 28px;
25 |
26 | .item {
27 | .rounded;
28 | overflow: hidden;
29 | float: left;
30 | margin: 8px;
31 |
32 | &:hover {
33 | .raised;
34 | }
35 | }
36 |
37 | .landscape {
38 | display: block;
39 | background: @col-back-panel;
40 | }
41 |
42 | .label {
43 | padding: 0 6px;
44 | line-height: 24px;
45 | text-align: center;
46 | }
47 |
48 | .date, .size {
49 | display: none;
50 | }
51 |
52 | .view-icons-sized(96px);
53 | }
54 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-theme.php:
--------------------------------------------------------------------------------
1 | context = $context;
9 | }
10 |
11 | public function get_icons() {
12 | $public_path = $this->context->get_setup()->get('PUBLIC_PATH');
13 | $theme = $this->context->query_option('view.theme', '-NONE-');
14 | $theme_path = $public_path . '/images/themes/' . $theme;
15 |
16 | $icons = [];
17 |
18 | if (is_dir($theme_path)) {
19 | if ($dir = opendir($theme_path)) {
20 | while (($name = readdir($dir)) !== false) {
21 | $path_parts = pathinfo($name);
22 | if (in_array(@$path_parts['extension'], Theme::$EXTENSIONS)) {
23 | $icons[$path_parts['filename']] = $theme . '/' . $name;
24 | }
25 | }
26 | closedir($dir);
27 | }
28 | }
29 |
30 | return $icons;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/view/base.js:
--------------------------------------------------------------------------------
1 | const {dom} = require('../util');
2 |
3 | const rootSelector = 'body';
4 | const topbarTpl =
5 | ``;
13 | const mainrowTpl =
14 | ``;
17 |
18 | const init = () => {
19 | const $root = dom(rootSelector)
20 | .attr('id', 'root')
21 | .clr()
22 | .app(topbarTpl)
23 | .app(mainrowTpl);
24 |
25 | return {
26 | $root,
27 | $topbar: $root.find('#topbar'),
28 | $toolbar: $root.find('#toolbar'),
29 | $flowbar: $root.find('#flowbar'),
30 | $mainrow: $root.find('#mainrow'),
31 | $content: $root.find('#content')
32 | };
33 | };
34 |
35 | module.exports = init();
36 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/core/resource.js:
--------------------------------------------------------------------------------
1 | const {includes} = require('../util');
2 | const config = require('../config');
3 | const settings = require('./settings');
4 |
5 | const imagesHref = settings.publicHref + 'images/';
6 | const uiHref = imagesHref + 'ui/';
7 | const themesHref = imagesHref + 'themes/';
8 | const defaultThemeHref = themesHref + 'default/';
9 | const defaultIcons = ['file', 'folder', 'folder-page', 'folder-parent', 'ar', 'aud', 'bin', 'img', 'txt', 'vid', 'x'];
10 |
11 |
12 | const image = id => uiHref + id + '.svg';
13 |
14 | const icon = id => {
15 | const baseId = (id || '').split('-')[0];
16 | const href = config.theme[id] || config.theme[baseId];
17 |
18 | if (href) {
19 | return themesHref + href;
20 | }
21 |
22 | if (includes(defaultIcons, id)) {
23 | return defaultThemeHref + id + '.svg';
24 | }
25 |
26 | if (includes(defaultIcons, baseId)) {
27 | return defaultThemeHref + baseId + '.svg';
28 | }
29 |
30 | return defaultThemeHref + 'file.svg';
31 | };
32 |
33 |
34 | module.exports = {
35 | image,
36 | icon
37 | };
38 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/info.less:
--------------------------------------------------------------------------------
1 | #info {
2 | overflow: auto;
3 | flex: 0 0 auto;
4 | order: 99;
5 |
6 | padding: 32px 32px 32px 48px;
7 | white-space: nowrap;
8 | overflow-x: hidden;
9 | width: 240px;
10 |
11 | .icon {
12 | width: 240px;
13 | height: 180px;
14 |
15 | img {
16 | .rounded;
17 | display: block;
18 | overflow: hidden;
19 | margin: 0 auto;
20 | width: 180px;
21 | height: 180px;
22 | }
23 |
24 | .thumb {
25 | width: 240px;
26 | }
27 | }
28 |
29 | .block {
30 | border-top: 1px solid @col-border;
31 | border-bottom: 1px solid @col-border;
32 | margin: 0 0 24px 0;
33 | padding: 24px 0;
34 | }
35 |
36 | .label {
37 | font-size: 16px;
38 | margin-bottom: 16px;
39 | }
40 |
41 | .time, .size, .content {
42 | line-height: 20px;
43 | height: 20px;
44 | }
45 |
46 | .qrcode {
47 | margin: 0 auto;
48 | width: 200px;
49 |
50 | img {
51 | display: block;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-php.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-html.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/core/types.js:
--------------------------------------------------------------------------------
1 | const {each, map} = require('../util');
2 | const config = require('../config');
3 |
4 | const reEndsWithSlash = /\/$/;
5 | const regexps = {};
6 |
7 |
8 | const escapeRegExp = sequence => {
9 | return sequence.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$]/g, '\\$&');
10 | // return sequence.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
11 | };
12 |
13 | const parse = types => {
14 | each(types, (patterns, type) => {
15 | const pattern = '^(' + map(patterns, p => '(' + escapeRegExp(p).replace(/\*/g, '.*') + ')').join('|') + ')$';
16 | regexps[type] = new RegExp(pattern, 'i');
17 | });
18 | };
19 |
20 | const getType = sequence => {
21 | if (reEndsWithSlash.test(sequence)) {
22 | return 'folder';
23 | }
24 |
25 | const slashidx = sequence.lastIndexOf('/');
26 | const name = slashidx >= 0 ? sequence.substr(slashidx + 1) : sequence;
27 | let result;
28 |
29 | each(regexps, (regexp, type) => {
30 | if (regexps[type].test(name)) {
31 | result = type;
32 | }
33 | });
34 |
35 | return result ? result : 'file';
36 | };
37 |
38 |
39 | parse(Object.assign({}, config.types));
40 |
41 | module.exports = {
42 | getType
43 | };
44 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/google-analytics.js:
--------------------------------------------------------------------------------
1 | const {map} = require('../util');
2 | const event = require('../core/event');
3 | const allsettings = require('../core/settings');
4 |
5 | const win = global.window;
6 | const settings = Object.assign({
7 | enabled: false,
8 | id: 'UA-000000-0'
9 | }, allsettings['google-analytics-ua']);
10 |
11 | const snippet = () => {
12 | /* eslint-disable */
13 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
14 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
15 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
16 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
17 | /* eslint-enable */
18 | };
19 |
20 | const init = () => {
21 | if (!settings.enabled) {
22 | return;
23 | }
24 |
25 | snippet();
26 |
27 | win.ga('create', settings.id, 'auto');
28 |
29 | event.sub('location.changed', item => {
30 | const loc = win.location;
31 | win.ga('send', 'pageview', {
32 | location: loc.protocol + '//' + loc.host + item.absHref,
33 | title: map(item.getCrumb(), i => i.label).join(' > ')
34 | });
35 | });
36 | };
37 |
38 | init();
39 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-css.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/server.js:
--------------------------------------------------------------------------------
1 | const {each, dom} = require('./util');
2 | const XHR = global.window.XMLHttpRequest;
3 |
4 | const request = data => {
5 | return new Promise(resolve => {
6 | const xhr = new XHR();
7 | const onReadyStateChange = () => {
8 | if (xhr.readyState === XHR.DONE) {
9 | try {
10 | resolve(JSON.parse(xhr.responseText));
11 | } catch (err) {
12 | resolve({err, txt: xhr.responseText});
13 | }
14 | }
15 | };
16 |
17 | xhr.open('POST', '?', true);
18 | xhr.onreadystatechange = onReadyStateChange;
19 | xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8');
20 | xhr.send(JSON.stringify(data));
21 | });
22 | };
23 |
24 | const formRequest = data => {
25 | const $form = dom('');
26 |
27 | each(data, (val, key) => {
28 | dom('')
29 | .attr('name', key)
30 | .attr('value', val)
31 | .appTo($form);
32 | });
33 |
34 | $form.appTo('body');
35 | $form[0].submit();
36 | $form.rm();
37 | };
38 |
39 | module.exports = {
40 | request,
41 | formRequest
42 | };
43 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/custom.js:
--------------------------------------------------------------------------------
1 | const marked = require('marked');
2 | const {each, dom} = require('../util');
3 | const server = require('../server');
4 | const event = require('../core/event');
5 | const allsettings = require('../core/settings');
6 |
7 |
8 | const settings = Object.assign({
9 | enabled: false
10 | }, allsettings.custom);
11 |
12 | const update = (data, key) => {
13 | const $el = dom(`#content-${key}`);
14 |
15 | if (data && data[key].content) {
16 | let content = data[key].content;
17 | if (data[key].type === 'md') {
18 | content = marked(content);
19 | }
20 | $el.html(content).show();
21 | } else {
22 | $el.hide();
23 | }
24 | };
25 |
26 | const onLocationChanged = item => {
27 | server.request({action: 'get', custom: item.absHref}).then(response => {
28 | const data = response && response.custom;
29 | each(['header', 'footer'], key => update(data, key));
30 | });
31 | };
32 |
33 | const init = () => {
34 | if (!settings.enabled) {
35 | return;
36 | }
37 |
38 | dom('').hide().preTo('#content');
39 | dom('').hide().appTo('#content');
40 |
41 | event.sub('location.changed', onLocationChanged);
42 | };
43 |
44 |
45 | init();
46 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/crumb.less:
--------------------------------------------------------------------------------
1 | #crumbbar {
2 | overflow: hidden;
3 | height: 48px;
4 | font-size: 16px;
5 | padding: 0 8px;
6 | // border-left: 1px solid rgba(0,0,0,0.05);
7 |
8 | a, a:active, a:visited {
9 | color: @col-text;
10 | cursor: pointer;
11 | text-decoration: none;
12 |
13 | &.active {
14 | font-weight: bold;
15 | }
16 | &:hover {
17 | color: @col-text-hover;
18 | }
19 | &:focus {
20 | outline: 0;
21 | }
22 | }
23 | .crumb {
24 | .eased-transition;
25 | display: inline-block;
26 | }
27 | .sep {
28 | width: 24px;
29 | height: 24px;
30 | padding: 12px 0;
31 | line-height: 48px;
32 | display: inline-block;
33 | vertical-align: top;
34 | }
35 | .crumb:first-of-type .sep {
36 | width: 0;
37 | }
38 | .label {
39 | line-height: 48px;
40 | display: inline-block;
41 | vertical-align: top;
42 | padding: 0 8px;
43 | }
44 | .hint {
45 | width: 20px;
46 | height: 20px;
47 | padding: 16px 0 0 0;
48 | line-height: 48px;
49 | display: inline-block;
50 | vertical-align: top;
51 | position: relative;
52 | top: -2px;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/pages/page.tpl.pug:
--------------------------------------------------------------------------------
1 | block init
2 |
3 |
4 | doctype html
5 | html(class='no-js', lang='en')
6 |
7 | head
8 | meta(charset='utf-8')
9 | meta(http-equiv='x-ua-compatible', content='ie=edge')
10 | title #{title}
11 | meta(name='description', content=title)
12 | meta(name='viewport', content='width=device-width, initial-scale=1')
13 | link(rel='shortcut icon', href!='= $public_href; ?>images/favicon/favicon-16-32.ico')
14 | link(rel='apple-touch-icon-precomposed', type='image/png', href!='= $public_href; ?>images/favicon/favicon-152.png')
15 | link(rel='stylesheet', href!='= $public_href; ?>css/styles.css')
16 |
17 | script(src!='= $public_href; ?>js/scripts.js', data-module=module)
18 |
19 | = $x_head_tags; ?>
20 |
21 | body#root(class=module)
22 |
23 | div#fallback-hints
24 |
25 | span.noJsMsg Works best with JavaScript enabled!
26 | span.noBrowserMsg Works best in #[a(href='http://browsehappy.com') modern browsers]!
27 |
28 | span.backlink #[a(href=pkg.homepage, title=`h5ai v${pkg.version} - ${pkg.description}`) powered by h5ai]
29 |
30 | block body
31 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/preview/preview-aud.js:
--------------------------------------------------------------------------------
1 | const {dom} = require('../../util');
2 | const format = require('../../core/format');
3 | const allsettings = require('../../core/settings');
4 | const preview = require('./preview');
5 |
6 | const settings = Object.assign({
7 | enabled: false,
8 | autoplay: true,
9 | types: []
10 | }, allsettings['preview-aud']);
11 | const tpl = '';
12 |
13 | const updateGui = () => {
14 | const el = dom('#pv-content-aud')[0];
15 | if (!el) {
16 | return;
17 | }
18 |
19 | preview.setLabels([
20 | preview.item.label,
21 | format.formatDate(el.duration * 1000, 'm:ss')
22 | ]);
23 | };
24 |
25 | const addUnloadFn = el => {
26 | el.unload = () => {
27 | el.src = '';
28 | el.load();
29 | };
30 | };
31 |
32 | const load = item => {
33 | return new Promise(resolve => {
34 | const $el = dom(tpl)
35 | .on('loadedmetadata', () => resolve($el))
36 | .attr('controls', 'controls');
37 | if (settings.autoplay) {
38 | $el.attr('autoplay', 'autoplay');
39 | }
40 | addUnloadFn($el[0]);
41 | $el.attr('src', item.absHref);
42 | });
43 | };
44 |
45 | const init = () => {
46 | if (settings.enabled) {
47 | preview.register(settings.types, load, updateGui);
48 | }
49 | };
50 |
51 | init();
52 |
--------------------------------------------------------------------------------
/test/tests/unit/util/parsePatten.js:
--------------------------------------------------------------------------------
1 | const {test, assert, insp} = require('scar');
2 | const {parsePattern} = require('../../../../src/_h5ai/public/js/lib/util');
3 |
4 | test('util.parsePattern()', () => {
5 | assert.equal(typeof parsePattern, 'function', 'is function');
6 |
7 | [
8 | ['', false, ''],
9 | [' ', false, '\\ '],
10 | ['a', false, 'a'],
11 | ['ä', false, 'ä'],
12 | ['á', false, 'á'],
13 | ['*', false, '\\*'],
14 | ['ab', false, 'ab'],
15 | ['rea', false, 'rea'],
16 | ['re:', false, 're:'],
17 | ['re:a', false, 're:a'],
18 | ['a b', false, 'a\\ b'],
19 | ['ab c', false, 'ab\\ c'],
20 | [' a ', false, '\\ a\\ '],
21 |
22 | ['', true, ''],
23 | [' ', true, ''],
24 | ['a', true, 'a'],
25 | ['ä', true, 'ä'],
26 | ['á', true, 'á'],
27 | ['*', true, '\\*'],
28 | ['ab', true, 'a.*?b'],
29 | ['rea', true, 'r.*?e.*?a'],
30 | [' re:', true, 'r.*?e.*?:'],
31 | ['are:', true, 'a.*?r.*?e.*?:'],
32 | ['re:', true, ''],
33 | ['re:a', true, 'a'],
34 | ['a b', true, 'a|b'],
35 | ['ab c', true, 'a.*?b|c'],
36 | [' a ', true, 'a']
37 | ].forEach(([pattern, advanced, exp], idx) => {
38 | assert.equal(parsePattern(pattern, advanced), exp, `fix#${idx} - (${insp(pattern)}, ${insp(advanced)}) -> ${insp(exp)}`);
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/ext/class-search.php:
--------------------------------------------------------------------------------
1 | context = $context;
8 | }
9 |
10 | public function get_paths($root, $pattern = null, $ignorecase = false) {
11 | $paths = [];
12 | if ($pattern && $this->context->is_managed_path($root)) {
13 | $re = Util::wrap_pattern($pattern);
14 | if ($ignorecase) {
15 | $re .= 'i';
16 | }
17 | $names = $this->context->read_dir($root);
18 | foreach ($names as $name) {
19 | $path = $root . '/' . $name;
20 | if (preg_match($re, @basename($path))) {
21 | $paths[] = $path;
22 | }
23 | if (@is_dir($path)) {
24 | $paths = array_merge($paths, $this->get_paths($path, $pattern, $ignorecase));
25 | }
26 | }
27 | }
28 | return $paths;
29 | }
30 |
31 | public function get_items($href, $pattern = null, $ignorecase = false) {
32 | $cache = [];
33 | $root = $this->context->to_path($href);
34 | $paths = $this->get_paths($root, $pattern, $ignorecase);
35 | $items = array_map(function ($path) {
36 | return Item::get($this->context, $path, $cache)->to_json_object();
37 | }, $paths);
38 | return $items;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/colors.less:
--------------------------------------------------------------------------------
1 | @col-red-500: #f44336;
2 | @col-green-500: #4caf50;
3 | @col-blue-400: #42a5f5;
4 | @col-pink-a200: #ff4081;
5 |
6 | @col-white: #ffffff;
7 | @col-grey-50: #fafafa;
8 | @col-grey-100: #f5f5f5;
9 | @col-grey-300: #e0e0e0;
10 | @col-grey-700: #616161;
11 | @col-grey-900: #212121;
12 |
13 | @col-text-primary-black: rgba(0,0,0,0.87);
14 | @col-text-secondary-black: rgba(0,0,0,0.54);
15 | @col-text-disabled-black: rgba(0,0,0,0.26);
16 | @col-divider-black: rgba(0,0,0,0.12);
17 |
18 | @col-text-primary-white: rgba(255,255,255,1.0);
19 | @col-text-secondary-white: rgba(255,255,255,0.7);
20 | @col-text-disabled-white: rgba(255,255,255,0.3);
21 | @col-divider-white: rgba(255,255,255,0.12);
22 |
23 | @col-text-selected: @col-text-primary-white;
24 | @col-back-selected: @col-blue-400;
25 |
26 | @col-text-native-selection: @col-text-primary-white;
27 | @col-back-native-selection: @col-pink-a200;
28 |
29 | @col-text: @col-text-primary-black;
30 | @col-back: @col-white;
31 | @col-back-paper: @col-white;
32 | @col-back-panel: @col-grey-50;
33 | @col-text-hover: @col-blue-400;
34 | @col-back-hover: rgba(0,0,0,0.03);
35 | @col-border: rgba(0,0,0,0.05);
36 | @col-border-strong: rgba(0,0,0,0.15);
37 | @col-border-stronger: rgba(0,0,0,0.3);
38 |
39 | @col-okay: @col-green-500;
40 | @col-error: @col-red-500;
41 |
42 | @col-link: @col-blue-400;
43 | @col-link-hover: @col-grey-900;
44 |
45 | @col-range-back: @col-grey-300;
46 | @col-range-thumb: @col-grey-700;
47 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/piwik-analytics.js:
--------------------------------------------------------------------------------
1 | const {dom, awaitLoad} = require('../util');
2 | const event = require('../core/event');
3 | const allsettings = require('../core/settings');
4 |
5 | const win = global.window;
6 | const settings = Object.assign({
7 | enabled: false,
8 | baseURL: 'not-set',
9 | idSite: 0
10 | }, allsettings['piwik-analytics']);
11 |
12 | const init = () => {
13 | if (!settings.enabled) {
14 | return;
15 | }
16 |
17 | // reference: http://piwik.org/docs/javascript-tracking/
18 |
19 | const pkBaseURL = (win.location.protocol === 'https:' ? 'https://' : 'http://') + settings.baseURL + '/';
20 | const queue = [];
21 | let piwikTracker = null;
22 |
23 | dom('').attr('src', pkBaseURL + 'piwik.js').appTo('body');
24 | awaitLoad().then(() => {
25 | piwikTracker = win.Piwik && win.Piwik.getTracker(pkBaseURL + 'piwik.php', settings.idSite);
26 | if (piwikTracker) {
27 | piwikTracker.enableLinkTracking();
28 | while (queue.length) {
29 | piwikTracker.trackPageView(queue.shift());
30 | }
31 | }
32 | });
33 |
34 | event.sub('location.changed', item => {
35 | const title = item.getCrumb().map(i => i.label).join(' > ');
36 | if (piwikTracker) {
37 | piwikTracker.trackPageView(title);
38 | } else {
39 | queue.push(title);
40 | }
41 | });
42 | };
43 |
44 |
45 | init();
46 |
--------------------------------------------------------------------------------
/test/util/pin.js:
--------------------------------------------------------------------------------
1 | const win = global.window;
2 | const doc = win.document;
3 |
4 | const pinned = {};
5 |
6 | const attr = (el, name, value) => {
7 | if (typeof el === 'string') {
8 | el = doc.querySelector(el);
9 | }
10 | if (value === undefined) {
11 | return el.getAttribute(name);
12 | }
13 | if (value === null) {
14 | return el.removeAttribute(name);
15 | }
16 | return el.setAttribute(name, value);
17 | };
18 |
19 | const rootChildren = () => {
20 | return [
21 | ...doc.querySelector('head').childNodes,
22 | ...doc.querySelector('body').childNodes
23 | ];
24 | };
25 |
26 | const pinHtml = () => {
27 | pinned.title = doc.title;
28 | pinned.htmlId = attr('html', 'id');
29 | pinned.htmlClasses = attr('html', 'class');
30 | pinned.bodyId = attr('body', 'id');
31 | pinned.bodyClasses = attr('body', 'class');
32 | pinned.els = rootChildren();
33 | // console.log('pinned', pinned);
34 | };
35 |
36 | const restoreHtml = () => {
37 | doc.title = pinned.title;
38 | attr('html', 'id', pinned.htmlId);
39 | attr('html', 'class', pinned.htmlClasses);
40 | attr('body', 'id', pinned.bodyId);
41 | attr('body', 'class', pinned.bodyClasses);
42 | rootChildren().forEach(el => {
43 | if (pinned.els.indexOf(el) < 0) {
44 | el.remove();
45 | }
46 | });
47 | // win.localStorage.clear();
48 | };
49 |
50 | module.exports = {
51 | pinHtml,
52 | restoreHtml
53 | };
54 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/preview/preview-vid.js:
--------------------------------------------------------------------------------
1 | const {dom} = require('../../util');
2 | const allsettings = require('../../core/settings');
3 | const preview = require('./preview');
4 |
5 | const settings = Object.assign({
6 | enabled: false,
7 | autoplay: true,
8 | types: []
9 | }, allsettings['preview-vid']);
10 | const tpl = '';
11 |
12 | const updateGui = () => {
13 | const el = dom('#pv-content-vid')[0];
14 | if (!el) {
15 | return;
16 | }
17 |
18 | const elW = el.offsetWidth;
19 | const elVW = el.videoWidth;
20 | const elVH = el.videoHeight;
21 |
22 | preview.setLabels([
23 | preview.item.label,
24 | String(elVW) + 'x' + String(elVH),
25 | String((100 * elW / elVW).toFixed(0)) + '%'
26 | ]);
27 | };
28 |
29 | const addUnloadFn = el => {
30 | el.unload = () => {
31 | el.src = '';
32 | el.load();
33 | };
34 | };
35 |
36 | const load = item => {
37 | return new Promise(resolve => {
38 | const $el = dom(tpl)
39 | .on('loadedmetadata', () => resolve($el))
40 | .attr('controls', 'controls');
41 | if (settings.autoplay) {
42 | $el.attr('autoplay', 'autoplay');
43 | }
44 | addUnloadFn($el[0]);
45 | $el.attr('src', item.absHref);
46 | });
47 | };
48 |
49 | const init = () => {
50 | if (settings.enabled) {
51 | preview.register(settings.types, load, updateGui);
52 | }
53 | };
54 |
55 | init();
56 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-request.php:
--------------------------------------------------------------------------------
1 | params = $data !== null ? $data : $params;
9 | }
10 |
11 | public function query($keypath = '', $default = Util::NO_DEFAULT) {
12 | $value = Util::array_query($this->params, $keypath, Util::NO_DEFAULT);
13 |
14 | if ($value === Util::NO_DEFAULT) {
15 | Util::json_fail(Util::ERR_MISSING_PARAM, 'parameter \'' . $keypath . '\' is missing', $default === Util::NO_DEFAULT);
16 | return $default;
17 | }
18 |
19 | return $value;
20 | }
21 |
22 | public function query_boolean($keypath = '', $default = Util::NO_DEFAULT) {
23 | $value = $this->query($keypath, $default);
24 | return filter_var($value, FILTER_VALIDATE_BOOLEAN);
25 | }
26 |
27 | public function query_numeric($keypath = '', $default = Util::NO_DEFAULT) {
28 | $value = $this->query($keypath, $default);
29 | Util::json_fail(Util::ERR_ILLIGAL_PARAM, 'parameter \'' . $keypath . '\' is not numeric', !is_numeric($value));
30 | return intval($value, 10);
31 | }
32 |
33 | public function query_array($keypath = '', $default = Util::NO_DEFAULT) {
34 | $value = $this->query($keypath, $default);
35 | Util::json_fail(Util::ERR_ILLIGAL_PARAM, 'parameter \'' . $keypath . '\' is no array', !is_array($value));
36 | return $value;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/topbar.less:
--------------------------------------------------------------------------------
1 | #topbar {
2 | .flex-row;
3 | overflow: hidden;
4 | flex: 0 0 auto;
5 | order: 1;
6 |
7 | background: @col-back-panel;
8 | border-bottom: 1px solid @col-border;
9 | z-index: 1;
10 | }
11 |
12 | #toolbar {
13 | overflow: hidden;
14 | flex: 0 0 auto;
15 | order: 1;
16 | height: 48px;
17 |
18 | .tool {
19 | .eased-transition;
20 | display: inline-block;
21 | cursor: pointer;
22 |
23 | img {
24 | display: inline-block;
25 | width: 24px;
26 | height: 24px;
27 | padding: 12px;
28 | }
29 |
30 | &:hover {
31 | background: @col-back-hover;
32 | }
33 | }
34 | }
35 |
36 | #flowbar {
37 | overflow: hidden;
38 | flex: 1 1 auto;
39 | order: 2;
40 | height: 48px;
41 | }
42 |
43 | #backlink {
44 | .eased-transition;
45 | display: block;
46 | overflow: hidden;
47 | flex: 0 0 auto;
48 | order: 99;
49 | text-align: center;
50 | padding: 6px 12px;
51 | overflow: hidden;
52 | height: 36px;
53 |
54 | &, &:active, &:visited {
55 | color: @col-text-disabled-black;
56 | cursor: pointer;
57 | text-decoration: none;
58 | }
59 | &:hover {
60 | color: @col-text-hover;
61 | background: @col-back-hover;
62 | }
63 | &:focus {
64 | outline: 0;
65 | }
66 |
67 | div {
68 | line-height: 18px;
69 | white-space: nowrap;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/mixins.less:
--------------------------------------------------------------------------------
1 | .hidden {
2 | display: none !important;
3 | }
4 |
5 | .invisible {
6 | visibility: hidden;
7 | }
8 |
9 | .clearfix:before,
10 | .clearfix:after {
11 | content: " ";
12 | display: table;
13 | }
14 | .clearfix:after {
15 | clear: both;
16 | }
17 |
18 | .raised {
19 | box-shadow: 0 1px 10px 0 rgba(0,0,0,0.5);
20 | }
21 |
22 | .popup {
23 | box-shadow: 0 1px 20px 0 rgba(0,0,0,0.5);
24 | }
25 |
26 | .rounded {
27 | border-radius: 2px;
28 | }
29 |
30 | .clear-appearance {
31 | -moz-appearance: none;
32 | -ms-appearance: none;
33 | -webkit-appearance: none;
34 | }
35 |
36 | .eased-transition {
37 | transition: all 0.2s ease-in-out;
38 | }
39 |
40 | .flex-base {
41 | display: flex;
42 | flex-wrap: nowrap;
43 | justify-content: flex-start;
44 | align-content: flex-start;
45 | align-items: stretch;
46 | }
47 |
48 | .flex-column {
49 | .flex-base;
50 | flex-direction: column;
51 | }
52 |
53 | .flex-row {
54 | .flex-base;
55 | flex-direction: row;
56 | }
57 |
58 | .el-button {
59 | .rounded;
60 | .eased-transition;
61 | color: @col-text-primary-white;
62 | background: @col-blue-400;
63 | cursor: pointer;
64 | text-decoration: none;
65 |
66 | &:hover {
67 | .raised;
68 | }
69 | }
70 |
71 | .el-input {
72 | .rounded;
73 | .clear-appearance;
74 | background: @col-back-paper;
75 | border: none;
76 | outline: none;
77 | background: @col-back-panel;
78 | border: 1px solid @col-border;
79 | }
80 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-py.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/contextmenu.less:
--------------------------------------------------------------------------------
1 | #cm-overlay {
2 | position: fixed;
3 | left: 0;
4 | top: 0;
5 | right: 0;
6 | bottom: 0;
7 | overflow: hidden;
8 | z-index: 200;
9 |
10 | .cm-panel {
11 | .popup;
12 | .rounded;
13 | display: block;
14 | position: absolute;
15 | left: 100px;
16 | top: 100px;
17 | color: @col-text;
18 | background: @col-back-paper;
19 | z-index: 10;
20 | overflow: auto;
21 | min-width: 200px;
22 |
23 | ul {
24 | margin: 0;
25 | padding: 0;
26 | list-style: none;
27 | text-align: left;
28 | }
29 | }
30 |
31 | .cm-label {
32 | padding: 8px 16px;
33 | white-space: nowrap;
34 | font-weight: bold;
35 | }
36 |
37 | .cm-entry {
38 | padding: 8px 16px;
39 | white-space: nowrap;
40 | cursor: pointer;
41 |
42 | &:hover {
43 | color: @col-text-hover;
44 | background: @col-back-hover;
45 | }
46 | }
47 |
48 | .cm-icon {
49 | position: relative;
50 | top: -2px;
51 |
52 | img {
53 | width: 20px;
54 | height: 20px;
55 | }
56 |
57 | &.no-icon {
58 | opacity: 0;
59 | }
60 | }
61 |
62 | .cm-text {
63 | margin: 0 0 0 12px;
64 | }
65 |
66 | .cm-sep {
67 | height: 1px;
68 | margin: 8px 0;
69 | padding: 0;
70 | border-top: 1px solid rgba(0,0,0,0.08);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/view/sidebar.js:
--------------------------------------------------------------------------------
1 | const {dom} = require('../util');
2 | const resource = require('../core/resource');
3 | const allsettings = require('../core/settings');
4 | const store = require('../core/store');
5 | const base = require('./base');
6 |
7 |
8 | const settings = Object.assign({
9 | disableSidebar: false
10 | }, allsettings.view);
11 | const storekey = 'sidebarIsVisible';
12 | const sidebarTpl = '';
13 | const toggleTpl =
14 | ``;
17 |
18 |
19 | const init = () => {
20 | const $sidebar = dom(sidebarTpl).hide();
21 | const $toggle = dom(toggleTpl);
22 | const $img = $toggle.find('img');
23 |
24 | const update = toggle => {
25 | let isVisible = store.get(storekey);
26 |
27 | if (toggle) {
28 | isVisible = !isVisible;
29 | store.put(storekey, isVisible);
30 | }
31 |
32 | if (isVisible) {
33 | $toggle.addCls('active');
34 | $img.attr('src', resource.image('back'));
35 | $sidebar.show();
36 | } else {
37 | $toggle.rmCls('active');
38 | $img.attr('src', resource.image('sidebar'));
39 | $sidebar.hide();
40 | }
41 | };
42 |
43 | if (!settings.disableSidebar) {
44 | $sidebar.appTo(base.$mainrow);
45 | $toggle.appTo(base.$toolbar).on('click', () => update(true));
46 | update();
47 | }
48 |
49 | return {
50 | $el: $sidebar
51 | };
52 | };
53 |
54 | module.exports = init();
55 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/view.less:
--------------------------------------------------------------------------------
1 | #view {
2 |
3 | a, a:active, a:visited {
4 | display: block;
5 | color: inherit;
6 | cursor: pointer;
7 | text-decoration: none;
8 | }
9 |
10 | ul {
11 | margin: 0;
12 | padding: 0;
13 | list-style: none;
14 | }
15 |
16 | .header {
17 | display: none;
18 | }
19 |
20 | .item {
21 | position: relative;
22 | white-space: nowrap;
23 | background: @col-back-paper;
24 |
25 | &:hover {
26 | color: @col-text-hover;
27 | background: @col-back-panel;
28 | }
29 | }
30 |
31 | .folder-parent {
32 |
33 | .date, .size {
34 | display: none;
35 | }
36 | }
37 |
38 | .icon {
39 | display: none;
40 | text-align: center;
41 |
42 | img {
43 | position: relative;
44 | top: 50%;
45 | transform: translateY(-50%);
46 | }
47 |
48 | .thumb {
49 | max-width: none;
50 | max-height: none;
51 | }
52 | }
53 |
54 | .label {
55 | display: block;
56 | overflow: hidden;
57 | text-align: left;
58 | text-overflow: ellipsis;
59 | }
60 |
61 | .date {
62 | text-align: right;
63 | width: 130px;
64 | }
65 |
66 | .size {
67 | text-align: right;
68 | width: 80px;
69 | }
70 |
71 | #view-hint {
72 | display: block;
73 | margin-top: 36px;
74 | text-align: center;
75 | color: @col-border;
76 | font-size: 5em;
77 | font-weight: bold;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/class-bootstrap.php:
--------------------------------------------------------------------------------
1 | query_boolean('refresh', false));
16 | $context = new Context($session, $request, $setup);
17 |
18 | if ($context->is_api_request()) {
19 | (new Api($context))->apply();
20 | } elseif ($context->is_info_request()) {
21 | $public_href = $setup->get('PUBLIC_HREF');
22 | $x_head_tags = $context->get_x_head_html();
23 | $fallback_mode = false;
24 | require __DIR__ . '/pages/info.php';
25 | } else {
26 | $public_href = $setup->get('PUBLIC_HREF');
27 | $x_head_tags = $context->get_x_head_html();
28 | $fallback_mode = $context->is_fallback_mode();
29 | $fallback_html = (new Fallback($context))->get_html();
30 | require __DIR__ . '/pages/index.php';
31 | }
32 | }
33 |
34 | public static function autoload($class_name) {
35 | $filename = 'class-' . strtolower($class_name) . '.php';
36 |
37 | foreach (Bootstrap::$autopaths as $path) {
38 | $file = __DIR__ . '/' . $path . '/' . $filename;
39 | if (file_exists($file)) {
40 | require_once $file;
41 | return true;
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/select.less:
--------------------------------------------------------------------------------
1 | #selection-rect {
2 | position: absolute;
3 | left: 0;
4 | top: 0;
5 | z-index: 2;
6 | border: 1px dashed @col-border-strong;
7 | background: rgba(0,0,0,0.1);
8 | }
9 |
10 | html.drag-select, html.drag-select * {
11 | cursor: move !important;
12 |
13 | -webkit-touch-callout: none;
14 | -webkit-user-select: none;
15 | -khtml-user-select: none;
16 | -moz-user-select: none;
17 | -ms-user-select: none;
18 | user-select: none;
19 |
20 | #view .item:hover {
21 | box-shadow: none !important;
22 | }
23 | }
24 |
25 | #view {
26 |
27 | .selector {
28 | display: none;
29 | position: absolute;
30 | left: 0;
31 | top: 0;
32 | width: 22px;
33 | height: 22px;
34 | background: @col-back-selected;
35 | cursor: pointer;
36 | border-radius: 0 0 2px 0;
37 | opacity: 0.6;
38 |
39 | &:hover {
40 | opacity: 0.8;
41 | }
42 | img {
43 | width: 100%;
44 | height: 100%;
45 | }
46 | }
47 |
48 | .item:hover .selector {
49 | display: block;
50 | }
51 |
52 | .item.selected:not(.selecting),
53 | .item.selecting:not(.selected) {
54 | color: @col-text-selected;
55 | background: @col-back-selected;
56 |
57 | .selector {
58 | display: block;
59 | opacity: 1;
60 | }
61 | &:hover {
62 | color: @col-text-selected;
63 | background: @col-back-selected;
64 | }
65 | }
66 |
67 | .no-match {
68 | display: none;
69 | margin-top: 36px;
70 | text-align: center;
71 | color: @col-border;
72 | font-size: 5em;
73 | font-weight: bold;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/preview/preview-img.js:
--------------------------------------------------------------------------------
1 | const {dom} = require('../../util');
2 | const server = require('../../server');
3 | const allsettings = require('../../core/settings');
4 | const preview = require('./preview');
5 |
6 | const settings = Object.assign({
7 | enabled: false,
8 | size: null,
9 | types: []
10 | }, allsettings['preview-img']);
11 | const tpl = '
';
12 |
13 | const updateGui = () => {
14 | const el = dom('#pv-content-img')[0];
15 | if (!el) {
16 | return;
17 | }
18 |
19 | const elW = el.offsetWidth;
20 |
21 | const labels = [preview.item.label];
22 | if (!settings.size) {
23 | const elNW = el.naturalWidth;
24 | const elNH = el.naturalHeight;
25 | labels.push(String(elNW) + 'x' + String(elNH));
26 | labels.push(String((100 * elW / elNW).toFixed(0)) + '%');
27 | }
28 | preview.setLabels(labels);
29 | };
30 |
31 | const requestSample = href => {
32 | return server.request({
33 | action: 'get',
34 | thumbs: [{
35 | type: 'img',
36 | href,
37 | width: settings.size,
38 | height: 0
39 | }]
40 | }).then(json => {
41 | return json && json.thumbs && json.thumbs[0] ? json.thumbs[0] : null;
42 | });
43 | };
44 |
45 | const load = item => {
46 | return Promise.resolve(item.absHref)
47 | .then(href => {
48 | return settings.size ? requestSample(href) : href;
49 | })
50 | .then(href => new Promise(resolve => {
51 | const $el = dom(tpl)
52 | .on('load', () => resolve($el))
53 | .attr('src', href);
54 | }));
55 | };
56 |
57 | const init = () => {
58 | if (settings.enabled) {
59 | preview.register(settings.types, load, updateGui);
60 | }
61 | };
62 |
63 | init();
64 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/tree.less:
--------------------------------------------------------------------------------
1 | #tree {
2 | overflow: auto;
3 | flex: 0 0 auto;
4 | order: 1;
5 |
6 | padding: 32px 32px 32px 16px;
7 | white-space: nowrap;
8 | max-width: 250px;
9 | overflow-x: hidden;
10 |
11 | a, a:active, a.visited {
12 | display: block;
13 | margin-left: 20px;
14 | padding: 3px 0;
15 | text-decoration: none;
16 | color: @col-text;
17 |
18 | &:hover {
19 | color: @col-text-hover;
20 | }
21 | }
22 |
23 | .active > a {
24 | font-weight: bold;
25 | }
26 |
27 | .indicator {
28 | display: block;
29 | float: left;
30 | padding: 3px 0;
31 | position: relative;
32 | top: -2px;
33 | cursor: pointer;
34 |
35 | img {
36 | // .eased-transition;
37 | width: 20px;
38 | height: 20px;
39 | zoom: 1;
40 | }
41 | }
42 |
43 | .item {
44 | clear: left;
45 |
46 | &.open > .indicator img {
47 | transform: rotate(90deg);
48 | }
49 | &.unknown > .indicator {
50 | opacity: 0.3;
51 | }
52 | &.none > .indicator {
53 | opacity: 0;
54 | cursor: inherit;
55 | }
56 | &.unknown > .content, &.none > .content, &.closed > .content {
57 | display: none;
58 | }
59 | }
60 |
61 | .icon {
62 | position: relative;
63 | top: -2px;
64 |
65 | img {
66 | width: 20px;
67 | height: 20px;
68 | }
69 | }
70 |
71 | .label {
72 | margin: 0 0 0 4px;
73 | }
74 |
75 | .content {
76 | margin: 0;
77 | padding: 0 0 0 20px;
78 | }
79 |
80 | .summary {
81 | color: @col-text-disabled-black;
82 | padding: 0 0 0 8px;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/crumb.js:
--------------------------------------------------------------------------------
1 | const {each, dom} = require('../util');
2 | const event = require('../core/event');
3 | const location = require('../core/location');
4 | const resource = require('../core/resource');
5 | const allsettings = require('../core/settings');
6 | const base = require('../view/base');
7 |
8 |
9 | const settings = Object.assign({
10 | enabled: false
11 | }, allsettings.crumb);
12 | const crumbbarTpl = '';
13 | const crumbTpl =
14 | `
15 |
16 |
17 | `;
18 | const pageHintTpl =
19 | `
`;
20 |
21 |
22 | const createHtml = item => {
23 | const $html = dom(crumbTpl);
24 | location.setLink($html, item);
25 |
26 | $html.find('.label').text(item.label);
27 |
28 | if (item.isCurrentFolder()) {
29 | $html.addCls('active');
30 | }
31 |
32 | if (!item.isManaged) {
33 | $html.app(dom(pageHintTpl));
34 | }
35 |
36 | item._$crumb = $html;
37 | $html[0]._item = item;
38 |
39 | return $html;
40 | };
41 |
42 | const onLocationChanged = item => {
43 | const $crumb = item._$crumb;
44 | const $crumbbar = dom('#crumbbar');
45 |
46 | if ($crumb && $crumb.parent()[0] === $crumbbar[0]) {
47 | $crumbbar.children().rmCls('active');
48 | $crumb.addCls('active');
49 | } else {
50 | $crumbbar.clr();
51 | each(item.getCrumb(), crumbItem => {
52 | $crumbbar.app(createHtml(crumbItem));
53 | });
54 | }
55 | };
56 |
57 | const init = () => {
58 | if (!settings.enabled) {
59 | return;
60 | }
61 |
62 | dom(crumbbarTpl).appTo(base.$flowbar);
63 |
64 | event.sub('location.changed', onLocationChanged);
65 | };
66 |
67 |
68 | init();
69 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/util/lo.js:
--------------------------------------------------------------------------------
1 | const is = x => x !== undefined && x !== null;
2 | const tof = (x, str) => typeof x === str;
3 | const isStr = x => tof(x, 'string');
4 | const isFn = x => tof(x, 'function');
5 | const isNum = x => tof(x, 'number');
6 | const hasLength = x => x && x.hasOwnProperty('length');
7 | const keys = obj => {
8 | if (!obj || isStr(obj)) {
9 | return [];
10 | }
11 | if (hasLength(obj)) {
12 | obj = Array.from(obj);
13 | }
14 | return Object.keys(obj);
15 | };
16 | const values = obj => keys(obj).map(key => obj[key]);
17 | const each = (obj, fn) => keys(obj).forEach(key => fn(obj[key], key));
18 | const filter = (obj, fn) => values(obj).filter(fn);
19 | const map = (obj, fn) => values(obj).map(fn);
20 | const includes = (obj, x) => values(obj).indexOf(x) >= 0;
21 | const compact = obj => filter(obj, x => !!x);
22 |
23 | const isInstanceOf = (x, constructor) => x ? x instanceof constructor : false;
24 | const toArray = x => Array.from(x);
25 |
26 | const difference = (obj1, obj2) => {
27 | obj2 = values(obj2);
28 | return filter(obj1, x => obj2.indexOf(x) < 0);
29 | };
30 | const intersection = (obj1, obj2) => {
31 | obj2 = values(obj2);
32 | return filter(obj1, x => obj2.indexOf(x) >= 0);
33 | };
34 | const cmp = (x, y) => x < y ? -1 : x > y ? 1 : 0;
35 | const sortBy = (obj, sel) => {
36 | const selFn = isFn(sel) ? sel : x => x[sel];
37 | const cmpFn = (x, y) => cmp(selFn(x), selFn(y));
38 | return values(obj).sort(cmpFn);
39 | };
40 | const debounce = (fn, delay) => {
41 | let id = null;
42 | return () => {
43 | clearTimeout(id);
44 | id = setTimeout(fn, delay);
45 | };
46 | };
47 |
48 | module.exports = {
49 | is,
50 | isStr,
51 | isFn,
52 | isNum,
53 | hasLength,
54 | keys,
55 | values,
56 | each,
57 | filter,
58 | map,
59 | includes,
60 | compact,
61 | isInstanceOf,
62 | toArray,
63 | difference,
64 | intersection,
65 | cmp,
66 | sortBy,
67 | debounce
68 | };
69 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/download.js:
--------------------------------------------------------------------------------
1 | const {each, dom} = require('../util');
2 | const server = require('../server');
3 | const event = require('../core/event');
4 | const location = require('../core/location');
5 | const resource = require('../core/resource');
6 | const allsettings = require('../core/settings');
7 |
8 | const settings = Object.assign({
9 | enabled: false,
10 | type: 'php-tar',
11 | packageName: 'package',
12 | alwaysVisible: false
13 | }, allsettings.download);
14 | const tpl =
15 | ``;
18 | let selectedItems = [];
19 | let $download;
20 |
21 |
22 | const onSelection = items => {
23 | selectedItems = items.slice(0);
24 | if (selectedItems.length) {
25 | $download.show();
26 | } else if (!settings.alwaysVisible) {
27 | $download.hide();
28 | }
29 | };
30 |
31 | const onClick = () => {
32 | const type = settings.type;
33 | let name = settings.packageName;
34 | const extension = type === 'shell-zip' ? 'zip' : 'tar';
35 |
36 | if (!name) {
37 | if (selectedItems.length === 1) {
38 | name = selectedItems[0].label;
39 | } else {
40 | name = location.getItem().label;
41 | }
42 | }
43 |
44 | const query = {
45 | action: 'download',
46 | as: name + '.' + extension,
47 | type,
48 | baseHref: location.getAbsHref(),
49 | hrefs: ''
50 | };
51 |
52 | each(selectedItems, (item, idx) => {
53 | query[`hrefs[${idx}]`] = item.absHref;
54 | });
55 |
56 | server.formRequest(query);
57 | };
58 |
59 | const init = () => {
60 | if (!settings.enabled) {
61 | return;
62 | }
63 |
64 | $download = dom(tpl)
65 | .hide()
66 | .appTo('#toolbar')
67 | .on('click', onClick);
68 |
69 | if (settings.alwaysVisible) {
70 | $download.show();
71 | }
72 |
73 | event.sub('selection', onSelection);
74 | };
75 |
76 |
77 | init();
78 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-fallback.php:
--------------------------------------------------------------------------------
1 | context = $context;
8 | }
9 |
10 | public function get_html($path = null) {
11 | if (!$path) {
12 | $path = $this->context->get_current_path();
13 | }
14 | $fallback_images_href = $this->context->get_setup()->get('PUBLIC_HREF') . 'images/fallback/';
15 |
16 | $cache = [];
17 | $folder = Item::get($this->context, $path, $cache);
18 | $items = $folder->get_content($cache);
19 | uasort($items, ['Item', 'cmp']);
20 |
21 | $html = '';
22 |
23 | $html .= '';
24 | $html .= ' | ';
25 | $html .= 'Name | ';
26 | $html .= 'Last modified | ';
27 | $html .= 'Size | ';
28 | $html .= '
';
29 |
30 | if ($folder->get_parent($cache)) {
31 | $html .= '';
32 | $html .= ' | ';
33 | $html .= 'Parent Directory | ';
34 | $html .= ' | ';
35 | $html .= ' | ';
36 | $html .= '
';
37 | }
38 |
39 | foreach ($items as $item) {
40 | $type = $item->is_folder ? 'folder' : 'file';
41 |
42 | $html .= '';
43 | $html .= ' | ';
44 | $html .= '' . basename($item->path) . ' | ';
45 | $html .= '' . date('Y-m-d H:i', $item->date) . ' | ';
46 | $html .= '' . ($item->size !== null ? intval($item->size / 1000) . ' KB' : '' ) . ' | ';
47 | $html .= '
';
48 | }
49 |
50 | $html .= '
';
51 |
52 | return $html;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-json.php:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | `;
20 | let inputIsVisible = false;
21 | let prevPattern = '';
22 | let $filter;
23 | let $input;
24 |
25 |
26 | const filterItems = (pattern = '') => {
27 | if (pattern === prevPattern) {
28 | return;
29 | }
30 | prevPattern = pattern;
31 |
32 | if (!pattern) {
33 | view.setLocation();
34 | return;
35 | }
36 |
37 | $filter.addCls('pending');
38 |
39 | const re = new RegExp(pattern, settings.ignorecase ? 'i' : '');
40 | const items = filter(location.getItem().content, item => re.test(item.label));
41 |
42 | $filter.rmCls('pending');
43 | view.setHint('noMatch');
44 | view.setItems(items);
45 | };
46 |
47 | const update = () => {
48 | if (inputIsVisible) {
49 | $filter.addCls('active');
50 | $input[0].focus();
51 | filterItems(parsePattern($input.val(), settings.advanced));
52 | } else {
53 | filterItems();
54 | $filter.rmCls('active');
55 | }
56 | };
57 |
58 | const toggle = () => {
59 | inputIsVisible = !inputIsVisible;
60 | update();
61 | };
62 |
63 | const reset = () => {
64 | inputIsVisible = false;
65 | $input.val('');
66 | update();
67 | };
68 |
69 | const init = () => {
70 | if (!settings.enabled) {
71 | return;
72 | }
73 |
74 | $filter = dom(tpl).appTo('#toolbar');
75 | $input = $filter.find('input');
76 |
77 | $filter.find('img').on('click', toggle);
78 | $input.on('keyup', debounce(update, settings.debounceTime));
79 | event.sub('location.changed', reset);
80 | };
81 |
82 |
83 | init();
84 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/ext/class-custom.php:
--------------------------------------------------------------------------------
1 | context = $context;
9 | }
10 |
11 | private function read_custom_file($path, $name, &$content, &$type) {
12 | $file_prefix = $this->context->get_setup()->get('FILE_PREFIX');
13 |
14 | foreach (Custom::$EXTENSIONS as $ext) {
15 | $file = $path . '/' . $file_prefix . '.' . $name . '.' . $ext;
16 | if (is_readable($file)) {
17 | $content = file_get_contents($file);
18 | $type = $ext;
19 | return;
20 | }
21 | }
22 | }
23 |
24 | public function get_customizations($href) {
25 | if (!$this->context->query_option('custom.enabled', false)) {
26 | return [
27 | 'header' => ['content' => null, 'type' => null],
28 | 'footer' => ['content' => null, 'type' => null]
29 | ];
30 | }
31 |
32 | $root_path = $this->context->get_setup()->get('FILE_PREFIX');
33 | $path = $this->context->to_path($href);
34 |
35 | $header = null;
36 | $header_type = null;
37 | $footer = null;
38 | $footer_type = null;
39 |
40 | $this->read_custom_file($path, 'header', $header, $header_type);
41 | $this->read_custom_file($path, 'footer', $footer, $footer_type);
42 |
43 | while ($header === null || $footer === null) {
44 | if ($header === null) {
45 | $this->read_custom_file($path, 'headers', $header, $header_type);
46 | }
47 | if ($footer === null) {
48 | $this->read_custom_file($path, 'footers', $footer, $footer_type);
49 | }
50 | if ($path === $root_path) {
51 | break;
52 | }
53 | $parent_path = Util::normalize_path(dirname($path));
54 | if ($parent_path === $path) {
55 | break;
56 | }
57 | $path = $parent_path;
58 | }
59 |
60 | return [
61 | 'header' => ['content' => $header, 'type' => $header_type],
62 | 'footer' => ['content' => $footer, 'type' => $footer_type]
63 | ];
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/core/format.js:
--------------------------------------------------------------------------------
1 | const {isNum} = require('../util');
2 |
3 | const decimalMetric = {
4 | t: 1000.0,
5 | k: 1000.0,
6 | u: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
7 | };
8 | const binaryMetric = {
9 | t: 1024.0,
10 | k: 1024.0,
11 | u: ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
12 | };
13 | let defaultMetric = decimalMetric;
14 |
15 | const datePatterns = [
16 | [/YYYY/, 'Y', 4],
17 | [/YY/, 'Y', 2],
18 | [/Y/, 'Y', 0],
19 | [/MM/, 'M', 2],
20 | [/M/, 'M', 0],
21 | [/DD/, 'D', 2],
22 | [/D/, 'D', 0],
23 | [/HH/, 'H', 2],
24 | [/H/, 'H', 0],
25 | [/mm/, 'm', 2],
26 | [/m/, 'm', 0],
27 | [/ss/, 's', 2],
28 | [/s/, 's', 0]
29 | ];
30 | let defaultDateFormat = 'YYYY-MM-DD HH:mm';
31 |
32 |
33 | const setDefaultMetric = useBinaryMetric => {
34 | defaultMetric = useBinaryMetric ? binaryMetric : decimalMetric;
35 | };
36 |
37 | const formatSize = (size, metric) => {
38 | metric = metric || defaultMetric;
39 |
40 | if (!isNum(size) || size < 0) {
41 | return '';
42 | }
43 |
44 | let i = 0;
45 | const maxI = metric.u.length - 1;
46 |
47 | while (size >= metric.t && i < maxI) {
48 | size /= metric.k;
49 | i += 1;
50 | }
51 | return (i <= 1 ? Math.round(size) : size.toFixed(1)).toString() + ' ' + metric.u[i];
52 | };
53 |
54 | const setDefaultDateFormat = dateFormat => {
55 | defaultDateFormat = dateFormat;
56 | };
57 |
58 | const formatNumber = (number, padding) => {
59 | let str = String(number);
60 | if (padding) {
61 | str = ('000' + str).substr(-padding);
62 | }
63 | return str;
64 | };
65 |
66 | const formatDate = (millis, format) => {
67 | if (!millis || !isNum(millis)) {
68 | return '';
69 | }
70 |
71 | format = format || defaultDateFormat;
72 |
73 | const date = new Date(millis);
74 | const d = {
75 | Y: date.getFullYear(),
76 | M: date.getMonth() + 1,
77 | D: date.getDate(),
78 | H: date.getHours(),
79 | m: date.getMinutes(),
80 | s: date.getSeconds()
81 | };
82 |
83 | datePatterns.forEach(pattern => {
84 | format = format.replace(pattern[0], formatNumber(d[pattern[1]], pattern[2]));
85 | });
86 |
87 | return format;
88 | };
89 |
90 |
91 | module.exports = {
92 | setDefaultMetric,
93 | formatSize,
94 | setDefaultDateFormat,
95 | formatDate
96 | };
97 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/search.js:
--------------------------------------------------------------------------------
1 | const {map, debounce, parsePattern, dom} = require('../util');
2 | const server = require('../server');
3 | const event = require('../core/event');
4 | const location = require('../core/location');
5 | const resource = require('../core/resource');
6 | const allsettings = require('../core/settings');
7 | const Item = require('../model/item');
8 | const view = require('../view/view');
9 |
10 |
11 | const settings = Object.assign({
12 | enabled: false,
13 | advanced: false,
14 | debounceTime: 300,
15 | ignorecase: true
16 | }, allsettings.search);
17 | const tpl =
18 | ``;
22 | let inputIsVisible = false;
23 | let prevPattern = '';
24 | let $search;
25 | let $input;
26 |
27 |
28 | const search = (pattern = '') => {
29 | if (pattern === prevPattern) {
30 | return;
31 | }
32 | prevPattern = pattern;
33 |
34 | if (!pattern) {
35 | view.setLocation();
36 | return;
37 | }
38 |
39 | $search.addCls('pending');
40 |
41 | server.request({
42 | action: 'get',
43 | search: {
44 | href: location.getAbsHref(),
45 | pattern,
46 | ignorecase: settings.ignorecase
47 | }
48 | }).then(response => {
49 | $search.rmCls('pending');
50 | view.setHint('noMatch');
51 | view.setItems(map(response.search, item => Item.get(item)));
52 | });
53 | };
54 |
55 | const update = () => {
56 | if (inputIsVisible) {
57 | $search.addCls('active');
58 | $input[0].focus();
59 | search(parsePattern($input.val(), settings.advanced));
60 | } else {
61 | search();
62 | $search.rmCls('active');
63 | }
64 | };
65 |
66 | const toggle = () => {
67 | inputIsVisible = !inputIsVisible;
68 | update();
69 | };
70 |
71 | const reset = () => {
72 | inputIsVisible = false;
73 | $input.val('');
74 | update();
75 | };
76 |
77 | const init = () => {
78 | if (!settings.enabled) {
79 | return;
80 | }
81 |
82 | $search = dom(tpl).appTo('#toolbar');
83 | $input = $search.find('input');
84 |
85 | $search.find('img').on('click', toggle);
86 | $input.on('keyup', debounce(update, settings.debounceTime));
87 | event.sub('location.changed', reset);
88 | };
89 |
90 |
91 | init();
92 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/preview/preview-txt.js:
--------------------------------------------------------------------------------
1 | const lolight = require('lolight');
2 | const marked = require('marked');
3 | const {keys, dom} = require('../../util');
4 | const allsettings = require('../../core/settings');
5 | const preview = require('./preview');
6 |
7 | const win = global.window;
8 | const XHR = win.XMLHttpRequest;
9 | const settings = Object.assign({
10 | enabled: false,
11 | styles: {}
12 | }, allsettings['preview-txt']);
13 | const preTpl = '';
14 | const divTpl = '';
15 |
16 | const updateGui = () => {
17 | const el = dom('#pv-content-txt')[0];
18 | if (!el) {
19 | return;
20 | }
21 |
22 | const container = dom('#pv-container')[0];
23 | el.style.height = container.offsetHeight + 'px';
24 |
25 | preview.setLabels([
26 | preview.item.label,
27 | preview.item.size + ' bytes'
28 | ]);
29 | };
30 |
31 | const requestTextContent = href => {
32 | return new Promise((resolve, reject) => {
33 | const xhr = new XHR();
34 | const callback = () => {
35 | if (xhr.readyState === XHR.DONE) {
36 | try {
37 | resolve(xhr.responseText || '');
38 | } catch (err) {
39 | reject(String(err));
40 | }
41 | }
42 | };
43 |
44 | xhr.open('GET', href, true);
45 | xhr.onreadystatechange = callback;
46 | xhr.send();
47 | });
48 | };
49 |
50 | const load = item => {
51 | return requestTextContent(item.absHref)
52 | .catch(err => '[request failed] ' + err)
53 | .then(content => {
54 | const style = settings.styles[item.type];
55 |
56 | if (style === 1) {
57 | return dom(preTpl).text(content);
58 | } else if (style === 2) {
59 | return dom(divTpl).html(marked(content));
60 | } else if (style === 3) {
61 | const $code = dom('').text(content);
62 | win.setTimeout(() => {
63 | lolight.el($code[0]);
64 | }, content.length < 20000 ? 0 : 500);
65 | return dom(preTpl).app($code);
66 | }
67 |
68 | return dom(divTpl).text(content);
69 | });
70 | };
71 |
72 | const init = () => {
73 | if (settings.enabled) {
74 | preview.register(keys(settings.styles), load, updateGui);
75 | }
76 | };
77 |
78 | init();
79 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/util/naturalCmp.js:
--------------------------------------------------------------------------------
1 | // Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
2 | // Author: Jim Palmer (based on chunking idea from Dave Koelle)
3 |
4 | // Modified to make it work with h5ai
5 |
6 | const reToken = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi;
7 | const reDate = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
8 | const reHex = /^0x[0-9a-f]+$/i;
9 | const reLeadingZero = /^0/;
10 |
11 | /* eslint-disable complexity */
12 | const naturalCmp = (a, b) => {
13 | // convert all to strings strip whitespace
14 | const x = String(a).trim();
15 | const y = String(b).trim();
16 |
17 | // chunk/tokenize
18 | const xTokens = x.replace(reToken, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
19 | const yTokens = y.replace(reToken, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
20 |
21 | // first try and sort Hex codes or Dates
22 | const xDate = parseInt(x.match(reHex), 16) || xTokens.length !== 1 && x.match(reDate) && Date.parse(x);
23 | const yDate = parseInt(y.match(reHex), 16) || xDate && y.match(reDate) && Date.parse(y) || null;
24 | if (yDate) {
25 | if (xDate < yDate) {
26 | return -1;
27 | }
28 | if (xDate > yDate) {
29 | return 1;
30 | }
31 | }
32 |
33 | // natural sorting through split numeric strings and default strings
34 | for (let idx = 0, len = Math.max(xTokens.length, yTokens.length); idx < len; idx += 1) {
35 | // find floats not starting with '0', string or 0 if not defined (Clint Priest)
36 | let xToken = !(xTokens[idx] || '').match(reLeadingZero) && parseFloat(xTokens[idx]) || xTokens[idx] || 0;
37 | let yToken = !(yTokens[idx] || '').match(reLeadingZero) && parseFloat(yTokens[idx]) || yTokens[idx] || 0;
38 |
39 | // handle numeric vs string comparison - number < string - (Kyle Adams)
40 | if (isNaN(xToken) !== isNaN(yToken)) {
41 | return isNaN(xToken) ? 1 : -1;
42 | }
43 |
44 | // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
45 | if (typeof xToken !== typeof yToken) {
46 | xToken = String(xToken);
47 | yToken = String(yToken);
48 | }
49 |
50 | if (xToken < yToken) {
51 | return -1;
52 | }
53 | if (xToken > yToken) {
54 | return 1;
55 | }
56 | }
57 | return 0;
58 | };
59 | /* eslint-enable */
60 |
61 | module.exports = {
62 | naturalCmp
63 | };
64 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/fallback.less:
--------------------------------------------------------------------------------
1 | #fallback {
2 | display: none;
3 | max-width: 960px;
4 | margin: 16px auto;
5 | padding: 32px 16px;
6 |
7 | table {
8 | display: block;
9 | width: 100%;
10 | border-collapse: collapse;
11 | background: @col-back-paper;
12 | }
13 | th, td {
14 | padding: 6px;
15 | text-align: left;
16 | border: none;
17 | border-bottom: 1px solid #f0f0f0;
18 | }
19 | th {
20 | color: #aaa;
21 | font-weight: normal;
22 | line-height: 36px;
23 | }
24 | td {
25 | overflow: hidden;
26 | white-space: nowrap;
27 | }
28 | a, a:active, a:visited {
29 | display: block;
30 | color: inherit;
31 | text-decoration: none;
32 | cursor: pointer;
33 |
34 | &:hover {
35 | color: @col-text-hover;
36 | }
37 | }
38 | .fb-i {
39 | width: 20px;
40 | padding-left: 12px;
41 |
42 | img {
43 | width: 20px;
44 | height: 20px;
45 | position: relative;
46 | top: -1px;
47 | }
48 | }
49 | .fb-n {
50 | width: 682px;
51 | max-width: 682px;
52 | }
53 | .fb-d {
54 | text-align: right;
55 | width: 160px;
56 | min-width: 160px;
57 | }
58 | .fb-s {
59 | text-align: right;
60 | width: 70px;
61 | min-width: 70px;
62 | padding-right: 12px;
63 | }
64 | }
65 |
66 | #fallback-hints {
67 | display: none;
68 | overflow: hidden;
69 | text-align: right;
70 | background: @col-back-panel;
71 | border-bottom: 1px solid @col-border;
72 |
73 | a, a:active, a:visited {
74 | .eased-transition;
75 | display: inline-block;
76 | line-height: 48px;
77 | color: @col-text-secondary-black;
78 | text-decoration: none;
79 | outline: 0;
80 |
81 | &:hover {
82 | color: @col-text-hover;
83 | }
84 | }
85 |
86 | .backlink {
87 | margin: 0 16px;
88 | }
89 |
90 | .noJsMsg, .noBrowserMsg {
91 | display: none;
92 | margin: 0 16px;
93 | color: @col-error;
94 | }
95 | }
96 |
97 | html.no-js, html.no-browser {
98 |
99 | #root {
100 | position: static;
101 | overflow: auto;
102 | }
103 |
104 | #fallback, #fallback-hints {
105 | display: block;
106 | }
107 | }
108 |
109 | html.no-js .noJsMsg {
110 | display: inline !important;
111 | }
112 |
113 | html.no-browser .noBrowserMsg {
114 | display: inline !important;
115 | }
116 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-js.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/x-pdf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-filesize.php:
--------------------------------------------------------------------------------
1 | size($path, $withFoldersize, $withDu);
9 | }
10 |
11 | public static function getCachedSize($path, $withFoldersize, $withDu) {
12 | if (array_key_exists($path, Filesize::$cache)) {
13 | return Filesize::$cache[$path];
14 | }
15 |
16 | $size = Filesize::getSize($path, $withFoldersize, $withDu);
17 |
18 | Filesize::$cache[$path] = $size;
19 | return $size;
20 | }
21 |
22 |
23 | private function __construct() {}
24 |
25 | private function read_dir($path) {
26 | $paths = [];
27 | if (is_dir($path)) {
28 | foreach (scandir($path) as $name) {
29 | if ($name !== '.' && $name !== '..') {
30 | $paths[] = $path . '/' . $name;
31 | }
32 | }
33 | }
34 | return $paths;
35 | }
36 |
37 | private function php_filesize($path, $recursive = false) {
38 | // if (PHP_INT_SIZE < 8) {
39 | // }
40 | $size = @filesize($path);
41 |
42 | if (!is_dir($path) || !$recursive) {
43 | return $size;
44 | }
45 |
46 | foreach ($this->read_dir($path) as $p) {
47 | $size += $this->php_filesize($p, true);
48 | }
49 | return $size;
50 | }
51 |
52 |
53 | private function exec($cmdv) {
54 | $cmd = implode(' ', array_map('escapeshellarg', $cmdv));
55 | $lines = [];
56 | $rc = null;
57 | exec($cmd, $lines, $rc);
58 | return $lines;
59 | }
60 |
61 | private function exec_du_all($paths) {
62 | $cmdv = array_merge(['du', '-sbL'], $paths);
63 | $lines = $this->exec($cmdv);
64 |
65 | $sizes = [];
66 | foreach ($lines as $line) {
67 | $parts = preg_split('/[\s]+/', $line, 2);
68 | $size = intval($parts[0], 10);
69 | $path = $parts[1];
70 | $sizes[$path] = $size;
71 | }
72 | return $sizes;
73 | }
74 |
75 | private function exec_du($path) {
76 | $sizes = $this->exec_du_all([$path]);
77 | return $sizes[$path];
78 | }
79 |
80 |
81 | private function size($path, $withFoldersize = false, $withDu = false) {
82 | if (is_file($path)) {
83 | return $this->php_filesize($path);
84 | }
85 |
86 | if (is_dir($path) && $withFoldersize) {
87 | if ($withDu) {
88 | return $this->exec_du($path);
89 | }
90 |
91 | return $this->php_filesize($path, true);
92 | }
93 |
94 | return null;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/main/info.less:
--------------------------------------------------------------------------------
1 | #root.info {
2 |
3 | #content {
4 | flex: 1 1 auto;
5 | order: 50;
6 | color: @col-text;
7 | text-align: center;
8 | }
9 |
10 | code {
11 | margin: 0 0.2em;
12 | padding: 2px 4px;
13 | border-radius: 4px;
14 | letter-spacing: 0.05em;
15 | background: @col-back-panel;
16 | border: 1px solid @col-border;
17 | font-size: 0.9em;
18 | }
19 |
20 | #header a {
21 | .eased-transition;
22 | font-size: 4em;
23 | font-weight: 300;
24 | margin: 0.8em 0 0 0;
25 | color: @col-text;
26 | text-decoration: none;
27 |
28 | &:hover {
29 | color: @col-text-hover;
30 | }
31 | }
32 |
33 | #support {
34 | margin: 24px auto;
35 | padding: 18px 0 6px 0;
36 | width: 292px;
37 | background: @col-back-panel;
38 | border: 1px solid @col-border;
39 | border-radius: 4px;
40 |
41 | input[type="image"] {
42 | border: 0;
43 | width: 100px;
44 | padding: 12px 48px;
45 | }
46 | }
47 |
48 | #pass {
49 | .el-input;
50 | display: inline-block;
51 | margin: 8px;
52 | padding: 0 12px;
53 | line-height: 28px;
54 | width: 200px;
55 | vertical-align: top;
56 | }
57 |
58 | #login, #logout {
59 | .el-button;
60 | display: inline-block;
61 | margin: 8px;
62 | padding: 0 12px;
63 | line-height: 28px;
64 | vertical-align: top;
65 | }
66 |
67 | #hint {
68 | margin: 12px auto;
69 | width: 320px;
70 | }
71 |
72 | #tests {
73 | display: inline-block;
74 | text-align: left;
75 | list-style-type: none;
76 | margin: 48px 0;
77 | padding: 0;
78 |
79 | .test {
80 | background: @col-back-paper;
81 | margin: 12px 0 0 0;
82 | padding: 8px 12px 12px 12px;
83 | border-bottom: 1px solid @col-border;
84 | }
85 | .label {
86 | display: inline-block;
87 | width: 250px;
88 | font-size: 1.4em;
89 | }
90 | .result {
91 | display: inline-block;
92 | width: 250px;
93 | text-align: right;
94 | font-size: 1.4em;
95 | font-weight: bold;
96 |
97 | &.passed {
98 | color: @col-okay;
99 | }
100 | &.failed {
101 | color: @col-error;
102 | }
103 | }
104 | .info {
105 | margin: 4px 0 0 0;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-item.php:
--------------------------------------------------------------------------------
1 | is_folder && !$item2->is_folder) {
6 | return -1;
7 | }
8 | if (!$item1->is_folder && $item2->is_folder) {
9 | return 1;
10 | }
11 |
12 | return strcasecmp($item1->path, $item2->path);
13 | }
14 |
15 | public static function get($context, $path, &$cache) {
16 | if (!Util::starts_with($path, $context->get_setup()->get('ROOT_PATH'))) {
17 | return null;
18 | }
19 |
20 | if (is_array($cache) && array_key_exists($path, $cache)) {
21 | return $cache[$path];
22 | }
23 |
24 | $item = new Item($context, $path);
25 |
26 | if (is_array($cache)) {
27 | $cache[$path] = $item;
28 | }
29 | return $item;
30 | }
31 |
32 | public $context;
33 | public $path;
34 | public $href;
35 | public $date;
36 | public $size;
37 | public $is_folder;
38 | public $is_content_fetched;
39 |
40 | private function __construct($context, $path) {
41 | $this->context = $context;
42 |
43 | $this->path = Util::normalize_path($path, false);
44 | $this->is_folder = is_dir($this->path);
45 | $this->href = $context->to_href($this->path, $this->is_folder);
46 | $this->date = @filemtime($this->path);
47 | $this->size = Util::filesize($context, $this->path);
48 | $this->is_content_fetched = false;
49 | }
50 |
51 | public function to_json_object() {
52 | $obj = [
53 | 'href' => $this->href,
54 | 'time' => $this->date * 1000, // seconds (PHP) to milliseconds (JavaScript)
55 | 'size' => $this->size
56 | ];
57 |
58 | if ($this->is_folder) {
59 | $obj['managed'] = $this->context->is_managed_href($this->href);
60 | $obj['fetched'] = $this->is_content_fetched;
61 | }
62 |
63 | return $obj;
64 | }
65 |
66 | public function get_parent(&$cache) {
67 | $parent_path = Util::normalize_path(dirname($this->path), false);
68 | if ($parent_path !== $this->path && Util::starts_with($parent_path, $this->context->get_setup()->get('ROOT_PATH'))) {
69 | return Item::get($this->context, $parent_path, $cache);
70 | }
71 | return null;
72 | }
73 |
74 | public function get_content(&$cache) {
75 | $items = [];
76 |
77 | if (!$this->context->is_managed_href($this->href)) {
78 | return $items;
79 | }
80 |
81 | $files = $this->context->read_dir($this->path);
82 | foreach ($files as $file) {
83 | $item = Item::get($this->context, $this->path . '/' . $file, $cache);
84 | $items[$item->path] = $item;
85 | }
86 |
87 | $this->is_content_fetched = true;
88 |
89 | return $items;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # h5ai
2 |
3 | [![license][license-img]][github] [![web][web-img]][web] [![github][github-img]][github]
4 |
5 | A modern HTTP web server index for Apache httpd, lighttpd, nginx and Cherokee.
6 |
7 |
8 | ## Important
9 |
10 | * Do **not** install any files from the `src` folder, they need to be
11 | preprocessed to work correctly!
12 | * Find a preprocessed package and detailed install instructions on the
13 | [project page][web].
14 | * For bug reports and feature requests please use [issues][github-issues].
15 |
16 |
17 | ## Build
18 |
19 | There are installation ready packages for the latest [releases][release] and
20 | [dev builds][develop]. But to build **h5ai** yourself either `git clone` or
21 | download the repository. From within the root folder run the following
22 | commands to find a fresh zipball in folder `build` (tested on linux only,
23 | requires [`node 6.0+`][node] to be installed).
24 |
25 | ~~~sh
26 | > npm install
27 | > npm run build
28 | ~~~
29 |
30 |
31 | ## License
32 |
33 | The MIT License (MIT)
34 |
35 | Copyright (c) 2016 Lars Jung (https://larsjung.de)
36 |
37 | Permission is hereby granted, free of charge, to any person obtaining a copy
38 | of this software and associated documentation files (the "Software"), to deal
39 | in the Software without restriction, including without limitation the rights
40 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41 | copies of the Software, and to permit persons to whom the Software is
42 | furnished to do so, subject to the following conditions:
43 |
44 | The above copyright notice and this permission notice shall be included in
45 | all copies or substantial portions of the Software.
46 |
47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53 | THE SOFTWARE.
54 |
55 |
56 | ## References
57 |
58 | **h5ai** profits from other projects, all of them licensed under the MIT license
59 | too. Exceptions are some [Material Design icons][material-design-icons] (CC BY 4.0).
60 |
61 |
62 | [web]: https://larsjung.de/h5ai/
63 | [github]: https://github.com/lrsjng/h5ai
64 | [github-issues]: https://github.com/lrsjng/h5ai/issues
65 | [release]: https://release.larsjung.de/h5ai/
66 | [develop]: https://release.larsjung.de/h5ai/develop/
67 | [node]: https://nodejs.org
68 | [material-design-icons]: https://github.com/google/material-design-icons
69 |
70 | [license-img]: https://img.shields.io/badge/license-MIT-a0a060.svg?style=flat-square
71 | [web-img]: https://img.shields.io/badge/web-larsjung.de/h5ai-a0a060.svg?style=flat-square
72 | [github-img]: https://img.shields.io/badge/github-lrsjng/h5ai-a0a060.svg?style=flat-square
73 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/view/viewmode.js:
--------------------------------------------------------------------------------
1 | const {each, dom} = require('../util');
2 | const event = require('../core/event');
3 | const resource = require('../core/resource');
4 | const allsettings = require('../core/settings');
5 | const sidebar = require('./sidebar');
6 | const base = require('./base');
7 | const view = require('./view');
8 |
9 |
10 | const settings = Object.assign({
11 | modeToggle: false
12 | }, allsettings.view);
13 | const settingsTpl =
14 | 'View
';
15 | const modeTpl =
16 | ``;
19 | const sizeTpl =
20 | '';
21 | const toggleTpl =
22 | ``;
25 | let modes;
26 | let sizes;
27 |
28 |
29 | const onChanged = (mode, size) => {
30 | dom('#viewmode-settings .mode').rmCls('active');
31 | dom('#viewmode-' + mode).addCls('active');
32 | dom('#viewmode-size').val(sizes.indexOf(size));
33 |
34 | if (settings.modeToggle === 'next') {
35 | mode = modes[(modes.indexOf(mode) + 1) % modes.length];
36 | }
37 | dom('#viewmode-toggle img').attr('src', resource.image('view-' + mode));
38 | };
39 |
40 | const addSettings = () => {
41 | if (modes.length < 2 && sizes.length < 2) {
42 | return;
43 | }
44 |
45 | const $viewBlock = dom(settingsTpl);
46 |
47 | if (modes.length > 1) {
48 | each(modes, mode => {
49 | dom(modeTpl.replace(/\[MODE\]/g, mode))
50 | .on('click', () => {
51 | view.setMode(mode);
52 | })
53 | .appTo($viewBlock);
54 | });
55 | }
56 |
57 | if (sizes.length > 1) {
58 | const max = sizes.length - 1;
59 | dom(sizeTpl)
60 | .attr('max', max)
61 | .on('input', ev => view.setSize(sizes[ev.target.valueAsNumber]))
62 | .on('change', ev => view.setSize(sizes[ev.target.valueAsNumber]))
63 | .appTo($viewBlock);
64 | }
65 |
66 | $viewBlock.appTo(sidebar.$el);
67 | };
68 |
69 | const onToggle = () => {
70 | const mode = view.getMode();
71 | const nextIdx = (modes.indexOf(mode) + 1) % modes.length;
72 | const nextMode = modes[nextIdx];
73 |
74 | view.setMode(nextMode);
75 | };
76 |
77 | const addToggle = () => {
78 | if (settings.modeToggle && modes.length > 1) {
79 | dom(toggleTpl)
80 | .on('click', onToggle)
81 | .appTo(base.$toolbar);
82 | }
83 | };
84 |
85 | const init = () => {
86 | modes = view.getModes();
87 | sizes = view.getSizes();
88 |
89 | addSettings();
90 | addToggle();
91 | onChanged(view.getMode(), view.getSize());
92 |
93 | event.sub('view.mode.changed', onChanged);
94 | };
95 |
96 |
97 | init();
98 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/sort.js:
--------------------------------------------------------------------------------
1 | const {each, toArray, dom, cmp, naturalCmp} = require('../util');
2 | const event = require('../core/event');
3 | const resource = require('../core/resource');
4 | const allsettings = require('../core/settings');
5 | const store = require('../core/store');
6 |
7 | const settings = Object.assign({
8 | enabled: false,
9 | column: 0,
10 | reverse: false,
11 | ignorecase: true,
12 | natural: false,
13 | folders: 0
14 | }, allsettings.sort);
15 | const storekey = 'ext/sort';
16 | const tpl = `
`;
17 |
18 | const getTypeOrder = item => item.isFolder() ? settings.folders : 1;
19 | const columnProps = {0: 'label', 1: 'time', 2: 'size'};
20 | const columnClasses = {0: 'label', 1: 'date', 2: 'size'};
21 |
22 |
23 | const cmpFn = (prop, reverse, ignorecase, natural) => {
24 | return (el1, el2) => {
25 | const item1 = el1._item;
26 | const item2 = el2._item;
27 |
28 | let res = getTypeOrder(item1) - getTypeOrder(item2);
29 | if (res !== 0) {
30 | return res;
31 | }
32 |
33 | let val1 = item1[prop];
34 | let val2 = item2[prop];
35 |
36 | if (isNaN(val1) || isNaN(val2)) {
37 | val1 = String(val1);
38 | val2 = String(val2);
39 |
40 | if (ignorecase) {
41 | val1 = val1.toLowerCase();
42 | val2 = val2.toLowerCase();
43 | }
44 | }
45 |
46 | res = natural ? naturalCmp(val1, val2) : cmp(val1, val2);
47 | return reverse ? -res : res;
48 | };
49 | };
50 |
51 | const sortItems = (column, reverse) => {
52 | const $headers = dom('#items li.header a');
53 | const $header = dom('#items li.header a.' + columnClasses[column]);
54 | const fn = cmpFn(columnProps[column], reverse, settings.ignorecase, settings.natural);
55 |
56 | store.put(storekey, {column, reverse});
57 |
58 | $headers.rmCls('ascending').rmCls('descending');
59 | $header.addCls(reverse ? 'descending' : 'ascending');
60 |
61 | dom(toArray(dom('#items .item:not(.folder-parent)')).sort(fn)).appTo('#items');
62 | };
63 |
64 | const onContentChanged = () => {
65 | const order = store.get(storekey);
66 | const column = order && order.column || settings.column;
67 | const reverse = order && order.reverse || settings.reverse;
68 |
69 | sortItems(column, reverse);
70 | };
71 |
72 | const addToggles = () => {
73 | const $header = dom('#items li.header');
74 |
75 | each(columnClasses, (cls, idx) => {
76 | const pos = idx === '0' ? 'app' : 'pre';
77 | $header
78 | .find('a.' + cls)[pos](tpl)
79 | .on('click', ev => {
80 | sortItems(idx, dom(ev.currentTarget).hasCls('ascending'));
81 | ev.preventDefault();
82 | });
83 | });
84 | };
85 |
86 | const init = () => {
87 | if (!settings.enabled) {
88 | return;
89 | }
90 |
91 | addToggles();
92 | event.sub('view.changed', onContentChanged);
93 | };
94 |
95 |
96 | init();
97 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/view-details.less:
--------------------------------------------------------------------------------
1 | #view.view-details {
2 |
3 | .view-details-sized(@size) {
4 |
5 | .item {
6 | .label, .date, .size {
7 | line-height: @size + 14px;
8 | }
9 | }
10 |
11 | .square {
12 | width: @size;
13 | height: @size;
14 |
15 | img {
16 | width: @size;
17 | height: @size;
18 | }
19 | }
20 |
21 | .label {
22 | margin: 0 246px 0 (@size + 32px);
23 | }
24 | }
25 |
26 | margin: 32px;
27 |
28 | .header {
29 | position: relative;
30 | white-space: nowrap;
31 | display: list-item;
32 | border-bottom: 1px solid rgba(0,0,0,0.07);
33 | border-radius: 2px 2px 0 0;
34 |
35 | .label, .date, .size {
36 | .eased-transition;
37 | line-height: 24px;
38 | padding: 0px 8px 16px 8px;
39 | opacity: 0.4;
40 | outline: 0;
41 |
42 | &:hover {
43 | opacity: 1;
44 | color: @col-text-hover;
45 | }
46 | }
47 |
48 | .sort {
49 | display: none;
50 | position: relative;
51 | top: -2px;
52 | width: 20px;
53 | height: 20px;
54 | padding: 0 4px;
55 | }
56 |
57 | .ascending .sort {
58 | display: inline;
59 | }
60 |
61 | .descending .sort {
62 | display: inline;
63 | transform: rotate(180deg);
64 | zoom: 1;
65 | }
66 | }
67 |
68 | .item {
69 | overflow: hidden;
70 | border-bottom: 1px solid rgba(0,0,0,0.07);
71 |
72 | &:hover {
73 | .raised;
74 | z-index: 1;
75 | }
76 |
77 | &:last-child {
78 | border-radius: 0 0 2px 2px;
79 | }
80 | }
81 |
82 | .square {
83 | display: inline-block;
84 | position: absolute;
85 | left: 16px;
86 | top: -1px;
87 | padding: 8px;
88 |
89 | .thumb {
90 | .rounded;
91 | box-shadow: 0 0 1px 0 rgba(0,0,0,0.2);
92 | }
93 | }
94 |
95 | .label, .date, .size {
96 | padding: 0 8px;
97 | }
98 |
99 | .date {
100 | position: absolute;
101 | right: 116px;
102 | top: 0;
103 | }
104 |
105 | .size {
106 | position: absolute;
107 | right: 16px;
108 | top: 0;
109 | }
110 |
111 | .view-details-sized(16px)
112 | }
113 |
114 | #view.view-details {
115 | &.width-0 {
116 | .label {
117 | margin-right: 4px;
118 | }
119 | .date {
120 | display: none;
121 | }
122 | .size {
123 | display: none;
124 | }
125 | }
126 |
127 | &.width-1 {
128 | .label {
129 | margin-right: 64px;
130 | }
131 | .date {
132 | display: none;
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-util.php:
--------------------------------------------------------------------------------
1 | $err, 'msg' => $msg]);
26 | }
27 | }
28 |
29 | public static function array_query($array, $keypath = '', $default = Util::NO_DEFAULT) {
30 | $value = $array;
31 |
32 | $keys = array_filter(explode('.', $keypath));
33 | foreach ($keys as $key) {
34 | if (!is_array($value) || !array_key_exists($key, $value)) {
35 | return $default;
36 | }
37 | $value = $value[$key];
38 | }
39 |
40 | return $value;
41 | }
42 |
43 | public static function starts_with($sequence, $head) {
44 | return substr($sequence, 0, strlen($head)) === $head;
45 | }
46 |
47 | public static function ends_with($sequence, $tail) {
48 | $len = strlen($tail);
49 | return $len === 0 ? true : substr($sequence, -$len) === $tail;
50 | }
51 |
52 | public static function wrap_pattern($pattern) {
53 | return Util::RE_DELIMITER . str_replace(Util::RE_DELIMITER, '\\' . Util::RE_DELIMITER, $pattern) . Util::RE_DELIMITER;
54 | }
55 |
56 | public static function passthru_cmd($cmd) {
57 | $rc = null;
58 | passthru($cmd, $rc);
59 | return $rc;
60 | }
61 |
62 | public static function exec_cmdv($cmdv) {
63 | if (!is_array($cmdv)) {
64 | $cmdv = func_get_args();
65 | }
66 | $cmd = implode(' ', array_map('escapeshellarg', $cmdv));
67 |
68 | $lines = [];
69 | $rc = null;
70 | exec($cmd, $lines, $rc);
71 | return implode("\n", $lines);
72 | }
73 |
74 | public static function exec_0($cmd) {
75 | $lines = [];
76 | $rc = null;
77 | try {
78 | @exec($cmd, $lines, $rc);
79 | return $rc === 0;
80 | } catch (Exception $e) {}
81 | return false;
82 | }
83 |
84 | public static function filesize($context, $path) {
85 | $withFoldersize = $context->query_option('foldersize.enabled', false);
86 | $withDu = $context->get_setup()->get('HAS_CMD_DU') && $context->query_option('foldersize.type', null) === 'shell-du';
87 | return Filesize::getCachedSize($path, $withFoldersize, $withDu);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/ext/preview.less:
--------------------------------------------------------------------------------
1 | #pv-overlay {
2 | position: fixed;
3 | left: 0;
4 | top: 0;
5 | right: 0;
6 | bottom: 0;
7 | z-index: 100;
8 |
9 | background: rgba(0,0,0,0.5);
10 | transition: background-color 0.3s ease-in-out;
11 |
12 | text-align: center;
13 | }
14 |
15 | #pv-overlay.fullscreen {
16 | background: @col-grey-900;
17 | }
18 |
19 | #pv-container {
20 | position: absolute;
21 | }
22 |
23 | #pv-spinner {
24 | position: absolute;
25 |
26 | .back {
27 | width: 240px;
28 | height: 240px;
29 | margin: -120px -120px;
30 | border-radius: 120px;
31 | opacity: 0.5;
32 | overflow: hidden;
33 | }
34 | .spinner {
35 | width: 100px;
36 | height: 100px;
37 | margin: -50px -50px;
38 | }
39 | }
40 |
41 | #pv-prev-area, #pv-next-area {
42 | position: absolute;
43 | top: 50%;
44 | cursor: pointer;
45 |
46 | img {
47 | .eased-transition;
48 | display: block;
49 | width: 48px;
50 | height: 48px;
51 | margin: -36px 0;
52 | padding: 12px;
53 | opacity: 0.5;
54 | }
55 |
56 | &:hover {
57 | img {
58 | .raised;
59 | opacity: 1;
60 | background: rgba(27,27,27,0.8);
61 | }
62 | }
63 | }
64 |
65 | #pv-prev-area {
66 | left: 0;
67 | img {
68 | border-radius: 0 8px 8px 0;
69 | padding-left: 48px;
70 | }
71 | }
72 |
73 | #pv-next-area {
74 | right: 0;
75 | img {
76 | border-radius: 8px 0 0 8px;
77 | padding-right: 48px;
78 | }
79 | }
80 |
81 | #pv-buttons {
82 | list-style: none;
83 | list-style-image: none;
84 | margin: 0;
85 | padding: 0;
86 |
87 | img {
88 | position: relative;
89 | width: 24px;
90 | height: 24px;
91 | padding: 12px
92 | }
93 |
94 | .bar-label {
95 | .eased-transition;
96 | display: block;
97 | color: @col-text-primary-white;
98 | height: 48px;
99 | line-height: 48px;
100 | padding: 0 12px;
101 | opacity: 0.7;
102 | }
103 |
104 | .bar-button {
105 | .eased-transition;
106 | display: block;
107 | line-height: 48px;
108 | opacity: 0.7;
109 | cursor: pointer;
110 |
111 | &:hover {
112 | opacity: 1.0;
113 | background: rgba(255,255,255,0.1);
114 | }
115 | }
116 |
117 | .bar-left {
118 | float: left;
119 | }
120 |
121 | .bar-right {
122 | float: right;
123 | }
124 | }
125 |
126 | #pv-bottombar {
127 | .raised;
128 | position: fixed;
129 | z-index: 5;
130 | left: 0;
131 | right: 0;
132 | bottom: 0;
133 | background: rgb(27,27,27);
134 | height: 48px;
135 | }
136 |
137 | #pv-overlay.fullscreen {
138 |
139 | #pv-bottombar {
140 | opacity: 0.5;
141 | }
142 | }
143 |
144 | @media only screen and (max-width: 700px) {
145 | #pv-prev-area, #pv-next-area {
146 | display: none !important;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/_h5ai/public/css/lib/view/sidebar.less:
--------------------------------------------------------------------------------
1 | #sidebar {
2 | overflow-x: hidden;
3 | overflow-y: auto;
4 | flex: 0 0 auto;
5 | order: 0;
6 | background: @col-back-panel;
7 | border-right: 1px solid @col-border;
8 | padding: 16px;
9 |
10 | position: absolute;
11 | top: 48px;
12 | min-height: 100%;
13 | z-index: 1;
14 |
15 | .block {
16 | display: block;
17 | margin: 0 0 24px 0;
18 | width: 168px;
19 |
20 | h1 {
21 | font-size: 1em;
22 | margin: 2px 0 6px 0;
23 | }
24 | }
25 |
26 | .button {
27 | .rounded;
28 | .eased-transition;
29 | display: inline-block;
30 | margin: 4px;
31 | color: @col-text;
32 | cursor: pointer;
33 |
34 | &:hover {
35 | background: @col-back-hover;
36 | }
37 |
38 | &.active {
39 | background: rgba(0,0,0,0.03);
40 | box-shadow: inset 0 0 4px 0 rgba(0,0,0,0.4);
41 | }
42 |
43 | img {
44 | width: 24px;
45 | height: 24px;
46 | padding: 12px;
47 | }
48 | }
49 |
50 | .select {
51 | .rounded;
52 | background: transparent;
53 | overflow: hidden;
54 | outline: 0;
55 | width: 160px;
56 | margin: 4px;
57 | line-height: 48px;
58 | }
59 |
60 | input, select {
61 | .clear-appearance;
62 | background: transparent;
63 | width: 100%;
64 | border: 0 solid #000;
65 | cursor: pointer;
66 | outline: 0;
67 |
68 | &:hover {
69 | background: @col-back-hover;
70 | }
71 | }
72 |
73 | select {
74 | width: 187px;
75 | padding: 0 8px;
76 | height: 48px;
77 | line-height: 48px;
78 | }
79 |
80 | input[type='range'] {
81 | .rounded;
82 | width: 144px;
83 | margin: 4px;
84 | padding: 8px;
85 | vertical-align: middle;
86 | height: 32px;
87 | line-height: 32px;
88 | }
89 |
90 | .range-track {
91 | .clear-appearance;
92 | border-width: 0;
93 | border-radius: 20px;
94 | background: @col-range-back;
95 | height: 6px;
96 | }
97 |
98 | .range-thumb {
99 | .clear-appearance;
100 | border-width: 0;
101 | border-radius: 20px;
102 | background: @col-range-thumb;
103 | width: 16px;
104 | height: 16px;
105 | }
106 |
107 | input[type='range']::-webkit-slider-runnable-track { .range-track; }
108 | input[type='range']::-moz-range-track { .range-track; }
109 | input[type='range']::-ms-track { .range-track; }
110 | input[type='range']::-ms-fill-lower { .range-track; }
111 | input[type='range']::-ms-fill-upper { .range-track; }
112 |
113 | input[type='range']::-webkit-slider-thumb { .range-thumb; margin-top: -5px; }
114 | input[type='range']::-moz-range-thumb { .range-thumb; }
115 | input[type='range']::-ms-thumb { .range-thumb; }
116 |
117 | #view-size {
118 | display: block;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/_h5ai/private/conf/types.json:
--------------------------------------------------------------------------------
1 | {
2 | "ar": ["*.tar.bz2", "*.crx"],
3 | "ar-apk": ["*.apk"],
4 | "ar-deb": ["*.deb"],
5 | "ar-gz": ["*.gz", "*.tar.gz", "*.tgz"],
6 | "ar-rar": ["*.rar"],
7 | "ar-rpm": ["*.rpm"],
8 | "ar-tar": ["*.tar"],
9 | "ar-zip": ["*.7z", "*.bz2", "*.jar", "*.lzma", "*.war", "*.z", "*.Z", "*.zip"],
10 | "aud": ["*.aif", "*.aiff", "*.flac", "*.m4a", "*.mid", "*.mp3", "*.mpa", "*.ra", "*.ogg", "*.wav", "*.wma"],
11 | "aud-pls": ["*.m3u", "*.m3u8", "*.pls"],
12 | "bin": ["*.class", "*.o", "*.so"],
13 | "bin-exe": ["*.bat", "*.cmd", "*.exe"],
14 | "img": ["*.xpm"],
15 | "img-bmp": ["*.bmp"],
16 | "img-gif": ["*.gif"],
17 | "img-ico": ["*.ico"],
18 | "img-jpg": ["*.jpg", "*.jpeg"],
19 | "img-png": ["*.png"],
20 | "img-raw": ["*.cr2", "*.nef"],
21 | "img-svg": ["*.svg"],
22 | "img-tiff": ["*.tiff"],
23 | "txt": ["*.text", "*.txt"],
24 | "txt-build": ["*.pom", "build.xml", "pom.xml"],
25 | "txt-c": ["*.c"],
26 | "txt-cpp": ["*.cpp"],
27 | "txt-css": ["*.css"],
28 | "txt-diff": ["*.diff", "*.patch"],
29 | "txt-go": ["*.go"],
30 | "txt-h": ["*.h"],
31 | "txt-html": ["*.htm", "*.html", "*.shtml", "*.xhtml"],
32 | "txt-hpp": ["*.hpp"],
33 | "txt-java": ["*.java"],
34 | "txt-scala": ["*.scala"],
35 | "txt-js": ["*.js"],
36 | "txt-json": ["*.json"],
37 | "txt-less": ["*.less"],
38 | "txt-log": ["*.log", "changelog*"],
39 | "txt-md": ["*.markdown", "*.md"],
40 | "txt-php": ["*.php"],
41 | "txt-py": ["*.py"],
42 | "txt-rb": ["*.rb"],
43 | "txt-rss": ["*.rss"],
44 | "txt-rtf": ["*.rtf"],
45 | "txt-rust": ["*.rs", "*.rlib"],
46 | "txt-script": ["*.conf", "*.bsh", "*.csh", "*.ini", "*.ksh", "*.sh", "*.shar", "*.tcl", "*.zsh"],
47 | "txt-source": [],
48 | "txt-tex": ["*.tex"],
49 | "txt-vcal": ["*.vcal"],
50 | "txt-xml": ["*.xml"],
51 | "vid": [],
52 | "vid-avi": ["*.avi"],
53 | "vid-flv": ["*.flv"],
54 | "vid-mkv": ["*.mkv"],
55 | "vid-mov": ["*.mov"],
56 | "vid-mp4": ["*.mp4", "*.m4v"],
57 | "vid-mpg": ["*.mpg"],
58 | "vid-rm": ["*.rm"],
59 | "vid-swf": ["*.swf"],
60 | "vid-ts": ["*.ts"],
61 | "vid-vob": ["*.vob"],
62 | "vid-webm": ["*.webm"],
63 | "vid-wmv": ["*.wmv"],
64 | "x": [],
65 | "x-bak": ["*.bak", "*~"],
66 | "x-calc": ["*.ods", "*.ots", "*.xlr", "*.xls", "*.xlsx"],
67 | "x-disc": ["*.cue", "*.iso"],
68 | "x-doc": ["*.doc", "*.docx", "*.odm", "*.odt", "*.ott"],
69 | "x-draw": ["*.drw"],
70 | "x-eps": ["*.eps"],
71 | "x-pdf": ["*.pdf"],
72 | "x-pres": ["*.odp", "*.otp", "*.pps", "*.ppt", "*.pptx"],
73 | "x-ps": ["*.ps"],
74 | "x-psd": ["*.psd"]
75 | }
76 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-less.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/thumbnails.js:
--------------------------------------------------------------------------------
1 | const {each, map, includes} = require('../util');
2 | const server = require('../server');
3 | const event = require('../core/event');
4 | const allsettings = require('../core/settings');
5 |
6 | const settings = Object.assign({
7 | enabled: false,
8 | img: ['img-bmp', 'img-gif', 'img-ico', 'img-jpg', 'img-png'],
9 | mov: ['vid-avi', 'vid-flv', 'vid-mkv', 'vid-mov', 'vid-mp4', 'vid-mpg', 'vid-webm'],
10 | doc: ['x-pdf', 'x-ps'],
11 | delay: 1,
12 | size: 100,
13 | exif: false,
14 | chunksize: 20
15 | }, allsettings.thumbnails);
16 | const landscapeRatio = 4 / 3;
17 |
18 |
19 | const queueItem = (queue, item) => {
20 | let type = null;
21 |
22 | if (includes(settings.img, item.type)) {
23 | type = 'img';
24 | } else if (includes(settings.mov, item.type)) {
25 | type = 'mov';
26 | } else if (includes(settings.doc, item.type)) {
27 | type = 'doc';
28 | } else {
29 | return;
30 | }
31 |
32 | if (item.thumbSquare) {
33 | item.$view.find('.icon.square img').addCls('thumb').attr('src', item.thumbSquare);
34 | } else {
35 | queue.push({
36 | type,
37 | href: item.absHref,
38 | ratio: 1,
39 | callback: src => {
40 | if (src && item.$view) {
41 | item.thumbSquare = src;
42 | item.$view.find('.icon.square img').addCls('thumb').attr('src', src);
43 | }
44 | }
45 | });
46 | }
47 |
48 | if (item.thumbRational) {
49 | item.$view.find('.icon.landscape img').addCls('thumb').attr('src', item.thumbRational);
50 | } else {
51 | queue.push({
52 | type,
53 | href: item.absHref,
54 | ratio: landscapeRatio,
55 | callback: src => {
56 | if (src && item.$view) {
57 | item.thumbRational = src;
58 | item.$view.find('.icon.landscape img').addCls('thumb').attr('src', src);
59 | }
60 | }
61 | });
62 | }
63 | };
64 |
65 | const requestQueue = queue => {
66 | const thumbs = map(queue, req => {
67 | return {
68 | type: req.type,
69 | href: req.href,
70 | width: Math.round(settings.size * req.ratio),
71 | height: settings.size
72 | };
73 | });
74 |
75 | return server.request({
76 | action: 'get',
77 | thumbs
78 | }).then(json => {
79 | each(queue, (req, idx) => {
80 | req.callback(json && json.thumbs ? json.thumbs[idx] : null);
81 | });
82 | });
83 | };
84 |
85 | const breakAndRequestQueue = queue => {
86 | const len = queue.length;
87 | const chunksize = settings.chunksize;
88 | let p = Promise.resolve();
89 | for (let i = 0; i < len; i += chunksize) {
90 | p = p.then(() => requestQueue(queue.slice(i, i + chunksize)));
91 | }
92 | };
93 |
94 | const handleItems = items => {
95 | const queue = [];
96 | each(items, item => queueItem(queue, item));
97 | breakAndRequestQueue(queue);
98 | };
99 |
100 | const onViewChanged = added => {
101 | setTimeout(() => handleItems(added), settings.delay);
102 | };
103 |
104 | const init = () => {
105 | if (!settings.enabled) {
106 | return;
107 | }
108 |
109 | event.sub('view.changed', onViewChanged);
110 | };
111 |
112 |
113 | init();
114 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/txt-rust.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/ar-rpm.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/ui/paypal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/l10n.js:
--------------------------------------------------------------------------------
1 | const {each, isStr, dom} = require('../util');
2 | const server = require('../server');
3 | const event = require('../core/event');
4 | const format = require('../core/format');
5 | const langs = require('../core/langs');
6 | const allsettings = require('../core/settings');
7 | const store = require('../core/store');
8 |
9 | const win = global.window;
10 | const settings = Object.assign({
11 | enabled: false,
12 | lang: 'en',
13 | useBrowserLang: true
14 | }, allsettings.l10n);
15 | const defaultTranslations = {
16 | isoCode: 'en',
17 | lang: 'english',
18 |
19 | dateFormat: 'YYYY-MM-DD HH:mm',
20 | details: 'details',
21 | download: 'download',
22 | empty: 'empty',
23 | files: 'files',
24 | filter: 'filter',
25 | folders: 'folders',
26 | grid: 'grid',
27 | icons: 'icons',
28 | language: 'Language',
29 | lastModified: 'Last modified',
30 | name: 'Name',
31 | noMatch: 'no match',
32 | parentDirectory: 'Parent Directory',
33 | search: 'search',
34 | size: 'Size',
35 | tree: 'Tree',
36 | view: 'View'
37 | };
38 | const blockTpl =
39 | `
40 |
Language
41 |
42 |
43 |
44 |
`;
45 | const optionTpl = '';
46 | const storekey = 'ext/l10n';
47 | const loaded = {
48 | en: Object.assign({}, defaultTranslations)
49 | };
50 | let currentLang = loaded.en;
51 |
52 |
53 | const update = lang => {
54 | if (lang) {
55 | currentLang = lang;
56 | }
57 |
58 | const sel = 'selected';
59 | dom('#langs option').rmAttr(sel).rmProp(sel);
60 | dom('#langs .' + currentLang.isoCode).attr(sel, '').prop(sel, true);
61 |
62 | each(currentLang, (value, key) => {
63 | dom('.l10n-' + key).text(value);
64 | dom('.l10n_ph-' + key).attr('placeholder', value);
65 | });
66 | format.setDefaultDateFormat(currentLang.dateFormat);
67 |
68 | dom('#items .item').each(el => {
69 | dom(el).find('.date').text(format.formatDate(el._item.time));
70 | });
71 | };
72 |
73 | const loadLanguage = isoCode => {
74 | if (loaded[isoCode]) {
75 | return Promise.resolve(loaded[isoCode]);
76 | }
77 |
78 | return server.request({action: 'get', l10n: [isoCode]}).then(response => {
79 | loaded[isoCode] = Object.assign({},
80 | defaultTranslations,
81 | response.l10n && response.l10n[isoCode],
82 | {isoCode}
83 | );
84 | return loaded[isoCode];
85 | });
86 | };
87 |
88 | const localize = (languages, isoCode, useBrowserLang) => {
89 | const storedIsoCode = store.get(storekey);
90 |
91 | if (languages[storedIsoCode]) {
92 | isoCode = storedIsoCode;
93 | } else if (useBrowserLang) {
94 | const browserLang = win.navigator.language || win.navigator.browserLanguage;
95 | if (browserLang) {
96 | if (languages[browserLang]) {
97 | isoCode = browserLang;
98 | } else if (browserLang.length > 2 && languages[browserLang.substr(0, 2)]) {
99 | isoCode = browserLang.substr(0, 2);
100 | }
101 | }
102 | }
103 |
104 | if (!languages[isoCode]) {
105 | isoCode = 'en';
106 | }
107 |
108 | loadLanguage(isoCode).then(update);
109 | };
110 |
111 | const initLangSelector = languages => {
112 | const $block = dom(blockTpl);
113 | const $select = $block.find('select')
114 | .on('change', ev => {
115 | const isoCode = ev.target.value;
116 | store.put(storekey, isoCode);
117 | localize(languages, isoCode, false);
118 | });
119 |
120 | each(languages, (language, isoCode) => {
121 | dom(optionTpl)
122 | .attr('value', isoCode)
123 | .addCls(isoCode)
124 | .text(isoCode + ' - ' + (isStr(language) ? language : language.lang))
125 | .appTo($select);
126 | });
127 |
128 | $block.appTo('#sidebar');
129 | };
130 |
131 | const init = () => {
132 | if (settings.enabled) {
133 | initLangSelector(langs);
134 | }
135 |
136 | event.sub('view.changed', () => {
137 | localize(langs, settings.lang, settings.useBrowserLang);
138 | });
139 | };
140 |
141 |
142 | init();
143 |
--------------------------------------------------------------------------------
/src/_h5ai/private/php/core/class-api.php:
--------------------------------------------------------------------------------
1 | context = $context;
10 | $this->request = $context->get_request();
11 | $this->setup = $context->get_setup();
12 | }
13 |
14 | public function apply() {
15 | $action = $this->request->query('action');
16 | $supported = ['download', 'get', 'login', 'logout'];
17 | Util::json_fail(Util::ERR_UNSUPPORTED, 'unsupported action', !in_array($action, $supported));
18 |
19 | $methodname = 'on_' . $action;
20 | $this->$methodname();
21 | }
22 |
23 | private function on_download() {
24 | Util::json_fail(Util::ERR_DISABLED, 'download disabled', !$this->context->query_option('download.enabled', false));
25 |
26 | $as = $this->request->query('as');
27 | $type = $this->request->query('type');
28 | $base_href = $this->request->query('baseHref');
29 | $hrefs = $this->request->query('hrefs', '');
30 |
31 | $archive = new Archive($this->context);
32 |
33 | set_time_limit(0);
34 | header('Content-Type: application/octet-stream');
35 | header('Content-Disposition: attachment; filename="' . $as . '"');
36 | header('Connection: close');
37 | $ok = $archive->output($type, $base_href, $hrefs);
38 |
39 | Util::json_fail(Util::ERR_FAILED, 'packaging failed', !$ok);
40 | exit;
41 | }
42 |
43 | private function on_get() {
44 | $response = [];
45 |
46 | foreach (['langs', 'options', 'types'] as $name) {
47 | if ($this->request->query_boolean($name, false)) {
48 | $methodname = 'get_' . $name;
49 | $response[$name] = $this->context->$methodname();
50 | }
51 | }
52 |
53 | if ($this->request->query_boolean('setup', false)) {
54 | $response['setup'] = $this->setup->to_jsono($this->context->is_admin());
55 | }
56 |
57 | if ($this->request->query_boolean('theme', false)) {
58 | $theme = new Theme($this->context);
59 | $response['theme'] = $theme->get_icons();
60 | }
61 |
62 | if ($this->request->query('items', false)) {
63 | $href = $this->request->query('items.href');
64 | $what = $this->request->query_numeric('items.what');
65 | $response['items'] = $this->context->get_items($href, $what);
66 | }
67 |
68 | if ($this->request->query('custom', false)) {
69 | Util::json_fail(Util::ERR_DISABLED, 'custom disabled', !$this->context->query_option('custom.enabled', false));
70 | $href = $this->request->query('custom');
71 | $custom = new Custom($this->context);
72 | $response['custom'] = $custom->get_customizations($href);
73 | }
74 |
75 | if ($this->request->query('l10n', false)) {
76 | Util::json_fail(Util::ERR_DISABLED, 'l10n disabled', !$this->context->query_option('l10n.enabled', false));
77 | $iso_codes = $this->request->query_array('l10n');
78 | $iso_codes = array_filter($iso_codes);
79 | $response['l10n'] = $this->context->get_l10n($iso_codes);
80 | }
81 |
82 | if ($this->request->query('search', false)) {
83 | Util::json_fail(Util::ERR_DISABLED, 'search disabled', !$this->context->query_option('search.enabled', false));
84 | $href = $this->request->query('search.href');
85 | $pattern = $this->request->query('search.pattern');
86 | $ignorecase = $this->request->query_boolean('search.ignorecase', false);
87 | $search = new Search($this->context);
88 | $response['search'] = $search->get_items($href, $pattern, $ignorecase);
89 | }
90 |
91 | if ($this->request->query('thumbs', false)) {
92 | Util::json_fail(Util::ERR_DISABLED, 'thumbnails disabled', !$this->context->query_option('thumbnails.enabled', false));
93 | Util::json_fail(Util::ERR_UNSUPPORTED, 'thumbnails not supported', !$this->setup->get('HAS_PHP_JPEG'));
94 | $thumbs = $this->request->query_array('thumbs');
95 | $response['thumbs'] = $this->context->get_thumbs($thumbs);
96 | }
97 |
98 | Util::json_exit($response);
99 | }
100 |
101 | private function on_login() {
102 | $pass = $this->request->query('pass');
103 | Util::json_exit(['asAdmin' => $this->context->login_admin($pass)]);
104 | }
105 |
106 | private function on_logout() {
107 | Util::json_exit(['asAdmin' => $this->context->logout_admin()]);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/_h5ai/public/js/lib/ext/info.js:
--------------------------------------------------------------------------------
1 | const kjua = require('kjua');
2 | const {isNum, dom} = require('../util');
3 | const event = require('../core/event');
4 | const format = require('../core/format');
5 | const resource = require('../core/resource');
6 | const allsettings = require('../core/settings');
7 | const store = require('../core/store');
8 |
9 |
10 | const settings = Object.assign({
11 | enabled: false,
12 | show: false,
13 | qrcode: true,
14 | qrColor: '#999'
15 | }, allsettings.info);
16 | const tpl =
17 | `
18 |
19 |
20 |
21 |
22 |
23 |
24 | ,
25 |
26 |
27 |
28 |
29 |
`;
30 | const settingsTpl =
31 | ``;
37 | const storekey = 'ext/info';
38 | let $img;
39 | let $label;
40 | let $time;
41 | let $size;
42 | let $content;
43 | let $folders;
44 | let $files;
45 | let $qrcode;
46 | let currentFolder;
47 |
48 |
49 | const updateSettings = () => {
50 | if (store.get(storekey)) {
51 | dom('#view-info').addCls('active');
52 | dom('#info').show();
53 | } else {
54 | dom('#view-info').rmCls('active');
55 | dom('#info').hide();
56 | }
57 | };
58 |
59 | const update = item => {
60 | let src = item.thumbRational || item.icon;
61 | const isThumb = !!item.thumbRational;
62 |
63 | if (item.isCurrentFolder() || !src) {
64 | src = resource.icon('folder');
65 | }
66 |
67 | $img.attr('src', src);
68 | if (isThumb) {
69 | $img.addCls('thumb');
70 | } else {
71 | $img.rmCls('thumb');
72 | }
73 |
74 | $label.text(item.label);
75 | if (isNum(item.time)) {
76 | $time.text(format.formatDate(item.time));
77 | } else {
78 | $time.text('.');
79 | }
80 |
81 | if (isNum(item.size)) {
82 | $size.text(format.formatSize(item.size));
83 | $size.show();
84 | } else {
85 | $size.hide();
86 | }
87 |
88 | if (item.isContentFetched) {
89 | const stats = item.getStats();
90 | $folders.text(stats.folders);
91 | $files.text(stats.files);
92 | $content.show();
93 | } else {
94 | $content.hide();
95 | }
96 |
97 | if (settings.qrcode) {
98 | const loc = global.window.location;
99 | $qrcode.clr().app(kjua({
100 | render: 'image',
101 | size: 200,
102 | fill: settings.qrFill,
103 | back: settings.qrBack,
104 | text: loc.protocol + '//' + loc.host + item.absHref,
105 | crisp: true,
106 | quiet: 1
107 | }));
108 | }
109 | };
110 |
111 | const onMouseenter = item => {
112 | update(item);
113 | };
114 |
115 | const onMouseleave = () => {
116 | update(currentFolder);
117 | };
118 |
119 | const onLocationChanged = item => {
120 | currentFolder = item;
121 | update(currentFolder);
122 | };
123 |
124 | const init = () => {
125 | if (!settings.enabled) {
126 | return;
127 | }
128 |
129 | const $info = dom(tpl).hide().appTo('#mainrow');
130 | $img = $info.find('.icon img');
131 | $label = $info.find('.label');
132 | $time = $info.find('.time');
133 | $size = $info.find('.size');
134 | $content = $info.find('.content');
135 | $folders = $info.find('.folders');
136 | $files = $info.find('.files');
137 | $qrcode = $info.find('.qrcode');
138 |
139 | if (!settings.qrcode) {
140 | $qrcode.rm();
141 | }
142 |
143 | dom(settingsTpl)
144 | .appTo('#sidebar')
145 | .find('#view-info')
146 | .on('click', ev => {
147 | store.put(storekey, !store.get(storekey));
148 | updateSettings();
149 | event.pub('resize');
150 | ev.preventDefault();
151 | });
152 |
153 | // ensure stored value is boolean, otherwise set default
154 | if (typeof store.get(storekey) !== 'boolean') {
155 | store.put(storekey, settings.show);
156 | }
157 | updateSettings();
158 |
159 | event.sub('location.changed', onLocationChanged);
160 | event.sub('item.mouseenter', onMouseenter);
161 | event.sub('item.mouseleave', onMouseleave);
162 | };
163 |
164 |
165 | init();
166 |
--------------------------------------------------------------------------------
/src/_h5ai/public/images/themes/comity/ar-deb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------