├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── ghu.js ├── package-lock.json ├── package.json ├── src ├── .eslintrc └── _h5ai │ ├── .htaccess │ ├── private │ ├── cache │ │ └── README.md │ ├── conf │ │ ├── l10n │ │ │ ├── af.json │ │ │ ├── bg.json │ │ │ ├── cs.json │ │ │ ├── da.json │ │ │ ├── de.json │ │ │ ├── el.json │ │ │ ├── en.json │ │ │ ├── es.json │ │ │ ├── et.json │ │ │ ├── fi.json │ │ │ ├── fr.json │ │ │ ├── he.json │ │ │ ├── hi.json │ │ │ ├── hr.json │ │ │ ├── hu.json │ │ │ ├── id.json │ │ │ ├── it.json │ │ │ ├── ja.json │ │ │ ├── ko.json │ │ │ ├── lv.json │ │ │ ├── nb.json │ │ │ ├── nl.json │ │ │ ├── pl.json │ │ │ ├── pt-br.json │ │ │ ├── pt-pt.json │ │ │ ├── ro.json │ │ │ ├── ru.json │ │ │ ├── sk.json │ │ │ ├── sl.json │ │ │ ├── sr.json │ │ │ ├── sv.json │ │ │ ├── tr.json │ │ │ ├── uk.json │ │ │ ├── zh-cn.json │ │ │ └── zh-tw.json │ │ ├── options.json │ │ └── types.json │ └── php │ │ ├── class-bootstrap.php │ │ ├── core │ │ ├── class-api.php │ │ ├── class-context.php │ │ ├── class-fallback.php │ │ ├── class-filesize.php │ │ ├── class-item.php │ │ ├── class-json.php │ │ ├── class-logger.php │ │ ├── class-request.php │ │ ├── class-session.php │ │ ├── class-setup.php │ │ ├── class-theme.php │ │ └── class-util.php │ │ ├── ext │ │ ├── class-archive.php │ │ ├── class-custom.php │ │ ├── class-search.php │ │ └── class-thumb.php │ │ └── pages │ │ ├── index.php.pug │ │ ├── info.php.pug │ │ └── page.tpl.pug │ └── public │ ├── .htaccess │ ├── cache │ └── README.md │ ├── css │ ├── lib │ │ ├── colors.less │ │ ├── ext │ │ │ ├── contextmenu.less │ │ │ ├── crumb.less │ │ │ ├── custom.less │ │ │ ├── filter.less │ │ │ ├── info.less │ │ │ ├── preview-aud.less │ │ │ ├── preview-img.less │ │ │ ├── preview-txt.less │ │ │ ├── preview-vid.less │ │ │ ├── preview.less │ │ │ ├── search.less │ │ │ ├── select.less │ │ │ └── tree.less │ │ ├── fonts.less │ │ ├── main │ │ │ └── info.less │ │ ├── misc.less │ │ ├── mixins.less │ │ ├── responsive.less │ │ └── view │ │ │ ├── content.less │ │ │ ├── fallback.less │ │ │ ├── mainrow.less │ │ │ ├── notification.less │ │ │ ├── root.less │ │ │ ├── sidebar.less │ │ │ ├── topbar.less │ │ │ ├── view-details.less │ │ │ ├── view-grid.less │ │ │ ├── view-icons.less │ │ │ └── view.less │ └── styles.less │ ├── ext │ └── README.md │ ├── images │ ├── fallback │ │ ├── file.png │ │ ├── folder-parent.png │ │ └── folder.png │ ├── favicon │ │ ├── favicon-152.png │ │ ├── favicon-16-32.ico │ │ ├── favicon-16.png │ │ ├── favicon-32.png │ │ └── favicon.svg │ ├── themes │ │ ├── README.md │ │ ├── comity │ │ │ ├── ar-apk.svg │ │ │ ├── ar-deb.svg │ │ │ ├── ar-rpm.svg │ │ │ ├── txt-css.svg │ │ │ ├── txt-go.svg │ │ │ ├── txt-html.svg │ │ │ ├── txt-js.svg │ │ │ ├── txt-less.svg │ │ │ ├── txt-md.svg │ │ │ ├── txt-php.svg │ │ │ ├── txt-py.svg │ │ │ ├── txt-rb.svg │ │ │ ├── txt-rust.svg │ │ │ ├── txt-script.svg │ │ │ └── x-pdf.svg │ │ └── default │ │ │ ├── ar.svg │ │ │ ├── aud.svg │ │ │ ├── bin.svg │ │ │ ├── file.svg │ │ │ ├── folder-page.svg │ │ │ ├── folder-parent.svg │ │ │ ├── folder.svg │ │ │ ├── img.svg │ │ │ ├── txt.svg │ │ │ ├── vid.svg │ │ │ └── x.svg │ └── ui │ │ ├── back.svg │ │ ├── crumb.svg │ │ ├── download.svg │ │ ├── filter.svg │ │ ├── info-toggle.svg │ │ ├── paypal.svg │ │ ├── preview-close.svg │ │ ├── preview-fullscreen.svg │ │ ├── preview-next.svg │ │ ├── preview-no-fullscreen.svg │ │ ├── preview-prev.svg │ │ ├── preview-raw.svg │ │ ├── search.svg │ │ ├── selected.svg │ │ ├── sidebar.svg │ │ ├── sort.svg │ │ ├── spinner.svg │ │ ├── tree-indicator.svg │ │ ├── tree-toggle.svg │ │ ├── view-details.svg │ │ ├── view-grid.svg │ │ └── view-icons.svg │ ├── index.php │ └── js │ ├── lib │ ├── config.js │ ├── core │ │ ├── event.js │ │ ├── format.js │ │ ├── langs.js │ │ ├── location.js │ │ ├── resource.js │ │ ├── settings.js │ │ ├── store.js │ │ └── types.js │ ├── ext │ │ ├── autorefresh.js │ │ ├── contextmenu.js │ │ ├── crumb.js │ │ ├── custom.js │ │ ├── download.js │ │ ├── filter.js │ │ ├── google-analytics.js │ │ ├── info.js │ │ ├── l10n.js │ │ ├── piwik-analytics.js │ │ ├── preview │ │ │ ├── index.js │ │ │ ├── preview-aud.js │ │ │ ├── preview-img.js │ │ │ ├── preview-txt.js │ │ │ ├── preview-vid.js │ │ │ └── preview.js │ │ ├── search.js │ │ ├── select.js │ │ ├── sort.js │ │ ├── thumbnails.js │ │ ├── title.js │ │ └── tree.js │ ├── init.js │ ├── main │ │ ├── index.js │ │ └── info.js │ ├── model │ │ └── item.js │ ├── server.js │ ├── util │ │ ├── dom.js │ │ ├── index.js │ │ ├── lo.js │ │ ├── misc.js │ │ └── natural_cmp.js │ └── view │ │ ├── base.js │ │ ├── notification.js │ │ ├── sidebar.js │ │ ├── view.js │ │ └── viewmode.js │ ├── pre.js │ └── scripts.js └── test ├── index.html ├── index.js ├── tests ├── premisses.js └── unit │ ├── core │ ├── event.js │ └── format.js │ └── util │ ├── naturalCmp.js │ └── parsePatten.js └── util ├── pin.js └── reqlib.js /.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 | [{*.json,*.yml}] 16 | indent_size = 2 17 | 18 | 19 | [{*.md,*.pug}] 20 | trim_trailing_whitespace = false 21 | 22 | 23 | [*.svg] 24 | insert_final_newline = false 25 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /local/ 3 | /node_modules/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.nyc_output/ 2 | /build/ 3 | /coverage/ 4 | /local/ 5 | /node_modules/ 6 | /npm-debug.log 7 | -------------------------------------------------------------------------------- /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, and nginx. 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 10.0+`][node] to be installed, might work on other 24 | configurations). 25 | 26 | ~~~sh 27 | > npm install 28 | > npm run build 29 | ~~~ 30 | 31 | 32 | ## License 33 | 34 | The MIT License (MIT) 35 | 36 | Copyright (c) 2020 Lars Jung (https://larsjung.de) 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in 46 | all copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 54 | THE SOFTWARE. 55 | 56 | 57 | ## References 58 | 59 | **h5ai** profits from other projects, all of them licensed under the MIT license 60 | too. Exceptions are some [Material Design icons][material-design-icons] (CC BY 4.0). 61 | 62 | 63 | [web]: https://larsjung.de/h5ai/ 64 | [github]: https://github.com/lrsjng/h5ai 65 | [github-issues]: https://github.com/lrsjng/h5ai/issues 66 | [release]: https://release.larsjung.de/h5ai/ 67 | [develop]: https://release.larsjung.de/h5ai/develop/ 68 | [node]: https://nodejs.org 69 | [material-design-icons]: https://github.com/google/material-design-icons 70 | 71 | [license-img]: https://img.shields.io/badge/license-MIT-a0a060.svg?style=flat-square 72 | [web-img]: https://img.shields.io/badge/web-larsjung.de/h5ai-a0a060.svg?style=flat-square 73 | [github-img]: https://img.shields.io/badge/github-lrsjng/h5ai-a0a060.svg?style=flat-square 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "h5ai", 3 | "version": "0.30.0", 4 | "description": "Modern HTTP web server index.", 5 | "homepage": "https://larsjung.de/h5ai/", 6 | "author": "Lars Jung (https://larsjung.de)", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/lrsjng/h5ai.git" 11 | }, 12 | "scripts": { 13 | "lint": "eslint .", 14 | "test": "node test", 15 | "build": "node ghu release", 16 | "precommit": "npm run -s lint && npm run -s test" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "7.12.10", 20 | "@babel/preset-env": "7.12.11", 21 | "eslint": "7.18.0", 22 | "ghu": "0.26.0", 23 | "jsdom": "16.4.0", 24 | "kjua": "0.9.0", 25 | "lolight": "1.4.0", 26 | "marked": "1.2.7", 27 | "normalize.css": "8.0.1", 28 | "null-loader": "4.0.1", 29 | "scar": "2.3.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | no-console: 1 4 | prefer-reflect: 0 5 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "Bahasa Indonesia", 3 | 4 | "dateFormat": "DD-MM-YYYY HH:mm", 5 | "details": "rincian", 6 | "download": "unduh", 7 | "empty": "kosong", 8 | "files": "berkas", 9 | "filter": "saring", 10 | "folders": "pelipat", 11 | "grid": "jaring", 12 | "icons": "ikon", 13 | "language": "Bahasa", 14 | "lastModified": "Di modifikasi", 15 | "name": "Nama", 16 | "noMatch": "tidak cocok", 17 | "parentDirectory": "Direktori induk", 18 | "search": "cari", 19 | "size": "Ukuran", 20 | "tree": "Pohon", 21 | "view": "Tampil" 22 | } 23 | -------------------------------------------------------------------------------- /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 | "language": "Linugua", 14 | "lastModified": "Ultima modifica", 15 | "name": "Nome", 16 | "noMatch": "nessun risultato", 17 | "parentDirectory": "Cartella Superiore", 18 | "search": "cerca", 19 | "size": "Dimensione", 20 | "tree": "Albero", 21 | "view": "Vista" 22 | } 23 | -------------------------------------------------------------------------------- /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/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/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/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/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/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "português do Brasil", 3 | 4 | "dateFormat": "DD-MM-YYYY HH:mm", 5 | "details": "detalhes", 6 | "download": "download", 7 | "empty": "vazio", 8 | "files": "arquivos", 9 | "filter": "filtro", 10 | "folders": "pastas", 11 | "grid": "grade", 12 | "icons": "ícones", 13 | "language": "Idioma", 14 | "lastModified": "Última modificação", 15 | "name": "Nome", 16 | "noMatch": "sem resultados", 17 | "parentDirectory": "Diretório acima", 18 | "search": "pesquisa", 19 | "size": "Tamanho", 20 | "tree": "Árvore", 21 | "view": "Visualização" 22 | } 23 | -------------------------------------------------------------------------------- /src/_h5ai/private/conf/l10n/pt-pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "português de Portugal", 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 | "language": "Idioma", 14 | "lastModified": "última modificação", 15 | "name": "Nome", 16 | "noMatch": "sem resultados", 17 | "parentDirectory": "Diretório acima", 18 | "search": "pesquisa", 19 | "size": "Tamanho", 20 | "tree": "Árvore", 21 | "view": "Visualização" 22 | } 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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 | session_write_close(); 35 | header('Content-Type: application/octet-stream'); 36 | header('Content-Disposition: attachment; filename="' . $as . '"'); 37 | header('Connection: close'); 38 | $ok = $archive->output($type, $base_href, $hrefs); 39 | 40 | Util::json_fail(Util::ERR_FAILED, 'packaging failed', !$ok); 41 | exit; 42 | } 43 | 44 | private function on_get() { 45 | $response = []; 46 | 47 | foreach (['langs', 'options', 'types'] as $name) { 48 | if ($this->request->query_boolean($name, false)) { 49 | $methodname = 'get_' . $name; 50 | $response[$name] = $this->context->$methodname(); 51 | } 52 | } 53 | 54 | if ($this->request->query_boolean('setup', false)) { 55 | $response['setup'] = $this->setup->to_jsono($this->context->is_admin()); 56 | } 57 | 58 | if ($this->request->query_boolean('theme', false)) { 59 | $theme = new Theme($this->context); 60 | $response['theme'] = $theme->get_icons(); 61 | } 62 | 63 | if ($this->request->query('items', false)) { 64 | $href = $this->request->query('items.href'); 65 | $what = $this->request->query_numeric('items.what'); 66 | $response['items'] = $this->context->get_items($href, $what); 67 | } 68 | 69 | if ($this->request->query('custom', false)) { 70 | Util::json_fail(Util::ERR_DISABLED, 'custom disabled', !$this->context->query_option('custom.enabled', false)); 71 | $href = $this->request->query('custom'); 72 | $custom = new Custom($this->context); 73 | $response['custom'] = $custom->get_customizations($href); 74 | } 75 | 76 | if ($this->request->query('l10n', false)) { 77 | Util::json_fail(Util::ERR_DISABLED, 'l10n disabled', !$this->context->query_option('l10n.enabled', false)); 78 | $iso_codes = $this->request->query_array('l10n'); 79 | $iso_codes = array_filter($iso_codes); 80 | $response['l10n'] = $this->context->get_l10n($iso_codes); 81 | } 82 | 83 | if ($this->request->query('search', false)) { 84 | Util::json_fail(Util::ERR_DISABLED, 'search disabled', !$this->context->query_option('search.enabled', false)); 85 | $href = $this->request->query('search.href'); 86 | $pattern = $this->request->query('search.pattern'); 87 | $ignorecase = $this->request->query_boolean('search.ignorecase', false); 88 | $search = new Search($this->context); 89 | $response['search'] = $search->get_items($href, $pattern, $ignorecase); 90 | } 91 | 92 | if ($this->request->query('thumbs', false)) { 93 | Util::json_fail(Util::ERR_DISABLED, 'thumbnails disabled', !$this->context->query_option('thumbnails.enabled', false)); 94 | Util::json_fail(Util::ERR_UNSUPPORTED, 'thumbnails not supported', !$this->setup->get('HAS_PHP_JPEG')); 95 | $thumbs = $this->request->query_array('thumbs'); 96 | $response['thumbs'] = $this->context->get_thumbs($thumbs); 97 | } 98 | 99 | Util::json_exit($response); 100 | } 101 | 102 | private function on_login() { 103 | $pass = $this->request->query('pass'); 104 | Util::json_exit(['asAdmin' => $this->context->login_admin($pass)]); 105 | } 106 | 107 | private function on_logout() { 108 | Util::json_exit(['asAdmin' => $this->context->logout_admin()]); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /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 .= ''; 26 | $html .= ''; 27 | $html .= ''; 28 | $html .= ''; 29 | 30 | if ($folder->get_parent($cache)) { 31 | $html .= ''; 32 | $html .= ''; 33 | $html .= ''; 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 .= ''; 45 | $html .= ''; 46 | $html .= ''; 47 | $html .= ''; 48 | } 49 | 50 | $html .= '
NameLast modifiedSize
folder-parentParent Directory
' . $type . '' . basename($item->path) . '' . date('Y-m-d H:i', $item->date) . '' . ($item->size !== null ? intval($item->size / 1000) . ' KB' : '' ) . '
'; 51 | 52 | return $html; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /src/_h5ai/private/php/core/class-json.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/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/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/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/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 | 58 | // Stop once we reach the root 59 | if ( 60 | $this->context->query_option('custom.stopSearchingAtRoot', true) && 61 | $path === $this->context->get_setup()->get('ROOT_PATH') 62 | ) { 63 | break; 64 | } 65 | $path = $parent_path; 66 | } 67 | 68 | return [ 69 | 'header' => ['content' => $header, 'type' => $header_type], 70 | 'footer' => ['content' => $footer, 'type' => $footer_type] 71 | ]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /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/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 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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!='images/favicon/favicon-16-32.ico') 14 | link(rel='apple-touch-icon-precomposed', type='image/png', href!='images/favicon/favicon-152.png') 15 | link(rel='stylesheet', href!='css/styles.css') 16 | 17 | script(src!='js/scripts.js', data-module=module) 18 | 19 | 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/.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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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 | image-orientation: from-image; 10 | 11 | max-width: 100%; 12 | max-height: 100%; 13 | 14 | background-color: @check-white; 15 | background-image: 16 | -webkit-linear-gradient(45deg, @check-black 25%, transparent 25%, transparent 75%, @check-black 75%, @check-black), 17 | -webkit-linear-gradient(45deg, @check-black 25%, transparent 25%, transparent 75%, @check-black 75%, @check-black); 18 | background-size: 60px 60px; 19 | background-position: 0 0, 30px 30px; 20 | 21 | &.loading { 22 | opacity: 0.5; 23 | margin-top: 32px; 24 | width: 240px; 25 | height: 240px; 26 | border-radius: 1000px; 27 | overflow: hidden; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/public/css/lib/misc.less: -------------------------------------------------------------------------------- 1 | ::-moz-selection { 2 | color: @col-text-native-selection; 3 | background: @col-back-native-selection; 4 | text-shadow: none; 5 | } 6 | ::selection { 7 | color: @col-text-native-selection; 8 | background: @col-back-native-selection; 9 | text-shadow: none; 10 | } 11 | 12 | *:focus { 13 | outline: none; 14 | } 15 | 16 | code, pre { 17 | font-family: @font-family-mono; 18 | font-size: @font-size-mono; 19 | font-weight: @font-weight; 20 | color: @col-text; 21 | } 22 | 23 | audio, canvas, iframe, img, svg, video { 24 | vertical-align: middle; 25 | } 26 | 27 | textarea { 28 | resize: vertical; 29 | } 30 | -------------------------------------------------------------------------------- /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/css/lib/responsive.less: -------------------------------------------------------------------------------- 1 | @media only screen and (max-width: 700px) { 2 | #crumbbar { 3 | .crumb:not(.active) { 4 | display: none; 5 | } 6 | .crumb.active .sep { 7 | width: 0; 8 | } 9 | } 10 | #view.view-details { 11 | // .header .label, .item .label { 12 | // margin-right: 80px !important; 13 | // } 14 | // .header .date, .item .date { 15 | // display: none; 16 | // } 17 | } 18 | #tree, #info { 19 | display: none !important; 20 | } 21 | } 22 | 23 | @media print { 24 | *, 25 | *:before, 26 | *:after, 27 | *:first-letter, 28 | *:first-line { 29 | background: transparent !important; 30 | color: #000 !important; 31 | box-shadow: none !important; 32 | text-shadow: none !important; 33 | } 34 | #toolbar, #sidebar, #tree, #info { 35 | display: none !important; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/public/css/lib/view/view-grid.less: -------------------------------------------------------------------------------- 1 | #view.view-grid { 2 | 3 | .view-grid-sized(@size) { 4 | 5 | .label { 6 | line-height: @size; 7 | } 8 | 9 | .square { 10 | width: @size; 11 | height: @size; 12 | 13 | img { 14 | width: @size; 15 | height: @size; 16 | } 17 | } 18 | } 19 | 20 | margin: 28px; 21 | 22 | .item { 23 | .rounded; 24 | overflow: hidden; 25 | float: left; 26 | margin: 8px; 27 | 28 | &:hover { 29 | .raised; 30 | } 31 | } 32 | 33 | .square { 34 | display: inline-block; 35 | vertical-align: top; 36 | } 37 | 38 | .label { 39 | display: inline-block; 40 | vertical-align: top; 41 | width: 180px; 42 | padding: 0 8px; 43 | } 44 | 45 | .date, .size { 46 | display: none; 47 | } 48 | 49 | .view-grid-sized(48px); 50 | } 51 | -------------------------------------------------------------------------------- /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/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/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/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/fallback/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrsjng/h5ai/aa94de4945e69ff742525cd4d648c91e5c9d8cd2/src/_h5ai/public/images/fallback/file.png -------------------------------------------------------------------------------- /src/_h5ai/public/images/fallback/folder-parent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrsjng/h5ai/aa94de4945e69ff742525cd4d648c91e5c9d8cd2/src/_h5ai/public/images/fallback/folder-parent.png -------------------------------------------------------------------------------- /src/_h5ai/public/images/fallback/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrsjng/h5ai/aa94de4945e69ff742525cd4d648c91e5c9d8cd2/src/_h5ai/public/images/fallback/folder.png -------------------------------------------------------------------------------- /src/_h5ai/public/images/favicon/favicon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrsjng/h5ai/aa94de4945e69ff742525cd4d648c91e5c9d8cd2/src/_h5ai/public/images/favicon/favicon-152.png -------------------------------------------------------------------------------- /src/_h5ai/public/images/favicon/favicon-16-32.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrsjng/h5ai/aa94de4945e69ff742525cd4d648c91e5c9d8cd2/src/_h5ai/public/images/favicon/favicon-16-32.ico -------------------------------------------------------------------------------- /src/_h5ai/public/images/favicon/favicon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrsjng/h5ai/aa94de4945e69ff742525cd4d648c91e5c9d8cd2/src/_h5ai/public/images/favicon/favicon-16.png -------------------------------------------------------------------------------- /src/_h5ai/public/images/favicon/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lrsjng/h5ai/aa94de4945e69ff742525cd4d648c91e5c9d8cd2/src/_h5ai/public/images/favicon/favicon-32.png -------------------------------------------------------------------------------- /src/_h5ai/public/images/favicon/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /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/images/themes/comity/ar-apk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/ar-rpm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-css.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-html.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-js.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-less.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-md.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-php.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-py.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-rb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-rust.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/txt-script.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/comity/x-pdf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/ar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/aud.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/bin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/folder-page.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/folder-parent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/img.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/txt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/vid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/themes/default/x.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/crumb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/info-toggle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/paypal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/preview-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/preview-fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/preview-next.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/preview-no-fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/preview-prev.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/preview-raw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/sidebar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/sort.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/tree-indicator.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/tree-toggle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/view-details.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/view-grid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/images/ui/view-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/_h5ai/public/index.php: -------------------------------------------------------------------------------- 1 | request(query).then(resp => Object.assign(config, resp)) 5 | }; 6 | -------------------------------------------------------------------------------- /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/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/core/langs.js: -------------------------------------------------------------------------------- 1 | const {langs} = require('../config'); 2 | module.exports = Object.assign({}, langs); 3 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | `has index page`; 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/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/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 | `
16 | download 17 |
`; 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/public/js/lib/ext/filter.js: -------------------------------------------------------------------------------- 1 | const {filter, debounce, parsePattern, 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 view = require('../view/view'); 7 | 8 | 9 | const settings = Object.assign({ 10 | enabled: false, 11 | advanced: false, 12 | debounceTime: 100, 13 | ignorecase: true 14 | }, allsettings.filter); 15 | const tpl = 16 | `
17 | filter 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/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/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 | `
32 |

Info

33 |
34 | view-info 35 |
36 |
`; 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/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 | 21 |
`; 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/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 = `sort order`; 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/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/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/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/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/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 on_ready_state_change = () => { 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 = on_ready_state_change; 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/util/index.js: -------------------------------------------------------------------------------- 1 | module.exports = Object.assign({}, 2 | require('./lo'), 3 | require('./dom'), 4 | require('./natural_cmp'), 5 | require('./misc') 6 | ); 7 | -------------------------------------------------------------------------------- /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/util/misc.js: -------------------------------------------------------------------------------- 1 | const esc_pattern = sequence => { 2 | return sequence.replace(/[\-\[\]{}()*+?.,\\$\^|#\s]/g, '\\$&'); 3 | }; 4 | 5 | const parse_pattern = (sequence, advanced) => { 6 | if (!advanced) { 7 | return esc_pattern(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 => esc_pattern(char)).join('.*?'); 16 | }).join('|'); 17 | }; 18 | 19 | module.exports = { 20 | parsePattern: parse_pattern 21 | }; 22 | -------------------------------------------------------------------------------- /src/_h5ai/public/js/lib/util/natural_cmp.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 RE_TOKEN = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; 7 | const RE_DATE = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/; 8 | const RE_HEX = /^0x[0-9a-f]+$/i; 9 | const RE_LEADING_ZERO = /^0/; 10 | 11 | /* eslint-disable complexity */ 12 | const natural_cmp = (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 x_toks = x.replace(RE_TOKEN, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0'); 19 | const y_toks = y.replace(RE_TOKEN, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0'); 20 | 21 | // first try and sort Hex codes or Dates 22 | const x_date = parseInt(x.match(RE_HEX), 16) || x_toks.length !== 1 && x.match(RE_DATE) && Date.parse(x); 23 | const y_date = parseInt(y.match(RE_HEX), 16) || x_date && y.match(RE_DATE) && Date.parse(y) || null; 24 | if (y_date) { 25 | if (x_date < y_date) { 26 | return -1; 27 | } 28 | if (x_date > y_date) { 29 | return 1; 30 | } 31 | } 32 | 33 | // natural sorting through split numeric strings and default strings 34 | for (let idx = 0, len = Math.max(x_toks.length, y_toks.length); idx < len; idx += 1) { 35 | // find floats not starting with '0', string or 0 if not defined (Clint Priest) 36 | let x_tok = !(x_toks[idx] || '').match(RE_LEADING_ZERO) && parseFloat(x_toks[idx]) || x_toks[idx] || 0; 37 | let y_tok = !(y_toks[idx] || '').match(RE_LEADING_ZERO) && parseFloat(y_toks[idx]) || y_toks[idx] || 0; 38 | 39 | // handle numeric vs string comparison - number < string - (Kyle Adams) 40 | if (isNaN(x_tok) !== isNaN(y_tok)) { 41 | return isNaN(x_tok) ? 1 : -1; 42 | } 43 | 44 | // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' 45 | if (typeof x_tok !== typeof y_tok) { 46 | x_tok = String(x_tok); 47 | y_tok = String(y_tok); 48 | } 49 | 50 | if (x_tok < y_tok) { 51 | return -1; 52 | } 53 | if (x_tok > y_tok) { 54 | return 1; 55 | } 56 | } 57 | return 0; 58 | }; 59 | /* eslint-enable */ 60 | 61 | module.exports = { 62 | naturalCmp: natural_cmp 63 | }; 64 | -------------------------------------------------------------------------------- /src/_h5ai/public/js/lib/view/base.js: -------------------------------------------------------------------------------- 1 | const {dom} = require('../util'); 2 | 3 | const SEL_ROOT = 'body'; 4 | const TPL_TOPBAR = 5 | ``; 13 | const TPL_MAINROW = 14 | `
15 |
16 |
`; 17 | 18 | const init = () => { 19 | const $root = dom(SEL_ROOT) 20 | .attr('id', 'root') 21 | .clr() 22 | .app(TPL_TOPBAR) 23 | .app(TPL_MAINROW); 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/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/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/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 | `
17 | viewmode-[MODE] 18 |
`; 19 | const sizeTpl = 20 | ''; 21 | const toggleTpl = 22 | `
23 | viewmode 24 |
`; 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/pre.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable func-names,no-var */ 2 | (function (win) { 3 | if (!win || win.window !== win || !win.document) { 4 | throw new Error('no-window'); 5 | } 6 | 7 | var no_browser = 'no-browser'; 8 | var doc_el = win.document.documentElement; 9 | doc_el.className = ''; 10 | 11 | function assert(msg, expr) { 12 | if (!expr) { 13 | doc_el.className = no_browser; 14 | throw new Error(no_browser + ': ' + msg); 15 | } 16 | } 17 | 18 | function is_fn(x) { 19 | return typeof x === 'function'; 20 | } 21 | 22 | assert('console', win.console && is_fn(win.console.log)); 23 | assert('assign', win.Object && is_fn(win.Object.assign)); 24 | assert('promise', is_fn(win.Promise)); 25 | // assert('xhr', is_fn(win.XMLHttpRequest)); // is object in safari 26 | assert('xhr', win.XMLHttpRequest); 27 | }(this)); 28 | /* eslint-enable */ 29 | -------------------------------------------------------------------------------- /src/_h5ai/public/js/scripts.js: -------------------------------------------------------------------------------- 1 | require('./lib/init'); 2 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | h5ai test suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | if (!global.window) { 2 | const JSDOM = require('jsdom').JSDOM; 3 | global.window = new JSDOM('').window; 4 | } 5 | 6 | const {test} = require('scar'); 7 | const {pin_html} = require('./util/pin'); 8 | 9 | require('./tests/premisses'); 10 | require('./tests/unit/core/event'); 11 | require('./tests/unit/core/format'); 12 | require('./tests/unit/util/naturalCmp'); 13 | require('./tests/unit/util/parsePatten'); 14 | 15 | pin_html(); 16 | 17 | test.cli({sync: true}); 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/tests/unit/core/event.js: -------------------------------------------------------------------------------- 1 | const {test, assert} = require('scar'); 2 | const reqlib = require('../../../util/reqlib'); 3 | const event = reqlib('core/event'); 4 | 5 | test('core.event', () => { 6 | assert.equal(typeof event, 'object', 'is object'); 7 | assert.deepEqual(Object.keys(event).sort(), ['sub', 'pub'].sort()); 8 | assert.equal(typeof event.sub, 'function'); 9 | assert.equal(typeof event.pub, 'function'); 10 | }); 11 | -------------------------------------------------------------------------------- /test/tests/unit/core/format.js: -------------------------------------------------------------------------------- 1 | const {test, assert} = require('scar'); 2 | const reqlib = require('../../../util/reqlib'); 3 | const format = reqlib('core/format'); 4 | 5 | test('core.format', () => { 6 | assert.equal(typeof format, 'object'); 7 | assert.deepEqual(Object.keys(format).sort(), ['setDefaultMetric', 'formatSize', 'setDefaultDateFormat', 'formatDate'].sort()); 8 | assert.equal(typeof format.setDefaultMetric, 'function'); 9 | assert.equal(typeof format.formatSize, 'function'); 10 | assert.equal(typeof format.setDefaultDateFormat, 'function'); 11 | assert.equal(typeof format.formatDate, 'function'); 12 | }); 13 | -------------------------------------------------------------------------------- /test/tests/unit/util/naturalCmp.js: -------------------------------------------------------------------------------- 1 | const {test, assert, insp} = require('scar'); 2 | const reqlib = require('../../../util/reqlib'); 3 | const {naturalCmp} = reqlib('util'); 4 | 5 | test('util.naturalCmp()', () => { 6 | assert.equal(typeof naturalCmp, 'function', 'is function'); 7 | 8 | [ 9 | '-1', 10 | '0', 11 | '00', 12 | '000', 13 | '001', 14 | '01', 15 | '02', 16 | '1', 17 | '3', 18 | 'a0', 19 | 'a00', 20 | 'a1', 21 | 'a2', 22 | 'a 0', 23 | 'a 00', 24 | 'a 000', 25 | 'a 01', 26 | 'a 1', 27 | 'a 2', 28 | 'a 3', 29 | 'a.1', 30 | 'a.1.0', 31 | 'a.1.1', 32 | 'a.1.1.0', 33 | 'a.1.10', 34 | 'z' 35 | ].forEach((b, idx, arr) => { 36 | if (idx === 0) { 37 | return; 38 | } 39 | const a = arr[idx - 1]; 40 | assert.equal(naturalCmp(a, b), -1, `fix#${idx} - ${insp(a)} < ${insp(b)}`); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/tests/unit/util/parsePatten.js: -------------------------------------------------------------------------------- 1 | const {test, assert, insp} = require('scar'); 2 | const reqlib = require('../../../util/reqlib'); 3 | const {parsePattern} = reqlib('util'); 4 | 5 | test('util.parsePattern()', () => { 6 | assert.equal(typeof parsePattern, 'function', 'is function'); 7 | 8 | [ 9 | ['', false, ''], 10 | [' ', false, '\\ '], 11 | ['a', false, 'a'], 12 | ['ä', false, 'ä'], 13 | ['á', false, 'á'], 14 | ['*', false, '\\*'], 15 | ['ab', false, 'ab'], 16 | ['rea', false, 'rea'], 17 | ['re:', false, 're:'], 18 | ['re:a', false, 're:a'], 19 | ['a b', false, 'a\\ b'], 20 | ['ab c', false, 'ab\\ c'], 21 | [' a ', false, '\\ a\\ '], 22 | 23 | ['', true, ''], 24 | [' ', true, ''], 25 | ['a', true, 'a'], 26 | ['ä', true, 'ä'], 27 | ['á', true, 'á'], 28 | ['*', true, '\\*'], 29 | ['ab', true, 'a.*?b'], 30 | ['rea', true, 'r.*?e.*?a'], 31 | [' re:', true, 'r.*?e.*?:'], 32 | ['are:', true, 'a.*?r.*?e.*?:'], 33 | ['re:', true, ''], 34 | ['re:a', true, 'a'], 35 | ['a b', true, 'a|b'], 36 | ['ab c', true, 'a.*?b|c'], 37 | [' a ', true, 'a'] 38 | ].forEach(([pattern, advanced, exp], idx) => { 39 | assert.equal(parsePattern(pattern, advanced), exp, `fix#${idx} - (${insp(pattern)}, ${insp(advanced)}) -> ${insp(exp)}`); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /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 root_children = () => { 20 | return [ 21 | ...doc.querySelector('head').childNodes, 22 | ...doc.querySelector('body').childNodes 23 | ]; 24 | }; 25 | 26 | const pin_html = () => { 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 = root_children(); 33 | // console.log('pinned', pinned); 34 | }; 35 | 36 | const restore_html = () => { 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 | root_children().forEach(el => { 43 | if (pinned.els.indexOf(el) < 0) { 44 | el.remove(); 45 | } 46 | }); 47 | // win.localStorage.clear(); 48 | }; 49 | 50 | module.exports = { 51 | pin_html, 52 | restore_html 53 | }; 54 | -------------------------------------------------------------------------------- /test/util/reqlib.js: -------------------------------------------------------------------------------- 1 | const reqlib = x => require(`../../src/_h5ai/public/js/lib/${x}`); 2 | 3 | module.exports = reqlib; 4 | --------------------------------------------------------------------------------