init from connect
@@ -0,0 +1,65 @@
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store*
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Node.js #
|
||||
###########
|
||||
lib-cov
|
||||
*.seed
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.gz
|
||||
|
||||
pids
|
||||
logs
|
||||
results
|
||||
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
# Git #
|
||||
#######
|
||||
*.orig
|
||||
*.BASE.*
|
||||
*.BACKUP.*
|
||||
*.LOCAL.*
|
||||
*.REMOTE.*
|
||||
|
||||
# Components #
|
||||
##############
|
||||
|
||||
/build
|
||||
/components
|
||||
@@ -0,0 +1 @@
|
||||
test
|
||||
@@ -0,0 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
@@ -0,0 +1,25 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2010 Sencha Inc.
|
||||
Copyright (c) 2011 LearnBoost
|
||||
Copyright (c) 2011 TJ Holowaychuk
|
||||
Copyright (c) 2014 Douglas Christopher Wilson
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Serve Index
|
||||
|
||||
Previously `connect.directory()`.
|
||||
|
||||
Usage:
|
||||
|
||||
```js
|
||||
var connect = require('connect');
|
||||
var serveIndex = require('serve-index');
|
||||
|
||||
var app = connect();
|
||||
|
||||
app.use(serveIndex('public/ftp', {'icons': true}));
|
||||
app.listen();
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Douglas Christopher Wilson
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,498 @@
|
||||
|
||||
/*!
|
||||
* Connect - directory
|
||||
* Copyright(c) 2011 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2014 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
// TODO: arrow key navigation
|
||||
// TODO: make icons extensible
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, fs = require('fs')
|
||||
, parse = require('url').parse
|
||||
, path = require('path')
|
||||
, normalize = path.normalize
|
||||
, sep = path.sep
|
||||
, extname = path.extname
|
||||
, join = path.join;
|
||||
var Batch = require('batch');
|
||||
var Negotiator = require('negotiator');
|
||||
|
||||
/*!
|
||||
* Icon cache.
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/*!
|
||||
* Default template.
|
||||
*/
|
||||
|
||||
var defaultTemplate = join(__dirname, 'public', 'directory.html');
|
||||
|
||||
/*!
|
||||
* Stylesheet.
|
||||
*/
|
||||
|
||||
var stylesheet = join(__dirname, 'public', 'style.css');
|
||||
|
||||
/**
|
||||
* Media types and the map for content negotiation.
|
||||
*/
|
||||
|
||||
var mediaTypes = [
|
||||
'text/html',
|
||||
'text/plain',
|
||||
'application/json'
|
||||
];
|
||||
|
||||
var mediaType = {
|
||||
'text/html': 'html',
|
||||
'text/plain': 'plain',
|
||||
'application/json': 'json'
|
||||
};
|
||||
|
||||
/**
|
||||
* Directory:
|
||||
*
|
||||
* Serve directory listings with the given `root` path.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `hidden` display hidden (dot) files. Defaults to false.
|
||||
* - `icons` display icons. Defaults to false.
|
||||
* - `filter` Apply this filter function to files. Defaults to false.
|
||||
* - `template` Optional path to html template. Defaults to a built-in template.
|
||||
* The following tokens are replaced:
|
||||
* - `{directory}` with the name of the directory.
|
||||
* - `{files}` with the HTML of an unordered list of file links.
|
||||
* - `{linked-path}` with the HTML of a link to the directory.
|
||||
* - `{style}` with the built-in CSS and embedded images.
|
||||
*
|
||||
* @param {String} root
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function directory(root, options){
|
||||
options = options || {};
|
||||
|
||||
// root required
|
||||
if (!root) throw new Error('directory() root path required');
|
||||
var hidden = options.hidden
|
||||
, icons = options.icons
|
||||
, view = options.view || 'tiles'
|
||||
, filter = options.filter
|
||||
, root = normalize(root + sep)
|
||||
, template = options.template || defaultTemplate;
|
||||
|
||||
return function directory(req, res, next) {
|
||||
if ('GET' != req.method && 'HEAD' != req.method) return next();
|
||||
|
||||
var url = parse(req.url)
|
||||
, dir = decodeURIComponent(url.pathname)
|
||||
, path = normalize(join(root, dir))
|
||||
, originalUrl = parse(req.originalUrl)
|
||||
, originalDir = decodeURIComponent(originalUrl.pathname)
|
||||
, showUp = path != root;
|
||||
|
||||
// null byte(s), bad request
|
||||
if (~path.indexOf('\0')) return next(createError(400));
|
||||
|
||||
// malicious path, forbidden
|
||||
if (0 != path.indexOf(root)) return next(createError(403));
|
||||
|
||||
// check if we have a directory
|
||||
fs.stat(path, function(err, stat){
|
||||
if (err) return 'ENOENT' == err.code
|
||||
? next()
|
||||
: next(err);
|
||||
|
||||
if (!stat.isDirectory()) return next();
|
||||
|
||||
// fetch files
|
||||
fs.readdir(path, function(err, files){
|
||||
if (err) return next(err);
|
||||
if (!hidden) files = removeHidden(files);
|
||||
if (filter) files = files.filter(filter);
|
||||
files.sort();
|
||||
|
||||
// content-negotiation
|
||||
var type = new Negotiator(req).preferredMediaType(mediaTypes);
|
||||
|
||||
// not acceptable
|
||||
if (!type) return next(createError(406));
|
||||
exports[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond with text/html.
|
||||
*/
|
||||
|
||||
exports.html = function(req, res, files, next, dir, showUp, icons, path, view, template){
|
||||
fs.readFile(template, 'utf8', function(err, str){
|
||||
if (err) return next(err);
|
||||
fs.readFile(stylesheet, 'utf8', function(err, style){
|
||||
if (err) return next(err);
|
||||
stat(path, files, function(err, stats){
|
||||
if (err) return next(err);
|
||||
files = files.map(function(file, i){ return { name: file, stat: stats[i] }; });
|
||||
files.sort(fileSort);
|
||||
if (showUp) files.unshift({ name: '..' });
|
||||
str = str
|
||||
.replace('{style}', style.concat(iconStyle(files, icons)))
|
||||
.replace('{files}', html(files, dir, icons, view))
|
||||
.replace('{directory}', dir)
|
||||
.replace('{linked-path}', htmlPath(dir));
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Content-Length', str.length);
|
||||
res.end(str);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond with application/json.
|
||||
*/
|
||||
|
||||
exports.json = function(req, res, files){
|
||||
files = JSON.stringify(files);
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.setHeader('Content-Length', files.length);
|
||||
res.end(files);
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond with text/plain.
|
||||
*/
|
||||
|
||||
exports.plain = function(req, res, files){
|
||||
files = files.join('\n') + '\n';
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.setHeader('Content-Length', files.length);
|
||||
res.end(files);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate an `Error` from the given status `code`
|
||||
* and optional `msg`.
|
||||
*
|
||||
* @param {Number} code
|
||||
* @param {String} msg
|
||||
* @return {Error}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function createError(code, msg) {
|
||||
var err = new Error(msg || http.STATUS_CODES[code]);
|
||||
err.status = code;
|
||||
return err;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort function for with directories first.
|
||||
*/
|
||||
|
||||
function fileSort(a, b) {
|
||||
return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) ||
|
||||
String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map html `dir`, returning a linked path.
|
||||
*/
|
||||
|
||||
function htmlPath(dir) {
|
||||
var curr = [];
|
||||
return dir.split('/').map(function(part){
|
||||
curr.push(encodeURIComponent(part));
|
||||
return part ? '<a href="' + curr.join('/') + '">' + part + '</a>' : '';
|
||||
}).join(' / ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load icon images, return css string.
|
||||
*/
|
||||
|
||||
function iconStyle (files, useIcons) {
|
||||
if (!useIcons) return '';
|
||||
var className;
|
||||
var i;
|
||||
var icon;
|
||||
var list = [];
|
||||
var rules = {};
|
||||
var selector;
|
||||
var selectors = {};
|
||||
var style = '';
|
||||
|
||||
for (i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
|
||||
var isDir = '..' == file.name || (file.stat && file.stat.isDirectory());
|
||||
icon = isDir ? icons.folder : icons[extname(file.name)] || icons.default;
|
||||
|
||||
var ext = extname(file.name);
|
||||
className = 'icon-' + (isDir ? 'directory' : (icons[ext] ? ext.substring(1) : 'default'));
|
||||
selector = '#files .' + className + ' .name';
|
||||
|
||||
if (!rules[icon]) {
|
||||
rules[icon] = 'background-image: url(data:image/png;base64,' + load(icon) + ');'
|
||||
selectors[icon] = [];
|
||||
list.push(icon);
|
||||
}
|
||||
|
||||
if (!~selectors[icon].indexOf(selector)) {
|
||||
selectors[icon].push(selector);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < list.length; i++) {
|
||||
icon = list[i];
|
||||
style += selectors[icon].join(',\n') + ' {\n ' + rules[icon] + '\n}\n';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map html `files`, returning an html unordered list.
|
||||
*/
|
||||
|
||||
function html(files, dir, useIcons, view) {
|
||||
return '<ul id="files" class="view-'+view+'">'
|
||||
+ (view == 'details' ? (
|
||||
'<li class="header">'
|
||||
+ '<span class="name">Name</span>'
|
||||
+ '<span class="size">Size</span>'
|
||||
+ '<span class="date">Modified</span>'
|
||||
+ '</li>') : '')
|
||||
+ files.map(function(file){
|
||||
var isDir = '..' == file.name || (file.stat && file.stat.isDirectory())
|
||||
, classes = []
|
||||
, path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
|
||||
|
||||
if (useIcons) {
|
||||
var ext = extname(file.name);
|
||||
ext = isDir ? '.directory' : (icons[ext] ? ext : '.default');
|
||||
classes.push('icon');
|
||||
classes.push('icon-' + ext.substring(1));
|
||||
}
|
||||
|
||||
path.push(encodeURIComponent(file.name));
|
||||
|
||||
var date = file.name == '..' ? ''
|
||||
: file.stat.mtime.toDateString()+' '+file.stat.mtime.toLocaleTimeString();
|
||||
var size = isDir ? '' : file.stat.size;
|
||||
|
||||
return '<li><a href="'
|
||||
+ normalizeSlashes(normalize(path.join('/')))
|
||||
+ '" class="'
|
||||
+ classes.join(' ') + '"'
|
||||
+ ' title="' + file.name + '">'
|
||||
+ '<span class="name">'+file.name+'</span>'
|
||||
+ '<span class="size">'+size+'</span>'
|
||||
+ '<span class="date">'+date+'</span>'
|
||||
+ '</a></li>';
|
||||
|
||||
}).join('\n') + '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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('/');
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter "hidden" `files`, aka files
|
||||
* beginning with a `.`.
|
||||
*
|
||||
* @param {Array} files
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function removeHidden(files) {
|
||||
return files.filter(function(file){
|
||||
return '.' != file[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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), done);
|
||||
});
|
||||
});
|
||||
|
||||
batch.end(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon map.
|
||||
*/
|
||||
|
||||
var icons = {
|
||||
'.js': 'page_white_code_red.png'
|
||||
, '.json': 'page_white_code.png'
|
||||
, '.c': 'page_white_c.png'
|
||||
, '.h': 'page_white_h.png'
|
||||
, '.cc': 'page_white_cplusplus.png'
|
||||
, '.php': 'page_white_php.png'
|
||||
, '.rb': 'page_white_ruby.png'
|
||||
, '.erb': 'page_white_ruby.png'
|
||||
, '.cpp': 'page_white_cplusplus.png'
|
||||
, '.as': 'page_white_actionscript.png'
|
||||
, '.cfm': 'page_white_coldfusion.png'
|
||||
, '.cs': 'page_white_csharp.png'
|
||||
, '.java': 'page_white_cup.png'
|
||||
, '.jsp': 'page_white_cup.png'
|
||||
, '.dll': 'page_white_gear.png'
|
||||
, '.ini': 'page_white_gear.png'
|
||||
, '.asp': 'page_white_code.png'
|
||||
, '.aspx': 'page_white_code.png'
|
||||
, '.clj': 'page_white_code.png'
|
||||
, '.css': 'page_white_code.png'
|
||||
, '.sass': 'page_white_code.png'
|
||||
, '.scss': 'page_white_code.png'
|
||||
, '.less': 'page_white_code.png'
|
||||
, '.htm': 'page_white_code.png'
|
||||
, '.html': 'page_white_code.png'
|
||||
, '.xhtml': 'page_white_code.png'
|
||||
, '.lua': 'page_white_code.png'
|
||||
, '.m': 'page_white_code.png'
|
||||
, '.pl': 'page_white_code.png'
|
||||
, '.py': 'page_white_code.png'
|
||||
, '.vb': 'page_white_code.png'
|
||||
, '.vbs': 'page_white_code.png'
|
||||
, '.xml': 'page_white_code.png'
|
||||
, '.yaws': 'page_white_code.png'
|
||||
, '.map': 'map.png'
|
||||
|
||||
, '.app': 'application_xp.png'
|
||||
, '.exe': 'application_xp.png'
|
||||
, '.bat': 'application_xp_terminal.png'
|
||||
, '.cgi': 'application_xp_terminal.png'
|
||||
, '.sh': 'application_xp_terminal.png'
|
||||
|
||||
, '.avi': 'film.png'
|
||||
, '.flv': 'film.png'
|
||||
, '.mv4': 'film.png'
|
||||
, '.mov': 'film.png'
|
||||
, '.mp4': 'film.png'
|
||||
, '.mpeg': 'film.png'
|
||||
, '.mpg': 'film.png'
|
||||
, '.ogv': 'film.png'
|
||||
, '.rm': 'film.png'
|
||||
, '.webm': 'film.png'
|
||||
, '.wmv': 'film.png'
|
||||
, '.fnt': 'font.png'
|
||||
, '.otf': 'font.png'
|
||||
, '.ttf': 'font.png'
|
||||
, '.woff': 'font.png'
|
||||
, '.bmp': 'image.png'
|
||||
, '.gif': 'image.png'
|
||||
, '.ico': 'image.png'
|
||||
, '.jpeg': 'image.png'
|
||||
, '.jpg': 'image.png'
|
||||
, '.png': 'image.png'
|
||||
, '.psd': 'page_white_picture.png'
|
||||
, '.xcf': 'page_white_picture.png'
|
||||
, '.pdf': 'page_white_acrobat.png'
|
||||
, '.swf': 'page_white_flash.png'
|
||||
, '.ai': 'page_white_vector.png'
|
||||
, '.eps': 'page_white_vector.png'
|
||||
, '.ps': 'page_white_vector.png'
|
||||
, '.svg': 'page_white_vector.png'
|
||||
|
||||
, '.ods': 'page_white_excel.png'
|
||||
, '.xls': 'page_white_excel.png'
|
||||
, '.xlsx': 'page_white_excel.png'
|
||||
, '.odp': 'page_white_powerpoint.png'
|
||||
, '.ppt': 'page_white_powerpoint.png'
|
||||
, '.pptx': 'page_white_powerpoint.png'
|
||||
, '.md': 'page_white_text.png'
|
||||
, '.srt': 'page_white_text.png'
|
||||
, '.txt': 'page_white_text.png'
|
||||
, '.doc': 'page_white_word.png'
|
||||
, '.docx': 'page_white_word.png'
|
||||
, '.odt': 'page_white_word.png'
|
||||
, '.rtf': 'page_white_word.png'
|
||||
|
||||
, '.dmg': 'drive.png'
|
||||
, '.iso': 'cd.png'
|
||||
, '.7z': 'box.png'
|
||||
, '.apk': 'box.png'
|
||||
, '.bz2': 'box.png'
|
||||
, '.cab': 'box.png'
|
||||
, '.deb': 'box.png'
|
||||
, '.gz': 'box.png'
|
||||
, '.jar': 'box.png'
|
||||
, '.lz': 'box.png'
|
||||
, '.lzma': 'box.png'
|
||||
, '.msi': 'box.png'
|
||||
, '.pkg': 'box.png'
|
||||
, '.rar': 'box.png'
|
||||
, '.rpm': 'box.png'
|
||||
, '.tar': 'box.png'
|
||||
, '.tbz2': 'box.png'
|
||||
, '.tgz': 'box.png'
|
||||
, '.tlz': 'box.png'
|
||||
, '.xz': 'box.png'
|
||||
, '.zip': 'box.png'
|
||||
|
||||
, '.accdb': 'page_white_database.png'
|
||||
, '.db': 'page_white_database.png'
|
||||
, '.dbf': 'page_white_database.png'
|
||||
, '.mdb': 'page_white_database.png'
|
||||
, '.pdb': 'page_white_database.png'
|
||||
, '.sql': 'page_white_database.png'
|
||||
|
||||
, '.gam': 'controller.png'
|
||||
, '.rom': 'controller.png'
|
||||
, '.sav': 'controller.png'
|
||||
|
||||
, 'folder': 'folder.png'
|
||||
, 'default': 'page_white.png'
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "serve-index",
|
||||
"description": "Serve directory listings",
|
||||
"version": "1.0.0",
|
||||
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/expressjs/serve-index.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/expressjs/serve-index/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"batch": "0.5.0",
|
||||
"negotiator": "0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"connect": "^2.13.0",
|
||||
"mocha": "^1.17.0",
|
||||
"should": "^3.0.0",
|
||||
"supertest": "~0.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha --reporter spec --require should"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<!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
|
||||
, links = $('files').all('a');
|
||||
|
||||
links.each(function(link){
|
||||
var text = link.textContent;
|
||||
|
||||
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>{linked-path}</h1>
|
||||
{files}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 426 B |
|
After Width: | Height: | Size: 507 B |
|
After Width: | Height: | Size: 555 B |
|
After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 666 B |
|
After Width: | Height: | Size: 346 B |
|
After Width: | Height: | Size: 653 B |
|
After Width: | Height: | Size: 634 B |
|
After Width: | Height: | Size: 567 B |
|
After Width: | Height: | Size: 516 B |
|
After Width: | Height: | Size: 804 B |
|
After Width: | Height: | Size: 635 B |
|
After Width: | Height: | Size: 739 B |
|
After Width: | Height: | Size: 794 B |
|
After Width: | Height: | Size: 818 B |
|
After Width: | Height: | Size: 663 B |
|
After Width: | Height: | Size: 740 B |
|
After Width: | Height: | Size: 807 B |
|
After Width: | Height: | Size: 793 B |
|
After Width: | Height: | Size: 817 B |
|
After Width: | Height: | Size: 879 B |
|
After Width: | Height: | Size: 833 B |
|
After Width: | Height: | Size: 779 B |
|
After Width: | Height: | Size: 621 B |
|
After Width: | Height: | Size: 801 B |
|
After Width: | Height: | Size: 839 B |
|
After Width: | Height: | Size: 830 B |
|
After Width: | Height: | Size: 813 B |
|
After Width: | Height: | Size: 703 B |
|
After Width: | Height: | Size: 641 B |
|
After Width: | Height: | Size: 858 B |
|
After Width: | Height: | Size: 774 B |
|
After Width: | Height: | Size: 294 B |
|
After Width: | Height: | Size: 591 B |
|
After Width: | Height: | Size: 664 B |
|
After Width: | Height: | Size: 512 B |
|
After Width: | Height: | Size: 587 B |
|
After Width: | Height: | Size: 656 B |
|
After Width: | Height: | Size: 666 B |
|
After Width: | Height: | Size: 603 B |
|
After Width: | Height: | Size: 587 B |
|
After Width: | Height: | Size: 592 B |
|
After Width: | Height: | Size: 724 B |
|
After Width: | Height: | Size: 309 B |
|
After Width: | Height: | Size: 621 B |
|
After Width: | Height: | Size: 700 B |
|
After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 579 B |
|
After Width: | Height: | Size: 536 B |
|
After Width: | Height: | Size: 638 B |
|
After Width: | Height: | Size: 618 B |
|
After Width: | Height: | Size: 623 B |
|
After Width: | Height: | Size: 663 B |
|
After Width: | Height: | Size: 676 B |
|
After Width: | Height: | Size: 582 B |
|
After Width: | Height: | Size: 639 B |
|
After Width: | Height: | Size: 402 B |
|
After Width: | Height: | Size: 516 B |
|
After Width: | Height: | Size: 612 B |
|
After Width: | Height: | Size: 603 B |
|
After Width: | Height: | Size: 296 B |
|
After Width: | Height: | Size: 616 B |
|
After Width: | Height: | Size: 669 B |
|
After Width: | Height: | Size: 614 B |
|
After Width: | Height: | Size: 554 B |
|
After Width: | Height: | Size: 706 B |
|
After Width: | Height: | Size: 779 B |
|
After Width: | Height: | Size: 688 B |
|
After Width: | Height: | Size: 618 B |
|
After Width: | Height: | Size: 620 B |
|
After Width: | Height: | Size: 538 B |
|
After Width: | Height: | Size: 650 B |
|
After Width: | Height: | Size: 588 B |
|
After Width: | Height: | Size: 523 B |
|
After Width: | Height: | Size: 626 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 565 B |
|
After Width: | Height: | Size: 634 B |
|
After Width: | Height: | Size: 342 B |
|
After Width: | Height: | Size: 315 B |
|
After Width: | Height: | Size: 668 B |
|
After Width: | Height: | Size: 644 B |
|
After Width: | Height: | Size: 702 B |
|
After Width: | Height: | Size: 309 B |
|
After Width: | Height: | Size: 651 B |
|
After Width: | Height: | Size: 734 B |
|
After Width: | Height: | Size: 613 B |
|
After Width: | Height: | Size: 386 B |
|
After Width: | Height: | Size: 777 B |
|
After Width: | Height: | Size: 903 B |
@@ -0,0 +1,257 @@
|
||||
* {
|
||||
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 a {
|
||||
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 a:focus,
|
||||
ul#files li a:hover {
|
||||
background: rgba(255,255,255,0.65);
|
||||
border: 1px solid #ececec;
|
||||
}
|
||||
ul#files li a.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 .name {
|
||||
width: 100%;
|
||||
background-position: 8px 5px;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
/*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 a {
|
||||
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 0;
|
||||
}
|
||||
#files .icon .name {
|
||||
text-indent: 41px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>tobi, loki, jane</p>
|
||||