initial commit

This commit is contained in:
AJ ONeal 2018-08-12 03:27:28 -06:00
commit 10d7e2a73b
97 changed files with 926 additions and 0 deletions

41
LICENSE Normal file
View File

@ -0,0 +1,41 @@
Copyright 2018 AJ ONeal
This is open source software; you can redistribute it and/or modify it under the
terms of either:
a) the "MIT License"
b) the "Apache-2.0 License"
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Apache-2.0 License Summary
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

46
README.md Normal file
View File

@ -0,0 +1,46 @@
serve-tpl-download
==================
A fork of the original `serve-index` template that, in combination with `serve-static`,
provides support for direct file downloads.
```js
var express = require('express');
var app = express();
var serveIndex = require('serve-index');
var serveTpl = require('serve-tpl-download');
var serveDirs = serveIndex({ template: serveTpl() });
app.use('/', function (req, res, next) {
// enable direct downloads for express.static()
if (req.query.download) {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', 'attachment; filename="'+path.basename(req.url)+'"');
}
express.static('./public')(req, res, function () {
serveDirs(req, res, next);
});
});
```
Additional Options
==================
### privatefiles
As an additional security precaution you can ignore files which are not world-readable.
For example, this would prevent files in a `~/.ssh` from being read even when `dotfiles` are allowed.
`{ privatefiles: 'ignore' }`
```js
var serveTpl = require('serve-tpl-download');
var serveTemplate = serveTpl({ privatefiles: 'ignore' })
```
This is most effective on Unix-based systems (macOS, Linux, Android).
Windows may rely on ACLs instead of user-group-other style permissions.

456
index.js Normal file
View File

@ -0,0 +1,456 @@
/*!
* telebit/serve-index
* Copyright(c) 2018 AJ ONeal
*
* Derivative work of github.com/expressjs/serve-index
* Copyright(c) 2011 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var escapeHtml = require('escape-html');
var fs = require('fs')
, path = require('path')
, normalize = path.normalize
, sep = path.sep
, extname = path.extname
, join = path.join;
var Batch = require('batch');
var mime = require('mime-types');
/**
* Module exports.
* @public
*/
var defaultPagepath = path.join(__dirname, 'public/directory.html');
var defaultStylepath = path.join(__dirname, 'public/style.css');
var defaultList = '<ul id="files" class="view-{view}">{head}{files}</ul>';
var defaultHead = '<li class="header">'
+ '<span class="name">Name</span>'
+ '<span class="size">Size</span>'
+ '<span class="date">Modified</span>'
+ '</li>'
;
var defaultFile = '<li><div>'
+ '<a href="{path}?download=true" class="download" title="Download {file.name}">'
+ '<span class="download">⬇️</span>'
+ '</a>'
+ '<a href="{path}" class="{classes}" title="{file.name}">'
+ '<span class="name">{file.name}</span>'
+ '<span class="size">{file.size}</span>'
+ '<span class="date">{file.date}</span>'
+ '</a>'
+ '</div></li>'
;
module.exports = function (opts) {
if (!opts) { opts = {}; }
return createHtmlRender({
pagepath: opts.pagepath || defaultPagepath
, stylepath: opts.stylepath || defaultStylepath
, list: opts.list || defaultList
, head: opts.head || defaultHead
, file: opts.file || defaultFile
, privatefiles: opts.privatefiles
});
};
/*!
* Icon cache.
*/
var cache = {};
/**
* Map html `files`, returning an html unordered list.
* @private
*/
function createHtmlFileList(opts, files, dir, useIcons, view) {
var ftpls = files.map(function (file) {
var classes = [];
var isDir = file.stat && file.stat.isDirectory();
var path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
if (useIcons) {
classes.push('icon');
if (isDir) {
classes.push('icon-directory');
} else {
var ext = extname(file.name);
var icon = iconLookup(file.name);
classes.push('icon');
classes.push('icon-' + ext.substring(1));
if (classes.indexOf(icon.className) === -1) {
classes.push(icon.className);
}
}
}
path.push(encodeURIComponent(file.name));
var date = file.stat && file.name !== '..'
? file.stat.mtime.toLocaleDateString() + ' ' + file.stat.mtime.toLocaleTimeString()
: '';
var size = file.stat && !isDir
? file.stat.size
: '';
var OCTAL = 8;
var WORLD_READ = parseInt(4, OCTAL); // R(4)W(2)X(1)
var hasWorldRead = file.mode | WORLD_READ;
if (!hasWorldRead && 'ignore' === opts.privatefiles) {
return '';
}
return opts.file.replace(/{path}/g, escapeHtml(normalizeSlashes(normalize(path.join('/')))))
.replace(/{classes}/g, escapeHtml(classes.join(' ')))
.replace(/{file.name}/g, escapeHtml(file.name))
.replace(/{file.size}/g, escapeHtml(size))
.replace(/{file.date}/g, escapeHtml(date))
;
}).filter(Boolean).join('\n');
var html = opts.list
.replace(/{view}/g, escapeHtml(view))
.replace(/{head}/g, view === 'details' ? opts.head : '')
.replace(/{files}/g, ftpls)
;
return html;
}
/**
* Create function to render html.
*/
function createHtmlRender(opts) {
return function render(locals, callback) {
// read template
fs.readFile(opts.pagepath, 'utf8', function (err, pageStr) {
fs.readFile(opts.stylepath, 'utf8', function (err, styleStr) {
if (err) return callback(err);
var body = pageStr
.replace(/\{style\}/g, styleStr.concat(iconStyle(locals.fileList, locals.displayIcons)))
.replace(/\{files\}/g, createHtmlFileList(opts, locals.fileList, locals.directory, locals.displayIcons, locals.viewName))
.replace(/\{directory\}/g, escapeHtml(locals.directory))
.replace(/\{linked-path\}/g, htmlPath(locals.directory))
;
callback(null, body);
});
});
};
}
/**
* Map html `dir`, returning a linked path.
*/
function htmlPath(dir) {
var parts = dir.split('/');
var crumb = new Array(parts.length);
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
if (part) {
parts[i] = encodeURIComponent(part);
crumb[i] = '<a href="' + escapeHtml(parts.slice(0, i + 1).join('/')) + '">' + escapeHtml(part) + '</a>';
}
}
return crumb.join(' / ');
}
/**
* Get the icon data for the file name.
*/
function iconLookup(filename) {
var ext = extname(filename);
// try by extension
if (icons[ext]) {
return {
className: 'icon-' + ext.substring(1),
fileName: icons[ext]
};
}
var mimetype = mime.lookup(ext);
// default if no mime type
if (mimetype === false) {
return {
className: 'icon-default',
fileName: icons.default
};
}
// try by mime type
if (icons[mimetype]) {
return {
className: 'icon-' + mimetype.replace('/', '-'),
fileName: icons[mimetype]
};
}
var suffix = mimetype.split('+')[1];
if (suffix && icons['+' + suffix]) {
return {
className: 'icon-' + suffix,
fileName: icons['+' + suffix]
};
}
var type = mimetype.split('/')[0];
// try by type only
if (icons[type]) {
return {
className: 'icon-' + type,
fileName: icons[type]
};
}
return {
className: 'icon-default',
fileName: icons.default
};
}
/**
* Load icon images, return css string.
*/
function iconStyle(files, useIcons) {
if (!useIcons) return '';
var i;
var list = [];
var rules = {};
var selector;
var selectors = {};
var style = '';
for (i = 0; i < files.length; i++) {
var file = files[i];
var isDir = file.stat && file.stat.isDirectory();
var icon = isDir
? { className: 'icon-directory', fileName: icons.folder }
: iconLookup(file.name);
var iconName = icon.fileName;
selector = '#files .' + icon.className + ' .name';
if (!rules[iconName]) {
rules[iconName] = 'background-image: url(data:image/png;base64,' + load(iconName) + ');'
selectors[iconName] = [];
list.push(iconName);
}
if (selectors[iconName].indexOf(selector) === -1) {
selectors[iconName].push(selector);
}
}
for (i = 0; i < list.length; i++) {
iconName = list[i];
style += selectors[iconName].join(',\n') + ' {\n ' + rules[iconName] + '\n}\n';
}
return style;
}
/**
* Load and cache the given `icon`.
*
* @param {String} icon
* @return {String}
* @api private
*/
function load(icon) {
if (cache[icon]) return cache[icon];
return cache[icon] = fs.readFileSync(__dirname + '/public/icons/' + icon, 'base64');
}
/**
* Normalizes the path separator from system separator
* to URL separator, aka `/`.
*
* @param {String} path
* @return {String}
* @api private
*/
function normalizeSlashes(path) {
return path.split(sep).join('/');
}
/**
* Send a response.
* @private
*/
function send (res, type, body) {
// security header for content sniffing
res.setHeader('X-Content-Type-Options', 'nosniff');
// standard headers
res.setHeader('Content-Type', type + '; charset=utf-8');
res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'));
// body
res.end(body, 'utf8');
}
/**
* Stat all files and return array of stat
* in same order.
*/
function stat(dir, files, cb) {
var batch = new Batch();
batch.concurrency(10);
files.forEach(function(file){
batch.push(function(done){
fs.stat(join(dir, file), function(err, stat){
if (err && err.code !== 'ENOENT') return done(err);
// pass ENOENT as null stat, not error
done(null, stat || null);
});
});
});
batch.end(cb);
}
/**
* Icon map.
*/
var icons = {
// base icons
'default': 'page_white.png',
'folder': 'folder.png',
// generic mime type icons
'font': 'font.png',
'image': 'image.png',
'text': 'page_white_text.png',
'video': 'film.png',
// generic mime suffix icons
'+json': 'page_white_code.png',
'+xml': 'page_white_code.png',
'+zip': 'box.png',
// specific mime type icons
'application/javascript': 'page_white_code_red.png',
'application/json': 'page_white_code.png',
'application/msword': 'page_white_word.png',
'application/pdf': 'page_white_acrobat.png',
'application/postscript': 'page_white_vector.png',
'application/rtf': 'page_white_word.png',
'application/vnd.ms-excel': 'page_white_excel.png',
'application/vnd.ms-powerpoint': 'page_white_powerpoint.png',
'application/vnd.oasis.opendocument.presentation': 'page_white_powerpoint.png',
'application/vnd.oasis.opendocument.spreadsheet': 'page_white_excel.png',
'application/vnd.oasis.opendocument.text': 'page_white_word.png',
'application/x-7z-compressed': 'box.png',
'application/x-sh': 'application_xp_terminal.png',
'application/x-msaccess': 'page_white_database.png',
'application/x-shockwave-flash': 'page_white_flash.png',
'application/x-sql': 'page_white_database.png',
'application/x-tar': 'box.png',
'application/x-xz': 'box.png',
'application/xml': 'page_white_code.png',
'application/zip': 'box.png',
'image/svg+xml': 'page_white_vector.png',
'text/css': 'page_white_code.png',
'text/html': 'page_white_code.png',
'text/less': 'page_white_code.png',
// other, extension-specific icons
'.accdb': 'page_white_database.png',
'.apk': 'box.png',
'.app': 'application_xp.png',
'.as': 'page_white_actionscript.png',
'.asp': 'page_white_code.png',
'.aspx': 'page_white_code.png',
'.bat': 'application_xp_terminal.png',
'.bz2': 'box.png',
'.c': 'page_white_c.png',
'.cab': 'box.png',
'.cfm': 'page_white_coldfusion.png',
'.clj': 'page_white_code.png',
'.cc': 'page_white_cplusplus.png',
'.cgi': 'application_xp_terminal.png',
'.cpp': 'page_white_cplusplus.png',
'.cs': 'page_white_csharp.png',
'.db': 'page_white_database.png',
'.dbf': 'page_white_database.png',
'.deb': 'box.png',
'.dll': 'page_white_gear.png',
'.dmg': 'drive.png',
'.docx': 'page_white_word.png',
'.erb': 'page_white_ruby.png',
'.exe': 'application_xp.png',
'.fnt': 'font.png',
'.gam': 'controller.png',
'.gz': 'box.png',
'.h': 'page_white_h.png',
'.ini': 'page_white_gear.png',
'.iso': 'cd.png',
'.jar': 'box.png',
'.java': 'page_white_cup.png',
'.jsp': 'page_white_cup.png',
'.lua': 'page_white_code.png',
'.lz': 'box.png',
'.lzma': 'box.png',
'.m': 'page_white_code.png',
'.map': 'map.png',
'.msi': 'box.png',
'.mv4': 'film.png',
'.pdb': 'page_white_database.png',
'.php': 'page_white_php.png',
'.pl': 'page_white_code.png',
'.pkg': 'box.png',
'.pptx': 'page_white_powerpoint.png',
'.psd': 'page_white_picture.png',
'.py': 'page_white_code.png',
'.rar': 'box.png',
'.rb': 'page_white_ruby.png',
'.rm': 'film.png',
'.rom': 'controller.png',
'.rpm': 'box.png',
'.sass': 'page_white_code.png',
'.sav': 'controller.png',
'.scss': 'page_white_code.png',
'.srt': 'page_white_text.png',
'.tbz2': 'box.png',
'.tgz': 'box.png',
'.tlz': 'box.png',
'.vb': 'page_white_code.png',
'.vbs': 'page_white_code.png',
'.xcf': 'page_white_picture.png',
'.xlsx': 'page_white_excel.png',
'.yaws': 'page_white_code.png'
};

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "serve-tpl-download",
"version": "1.0.0",
"description": "A template for serve-static with a direct download option (requires serve-index)",
"homepage": "https://git.coolaj86.com/coolaj86/serve-tpl-download.js",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://git.coolaj86.com/coolaj86/serve-tpl-download.js.git"
},
"keywords": [
"serve-static",
"serve-index"
],
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "(MIT OR Apache-2.0)"
}

97
public/directory.html Normal file
View File

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>listing directory {directory}</title>
<style>{style}</style>
<script>
function $(id){
var el = 'string' == typeof id
? document.getElementById(id)
: id;
el.on = function(event, fn){
if ('content loaded' == event) {
event = window.attachEvent ? "load" : "DOMContentLoaded";
}
el.addEventListener
? el.addEventListener(event, fn, false)
: el.attachEvent("on" + event, fn);
};
el.all = function(selector){
return $(el.querySelectorAll(selector));
};
el.each = function(fn){
for (var i = 0, len = el.length; i < len; ++i) {
fn($(el[i]), i);
}
};
el.getClasses = function(){
return this.getAttribute('class').split(/\s+/);
};
el.addClass = function(name){
var classes = this.getAttribute('class');
el.setAttribute('class', classes
? classes + ' ' + name
: name);
};
el.removeClass = function(name){
var classes = this.getClasses().filter(function(curr){
return curr != name;
});
this.setAttribute('class', classes.join(' '));
};
return el;
}
function search() {
var str = $('search').value.toLowerCase();
var links = $('files').all('a');
links.each(function(link){
var text = link.textContent.toLowerCase();
if ('..' == text) return;
if (str.length && ~text.indexOf(str)) {
link.addClass('highlight');
} else {
link.removeClass('highlight');
}
});
}
$(window).on('content loaded', function(){
$('search').on('keyup', search);
});
</script>
</head>
<body class="directory">
<input id="search" type="text" placeholder="Search" autocomplete="off" />
<div id="wrapper">
<h1><a href="/">~</a>{linked-path}</h1>
{files}
</div>
<script>
'use strict';
window.fetch(document.location, {
method: 'GET'
, cache: 'no-store'
, headers: {
'Accept': 'application/json' //; charset=utf-8
}
}).then(function (resp) {
return resp.json().then(function (files) {
console.log(files);
});
});
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

BIN
public/icons/box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

BIN
public/icons/cd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
public/icons/controller.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

BIN
public/icons/drive.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

BIN
public/icons/film.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
public/icons/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
public/icons/font.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

BIN
public/icons/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

BIN
public/icons/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

BIN
public/icons/page.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

BIN
public/icons/page_add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

BIN
public/icons/page_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

BIN
public/icons/page_copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

BIN
public/icons/page_edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

BIN
public/icons/page_error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

BIN
public/icons/page_excel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

BIN
public/icons/page_find.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

BIN
public/icons/page_gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

BIN
public/icons/page_go.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

BIN
public/icons/page_green.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

BIN
public/icons/page_key.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
public/icons/page_link.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

BIN
public/icons/page_paste.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

BIN
public/icons/page_red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

BIN
public/icons/page_save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

BIN
public/icons/page_white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

BIN
public/icons/page_word.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

BIN
public/icons/page_world.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

0
public/serve-index.html Normal file
View File

266
public/style.css Normal file
View File

@ -0,0 +1,266 @@
* {
margin: 0;
padding: 0;
outline: 0;
}
body {
padding: 80px 100px;
font: 13px "Helvetica Neue", "Lucida Grande", "Arial";
background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9));
background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9);
background-repeat: no-repeat;
color: #555;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3 {
font-size: 22px;
color: #343434;
}
h1 em, h2 em {
padding: 0 5px;
font-weight: normal;
}
h1 {
font-size: 60px;
}
h2 {
margin-top: 10px;
}
h3 {
margin: 5px 0 10px 0;
padding-bottom: 5px;
border-bottom: 1px solid #eee;
font-size: 18px;
}
ul li {
list-style: none;
}
ul li:hover {
cursor: pointer;
color: #2e2e2e;
}
ul li .path {
padding-left: 5px;
font-weight: bold;
}
ul li .line {
padding-right: 5px;
font-style: italic;
}
ul li:first-child .path {
padding-left: 0;
}
p {
line-height: 1.5;
}
a {
color: #555;
text-decoration: none;
}
a:hover {
color: #303030;
}
#stacktrace {
margin-top: 15px;
}
.directory h1 {
margin-bottom: 15px;
font-size: 18px;
}
ul#files {
width: 100%;
height: 100%;
overflow: hidden;
}
ul#files li {
float: left;
width: 30%;
line-height: 25px;
margin: 1px;
}
ul#files li div {
display: block;
height: 25px;
border: 1px solid transparent;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
overflow: hidden;
white-space: nowrap;
}
ul#files li div:focus,
ul#files li div:hover {
background: rgba(255,255,255,0.65);
border: 1px solid #ececec;
}
ul#files li div.highlight {
-webkit-transition: background .4s ease-in-out;
background: #ffff4f;
border-color: #E9DC51;
}
#search {
display: block;
position: fixed;
top: 20px;
right: 20px;
width: 90px;
-webkit-transition: width ease 0.2s, opacity ease 0.4s;
-moz-transition: width ease 0.2s, opacity ease 0.4s;
-webkit-border-radius: 32px;
-moz-border-radius: 32px;
-webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03);
-moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03);
-webkit-font-smoothing: antialiased;
text-align: left;
font: 13px "Helvetica Neue", Arial, sans-serif;
padding: 4px 10px;
border: none;
background: transparent;
margin-bottom: 0;
outline: none;
opacity: 0.7;
color: #888;
}
#search:focus {
width: 120px;
opacity: 1.0;
}
/*views*/
#files span {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
text-indent: 10px;
}
#files .name {
background-repeat: no-repeat;
}
#files .icon .name {
text-indent: 28px;
}
/*tiles*/
.view-tiles .download {
background-position: 8px 5px;
}
.view-tiles .name {
width: 100%;
background-position: 8px 5px;
}
#files span.size,
#files span.date,
.view-tiles .size,
.view-tiles .date {
display: none;
}
/*details*/
ul#files.view-details li {
float: none;
display: block;
width: 90%;
}
ul#files.view-details li.header {
height: 25px;
background: #000;
color: #fff;
font-weight: bold;
}
.view-details .header {
border-radius: 5px;
}
.view-details .name {
width: 60%;
background-position: 8px 5px;
}
.view-details .size {
width: 10%;
}
.view-details .date {
width: 30%;
}
.view-details .size,
.view-details .date {
text-align: right;
direction: rtl;
}
ul#files li a.download {
display: inline;
}
/*mobile*/
@media (max-width: 768px) {
body {
font-size: 13px;
line-height: 16px;
padding: 0;
}
#search {
position: static;
width: 100%;
font-size: 2em;
line-height: 1.8em;
text-indent: 10px;
border: 0;
border-radius: 0;
padding: 10px 0;
margin: 0;
}
#search:focus {
width: 100%;
border: 0;
opacity: 1;
}
.directory h1 {
font-size: 2em;
line-height: 1.5em;
color: #fff;
background: #000;
padding: 15px 10px;
margin: 0;
}
ul#files {
border-top: 1px solid #cacaca;
}
ul#files li {
float: none;
width: auto !important;
display: block;
border-bottom: 1px solid #cacaca;
font-size: 2em;
line-height: 1.2em;
text-indent: 0;
margin: 0;
}
ul#files li:nth-child(odd) {
background: #e0e0e0;
}
ul#files li div {
height: auto;
border: 0;
border-radius: 0;
padding: 15px 10px;
}
ul#files li a:focus,
ul#files li a:hover {
border: 0;
}
#files .header,
#files .size,
#files .date {
display: none !important;
}
#files .name {
float: none;
display: inline-block;
width: 100%;
text-indent: 0;
background-position: 0 50%;
}
#files .icon .name {
text-indent: 41px;
}
}